recoll-1.36.1/0000755000175000017500000000000014521161751010066 500000000000000recoll-1.36.1/qtgui/0000755000175000017500000000000014521161751011217 500000000000000recoll-1.36.1/qtgui/actsearch.ui0000644000175000017500000000153414410615043013431 00000000000000 ActSearchDLG 0 0 314 43 Menu search true 15 QComboBox::NoInsert false recoll-1.36.1/qtgui/multisave.cpp0000644000175000017500000001151014427373216013660 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include using namespace std; #include #include #include #include "recoll.h" #include "multisave.h" #include "smallut.h" #include "log.h" #include "pathut.h" #include "internfile.h" #include "rcldoc.h" const unsigned int maxlen = 200; void multiSave(QWidget *p, vector& docs) { QFileDialog fdialog(p, QWidget::tr("Create or choose save directory")); fdialog.setAcceptMode(QFileDialog::AcceptSave); fdialog.setFileMode(QFileDialog::Directory); fdialog.setOption(QFileDialog::ShowDirsOnly); if (fdialog.exec() == 0) return; QStringList dirl = fdialog.selectedFiles(); if (dirl.size() != 1) { // Can't happen ? QMessageBox::warning(0, "Recoll", QWidget::tr("Choose exactly one directory")); return; } string dir(qs2path(dirl[0])); LOGDEB2("multiSave: got dir " << dir << "\n"); /* Save doc to files in target directory. Issues: - It is quite common to have docs in the array with the same file names, e.g. all messages in a folder have the same file name (the folder's). - There is no warranty that the ipath is going to be acceptable as a file name or interesting at all. We don't use it. - We have to make sure the names don't end up too long. If collisions occur, we add a numeric infix (e.g. somefile.23.pdf). We never overwrite existing files and don't give the user an option to do it (they can just as well save to an empty directory and use the file manager to accomplish whatever they want). We don't try hard to protect against race-conditions though. The existing file names are read before beginning the save sequence, and collisions appearing after this are handled by aborting. There is a window between existence check and creation because idoctofile does not use O_EXCL */ set existingNames; string reason; if (!listdir(dir, reason, existingNames)) { QMessageBox::warning(0, "Recoll", QWidget::tr("Could not read directory: ") + path2qs(reason)); return; } set toBeCreated; vector filenames; for (vector::iterator it = docs.begin(); it != docs.end(); it++) { string utf8fn; it->getmeta(Rcl::Doc::keyfn, &utf8fn); string suffix = path_suffix(utf8fn); LOGDEB("Multisave: [" << (utf8fn) << "] suff [" << (suffix) << "]\n" ); if (suffix.empty() || suffix.size() > 10) { suffix = theconfig->getSuffixFromMimeType(it->mimetype); LOGDEB("Multisave: suff from config [" << (suffix) << "]\n" ); } string simple = path_basename(utf8fn, string(".") + suffix); LOGDEB("Multisave: simple [" << (simple) << "]\n" ); if (simple.empty()) simple = "rclsave"; if (simple.size() > maxlen) { simple = simple.substr(0, maxlen); } for (int vers = 0; ; vers++) { ostringstream ss; ss << simple; if (vers) ss << "." << vers; if (!suffix.empty()) ss << "." << suffix; string fn = qs2path(u8s2qs(ss.str())); if (existingNames.find(fn) == existingNames.end() && toBeCreated.find(fn) == toBeCreated.end()) { toBeCreated.insert(fn); filenames.push_back(fn); break; } } } for (unsigned int i = 0; i != docs.size(); i++) { string fn = path_cat(dir, filenames[i]); if (path_exists(fn)) { QMessageBox::warning(0, "Recoll", QWidget::tr("Unexpected file name collision, " "cancelling.")); return; } // There is still a race condition here, should we care ? TempFile temp;// not used if (!FileInterner::idocToFile(temp, fn, theconfig, docs[i], false)) { QMessageBox::warning( 0, "Recoll", QWidget::tr("Cannot extract document: ") + path2qs(docs[i].url) + " | " + u8s2qs(docs[i].ipath)); } } } recoll-1.36.1/qtgui/images/0000755000175000017500000000000014521161751012464 500000000000000recoll-1.36.1/qtgui/images/prevpage.png0000644000175000017500000000263314410615043014722 00000000000000PNG  IHDRĴl;gAMA7 cHRMz&u0`:pQ<bKGDIDAT8ˍ[lUgn;;v/[ʥ\*"E@҂! HD ꋆ/#JH "P)5`7*mi]vn6;"'3|Gc= 0md3IJ ̃vqW\;yY WQ[ ̒OBf.<Ns9&5<\prvݿY5w;:>gg5yQmyi `Br>r?|q4ÍZs>Xk2Bn -iݕlZ 73Ɍ* %$ #[|X;abpryP;A$YiKC$0[#~+konhʮ;m¥^A&5Pb~gMg= \Lbu!b1s7$$嵊O^cFF!UjΡLbIH 5 B P;-]^@WfRyN/34( DA !9?c3k݆/K)=k>u ͝Y~()811uQ0 TOUU|~,qau kk<6`!vI#t;˪lkJVHAqY}'nkNrAuز&e,gs+%صEԖ\0l,9'b).5#$妙'nǼlTp䲊6yA5Tn9 .+K4FGXVfRRlĘZ~8mL?}Hb4iR;CBWu}QA4#z[`r煡b7zf;JdyߵnEy ƿ1 /.<(!wY߻yٕ[Sf ``肮[IȀ]6) ]UUP:F`y޿|EV]Vxi9`Z`md,,!HP%(q;AtZn*,iEi}?\ҡ+P@*WLEC]{8'Oml7'>|[)e,̣Jkʝ,ل#;osB/QU~c_ȥI BH>kxtl&Hq;gǞ=pOOXiY aɶ,Civ^?nA@ X=%tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00IIENDB`recoll-1.36.1/qtgui/images/nextpage.png0000644000175000017500000000263414410615043014725 00000000000000PNG  IHDRĴl;gAMA7 cHRMz&u0`:pQ<bKGDIDAT8ˍYlTU箳3P,m)Ѕ- `q!A܂ H(11`4 (5(* v.tu:wÐ"A,'9INww*0γ ؗBƿC2Ȫ31D?J<8{;(p"UP^²adt!nu\'*8)ٴ~=;7в.0Mț`0SWw]tϙDLIl=!́@F'`" 5ry w%v k7cvtRS0l A,  *F(fw/W <;9Z5lv,Oܛ|Q"B#aĀWx#&!0G+|".%ľ2 ]gW^k>\*ޘA6tO;n{Pcv,[*vS Lb D|2FgYuJ*t= ,T30x9wKlv"`VoؒI~%tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00IIENDB`recoll-1.36.1/qtgui/images/firstpage.png0000644000175000017500000000275414410615043015101 00000000000000PNG  IHDRj gAMA a cHRMz&u0`:pQ<PLTE2i2i2i2h2h2h2g7e2i2i2i2j2k2i2h2h3feee2i2i2l>wFz4k2g7feee2i2i2h9f>wH{2h8eeee2i2feeeeee2i3l5l8eeee2h2h?wFz4gOb2h2h2gAc2h2i2hAf2jBfCf2gDe2fFc7s6fVby4l5k>ceee2h3eeeeeee5f3hN~4gFceee9e4h4gIceee9e2f=pW=p4iAr4iVmꝷzZ2obu8qن}2oރ#=M< 9O53S`b[C*e+ *T+Q0bKGD} pHYs ,tIME 'tg1IDATc`F&fV6v/ (KLJNIMȦgdfeCąeKJKKKK+*U@ªUe5@PV[VWSؤ^Q[խˠo[_V[[;&N242f01hVOk}ČNac/rP$|%`eh8dT+?ܼ=߉t \[tLyxYi1Gy~Scpآ% ]{sbZ/)/CpPA3^W䙧hkiaZ:0J+SLԚ gdi]ާ3@AYe QJ3^R&=QBa(!dRUbJqAVwo):.]4v12*{ZH R L f6gk}|jR)Vogt]@iSGsVJcgjbJH T綀^ ) DH1n%*$m21ܨF3 顄MB6 WD s+UmK <#uVl4m"2pVwf'aRdϾ:ׄdl0nF ~omH Bi"m⥔OkSGL`gG|o̱a lmpMۄ Ջe;`٤OjzGh;P>ٝͅ&rqMM؂ Q8<dpA '1d*[9zUsꗋSy'b*kT *HRo^M2]hBϓJ%tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00IIENDB`recoll-1.36.1/qtgui/images/table.png0000644000175000017500000000202714410615043014175 00000000000000PNG  IHDRj gAMA|Q cHRMz%u0`:o_FPLTEql|jzhxevdtaq`q]o\n_pbsduk{n}m|pwzmpqdrapao`o`n_n^m]l]l~^n`p_pbpbpbqtؼ׹ԶҶҽmrnrmpnqqopspsptqxryzr{s{t|{eb)tRNS|wbKGDH pHYs  ~tIME 'tg1IDATc`YX98x8xD4ut MLL-,DQml]\]a \<}B{3P  GD2DEC1X;Γ!>!$&1$`ƐE8!+pCn|܂B SKTBU1W WBk]P^W_čMM`SZZ;:{z'L4yTq I))iY9yEE%eEEEU5u 1  Q@%tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00IIENDB`recoll-1.36.1/qtgui/images/recoll.icns0000644000175000017500000002156614410615043014547 00000000000000icns#vis32h쾐㙓뾑⚔𾌎嗐ӵǩӎﮔԍ쾾½{ss斎|tt嗐||Ϭӏ쩏ґ嬖З㙙羕ޞ䘒쾐㙓_ܙ}ۚ~ߗ{z|~ܙĮ¿|z{օ}}~zxy؃{}mkz|nl|~ĝ÷|z{օ}~߮|~ԇخό֛ݙ~|~ܙ}ffh@10LihY226cgfVh@10LihY226cgfhhj@/.MkjY/04eih@@?SZZM??FZYWB@@11/[lmN./@jie40100/R``G..?kje4/0LMIvGML>ije`nZ./4ejihid`mY/04eihYZVR\S?@BWYY220UdeJ00@ihd512M0ZjkM/0@hhd512665WeeM44Bdc`956ccdB44MeeW569`dcggi@0/MjiY115dhgffh@10MihY226cgfs8mkil32 Kޞ嗓ޞ嗓ޞ嗓ޞ嗓ݟ䘔ᛎ蔏󼋐뿐콑쿏뿏켑𽎓ṋ⽛߁ނx{zzy~ۡsuty斑tvuz䘔tvuz嗓tvuz䘔rtsw铎 ¤򼌑뿏켑쿏쿏/迒꽔偗ܠ⚖ߝ斒ޞ嗓ޞ嗓ޞ嗓ޞ嗓ݯ|߬~څݯ|߬~څݯ|߬~څݯ|߬~ځفܯ}ެف؁x~{z~~}ށ݃|}xӈy}|}vۀ{|΍~Ն~ύ|Ն΍}Ն|ы~zׄŔˏѩ}Ҭρ΁lustp|܁ہܩqxwxtެف؁ݩpwvws߬~ځفܩqxwxtݭف؁irpqm}x}||{߃~zҊ{~xق}~΍}Ն~Ό|Ն~ύ|Ն}ό{օ̐҉دڬցՁޯ{}ہځݯ|߬~څݯ|߬~څݯ|߬~څݯ|߬~ځقfhM043329`gffeiL1325dgfhM043329`gffeiL1325dgfhM043329`gffeiL1325dgfhM043329`gffeiL1325dgfhM043329`gffeiL1325dgfhjM-21108bihhglL/103fihMLMLM01-Kmhiijb7.001,Nlhiijg2/042Lhdeef_:34451Mgefc63431Lieffg`923340Mhfgd5231Mjfggha923340Mhfgd52320Jfbccd]81223/Nighe4129:6Vxsttvm@799:6Mb`^;9`a[m]a`_cL79;^`ghbudhgfjK0214ehgfgatcgfeiL1325dgfgatcgfeiL1325dgfef`sbfedhL2436cfeijdxfjihmK-100/2gjiL MHtVJMLML1/Hd`aab[70112-Mjhif30131Mjfggha:23340Mhfgd5231Lieffg`923340Mhfgd5231Lieffg`923340Mhfgd52320Ljfggh`81223/Mighe41253Lgcdde_;45562Mfdeb745dfM26554:^eddcgL3547bedgiM/32219ahggfjL0214ehgfhM043329`gffeiL1325dgfhM043329`gffeiL1325dgfhM043329`gffeiL1325dgfhM043329`gffeiL1325dgfl8mkih32 ӆ菔ӆ菔ӆ菔ӆ菔ӆ菔ӆ菔ӆ菔ӆ菔ӆ膔Ȇӆӆӆӆӆӆӆӆӆӆ膾vӆ菔vӆ菔vӆ菔vӆ菔vӆ菔vӆ菔vӆ菔vӆ菔vӆ膔ӆӆӆӆӆӆӆӆӆӆ膔ӆ菔ӆ菔ӆ菔ӆ菔ӆ菔ӆ菔ӆ菔ӆ菔ӆ膔ÆÆÆÆÆÆÆÆÆنÆÆُÆُÆُÆُÆُÆُÆُÆُÆن vvvvvvvvvنÆÆُÆُÆُÆُÆُÆُÆُÆُÆنÆÆÆÆÆÆÆÆÆنfY3Lf@3fY3Lf@3fY3Mf@3fY3Lf@3fY3Lf@3fY3Mf@3fY3Lf@3fY3Mf@3fY3Mf@3fYS@LYF@Y3@fM3Yf3@fM3Yf3@fL3Yf3@fL3Yf3@fM3Yf3@fL3Yf3@fL3Yf3@fM3Yf3@fL3Yf3LavLff@3ff@3ff@3ff@3ff@3ff@3ff@3ff@3ff@3f@Qb@SY@3@fL3Yf3@fM3Yf3@fM3Yf3@fL3Yf3@fM3Yf3@fM3Yf3@fL3Yf3@fM3Yf3@fM3Yf3fY3Lf@3fY3Mf@3fY3Mf@3fY3Lf@3fY3Mf@3fY3Mf@3fY3Lf@3fY3Mf@3fY3Mf@3fh8mk recoll-1.36.1/qtgui/images/menu.png0000644000175000017500000000123414410615043014051 00000000000000PNG  IHDRaiCCPICC profile(}=H@_SVqXVJЪɥB$Qp-8XupqU?@]%1ݽ;@jfxLdW+Ћ1"1SM&_.ʳ9Hc`sΛ`E,]|Uy׮[a&Q1q I)қlݶ} wwڽm^Y4a8xHMXa0UT5Є5ut Є ML-Є-mlЄ]\=Є=}|c' 0(8$4 y#QE %tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00IIENDB`recoll-1.36.1/qtgui/images/interro.png0000644000175000017500000000067614410615043014600 00000000000000PNG  IHDR:gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME  y>IDATUΡN@Y2dJ@"cjJI-ؒ@Ŏ *%XƂC0(H5%tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00ItEXtSoftwareAdobe ImageReadyqe<IENDB`recoll-1.36.1/qtgui/images/asearch.png0000644000175000017500000000226414410615043014517 00000000000000PNG  IHDRj gAMA a cHRMz&u0`:pQ<=PLTExoeZqHQw@@c_0zM'aYktx---ꩼ֏،ޟս٬͈އLPW...ۀ׌jēEGQv胆󚢸ⅉյ⎚|~ԫLג0̘\W=%ɠW{ߚ3[ũڶmzؓ/`뻡ӳrwՑ0wSrr|}dSίn׮c\AbLZ$ tRNSU> Ձ.sbKGD= pHYs  tIME 'tg1GIDATc`xx_ ¼ D"b %*-#&,'%$&VQU5E11u D"t/D? 0(8$4,茈H0KTtLl\|BbR2)Դ̬<3!¬iEŁ%e@aʪں&Cdd -m@N5.1u/9{z'L4yT1P_N>csΛ`"0!8H/Yl daCÕVYnFyiMlݶ;$a`{JJbD+'>n0dZ{%tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00IIENDB`recoll-1.36.1/qtgui/images/down.png0000644000175000017500000000300414410615043014051 00000000000000PNG  IHDRj gAMA|Q cHRMz%u0`:o_FPLTE-!##Wy PmQo UoVob Q2! O S W \c io-!-@ }34! {72# y<2#)y#xm#yooo!yk$zW PQ UVb4z&ox^% Q2! O S W \c iox xB wDD wD x xDI''2 z!vyU)) "|(&|(({(&y)~#x&{ {u#yq!x!kc rtRNS&#""'!;kfbcmTl !"  5.*#G)+31*sec`dabe!^'bKGDH pHYs  tIME 'tg1mIDATc```C#cS3s&0```` K+k[;{0 ;ϟ œ`@anj >0O!BaaIL „ţ&M>,,4m欩12²q͙57>A&>oY 32sr/YlVYn6lݶ}]ݷCK;~{9{e aK v[Paw:}oU…ժ?yg_Ԩ# kԾ|w4Q?|K6Noߛt1Ä=XI*%tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00IIENDB`recoll-1.36.1/qtgui/images/spell.png0000644000175000017500000000067214410615043014231 00000000000000PNG  IHDR6qgAMA a cHRMz&u0`:pQ<-PLTE!4))XXX mtRNSJbKGDo0O pHYs  #utIME 'tg1cIDATc` t0&  $6-mPm׳ esC KK+8a#C3")jaKCWdfp !VtM"M%tEXtdate:create2019-05-13T15:39:08+02:008vF%tEXtdate:modify2019-05-13T15:39:08+02:00IIENDB`recoll-1.36.1/qtgui/images/close.png0000644000175000017500000000204314410615043014211 00000000000000PNG  IHDRj gAMA7 cHRMz&u0`:pQ<PLTE;;Z;;Z::Y99X88W<<\<<\;;Z66T66T55S<<\<<\55S44R<<\33Q33Q22P22P11O//M..L..L--K,,I++H**G**G**G44R))F))F))F))F11O<<\AA`GGfFFe??_99X>>^ssچNNn\\|MMmXXxԹ崴==]bb쳳rr箮~~ccRRr22P]]}88WkkYYy55SSSsTTt--KJJjWWw阘::Y``ddnnppzzDDcxx}}wwIIi;;Zggee66THHhaaM-&tRNS@@000@@0&ybKGDHtIME 'tg1CIDATc`! ʁ spkhjisq ytt `¼&P`f.$ f糰k+[Q0=89ػ9]==]] ]ALj>~@ "fV w(I0f DEե¬1  kI bXIDATe!@ D0'yX #F 1+*PaԚfq% Ɇš4TS )ƻ^{؆TShf64\_f Ǣ,PS͍YWk oz&8#sc~%tEXtdate:create2019-09-13T14:49:22+02:00%tEXtdate:modify2019-09-04T08:02:25+02:00KߗIENDB`recoll-1.36.1/qtgui/firstidx.ui0000644000175000017500000001253714410615043013335 00000000000000 FirstIdxDialog 0 0 502 503 First indexing setup 0 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> Qt::RichText true Indexing configuration false false This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Indexing schedule false This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Start indexing now Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() FirstIdxDialog accept() 248 254 157 274 buttonBox rejected() FirstIdxDialog reject() 316 260 286 274 runidxPB clicked() FirstIdxDialog accept() 215 400 215 228 recoll-1.36.1/qtgui/searchclause_w.h0000644000175000017500000000277514410615043014306 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef SEARCHCLAUSE_H #define SEARCHCLAUSE_H // A class for entry of a search clause: type (OR/AND/etc.), distance // for PHRASE or NEAR, and text #include #include #include "searchdata.h" class QVBoxLayout; class QHBoxLayout; class QComboBox; class QSpinBox; class QLineEdit; class SearchClauseW : public QWidget { Q_OBJECT public: SearchClauseW(QWidget* parent = 0); Rcl::SearchDataClause *getClause(); void setFromClause(Rcl::SearchDataClauseSimple *cl); void clear(); QComboBox* sTpCMB; QComboBox* fldCMB; QSpinBox* proxSlackSB; QLineEdit* wordsLE; public slots: virtual void tpChange(int); protected slots: virtual void languageChange(); }; #endif // SEARCHCLAUSE_H recoll-1.36.1/qtgui/systray.h0000644000175000017500000000243714410615043013027 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SYSTRAY_H_INCLUDED_ #define _SYSTRAY_H_INCLUDED_ #include #include class RclMain; class RclTrayIcon : public QSystemTrayIcon { Q_OBJECT public: RclTrayIcon(RclMain *mainw, const QIcon& icon, QObject* parent = 0) : QSystemTrayIcon(icon, parent), m_mainw(mainw) { init(); } public slots: void onRestore(); void onActivated(QSystemTrayIcon::ActivationReason reason); private: void init(); RclMain *m_mainw; }; #endif /* _SYSTRAY_H_INCLUDED_ */ recoll-1.36.1/qtgui/specialindex.h0000644000175000017500000000335614410615043013762 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SPECIDX_W_H_INCLUDED_ #define _SPECIDX_W_H_INCLUDED_ #include #include #include "ui_specialindex.h" class QPushButton; class SpecIdxW : public QDialog, public Ui::SpecIdxW { Q_OBJECT public: SpecIdxW(QWidget * parent = 0) : QDialog(parent) { setupUi(this); selPatsLE->setEnabled(false); connect(targBrowsePB, SIGNAL(clicked()), this, SLOT(onTargBrowsePB_clicked())); connect(targLE, SIGNAL(textChanged(const QString&)), this, SLOT(onTargLE_textChanged(const QString&))); connect(diagsBrowsePB, SIGNAL(clicked()), this, SLOT(onDiagsBrowsePB_clicked())); } bool noRetryFailed(); bool eraseFirst(); std::vector selpatterns(); std::string toptarg(); std::string diagsfile(); public slots: void onTargLE_textChanged(const QString&); void onTargBrowsePB_clicked(); void onDiagsBrowsePB_clicked(); }; #endif /* _SPECIDX_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/advshist.h0000644000175000017500000000420114410615043013125 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _ADVSHIST_H_INCLUDED_ #define _ADVSHIST_H_INCLUDED_ #include "autoconfig.h" #include #include "recoll.h" #include #include "searchdata.h" /** Advanced search history. * * We store previous searches using the "dynconf" mechanism, as string * entries under the "advSearchHist" key. The strings are generated by * translating the SearchData structure to XML, which is done by * calling SearchData::asXML(). * When reading, we use a QXmlSimpleReader and QXmlDefaultHandler to * turn the XML back into a SearchData object, which is then passed to * the advanced search object fromSearch() method to rebuild the * window state. * * XML generation is performed by ../rcldb/searchdataxml.cpp. * See xmltosd.h for a schema description */ class AdvSearchHist { public: AdvSearchHist(); ~AdvSearchHist(); AdvSearchHist(const AdvSearchHist&) = delete; AdvSearchHist& operator=(const AdvSearchHist&) = delete; // Add entry bool push(std::shared_ptr); // Get latest. does not change state std::shared_ptr getnewest(); // Cursor std::shared_ptr getolder(); std::shared_ptr getnewer(); void clear(); private: bool read(); int m_current{-1}; std::vector > m_entries; }; #endif // _ADVSHIST_H_INCLUDED_ recoll-1.36.1/qtgui/uiprefs.ui0000644000175000017500000016274214444307651013174 00000000000000 uiPrefsDialogBase 0 0 750 750 Recoll - User Preferences true 0 User interface Choose editor applications Qt::Horizontal Start with simple search mode: false Qt::Horizontal 40 20 1 0 Limit the size of the search history. Use 0 to disable, -1 for unlimited. Maximum size of search history (0: disable, -1: unlimited): false -1 0 Qt::Horizontal 40 20 Start with advanced search dialog open. false Remember sort activation state. false 1 0 Depth of side filter directory tree false 0 99 2 Qt::Horizontal 40 20 Qt::Horizontal Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Document filter choice style: Buttons Panel buttonGroup_2 Toolbar Combobox buttonGroup_2 Menu buttonGroup_2 Hide some user interface elements. Hide: Toolbars false Status bar false Show button instead. Menu bar false Show choice in menu only. Simple search type false Clear/Search buttons false Show system tray icon. false Close to tray instead of exiting. false Generate desktop notifications. false Suppress all beeps. false Show warning when opening temporary file. true Disable Qt autocompletion in search entry. false Start search on completer popup activation. true 1 0 Maximum number of history entries in completer list Number of history entries in completer: false 0 10 Qt::Horizontal 40 20 Displays the total number of occurences of the term in the index Show hit counts in completer popup. false Qt::Horizontal 1 0 Texts over this size will not be highlighted in preview (too slow). Maximum text size highlighted for preview (kilobytes) false 0 5000 100 3000 Qt::Horizontal 40 20 Prefer HTML to plain text for preview. false Make links inside the preview window clickable, and start an external browser when they are clicked. Activate links in preview. false Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Plain text to HTML line style <BR> buttonGroup <PRE> buttonGroup <PRE> + wrap buttonGroup Qt::Horizontal Highlight CSS style for query terms false 50 0 Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... true QComboBox::NoInsert 1 0 Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): false 0.100000000000000 1.000000000000000 Qt::Horizontal 40 20 Application Qt style sheet false Resets the style sheet to default None (default) Uses the default dark mode style sheet Dark mode Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Choose QSS File Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Expanding 20 70 Shortcuts Use F1 to access the manual Qt::Horizontal 40 20 Reset shortcuts defaults Result List 1 0 Number of entries in a result page false 1 9999 8 Qt::Horizontal 40 20 Result list font false Opens a dialog to select the result list font Helvetica-10 Resets the result list font to the system default Reset Qt::Horizontal 40 20 Edit result paragraph format string Edit result page html header insert Date format (strftime(3)) false 30 0 Abstract snippet separator false 30 0 Qt::Horizontal 40 20 User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Snippets window CSS file false Opens a dialog to select the Snippets window CSS style sheet file Choose Resets the Snippets window style Reset Qt::Horizontal 40 20 1 0 Maximum number of snippets displayed in the snippets window false 1 10000000 10 1000 Qt::Horizontal 40 20 Sort snippets by page number (default: by weight). false Display a Snippets link even if the document has no pages (needs restart). false Qt::Vertical 20 40 Result Table Hide result table header. false Show result table row headers. false Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. false To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Do not display metadata when hovering over rows. false Qt::Vertical 20 40 Search parameters If checked, results with the same content under different names will only be shown once. Hide duplicate results. false Stemming language false Qt::Horizontal 40 20 QFrame::HLine QFrame::Sunken A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Automatically add phrase to simple searches 1 0 Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Autophrase term frequency threshold percentage false 0.200000000000000 2.000000000000000 Qt::Horizontal 40 20 QFrame::HLine QFrame::Sunken Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Dynamically build abstracts Do we synthetize an abstract even if the document seemed to have one? Replace abstracts from documents 2 0 Synthetic abstract size (characters) false 1 0 80 100000 10 250 Qt::Horizontal 40 20 1 0 Synthetic abstract context words false 2 20000 4 Qt::Horizontal 40 20 QFrame::HLine QFrame::Sunken 1 0 The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Query language magic file name suffixes. false Enable 30 0 Qt::Horizontal 40 20 QFrame::HLine QFrame::Sunken Add common spelling approximations for rare terms. Automatic spelling approximation. Max spelling distance 0 1 Qt::Horizontal 40 20 1 0 Synonyms file false Enable 30 0 Choose Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Expanding 20 70 External Indexes QAbstractItemView::ExtendedSelection Toggle selected Activate All Deactivate All Set path translations for the selected index or for the main one if no selection exists. Paths translations QFrame::HLine QFrame::Sunken Remove from list. This has no effect on the disk index. Remove selected Qt::Horizontal QSizePolicy::Expanding 16 20 true Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Add index Misc The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Work around Tamil QTBUG-78923 by inserting space before anchor text Qt::Vertical 20 40 Qt::Horizontal QSizePolicy::Expanding 210 20 Apply changes &OK true true Discard changes &Cancel true recoll-1.36.1/qtgui/viewaction_w.h0000644000175000017500000000320614410615043014002 00000000000000/* Copyright (C) 2006-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _VIEWACTION_W_H_INCLUDED_ #define _VIEWACTION_W_H_INCLUDED_ #include #include "ui_viewaction.h" class QDialog; class QMouseEvent; class QTableWidget; class ViewAction : public QDialog, public Ui::ViewActionBase { Q_OBJECT public: ViewAction(QWidget* parent = 0) : QDialog(parent) { setupUi(this); init(); } ~ViewAction() {} ViewAction(const ViewAction&) = delete; ViewAction& operator=(const ViewAction&) = delete; void selectMT(const QString& mt); public slots: virtual void editActions(); virtual void onCurrentItemChanged(QTableWidgetItem *, QTableWidgetItem *); virtual void onUseDesktopCBToggled(int); virtual void onSetExceptCBToggled(int); virtual void onSelSameClicked(); private: virtual void init(); virtual void fillLists(); }; #endif /* _VIEWACTION_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/rclm_idx.cpp0000644000175000017500000004747214477553226013476 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include "safeunistd.h" #include #include #include "execmd.h" #include "log.h" #include "transcode.h" #include "indexer.h" #include "rclmain_w.h" #include "specialindex.h" #include "readfile.h" #include "snippets_w.h" #include "idxstatus.h" #include "conftree.h" using namespace std; static std::string recollindex; // This is called from periodic100 if we started an indexer, or from // the rclmain idxstatus file watcher, every time the file changes. // Returns true if a real time indexer is currently monitoring, false else. bool RclMain::updateIdxStatus() { bool ret{false}; DbIxStatus status; readIdxStatus(theconfig, status); QString msg = tr("Indexing in progress: "); QString phs; switch (status.phase) { case DbIxStatus::DBIXS_NONE:phs=tr("None");break; case DbIxStatus::DBIXS_FILES: phs=tr("Updating");break; case DbIxStatus::DBIXS_FLUSH: phs=tr("Flushing");break; case DbIxStatus::DBIXS_PURGE: phs=tr("Purge");break; case DbIxStatus::DBIXS_STEMDB: phs=tr("Stemdb");break; case DbIxStatus::DBIXS_CLOSING:phs=tr("Closing");break; case DbIxStatus::DBIXS_DONE:phs=tr("Done");break; case DbIxStatus::DBIXS_MONITOR:phs=tr("Monitor"); ret = true;break; default: phs=tr("Unknown");break; } msg += phs + " "; if (status.phase == DbIxStatus::DBIXS_FILES) { QString sdocs = status.docsdone > 1 ?tr("documents") : tr("document"); QString sfiles = status.filesdone > 1 ? tr("files") : tr("file"); QString serrors = status.fileerrors > 1 ? tr("errors") : tr("error"); QString stats; if (status.dbtotdocs > 0) { stats = QString("(%1 ") + sdocs + "/%2 " + sfiles + "/%3 " + serrors + "/%4 " + tr("total files)"); stats = stats.arg(status.docsdone).arg(status.filesdone). arg(status.fileerrors).arg(status.totfiles); } else { stats = QString("(%1 ") + sdocs + "/%2 " + sfiles + "/%3 " + serrors + ") "; stats = stats.arg(status.docsdone).arg(status.filesdone). arg(status.fileerrors); } msg += stats + " "; } string mf;int ecnt = 0; string fcharset = theconfig->getDefCharset(true); // If already UTF-8 let it be, else try to transcode, or url-encode if (!transcode(status.fn, mf, "UTF-8", "UTF-8", &ecnt) || ecnt) { if (!transcode(status.fn, mf, fcharset, "UTF-8", &ecnt) || ecnt) { mf = url_encode(status.fn, 0); } } msg += QString::fromUtf8(mf.c_str()); statusBar()->showMessage(msg, 4000); return ret; } // This is called by a periodic timer to check the status of // indexing, a possible need to exit, and cleanup exited viewers // We don't need thread locking, but we're not reentrant either static bool periodic100busy(false); class Periodic100Guard { public: Periodic100Guard() { periodic100busy = true;} ~Periodic100Guard() { periodic100busy = false;} }; void RclMain::periodic100() { if (periodic100busy) return; Periodic100Guard guard; LOGDEB2("Periodic100\n" ); if (recollindex.empty()) { #ifdef _WIN32 // We are not in the PATH in general. make recollindex a full path recollindex = path_cat(path_thisexecpath(), "recollindex"); #else recollindex = "recollindex"; #endif } if (!m_idxreasontmp || !m_idxreasontmp->ok()) { // We just store the pointer and let the tempfile cleaner deal // with delete on exiting TempFile temp(".txt"); m_idxreasontmp = rememberTempFile(temp); } bool isMonitor{false}; if (m_idxproc) { // An indexing process was launched. If its' done, see status. int status; bool exited = m_idxproc->maybereap(&status); if (exited) { QString reasonmsg; if (m_idxreasontmp && m_idxreasontmp->ok()) { string reasons; file_to_string(m_idxreasontmp->filename(), reasons); if (!reasons.empty()) { ConfSimple rsn(reasons); vector sects = rsn.getNames(""); for (const auto& nm : sects) { string val; if (rsn.get(nm, val)) { reasonmsg.append(u8s2qs(string("
") + nm + " : " + val)); } } } } deleteZ(m_idxproc); if (status) { if (m_idxkilled) { QMessageBox::warning(0, "Recoll", tr("Indexing interrupted")); m_idxkilled = false; } else { QString msg(tr("Indexing failed")); if (!reasonmsg.isEmpty()) { msg.append(tr(" with additional message: ")); msg.append(reasonmsg); } QMessageBox::warning(0, "Recoll", msg); } } else { // On the first run, show missing helpers. We only do this once if (m_firstIndexing) showMissingHelpers(); if (!reasonmsg.isEmpty()) { QString msg = tr("Non-fatal indexing message: "); msg.append(reasonmsg); QMessageBox::warning(0, "Recoll", msg); } } string reason; maybeOpenDb(reason, 1); populateSideFilters(SFUR_INDEXCONTENTS); } else { // update/show status even if the status file did not // change (else the status line goes blank during // lengthy operations). isMonitor = updateIdxStatus(); } } // Update the "start/stop indexing" menu entry, can't be done from // the "start/stop indexing" slot itself IndexerState prevstate = m_indexerState; if (m_idxproc) { m_indexerState = IXST_RUNNINGMINE; fileToggleIndexingAction->setText(tr("Stop &Indexing")); fileToggleIndexingAction->setEnabled(true); fileStartMonitorAction->setEnabled(false); fileBumpIndexingAction->setEnabled(isMonitor); fileRebuildIndexAction->setEnabled(false); actionSpecial_Indexing->setEnabled(false); periodictimer->setInterval(200); } else { Pidfile pidfile(theconfig->getPidfile()); pid_t pid = pidfile.open(); fileBumpIndexingAction->setEnabled(false); if (pid == getpid()) { // Locked by me m_indexerState = IXST_NOTRUNNING; fileToggleIndexingAction->setText(tr("Index locked")); fileToggleIndexingAction->setEnabled(false); fileStartMonitorAction->setEnabled(false); fileBumpIndexingAction->setEnabled(false); fileRebuildIndexAction->setEnabled(false); actionSpecial_Indexing->setEnabled(false); periodictimer->setInterval(1000); } else if (pid == 0) { m_indexerState = IXST_NOTRUNNING; fileToggleIndexingAction->setText(tr("Update &Index")); fileToggleIndexingAction->setEnabled(true); fileStartMonitorAction->setEnabled(true); fileBumpIndexingAction->setEnabled(false); fileRebuildIndexAction->setEnabled(true); actionSpecial_Indexing->setEnabled(true); periodictimer->setInterval(1000); } else { // Real time or externally started batch indexer running m_indexerState = IXST_RUNNINGNOTMINE; fileToggleIndexingAction->setText(tr("Stop &Indexing")); fileToggleIndexingAction->setEnabled(true); DbIxStatus status; readIdxStatus(theconfig, status); if (status.hasmonitor) { // Real-time indexer running. We can trigger an // incremental pass fileBumpIndexingAction->setEnabled(true); } fileStartMonitorAction->setEnabled(false); fileRebuildIndexAction->setEnabled(false); actionSpecial_Indexing->setEnabled(false); periodictimer->setInterval(200); } } if ((prevstate == IXST_RUNNINGMINE || prevstate == IXST_RUNNINGNOTMINE) && m_indexerState == IXST_NOTRUNNING) { showTrayMessage(tr("Indexing done")); } // Possibly cleanup the dead viewers for (auto it = m_viewers.begin(); it != m_viewers.end(); ) { int status; if ((*it)->maybereap(&status)) { delete *it; it = m_viewers.erase(it); } else { it++; } } } bool RclMain::checkIdxPaths() { string badpaths; vector args{recollindex, "-c", theconfig->getConfDir(), "-E"}; ExecCmd::backtick(args, badpaths); if (!badpaths.empty()) { int rep = QMessageBox::warning( 0, tr("Bad paths"), tr("Empty or non-existant paths in configuration file. " "Click Ok to start indexing anyway " "(absent data will not be purged from the index):\n") + path2qs(badpaths), QMessageBox::Ok | QMessageBox::Cancel); if (rep == QMessageBox::Cancel) return false; } return true; } // This gets called when the "Update index/Stop indexing" action is // activated. It executes the requested action. The menu // entry will be updated by the indexing status check void RclMain::toggleIndexing() { switch (m_indexerState) { case IXST_RUNNINGMINE: if (m_idxproc) { // Indexing was in progress, request stop. Let the periodic // routine check for the results. if (m_idxproc->requestChildExit()) { m_idxkilled = true; } } break; case IXST_RUNNINGNOTMINE: { #ifdef _WIN32 // No simple way to signal the process. Use the stop file std::fstream ost; if (!path_streamopen(theconfig->getIdxStopFile(), std::fstream::out, ost)) { LOGSYSERR("toggleIndexing", "path_streamopen", theconfig->getIdxStopFile()); } #else Pidfile pidfile(theconfig->getPidfile()); pid_t pid = pidfile.open(); if (pid > 0) kill(pid, SIGTERM); #endif // !_WIN32 } break; case IXST_NOTRUNNING: { // Could also mean that no helpers are missing, but then we won't try to show a message // anyway (which is what firstIndexing is used for) string mhd; m_firstIndexing = !theconfig->getMissingHelperDesc(mhd); if (!checkIdxPaths()) { return; } vector args{"-c", theconfig->getConfDir()}; if (m_idxreasontmp && m_idxreasontmp->ok()) { args.push_back("-R"); args.push_back(m_idxreasontmp->filename()); } m_idxproc = new ExecCmd; m_idxproc->startExec(recollindex, args, false, false); } break; case IXST_UNKNOWN: return; } } void RclMain::startMonitor() { DbIxStatus status; readIdxStatus(theconfig, status); if (nullptr == m_idxproc && m_indexerState == IXST_NOTRUNNING) { if (!checkIdxPaths()) { return; } vector args{"-c", theconfig->getConfDir()}; if (m_idxreasontmp && m_idxreasontmp->ok()) { args.push_back("-R"); args.push_back(m_idxreasontmp->filename()); } args.push_back("-mw"); args.push_back("0"); m_idxproc = new ExecCmd; m_idxproc->startExec(recollindex, args, false, false); } } void RclMain::bumpIndexing() { DbIxStatus status; readIdxStatus(theconfig, status); if (status.hasmonitor) { path_utimes(path_cat(theconfig->getConfDir(), "recoll.conf"), nullptr); } } static void delay(int millisecondsWait) { QEventLoop loop; QTimer t; t.connect(&t, SIGNAL(timeout()), &loop, SLOT(quit())); t.start(millisecondsWait); loop.exec(); } void RclMain::rebuildIndex() { if (!m_idxreasontmp || !m_idxreasontmp->ok()) { // We just store the pointer and let the tempfile cleaner deal // with delete on exiting TempFile temp(".txt"); m_idxreasontmp = rememberTempFile(temp); } if (m_indexerState == IXST_UNKNOWN) { delay(1500); } switch (m_indexerState) { case IXST_UNKNOWN: case IXST_RUNNINGMINE: case IXST_RUNNINGNOTMINE: return; //?? Should not have been called case IXST_NOTRUNNING: { if (m_idxproc) { LOGERR("RclMain::rebuildIndex: current indexer exec not null\n" ); return; } int rep = QMessageBox::Ok; if (path_exists(theconfig->getDbDir())) { rep = QMessageBox::warning( 0, tr("Erasing index"), tr("Reset the index and start from scratch ?"), QMessageBox::Ok | QMessageBox::Cancel); } if (rep == QMessageBox::Ok) { #ifdef _WIN32 // Under windows, it is necessary to close the db here, // else Xapian won't be able to do what it wants with the // (open) files. Of course if there are several GUI // instances, this won't work... Also it's quite difficult // to make sure that there are no more references to the // db because, for example of the Enquire objects inside // Query inside Docsource etc. // // !! At this moment, this does not work if a preview has // !! been opened. Could not find the reason (mysterious // !! Xapian::Database reference somewhere?). The indexing // !! fails, leaving a partial index directory. Then need // !! to restart the GUI to succeed in reindexing. if (rcldb) { resetSearch(); deleteZ(m_snippets); rcldb->close(); } #endif // _WIN32 // Could also mean that no helpers are missing, but then we // won't try to show a message anyway (which is what // firstIndexing is used for) string mhd; m_firstIndexing = !theconfig->getMissingHelperDesc(mhd); if (!checkIdxPaths()) { return; } vector args{"-c", theconfig->getConfDir(), "-z"}; if (m_idxreasontmp && m_idxreasontmp->ok()) { args.push_back("-R"); args.push_back(m_idxreasontmp->filename()); } m_idxproc = new ExecCmd; m_idxproc->startExec(recollindex, args, false, false); } } break; } } void SpecIdxW::onTargBrowsePB_clicked() { QString dir = myGetFileName(true, tr("Top indexed entity"), true); targLE->setText(dir); } void SpecIdxW::onDiagsBrowsePB_clicked() { QString fn = myGetFileName(false, tr("Diagnostics file")); diagsLE->setText(fn); } bool SpecIdxW::noRetryFailed() { return !retryFailedCB->isChecked(); } bool SpecIdxW::eraseFirst() { return eraseBeforeCB->isChecked(); } std::vector SpecIdxW::selpatterns() { vector pats; string text = qs2utf8s(selPatsLE->text()); if (!text.empty()) { stringToStrings(text, pats); } return pats; } std::string SpecIdxW::toptarg() { return qs2utf8s(targLE->text()); } std::string SpecIdxW::diagsfile() { return qs2utf8s(diagsLE->text()); } void SpecIdxW::onTargLE_textChanged(const QString& text) { if (text.isEmpty()) selPatsLE->setEnabled(false); else selPatsLE->setEnabled(true); } static string execToString(const string& cmd, const vector& args) { string command = cmd + " "; for (vector::const_iterator it = args.begin(); it != args.end(); it++) { command += "{" + *it + "} "; } return command; } void RclMain::specialIndex() { LOGDEB("RclMain::specialIndex\n" ); if (!m_idxreasontmp || !m_idxreasontmp->ok()) { // We just store the pointer and let the tempfile cleaner deal // with delete on exiting TempFile temp(".txt"); m_idxreasontmp = rememberTempFile(temp); } switch (m_indexerState) { case IXST_UNKNOWN: case IXST_RUNNINGMINE: case IXST_RUNNINGNOTMINE: return; //?? Should not have been called case IXST_NOTRUNNING: default: break; } if (m_idxproc) { LOGERR("RclMain::rebuildIndex: current indexer exec not null\n" ); return; } if (!specidx) // ?? return; vector args{"-c", theconfig->getConfDir()}; if (m_idxreasontmp && m_idxreasontmp->ok()) { args.push_back("-R"); args.push_back(m_idxreasontmp->filename()); } string top = specidx->toptarg(); if (!top.empty()) { args.push_back("-r"); } string diagsfile = specidx->diagsfile(); if (!diagsfile.empty()) { args.push_back("--diagsfile"); args.push_back(diagsfile); } if (specidx->eraseFirst()) { if (top.empty()) { args.push_back("-Z"); } else { args.push_back("-e"); // -e also needs -i, else we don't reindex, just erase args.push_back("-i"); } } if (!specidx->noRetryFailed()) { args.push_back("-k"); } else { args.push_back("-K"); } vector selpats = specidx->selpatterns(); if (!selpats.empty() && top.empty()) { QMessageBox::warning(0, tr("Selection patterns need topdir"), tr("Selection patterns can only be used with a " "start directory"), QMessageBox::Ok, QMessageBox::NoButton); return; } for (vector::const_iterator it = selpats.begin(); it != selpats.end(); it++) { args.push_back("-p"); args.push_back(*it); } if (!top.empty()) { args.push_back(top); } m_idxproc = new ExecCmd; LOGINFO("specialIndex: exec: " << execToString(recollindex, args) <startExec(recollindex, args, false, false); } void RclMain::updateIdxForDocs(vector& docs) { if (m_idxproc) { QMessageBox::warning(0, tr("Warning"), tr("Can't update index: indexer running"), QMessageBox::Ok, QMessageBox::NoButton); return; } vector paths; if (Rcl::docsToPaths(docs, paths)) { vector args{"-c", theconfig->getConfDir(), "-i",}; if (m_idxreasontmp && m_idxreasontmp->ok()) { args.push_back("-R"); args.push_back(m_idxreasontmp->filename()); } args.insert(args.end(), paths.begin(), paths.end()); m_idxproc = new ExecCmd; m_idxproc->startExec(recollindex, args, false, false); // Call periodic100 to update the menu entries states periodic100(); } else { QMessageBox::warning(0, tr("Warning"), tr("Can't update index: internal error"), QMessageBox::Ok, QMessageBox::NoButton); return; } } recoll-1.36.1/qtgui/ssearch_w.h0000644000175000017500000001022314427373216013272 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SSEARCH_W_H_INCLUDED_ #define _SSEARCH_W_H_INCLUDED_ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include "recoll.h" #include "searchdata.h" #include "ui_ssearchb.h" struct SSearchDef; class SSearch; class QCompleter; class QTimer; class QShortcut; class RclCompleterModel : public QAbstractTableModel { Q_OBJECT public: RclCompleterModel(SSearch *parent = 0) : QAbstractTableModel((QWidget*)parent), m_parent(parent) { init(); } int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; public slots: virtual void onPartialWord(int, const QString&, const QString&); private: void init(); std::vector> currentlist; int firstfromindex; QPixmap clockPixmap; QPixmap interroPixmap; SSearch *m_parent{nullptr}; }; class SSearch : public QWidget, public Ui::SSearchBase { Q_OBJECT public: // The values MUST NOT change, there are assumptions about them in // different parts of the code enum SSearchType {SST_ANY = 0, SST_ALL = 1, SST_FNM = 2, SST_LANG = 3}; SSearch(QWidget* parent = 0, const char * = 0) : QWidget(parent) { setupUi(this); init(); } virtual void init(); virtual void setAnyTermMode(); virtual bool hasSearchString(); // Return last performed search as XML text. virtual std::string asXML(); // Restore ssearch UI from saved search virtual bool fromXML(const SSearchDef& fxml); virtual QString currentText(); virtual bool eventFilter(QObject *target, QEvent *event); virtual void setupButtons(); public slots: virtual void onSearchTypeChanged(int); virtual void setSearchString(const QString& text); virtual void startSimpleSearch(); virtual void addTerm(QString); virtual void onWordReplace(const QString&, const QString&); virtual void takeFocus(); // Forget current entry and any state (history) virtual void clearAll(); virtual void setPrefs(); virtual void onNewShortcuts(); private slots: virtual void searchTextChanged(const QString&); virtual void searchTextEdited(const QString&); virtual void onCompletionActivated(const QString&); virtual void restoreText(); virtual void onHistoryClicked(); virtual void onCompleterShown(); signals: void startSearch(std::shared_ptr, bool); void setDescription(QString); void clearSearch(); void partialWord(int, const QString& text, const QString &partial); void ssearchTypeChanged(int typ); private: int getPartialWord(QString& word); bool startSimpleSearch(const std::string& q, int maxexp = -1); bool checkExtIndexes(const std::vector& dbs); RclCompleterModel *m_completermodel{nullptr}; QCompleter *m_completer{nullptr}; QShortcut *m_histsc{nullptr}; /* We save multiword entries because the completer replaces them with the completion */ QString m_savedEditText; /* Saved xml version of the search, as we start it */ std::string m_xml; }; #endif /* _SSEARCH_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/viewaction_w.cpp0000644000175000017500000001675214427373216014362 00000000000000/* Copyright (C) 2006-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "viewaction_w.h" #include #include #include #include #include "recoll.h" #include "log.h" #include "guiutils.h" #include "smallut.h" #include "pathut.h" using namespace std; void ViewAction::init() { selSamePB->setEnabled(false); connect(closePB, SIGNAL(clicked()), this, SLOT(close())); connect(chgActPB, SIGNAL(clicked()), this, SLOT(editActions())); connect(actionsLV, SIGNAL(currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)), this, SLOT(onCurrentItemChanged(QTableWidgetItem *, QTableWidgetItem *))); useDesktopCB->setChecked(prefs.useDesktopOpen); onUseDesktopCBToggled(prefs.useDesktopOpen); connect(useDesktopCB, SIGNAL(stateChanged(int)), this, SLOT(onUseDesktopCBToggled(int))); connect(setExceptCB, SIGNAL(stateChanged(int)), this, SLOT(onSetExceptCBToggled(int))); connect(selSamePB, SIGNAL(clicked()), this, SLOT(onSelSameClicked())); resize(QSize(640, 480).expandedTo(minimumSizeHint())); } void ViewAction::onUseDesktopCBToggled(int onoff) { prefs.useDesktopOpen = onoff != 0; fillLists(); setExceptCB->setEnabled(prefs.useDesktopOpen); } void ViewAction::onSetExceptCBToggled(int onoff) { newActionLE->setEnabled(onoff != 0); } void ViewAction::fillLists() { currentLBL->clear(); actionsLV->clear(); actionsLV->verticalHeader()->setDefaultSectionSize(20); vector > defs; theconfig->getMimeViewerDefs(defs); actionsLV->setRowCount(defs.size()); set viewerXs; if (prefs.useDesktopOpen) { viewerXs = theconfig->getMimeViewerAllEx(); } int row = 0; for (const auto& def : defs) { actionsLV->setItem(row, 0, new QTableWidgetItem(u8s2qs(def.first))); if (!prefs.useDesktopOpen || viewerXs.find(def.first) != viewerXs.end()) { actionsLV->setItem(row, 1, new QTableWidgetItem(u8s2qs(def.second))); } else { actionsLV->setItem(row, 1, new QTableWidgetItem(tr("Desktop Default"))); } row++; } QStringList labels(tr("MIME type")); labels.push_back(tr("Command")); actionsLV->setHorizontalHeaderLabels(labels); } void ViewAction::selectMT(const QString& mt) { actionsLV->clearSelection(); QListitems = actionsLV->findItems(mt, Qt::MatchFixedString|Qt::MatchCaseSensitive); for (QList::iterator it = items.begin(); it != items.end(); it++) { (*it)->setSelected(true); actionsLV->setCurrentItem(*it, QItemSelectionModel::Columns); } } void ViewAction::onSelSameClicked() { actionsLV->clearSelection(); QString value = currentLBL->text(); if (value.isEmpty()) return; string action = qs2utf8s(value); LOGDEB1("ViewAction::onSelSameClicked: value: " << action << endl); vector > defs; theconfig->getMimeViewerDefs(defs); for (const auto& def : defs) { if (def.second == action) { QListitems = actionsLV->findItems( u8s2qs(def.first), Qt::MatchFixedString|Qt::MatchCaseSensitive); for (QList::iterator it = items.begin(); it != items.end(); it++) { (*it)->setSelected(true); actionsLV->item((*it)->row(), 1)->setSelected(true); } } } } void ViewAction::onCurrentItemChanged(QTableWidgetItem *item, QTableWidgetItem *) { currentLBL->clear(); selSamePB->setEnabled(false); if (nullptr == item) { return; } QTableWidgetItem *item0 = actionsLV->item(item->row(), 0); string mtype = qs2utf8s(item0->text()); vector > defs; theconfig->getMimeViewerDefs(defs); for (const auto& def : defs) { if (def.first == mtype) { currentLBL->setText(u8s2qs(def.second)); selSamePB->setEnabled(true); return; } } } void ViewAction::editActions() { QString action0; bool except0 = false; set viewerXs = theconfig->getMimeViewerAllEx(); vector mtypes; bool dowarnmultiple = true; for (int row = 0; row < actionsLV->rowCount(); row++) { QTableWidgetItem *item0 = actionsLV->item(row, 0); if (!item0->isSelected()) continue; string mtype = qs2utf8s(item0->text()); mtypes.push_back(mtype); QTableWidgetItem *item1 = actionsLV->item(row, 1); QString action = item1->text(); bool except = viewerXs.find(mtype) != viewerXs.end(); if (action0.isEmpty()) { action0 = action; except0 = except; } else { if ((action != action0 || except != except0) && dowarnmultiple) { switch (QMessageBox::warning( 0, "Recoll", tr("Changing entries with different current values"), QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Cancel)) { case QMessageBox::Ignore: dowarnmultiple = false; break; case QMessageBox::Cancel: default: return; } } } } if (action0.isEmpty()) return; string sact = qs2utf8s(newActionLE->text()); if (!sact.empty()) { trimstring(sact); #ifdef _WIN32 path_slashize(sact); #endif } for (const auto& entry : mtypes) { auto xit = viewerXs.find(entry); if (setExceptCB->isChecked()) { if (xit == viewerXs.end()) { viewerXs.insert(entry); } } else { if (xit != viewerXs.end()) { viewerXs.erase(xit); } } // An empty action will restore the default (erase from // topmost conftree). IF there is no default in the system // files, and the entry only came from the user file, it will // be erased and the mime will go away from the list, which is // not what we want ! This not trivial to test because // rclconfig has no way to tell us if the value comes from the // system config or the user file. So just check if the value // disappears after setting it, and restore it if it does. string oldvalue = theconfig->getMimeViewerDef(entry, "", 0); theconfig->setMimeViewerDef(entry, sact); string newvalue = theconfig->getMimeViewerDef(entry, "", 0); if (!oldvalue.empty() && newvalue.empty()) { theconfig->setMimeViewerDef(entry, oldvalue); } } theconfig->setMimeViewerAllEx(viewerXs); fillLists(); } recoll-1.36.1/qtgui/ssearchb.ui0000644000175000017500000000664714410615043013300 00000000000000 SSearchBase 0 0 593 48 SSearchBase 2 4 4 4 4 3 false Erase search entry Clear false Start query Search Qt::TabFocus Choose search type. 8 0 155 0 true Show query history :/images/clock.png true Main menu :/images/menu.png recoll-1.36.1/qtgui/ptrans_w.cpp0000644000175000017500000000773614516754001013515 00000000000000/* Copyright (C) 2006-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include using namespace std; #include #include #include #include #include #include #include "recoll.h" #include "log.h" #include "guiutils.h" #include "conftree.h" #include "ptrans_w.h" void EditTrans::init(const string& dbdir) { m_dbdir = path_canon(dbdir); connect(transTW, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this, SLOT(onItemDoubleClicked(QTableWidgetItem *))); connect(cancelPB, SIGNAL(clicked()), this, SLOT(close())); QString lab = whatIdxLA->text(); lab.append(path2qs(m_dbdir)); whatIdxLA->setText(lab); QStringList labels(tr("Path in index")); labels.push_back(tr("Translated path")); transTW->setHorizontalHeaderLabels(labels); ConfSimple *conftrans = theconfig->getPTrans(); if (!conftrans) return; int row = 0; vector opaths = conftrans->getNames(m_dbdir); for (const auto& opath : opaths) { transTW->setRowCount(row+1); transTW->setItem(row, 0, new QTableWidgetItem(path2qs(opath))); string npath; conftrans->get(opath, npath, m_dbdir); transTW->setItem(row, 1, new QTableWidgetItem(path2qs(npath))); row++; } resize(QSize(640, 300).expandedTo(minimumSizeHint())); } void EditTrans::onItemDoubleClicked(QTableWidgetItem *item) { transTW->editItem(item); } void EditTrans::on_savePB_clicked() { ConfSimple *conftrans = theconfig->getPTrans(); if (!conftrans) { QMessageBox::warning(0, "Recoll", tr("Config error")); return; } conftrans->holdWrites(true); conftrans->eraseKey(m_dbdir); for (int row = 0; row < transTW->rowCount(); row++) { QTableWidgetItem *item0 = transTW->item(row, 0); string from = qs2path(item0->text()); QTableWidgetItem *item1 = transTW->item(row, 1); string to = qs2path(item1->text()); conftrans->set(from, to, m_dbdir); } conftrans->holdWrites(false); // The rcldb does not use the same configuration object, but a // copy. Force a reopen, this is quick. string reason; maybeOpenDb(reason, true); close(); } void EditTrans::on_addPB_clicked() { transTW->setRowCount(transTW->rowCount()+1); int row = transTW->rowCount()-1; transTW->setItem(row, 0, new QTableWidgetItem(tr("Original path"))); transTW->setItem(row, 1, new QTableWidgetItem(tr("Local path"))); transTW->editItem(transTW->item(row, 0)); } void EditTrans::on_delPB_clicked() { QModelIndexList indexes = transTW->selectionModel()->selectedIndexes(); vector rows; for (int i = 0; i < indexes.size(); i++) { rows.push_back(indexes.at(i).row()); } sort(rows.begin(), rows.end()); rows.resize(unique(rows.begin(), rows.end()) - rows.begin()); for (int i = rows.size()-1; i >= 0; i--) { transTW->removeRow(rows[i]); } } void EditTrans::on_transTW_itemSelectionChanged() { QModelIndexList indexes = transTW->selectionModel()->selectedIndexes(); if(indexes.size() < 1) delPB->setEnabled(0); else delPB->setEnabled(1); } recoll-1.36.1/qtgui/snippets.ui0000644000175000017500000001012614410615043013336 00000000000000 Snippets 0 0 640 400 Snippets 1 0 QFrame::StyledPanel QFrame::Raised 1 1 0 0 X Qt::ToolButtonTextOnly Find: 0 0 0 0 Next 0 0 Prev Qt::Horizontal QDialogButtonBox::Close searchClosePB clicked() searchFM hide() 33 414 328 414 recoll-1.36.1/qtgui/i18n/0000755000175000017500000000000014521161751011776 500000000000000recoll-1.36.1/qtgui/i18n/recoll_pl.ts0000644000175000017500000070443114444307651014257 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Każdy warunek Any clause Któryś warunek texts teksty spreadsheets arkusze kalkulacyjne presentations prezentacje media multimedia messages wiadomości other pozostałe Bad multiplier suffix in size filter Błędna jednostka we filtrze rozmiaru text tekstowe spreadsheet arkusze presentation prezentacje message wiadomości Advanced Search Zaawansowane szukanie History Next Następna historia History Prev Poprzednia historia Load next stored search Załaduj następne wyszukiwanie Load previous stored search Załaduj poprzednie wyszukiwanie AdvSearchBase Advanced search Dokładne szukanie Restrict file types Określ typ pliku Save as default Zapamiętaj wybrane typy Searched file types Przeszukaj plik All ----> Wszystkie ----> Sel -----> Zaznaczone -----> <----- Sel <----- Zaznaczone <----- All <----- Wszystkie Ignored file types Pomiń pliki Enter top directory for search Podaj szczyt katalogu szukania Browse Przeglądaj Restrict results to files in subtree: Tylko pliki LEŻĄCE W katalogu: Start Search Szukaj Search for <br>documents<br>satisfying: Wyszukaj <br>dokumentów<br>spełniających następujące warunki: Delete clause Usuń warunek Add clause Dodaj warunek Check this to enable filtering on file types Zaznacz, by określić typ pliku By categories Jako kategoria Check this to use file categories instead of raw mime types Zaznacz, by użyć kategorii Close Zamknij All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Wszystkie niepuste pola po prawej stronie będą połączone z A ("Wszystkie klauzule" wybór) lub OR ("Każda klauzula" wybór). <br>"Jakiekolwiek" "Wszystkie" i "Żadne typy pól" nie mogą zaakceptować kombinacji prostych słów, oraz wyrazy zawarte w kwotach podwójnych.<br>Pola bez danych są ignorowane. Invert LEŻĄCE POZA Minimum size. You can use k/K,m/M,g/G as multipliers Dopuszczalne jednostki: k/K, m/M, g/G Min. Size Większy od: Maximum size. You can use k/K,m/M,g/G as multipliers Dopuszczalne jednostki: k/K, m/M, g/G Max. Size Mniejszy od: Select Wybierz Filter Filtry From Po: To Przed: Check this to enable filtering on dates Zaznacz, by określić datę Filter dates Po dacie Find Znajdź Check this to enable filtering on sizes Zaznacz, by określić rozmiar Filter sizes Po rozmiarze Filter birth dates ConfIndexW Can't write configuration file Nie można pisać w pliku konfiguracji Global parameters Parametry globalne Local parameters Parametry lokalne Search parameters Parametry szukania Top directories Szczytowe katalogi The list of directories where recursive indexing starts. Default: your home. Lista katalogów rekursywnego indeksowania. Domyślnie: Twój katalog domowy. Skipped paths Wykluczone ścieżki These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Są to nazwy ścieżek katalogów, których indeksowanie nie wprowadzi.<br>Elementy ścieżki mogą zawierać karty wieloznaczne. Wpisy muszą odpowiadać ścieżkom obserwowanym przez indeksatora (np. jeśli topdirs zawiera '/home/me' i '/home' jest w rzeczywistości linkiem do '/usr/home', poprawny wpis ścieżki pominiętej będzie '/home/me/tmp*', nie '/usr/home/me/tmp*') Stemming languages Reguły ciosania (ang. stemming languages) The languages for which stemming expansion<br>dictionaries will be built. Języki, dla których zostaną zbudowane dodatkowe<br>słowniki. Log file name Nazwa pliku dziennika (logs) The file where the messages will be written.<br>Use 'stderr' for terminal output Plik w którym będą zapisywane wiadomości.<br>Użyj 'stderr' dla terminali wyjściowych Log verbosity level Poziom stężenia komunikatu This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Ta wartość dostosowuje ilość wiadomości,<br>od tylko błędów do wielu danych debugowania. Index flush megabytes interval Interwał (megabajty) opróżniania indeksowania This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Ta wartość dostosowuje ilość danych, które są indeksowane między spłukiwaniami na dysku.<br>Pomaga to kontrolować użycie pamięci indeksatora. Domyślnie 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Jest to procent wykorzystania dysku - całkowite użycie dysku, a nie rozmiar indeksu - przy którym indeksowanie nie będzie udane i zatrzymane.<br>The default value of 0 removes any limit. No aspell usage Brak użycia Aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Wyłącza używanie aspektu do generowania przybliżenia pisowni w narzędziu eksploratora terminu.<br> Przydatne jeśli aspekt jest nieobecny lub nie działa. Aspell language Język Aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Język słownika aspektu powinien wyglądać jak 'en' lub 'fr' . .<br>Jeśli ta wartość nie jest ustawiona, środowisko NLS zostanie użyte do jej obliczenia, co zwykle działa. Aby dowiedzieć się, co jest zainstalowane w systemie, wpisz 'aspell config' i szukaj . w plikach w katalogu 'data-dir'. Database directory name Nazwa katalogu bazy danych The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Nazwa katalogu, w którym ma być przechowywany indeks<br>Ścieżka niebezwzględna jest przyjmowana w stosunku do katalogu konfiguracyjnego. Domyślnym jest 'xapiandb'. Unac exceptions Wyjątki niezwiązane <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Są to wyjątki od mechanizmu unac, który domyślnie usuwa wszystkie diakrytyki i wykonuje dekompozycję kanoniczną. Możesz nadpisać nieakcentowanie niektórych znaków, w zależności od języka i określić dodatkowe rozkłady znaków. . dla ligatur. W każdym oddzielonym spacją wpis pierwszy znak jest jednym źródłowym, a reszta jest tłumaczeniem. Process the WEB history queue Przejdź do kolejki historii web Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Włącza indeksowanie odwiedzonych stron Firefoksu.<br>(musisz również zainstalować wtyczkę Firefoksa Recoll) Web page store directory name Nazwa katalogu dla trzymania stron web The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Nazwa katalogu, w którym można przechowywać kopie odwiedzanych stron internetowych.<br>Ścieżka nie jest bezwzględna w stosunku do katalogu konfiguracyjnego. Max. size for the web store (MB) Maks. rozmiar dla schowka webowego (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Wpisy zostaną poddane recyklingowi po osiągnięciu rozmiaru.<br>Tylko zwiększenie rozmiaru ma sens, ponieważ zmniejszenie wartości nie spowoduje obcięcia istniejącego pliku (tylko miejsce na odpadach). Automatic diacritics sensitivity Automatyczna czułość na diakrytyki <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatyczne wyzwalanie wrażliwości diakrytycznej jeśli wyszukiwane hasło ma znaki akcentowane (nie w unac_except_trans). W innym przypadku musisz użyć języka zapytania i modyfikatora <i>D</i> , aby określić czułość diakrytyczną. Automatic character case sensitivity Automatyczna czułość wielkości znaków <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatyczne wyzwalanie czułości liter, jeśli wpis ma wielkie litery w jednej z pozycji oprócz pierwszej pozycji. W innym przypadku musisz użyć języka zapytania i modyfikatora <i>C</i> , aby określić wielkość liter. Maximum term expansion count Maksymalna liczba rozszerzeń terminu <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximum expansion count for a single term (np.: when use wildcards). Wartość domyślna 10 000 jest rozsądna i pozwoli uniknąć zapytań, które pojawiają się w stanie zamrożonym, gdy silnik porusza się na liście terminów. Maximum Xapian clauses count Maksymalna liczba klauzuli Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Struktura danych sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sdd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sdd W niektórych przypadkach wynik czasowej ekspansji może być mnożnikowy i chcemy uniknąć wykorzystywania nadmiernej pamięci. Domyślna wartość 100 000 powinna być w większości przypadków wystarczająco wysoka i kompatybilna z aktualnymi typowymi konfiguracjami sprzętu. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... Języki, dla których będą budowane niestandardowe słowniki.<br>Zobacz dokumentację Xapian stemmer, aby uzyskać możliwe wartości. Np. english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Językiem słownika aspektu są 2-literowe kody językowe, np. 'en', 'fr' . .<br>Jeśli ta wartość nie jest ustawiona, środowisko NLS zostanie użyte do jej obliczenia, co zwykle działa. Aby dowiedzieć się, co jest zainstalowane w systemie, wpisz 'aspell config' i szukaj . w plikach w katalogu 'data-dir'. Indexer log file name Nazwa pliku dziennika indeksu If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Jeśli puste, użyta zostanie powyższa nazwa pliku dziennika. Użyteczne może być posiadanie osobnego dziennika do celów diagnostycznych, ponieważ wspólny dziennik zostanie usunięty po uruchomieniu<br>interfejsu graficznego. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Procent pełnego progu dysku, przy którym przestajemy indeksować<br>Np. 90% aby zatrzymać się na 90% pełnym, 0 lub 100 oznacza brak limitu) Web history Historia sieci Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Tylko typy mime An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Lista ekskluzywnych typów indeksowanych mime.<br>Nic innego nie zostanie zindeksowane. Zwykle puste i nieaktywne Exclude mime types Wyklucz typy mime Mime types not to be indexed Typy Mime nie mają być indeksowane Max. compressed file size (KB) Maks. rozmiar skompresowanego pliku (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Wartość progowa od której skompresowane pliki przestają być przetwarzane. Brak limitu to -1, 0 wyłącza przetwarzanie plików skompresowanych. Max. text file size (MB) Maks. rozmiar plików tekstowych (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Wartość progowa po której pliki tekstowe przestają być przetwarzane. Brak limitu to -1. Używaj do wykluczenia gigantycznych plików dziennika systemowego (logs). Text file page size (KB) Rozmiar strony pliku tekstowego (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Indeksując dzieli plik tekstowy na podane kawałki (jeśli różne od -1). Pomocne przy szukaniu w wielkich plikach (np.: dzienniki systemowe). Max. filter exec. time (s) Maks. czas wykonania filtra (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Przerywa po tym czasie zewnętrzne filtrowanie. Dla rzadkich przypadków (np.: postscript) kiedy dokument może spowodować zapętlenie filtrowania. Brak limitu to -1. Global Globalnie CronToolW Cron Dialog Ustaw cykl (CRON) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indeksuj cyklicznie (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Każde pole może zawierać wieloznacznik (*), pojdyńczą wartość, listę po przecinku (1,3,5) oraz zakres (1-7). Tak samo<span style=" font-style:italic;">jak</span>gdyby to był plik Crontab. Dlatego możliwe jest użycie składni Crontab. (zobacz crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Przykładowo wpisując <span style=" font-family:'Courier New,courier';">*</span> w <span style=" font-style:italic;">"Dni tygodnia", </span><span style=" font-family:'Courier New,courier';">12,19</span> w <span style=" font-style:italic;">"Godziny"</span> oraz <span style=" font-family:'Courier New,courier';">15</span> w <span style=" font-style:italic;">"Minuty"</span> uruchomili byśmy indeksowanie (recollindex) każdego dnia o 00:15 oraz 19:15</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Indeksowanie cykliczne (nawet te bardzo częste) jest mniej efektowne niż indeksowanie w czasie rzeczywistym.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Dni tygodnia (* or 0-7, 0 lub 7 to Niedziela) Hours (* or 0-23) Godziny (* lub 0-23) Minutes (0-59) Minuty (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Kliknij <span style=" font-style:italic;">Wyłącz</span>, aby zatrzymać automatyczne indeksowanie. <span style=" font-style:italic;">Włącz</span>, aby je rozpocząć. <span style=" font-style:italic;">Anuluj</span>, aby utrzymać obecny stan.</p></body></html> Enable Włącz Disable Wyłącz It seems that manually edited entries exist for recollindex, cannot edit crontab Nie można zmienić crontab. Wygląda na to, że istnieją ręczne wpisy dla recollindex. Error installing cron entry. Bad syntax in fields ? Błąd przy rejestrowaniu cyklu. Błędna składnia w polach? EditDialog Dialog Okno dialogowe EditTrans Source path Ścieżka źródłowa Local path Ścieżka lokalna Config error Błąd konfiguracji Original path Ścieżka oryginalna EditTransBase Path Translations Ścieżka tłumaczenia Setting path translations for Ustawienie ścieżki translacji dla Select one or several file types, then use the controls in the frame below to change how they are processed Wybierz jeden lub kilka typów pliku, następnie wskaż w ramce poniżej jak mają zostać przetworzone Add Dodaj Delete Usuń Cancel Anuluj Save Zapisz FirstIdxDialog First indexing setup Początkowa konfiguracja indeksowania <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Indeks dla tej konfiguracji nie istnieje.</span><br /><br />Jeśli tylko chcesz indeksować swój katalog domowy użwyając fabrcznych ustawień, wciśnij przycisk <span style=" font-style:italic;">Rozpocznij indeksowanie </span>. Szczegóły możesz ustawić również później. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jeśli chesz mieć większą kontrolę, użyj następujących odnośników w celu konfiguracji indeksowania oraz jego harmonogramu.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">To samo możesz również otrzymać poźniej wybierając <span style=" font-style:italic;">Ustawienia</span> z menu.</p></body></html> Indexing configuration Konfiguracja indeksowania This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Tutaj możesz wybrać katalogi do indeksowania, oraz inne parametry tj. wyłączenie ścieżek plików czy ich nazw, domyślny zestaw znaków, etc. Indexing schedule Harmonogram indeksowania This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Tutaj możesz wybrać między indeksowaniem w kolejce, a indeksowaniem nabierząco, jak i ustaleniem automatycznej kolejki indeksowania (dzięki Cron) Start indexing now Rozpocznij indeksowanie FragButs %1 not found. %1 nie znaleziono. %1: %2 %1: %2 Fragment Buttons Przyciski fragmentów Query Fragments Fragmenty zapytania IdxSchedW Index scheduling setup Konfiguracja harmonogramu indeksowania <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Indeksowanie <span style=" font-weight:600;">Recoll</span> może być uruchomione na stałe (indeksując każdą zmianę) lub w określonych cyklach.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Instrukcja obsługi (EN) może pomóc wybrać rozwiązanie dla Ciebie (wciśnij F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Narzędzie to pomoże Ci zaplanować indeksowanie cykliczne lub wybierzesz indeksowanie "na bieżąco" po zalogowaniu (lub jedno i drugie, co rzadko jest sendowne).</p></body></html> Cron scheduling Planowanie z użyciem Cron The tool will let you decide at what time indexing should run and will install a crontab entry. Tutaj zdecydujesz o jakim czasie indeksowanie ma być uruchamiane (po przez wpis do crontab). Real time indexing start up Uruchom indeksowanie "na bieżąco" Decide if real time indexing will be started when you log in (only for the default index). Pozwala uruchomić indeksowanie po zalogowaniu. ListDialog Dialog Okno dialogowe GroupBox Grupa Main No db directory in configuration Brak katalogu dla bazy danych w konfiguracji Could not open database in Nie można otworzyć bazy danych w . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Kliknij Anuluj, jeśli chcesz edytować plik konfiguracyjny przed rozpoczęciem indeksowania lub Ok, aby kontynuować. Configuration problem (dynconf Problem z konfiguracją (dynconf "history" file is damaged or un(read)writeable, please check or remove it: Plik "history" jest uszkodzony lub brak możliwości jego odczytu/zapisu, zmień to lub go usuń: "history" file is damaged, please check or remove it: "historia" plik jest uszkodzony, sprawdź lub usuń: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Szukaj: &Next &Następny &Previous &Poprzedni Match &Case Sprawdzaj &wielkość liter Clear Wyczyść Creating preview text Tworzę podgląd tekstu Loading preview text into editor Ładuję podgląd tekstu do edytora Cannot create temporary directory Nie można utworzyć katalogu tymczasowego Cancel Anuluj Close Tab Zamknij kartę Missing helper program: Brak programu usprawniającego: Can't turn doc into internal representation for Nie mogę przemienić dokumentu na władny format Cannot create temporary directory: Nie można utworzyć katalogu tymczasowego: Error while loading file Błąd ładowania pliku Form Formularz Tab 1 Tab 1 Open Otwórz Canceled Anulowane Error loading the document: file missing. Błąd ładowania dokumentu: brak pliku. Error loading the document: no permission. Błąd ładowania dokumentu: brak uprawnień. Error loading: backend not configured. Błąd ładowania: backend nie został skonfigurowany. Error loading the document: other handler error<br>Maybe the application is locking the file ? Błąd ładowania dokumentu: inny błąd obsługi<br>Może aplikacja blokuje plik? Error loading the document: other handler error. Błąd ładowania dokumentu: inny błąd obsługi <br>Attempting to display from stored text. <br>Ustawianie wyświetlania z zapisanego tekstu. Could not fetch stored text Nie można pobrać zapisanego tekstu Previous result document Poprzedni wynik Next result document Następny wynik Preview Window Podgląd okna Close Window Zamknij okno Next doc in tab Następny dokument w karcie Previous doc in tab Poprzedni dokument w zakładce Close tab Zamknij kartę Print tab Print tab Close preview window Zamknij okno podglądu Show next result Pokaż następny wynik Show previous result Pokaż poprzedni wynik Print Drukuj PreviewTextEdit Show fields Pokaż pola Show main text Pokaż tekst główny Print Drukuj Print Current Preview Drukuj obecny podgląd Show image Pokaż obraz Select All Zaznacz wszystko Copy Kopiuj Save document to file Zapisz dokument do pliku Fold lines Zwiń linie Preserve indentation Zachowaj wcięcia Open document Otwórz dokument Reload as Plain Text Reload as HTML QObject Global parameters Parametry globalne Local parameters Parametry lokalne <b>Customised subtrees <b>Dostosuj subdrzewa The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Lista podkatalogów w zindeksowanej hierarchii <br>, gdzie niektóre parametry muszą być ponownie zdefiniowane. Domyślnie: puste. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Parametry, które następują, są ustawione na najwyższym poziomie, jeśli nic<br>lub pusta linia jest zaznaczona na liście powyżej lub dla wybranego podkatalogu.<br>Możesz dodać lub usunąć katalogi klikając przycisk +/-. Skipped names Wykluczenia These are patterns for file or directory names which should not be indexed. Tutaj ustawiasz reguły wykluczające indeksowanie plików i katalogów. Default character set Domyślny zestaw znaków This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Jest to zestaw znaków używany do odczytu plików, które nie identyfikują zestawu znaków wewnętrznie, na przykład plików czysto tekstowych.<br>The default value is empty and the value of the NLS environnement Follow symbolic links Idź za dowiązaniami symbolicznymi Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Indeksując, idź za dowiązaniami symbolicznymi. Domyślnia wartość to NIE, chroni przed zduplikowanymi indeksami. Index all file names Indeksuj wszystkie nazwy plików Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indeksuj nazwy plików dla których zawartość nie może być rozpoznana lub przetworzona (Nie lub nieobsługiwany typ MIME). Domyślnie Tak. Beagle web history Historia sieci Beagle Search parameters Parametry szukania Web history Historia sieci Default<br>character set Domyślny<br>zestaw znaków Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Zestaw znaków używany do czytania plików, które nie identyfikują zestawu znaków wewnętrznie, na przykład plików czystego tekstu.<br>The default value is empty and the value of the NLS environnement Ignored endings Ignorowane zakończenia These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Są to zakończenia nazwy pliku dla plików, które będą indeksowane tylko przez zawartość (bez próby identyfikacji typu MIME, bez dekompresji, bez indeksowania zawartości. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Są to zakończenia nazwy pliku dla plików, które będą indeksowane tylko po nazwie (brak próby identyfikacji typu MIME, brak dekompresji, brak indeksowania zawartości). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>Parametry, które następują, są ustawione na najwyższym poziomie, jeśli na liście powyżej zaznaczono nic lub pustą linię lub dla wybranego podkatalogu. Możesz dodać lub usunąć katalogi klikając przycisk +/-. QWidget Create or choose save directory Utwórz lub wybierz katalog zapisu Choose exactly one directory Wybierz dokładnie jeden katalog Could not read directory: Nie można odczytać katalogu: Unexpected file name collision, cancelling. Nieoczekiwana kolizja nazwy pliku, anulowanie. Cannot extract document: Nie można wyodrębnić dokumentu: &Preview &Poprzedni &Open &Otwórz Open With Otwórz za pomocą Run Script Uruchom skrypt Copy &File Name &Kopiuj nazwę pliku Copy &URL Kopiuj &URL &Write to File Zapisz &do pliku Save selection to files Zapisz zaznaczenie do pliku Preview P&arent document/folder Podgląd rodzica dokumentu|katalogu &Open Parent document/folder &Otwórz dokument|katalog rodzica Find &similar documents Znajdź &podobne dokumenty Open &Snippets window Otwórz okno &snipetów Show subdocuments / attachments Pokaż poddokumenty|załączniki &Open Parent document Otwórz dokument nadrzędny &Open Parent Folder &Otwórz folder nadrzędny Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Nie pokazuj ponownie. RTIToolW Real time indexing automatic start Automatyczny start indeksowania w czasie rzeczywistym <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indeksacja może być uruchomiona w tle (daemon), aktualizując indeks nabierząco. Zyskujesz zawsze aktualny indeks, tracąc część zasobów systemowych.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Uruchom indeksowanie w tle razem ze startem komputera. Also start indexing daemon right now. Dodatkowo natychmiast uruchom indeksowanie w tle. Replacing: Podmiana: Replacing file Podmiana pliku Can't create: Nie mogę utworzyć: Warning Ostrzeżenie Could not execute recollindex Nie można wykonać recollindex Deleting: Usuwanie: Deleting file Usuwanie pliku Removing autostart Usuwanie autostartu Autostart file deleted. Kill current process too ? Usunięto plik autostartu. Zamknąć również bieżący proces? RclCompleterModel Hits RclMain About Recoll Karta Recoll Executing: [ Wykonuję: [ Cannot retrieve document info from database Brak możliwości pobrania informacji o dokumencie z bazy danych Warning Ostrzeżenie Can't create preview window Nie można utworzyć okna podglądu Query results Wynik zapytania Document history Historia dokumentów History data Historia danych Indexing in progress: Indeksowanie w tracie: Files Pliki Purge Wyczyść Stemdb Stemdb Closing Zamykanie Unknown Nieznane This search is not active any more To wyszukanie przestało być aktywne Can't start query: Może't start zapytania: Bad viewer command line for %1: [%2] Please check the mimeconf file Błędna linia poleceń przeglądarki dla %1: [%2] Sprawdź plik mimeconf Cannot extract document or create temporary file Nie można wypakować dokumentu lub stworzyć plik tymczasowy (no stemming) wyłącz ciosanie (ang. stemming) (all languages) (każdy język) error retrieving stemming languages Błąd pobierania "reguł ciosania" (ang. stemming languages) Update &Index Odśwież &Indeks Indexing interrupted Indeksowanie przerwane Stop &Indexing Zatrzymaj &Indeksowanie All Wszystko media multimedia message wiadomości other pozostałe presentation prezentacje spreadsheet arkusze text tekstowe sorted posortowane filtered przefiltrowane External applications/commands needed and not found for indexing your file types: Aplikacje zewnętrzne/polecenia potrzebne i nie znaleziono do indeksowania typów plików: No helpers found missing Wszystkie rozszerzenia znaleziono Missing helper programs Brakujące rozszerzenia Save file dialog Zapisz okno dialogowe Choose a file name to save under Wybierz nazwę pliku do zapisania Document category filter Filtr kategorii dokumentu No external viewer configured for mime type [ Brak skonfigurowanej zewnętrzenej przeglądarki typów MIME [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Brak przeglądarki dla typu MIME %1: %2 . Chcesz to ustawić teraz? Can't access file: Nie mogę uzyskać dostępu do pliku: Can't uncompress file: Nie mogę wypakować pliku: Save file Zapisz plik Result count (est.) Liczba wyników (szac.) Query details Szczegóły zapytania Could not open external index. Db not open. Check external indexes list. Nie mogę otworzyc zewnętrznego indeksu. Nie otwarta baza danych. Sprawdź listę zewnętrznych indeksów. No results found Brak wyników None Nic Updating Odświeżanie Done Zakończone Monitor Sprawdzanie Indexing failed Porażka indeksowania The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Obecny proces indeksowania uruchomiono z innego okna. Kliknij Ok, by zamknąć proces. Erasing index Usuwanie indeksu Reset the index and start from scratch ? Ponownie spisać indeks od zera? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Trwa zapytanie.<br>Ze względu na ograniczenia biblioteki indeksowania,<br>anulowanie zakończy program Error Błąd Index not open Indeks jest zamknięty Index query error Błąd odpytania indeksu Indexed Mime Types Indeksowane typy Mime Content has been indexed for these MIME types: Zawartość została zindeksowana dla tych typów MIME: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Indeks nie aktualny dla tego pliku. Odmawiam ryzyka pokazania błędnego wpisu. Kliknij Ok, aby zaktualizować indeks dla tego pliku, a następnie ponownie uruchom zapytanie po zakończeniu indeksowania. Inne, Anuluj. Can't update index: indexer running Nie mogę zaktualizować indeksu: pracujący indekser Indexed MIME Types Zaindeksowane typy MIME Bad viewer command line for %1: [%2] Please check the mimeview file Błędna komenda przeglądarki dla typu %1: [%2] Sprawdź plik widoku MIME Viewer command line for %1 specifies both file and parent file value: unsupported Polecenie czytnika dla %1 podaje zarówno plik jak i wartość pliku rodzica: niewspierane Cannot find parent document Nie można odszukać rodzica dokumentu Indexing did not run yet Indeksowanie nie zostało jeszcze uruchomione External applications/commands needed for your file types and not found, as stored by the last indexing pass in Brak zewnętrznych aplikacji|komend wymaganych przez twoje typy plików. Index not up to date for this file. Refusing to risk showing the wrong entry. Indeks tego pliku jest nieaktualny. Odmawiam podania błędnych wyników. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Kliknij Ok by uaktualnić indeks tego pliku, po zakończeniu ponów zapytanie lub Anuluj. Indexer running so things should improve when it's done Indeksowanie w trakcie, spodziewana poprawa po zakończeniu. Sub-documents and attachments Poddokumenty i załączniki Document filter Filtr dokumentów Index not up to date for this file. Refusing to risk showing the wrong entry. Indeks tego pliku jest nieaktualny. Odmawiam podania błędnych wyników. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Kliknij Ok, aby zaktualizować indeks dla tego pliku, a następnie musisz ponownie uruchomić zapytanie po zakończeniu indeksowania. The indexer is running so things should improve when it's done. Indekser jest uruchomiony, więc rzeczy powinny się poprawić, gdy's skończy. The document belongs to an external indexwhich I can't update. Dokument należy do zewnętrznego indeksu, który mogę't aktualizować. Click Cancel to return to the list. Click Ignore to show the preview anyway. Kliknij przycisk Anuluj, aby powrócić do listy. Kliknij przycisk Ignoruj, aby pokazać podgląd. Duplicate documents Duplikaty dokumentów These Urls ( | ipath) share the same content: Te URLe ( | ipath) mają tą samą zawartość: Bad desktop app spec for %1: [%2] Please check the desktop file Błędna specyfikacja aplikacji desktopowej dla %1: [%2] Sprawdź plik pulpitu Bad paths Złe ścieżki Bad paths in configuration file: Błędne ścieżki w pliku konfiguracyjnym: Selection patterns need topdir Wzory selekcji wymagają topdir Selection patterns can only be used with a start directory Wzory wyboru mogą być używane tylko z katalogiem startowym No search Brak wyszukiwania No preserved previous search Nie zachowano poprzedniego wyszukiwania Choose file to save Wybierz plik do zapisania Saved Queries (*.rclq) Zapisane zapytania (*.rclq) Write failed Zapisanie nie powiodło się Could not write to file Nie można zapisać do pliku Read failed Błąd odczytu Could not open file: Nie można otworzyć pliku: Load error Błąd ładowania Could not load saved query Nie można załadować zapisanego zapytania Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Otwieranie tymczasowej kopii. Edycje zostaną utracone, jeśli nie't zapisz<br/>w stałej lokalizacji. Do not show this warning next time (use GUI preferences to restore). Nie pokazuj tego ostrzeżenia następnym razem (użyj preferencji GUI, aby przywrócić). Disabled because the real time indexer was not compiled in. Wyłączone, ponieważ indeks czasu rzeczywistego nie został skompilowany. This configuration tool only works for the main index. To narzędzie konfiguracyjne działa tylko dla głównego indeksu. The current indexing process was not started from this interface, can't kill it Bieżący proces indeksowania nie został uruchomiony z tego interfejsu, może't zabić go The document belongs to an external index which I can't update. Dokument należy do indeksu zewnętrznego, który mogę't aktualizować. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Kliknij przycisk Anuluj, aby powrócić do listy. <br>Kliknij przycisk Ignoruj, aby pokazać podgląd i tak (i zapamiętać dla tej sesji). Index scheduling Harmonogram indeksowania Sorry, not available under Windows for now, use the File menu entries to update the index Przepraszamy, obecnie niedostępne w systemie Windows, użyj wpisów menu pliku do aktualizacji indeksu Can't set synonyms file (parse error?) Czy można't ustawić plik synonimów (błąd analizacji?) Index locked Indeks zablokowany Unknown indexer state. Can't access webcache file. Nieznany stan indeksatora. Może't uzyskać dostęp do pliku pamięci podręcznej. Indexer is running. Can't access webcache file. Indekser jest uruchomiony. Może't uzyskać dostęp do pliku pamięci podręcznej. with additional message: z dodatkową wiadomością: Non-fatal indexing message: Wiadomość indeksowania niezakończona zgonem: Types list empty: maybe wait for indexing to progress? Lista typów pusta: może poczekać na indeksowanie do postępu? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Wiersz poleceń przeglądarki dla %1 określa plik nadrzędny, ale adres URL to http[s]: nieobsługiwany Tools Narzędzia Results Wyniki (%d documents/%d files/%d errors/%d total files) (%d dokumentów/%d plików/%d błędów/%d wszystkich plików) (%d documents/%d files/%d errors) (%d dokumentów/%d plików/%d błędów) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Puste lub nieodległe ścieżki w pliku konfiguracyjnym. Kliknij Ok, aby rozpocząć indeksowanie (brak danych nie zostanie wyczyszczony z indeksu): Indexing done Indeksowanie wykonane Can't update index: internal error Można't aktualizować indeks: błąd wewnętrzny Index not up to date for this file.<br> Indeks nie jest aktualny dla tego pliku.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Wydaje się również, że ostatnia aktualizacja indeksu dla pliku nie powiodła się.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Kliknij Ok, aby zaktualizować indeks dla tego pliku. Po zakończeniu indeksowania musisz uruchomić zapytanie ponownie.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Kliknij przycisk Anuluj, aby powrócić do listy.<br>Kliknij przycisk Ignoruj, aby pokazać podgląd i tak (i zapamiętać dla tej sesji). Istnieje ryzyko pojawienia się złego wpisu.<br/> documents dokumenty document dokument files pliki file plik errors błędy error błąd total files) całkowita ilość plików) No information: initial indexing not yet performed. Brak informacji: nie przeprowadzono jeszcze indeksowania początkowego. Batch scheduling Harmonogram partii The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Narzędzie pozwoli Ci zdecydować, w jakim czasie powinno działać indeksowanie. Używa harmonogramu zadań Windows. Confirm Potwierdź Erasing simple and advanced search history lists, please click Ok to confirm Usuwanie prostych i zaawansowanych list historii, kliknij Ok, aby potwierdzić Could not open/create file Nie można otworzyć/utworzyć pliku F&ilter &Ulepszony Could not start recollindex (temp file error) Nie można uruchomić recollindex (błąd pliku tymczasowego) Could not read: Nie można odczytać This will replace the current contents of the result list header string and GUI qss file name. Continue ? To zastąpi bieżącą zawartość nagłówka listy wyników i nazwy pliku qss GUI. Kontynuować? You will need to run a query to complete the display change. Aby dokończyć zmianę wyświetlacza, musisz uruchomić zapytanie. Simple search type Prosty typ wyszukiwania Any term Któryś termin All terms Każdy termin File name Nazwa pliku Query language Język zapytań Stemming language Język ciosania Main Window Główne okno Focus to Search Skoncentruj się na wyszukiwaniu Focus to Search, alt. Skoncentruj się na wyszukiwaniu, altro. Clear Search Wyczyść wyszukiwanie Focus to Result Table Skoncentruj się na tabeli wyników Clear search Wyczyść wyszukiwanie Move keyboard focus to search entry Przenieś skupienie klawiatury do wpisu wyszukiwania Move keyboard focus to search, alt. Przenieś ostrość klawiatury do wyszukiwania, alt. Toggle tabular display Przełącz wyświetlanie tabel Move keyboard focus to table Przenieś ostrość klawiatury do tabeli Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Poprzednia strona Next page Następna strona &File &Plik E&xit &Zakończ &Tools &Narzędzia &Help &Pomoc &Preferences &Ustawienia Search tools Narzędzia wyszukiwania Result list Wyniki &About Recoll &Karta Recoll Document &History &Historia dokumentu Document History Historia Dokumentu &Advanced Search &Zaawansowane szukanie Advanced/complex Search Złożone szukanie &Sort parameters Parametry &sortowania Sort parameters Parametry sortowania Next page of results Następna strona wyników Previous page of results Poprzednia strona wyników &Query configuration &Konfiguracja zapytania &User manual &Instrukcja Recoll Rozbij Ctrl+Q Ctrl + Q Update &index &Aktualizacja indeksu Term &explorer Przejżyj &terminy Term explorer tool Przeglądanie terminów External index dialog Zewnętrzny indeks &Erase document history &Usuń historię dokumentu First page Pierwsza strona Go to first page of results Przejdź do pierwszej strony wyników &Indexing configuration &Konfiguracja indeksowania All Wszystko &Show missing helpers Pokaż &brakujących pomocników PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen Pełen &Ekran F11 F11 Full Screen Pełen ekran &Erase search history &Usuń historię szukania sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Sortuj po dacie: od najstarszego sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Sortuj po dacie: od najnowszego Show Query Details Pokaż szczegóły zapytania Show results as table Pokaż wyniki jako tabelę &Rebuild index &Odnów indeks &Show indexed types Pokaż zaindeksowane &typy Shift+PgUp Przesuń+PgUp &Indexing schedule &Plan indeksowania E&xternal index dialog Zewnętrzny indeks &Index configuration &Konfiguracja indeksu &GUI configuration Konfiguracja &GUI &Results &Wyniki Sort by date, oldest first Sortuj po dacie: od najstarszego Sort by date, newest first Sortuj po dacie: od najnowszego Show as table Pokaż jako tabela Show results in a spreadsheet-like table Pokaż wyniki jako arkusz Save as CSV (spreadsheet) file Zapisz jako plik CSV (arkusz) Saves the result into a file which you can load in a spreadsheet Zapisz wyniki do pliku czytelnego przez arkusz Next Page Następna strona Previous Page Poprzednia strona First Page Pierwsza strona Query Fragments Fragmenty zapytania With failed files retrying Z nieudanymi plikami próbnymi Next update will retry previously failed files Następna aktualizacja ponownie spróbuje poprzednio nieudane pliki Save last query Zapisz ostatnie zapytanie Load saved query Załaduj zapisane zapytanie Special Indexing Indeksowanie specjalne Indexing with special options Indeksowanie ze specjalnymi opcjami Indexing &schedule &Indeksowanie harmonogramu Enable synonyms Włącz synonimy &View &Widok Missing &helpers Brakujący &pomocnik Indexed &MIME types Zindeksowane typy &MIME Index &statistics &Statystyki indeksu Webcache Editor Edytor webcache Trigger incremental pass Wyzwalaj przyrostowe przejście E&xport simple search history &Eksportuj prostą historię wyszukiwania Use default dark mode Użyj domyślnego trybu ciemnego Dark mode Tryb ciemny &Query &Zapytanie Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Po dacie Assisted complex search Filter birth dates RclTrayIcon Restore Przywróć Quit Wyjdź RecollModel Abstract Abstrakcja Author Autor Document size Rozmiar dokumentu Document date Data dokumentu File size Rozmiar pliku File name Nazwa pliku File date Data pliku Ipath IŚcieżka Keywords Słowa klucze Mime type Typ Mime Original character set Oryginalny zestaw znaków Relevancy rating Trafność Title Tytuł URL Adres URL Mtime Czas modyfikacji Date Data Date and time Data i czas Ipath IŚcieżka MIME type Typ MIME Can't sort by inverse relevance Można't sortować według odwrotnej istotności ResList Result list Lista wyników Unavailable document Dokument niedostępny Previous Poprzedni Next Następny <p><b>No results found</b><br> <p><b>Nie znaleziono żadnych wyników</b><br> &Preview &Poprzedni Copy &URL Kopiuj &URL Find &similar documents Znajdź &podobne dokumenty Query details Szczegóły zapytania (show query) (Pokaż zapytanie) Copy &File Name &Kopiuj nazwę pliku filtered przefiltrowane sorted posortowane Document history Historia dokumentu Preview Poprzedni Open Otwórz <p><i>Alternate spellings (accents suppressed): </i> <p><i>Alternatywne pisownie (akcenty zablokowane): </i> &Write to File Zapisz &do pliku Preview P&arent document/folder Podgląd rodzica dokumentu|katalogu &Open Parent document/folder &Otwórz dokument|katalog rodzica &Open &Otwórz Documents Dokumenty out of at least z co najmniej for dla <p><i>Alternate spellings: </i> <p><i>Alternatywne pisownie: </i> Open &Snippets window Otwórz okno &snipetów Duplicate documents Duplikaty dokumentów These Urls ( | ipath) share the same content: Te URLe ( | ipath) mają tą samą zawartość: Result count (est.) Liczba wyników (oszacowana) Snippets Snipety This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Reset sortowania &Delete column &Usuń kolumnę Add " Dodaj " " column " kolumna Save table to CSV file Zapisz tabelę jako plik CSV Can't open/create file: Nie można otworzyć|utworzyć pliku: &Preview &Poprzedni &Open &Otwórz Copy &File Name &Kopiuj nazwę pliku Copy &URL Kopiuj &URL &Write to File Zapisz &do pliku Find &similar documents Znajdź &podobne dokumenty Preview P&arent document/folder Podgląd rodzica dokumentu|katalogu &Open Parent document/folder &Otwórz dokument|katalog rodzica &Save as CSV &Zapisz jako CSV Add "%1" column Dodaj "%1" kolumnę Result Table Tabela wyników Open Otwórz Open and Quit Otwórz i wyjdź Preview Poprzedni Show Snippets Pokaż Snippety Open current result document Otwórz bieżący wynik Open current result and quit Otwórz bieżący wynik i wyjdź Show snippets Pokaż fragmenty Show header Pokaż nagłówek Show vertical header Pokaż pionowy nagłówek Copy current result text to clipboard Skopiuj bieżący wynik do schowka Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Poprzedni &Open &Otwórz Copy &File Name &Kopiuj nazwę pliku Copy &URL Kopiuj &URL &Write to File Zapisz &do pliku Find &similar documents Znajdź &podobne dokumenty Preview P&arent document/folder Podgląd rodzica dokumentu|katalogu &Open Parent document/folder &Otwórz dokument|katalog rodzica ResultPopup &Preview &Poprzedni &Open &Otwórz Copy &File Name &Kopiuj nazwę pliku Copy &URL Kopiuj &URL &Write to File Zapisz &do pliku Save selection to files Zapisz zaznaczenie do pliku Preview P&arent document/folder Podgląd rodzica dokumentu|katalogu &Open Parent document/folder &Otwórz dokument|katalog rodzica Find &similar documents Znajdź &podobne dokumenty Open &Snippets window Otwórz okno &snipetów Show subdocuments / attachments Pokaż poddokumenty|załączniki Open With Otwórz za pomocą Run Script Uruchom skrypt SSearch Any term Któryś termin All terms Każdy termin File name Nazwa pliku Completions Ukończenia Select an item: Wybierz element: Too many completions Zbyt wiele uzupełnień Query language Język zapytań Bad query string Błędne zapytanie Out of memory Brak pamięci Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Wprowadź wyrażenie języka zapytania. Arkusz oszustwa:<br> <i>term1 term2</i> : 'term1' i 'term2' w dowolnym polu.<br> <i>field:term1</i> : 'term1' w polu ''<br> Standardowe nazwy pola/synonimy:<br> tytuł/przedmiot/podpis, autor/od odbiorcy/odbiorcy, nazwa pliku, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Dwa interwały dat: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> Nie jest dozwolony żaden rzeczywisty nawias.<br> <i>"term1 term2"</i> : fraza (musi występować dokładnie). Możliwe modyfikatory:<br> <i>"term1 term2"p</i> : niezamówione wyszukiwanie zbliżeniowe z domyślną odległością.<br> Użyj <b>Pokaż link Zapytania</b> w razie wątpliwości co do wyniku i zobacz instrukcję (&<unk> ) 1>), aby uzyskać więcej informacji. Enter file name wildcard expression. Wprowadź wieloznakowe (wildcard) wyrażenie nazwy pliku Enter search terms here. Type ESC SPC for completions of current term. Wprowadź tutaj szkane terminy. Wpisz ESC SPC by uzupełnić bieżący termin. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Wprowadź wyrażenie języka zapytania. Arkusz oszustwa:<br> <i>term1 term2</i> : 'term1' i 'term2' w dowolnym polu.<br> <i>field:term1</i> : 'term1' w polu ''<br> Standardowe nazwy pola/synonimy:<br> tytuł/przedmiot/podpis, autor/od odbiorcy/odbiorcy, nazwa pliku, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, data, rozmiar.<br> Dwa interwały daty: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> Możesz użyć nawiasów, aby sprawy były bardziej przejrzyste.<br> <i>"term1 term2"</i> : fraza (musi występować dokładnie). Możliwe modyfikatory:<br> <i>"term1 term2"p</i> : niezamówione wyszukiwanie zbliżeniowe z domyślną odległością.<br> Użyj <b>Pokaż link Zapytania</b> w razie wątpliwości co do wyniku i zobacz instrukcję (&<unk> ) 1>), aby uzyskać więcej informacji. Stemming languages for stored query: Zapamiętywanie języków dla zapamiętanego zapytania: differ from current preferences (kept) różnią się od aktualnych preferencji (zachowanych) Auto suffixes for stored query: Automatyczne przyrostki dla zapisanego zapytania: External indexes for stored query: Zewnętrzne indeksy dla zapisanego zapytania: Autophrase is set but it was unset for stored query Autofraza jest ustawiona, ale była nieustawiona dla przechowywanych zapytań Autophrase is unset but it was set for stored query Autofraza jest nieustawiona, ale została ustawiona dla przechowywanych zapytań Enter search terms here. Wprowadź wyszukiwane frazy tutaj. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; granica: 1px w postaci litej czarnej; border-collapse: collapse; załamanie granicy: załamanie się; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Zestawienie języka zapytania. W wątpliwości: kliknij <b>Pokaż zapytanie</b>.&nbsp; You should really look at the manual (F1)</p> Naprawdę powinieneś spojrzeć na podręcznik (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Co</th><th>Przykłady</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>I</td><td>jeden drugi&nbsp;&nbsp;&nbsp;jeden ORAZ dwa&nbsp;&nbsp;&nbsp;jeden && dwa</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Lub</td><td>jeden LUB dwa&nbsp;&nbsp;&nbsp;jeden || dwa</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Złożony boolean. LUB ma priorytet, użyj nawiasów&nbsp; where needed</td><td>(one AND two) OR three</td></tr> w razie potrzeby</td><td>(jeden i dwa) LUB trzy</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Nie</td><td>termin</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Fraza</td><td>"duma i uprzedzenie"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Nieuporządkowany proks (domyślny slack=10)</td><td>"Rezerwuj&nbsp;dumę"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Bez rozszerzenia łodygi: kapitalizuj</td><td>Piętro</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Określony polem</td><td>autor:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>I wewnątrz pola (bez zamówienia)</td><td>autor:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>LUB wewnątrz pola</td><td>autor:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Nazwy pól</td><td>tytuł/temat/podpis&nbsp;&nbsp;autor/od<br>odbiorca/do&nbsp;&nbsp;nazwa pliku&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Filtr ścieżki katalogu</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Filtr typu MIME</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Przedziały daty</td><td>data:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Można't otworzyć indeks Could not restore external indexes for stored query:<br> Nie można przywrócić zewnętrznych indeksów dla zapisanego zapytania:<br> ??? ??? Using current preferences. Używanie aktualnych preferencji. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Wyczyść Ctrl+S Ctrl + S Erase search entry Usuń szukany wpis Search Szukaj Start query Start zapytania Enter search terms here. Type ESC SPC for completions of current term. Wprowadź tutaj szkane terminy. Wpisz ESC SPC by uzupełnić bieżący termin. Choose search type. Wybierz typ szukania. Show query history Pokaż historię zapytań Enter search terms here. Wprowadź wyszukiwane frazy tutaj. Main menu Menu główne SearchClauseW SearchClauseW Szukaj ClauseW Any of these Każdy z tych objawów All of these Wszystkie te None of these Żadne z tych This phrase To zdanie Terms in proximity Terminy w pobliżu File name matching Nazwa pliku pasująca Select the type of query that will be performed with the words Wybierz typ zapytania, który będzie użyty z wyrazami Number of additional words that may be interspersed with the chosen ones Liczba dodatkowych wyrazów, które mogą być przeplatane z wybranymi In field W polu No field Bez pola Any Któryś All Każdy None Żaden Phrase Fraza Proximity Sąsiedztwo File name Nazwa pliku Snippets Snippets Snipety X X Find: Znajdź: Next Następny Prev Poprzedni SnippetsW Search Szukaj <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Przepraszamy, nie znaleziono dokładnego dopasowania w granicach. Prawdopodobnie dokument jest bardzo duży i generator fragmentów zgubił się w labiryncie...</p> Sort By Relevance Sortuj według trafności Sort By Page Sortuj według Strony Snippets Window Okno snippetów Find Znajdź Find (alt) Znajdź (alt) Find Next Znajdź następny Find Previous Znajdź Poprzedni Hide Ukryj Find next Znajdź następne Find previous Znajdź poprzedni Close window Zamknij okno SortForm Date Data Mime type Typ Mime SortFormBase Sort Criteria Kryteria sortowania Sort the Sortuj most relevant results by: najistotniejsze wyniki przez: Descending Malejąco Close Zamknij Apply Zastosuj SpecIdxW Special Indexing Indeksowanie specjalne Do not retry previously failed files. Nie ponawiaj poprzednio nieudanych plików. Else only modified or failed files will be processed. Inne pliki zmodyfikowane lub nieudane zostaną przetworzone. Erase selected files data before indexing. Wyczyść wybrane pliki przed indeksowaniem. Directory to recursively index Katalog do indeksu rekursywnego Browse Przeglądaj Start directory (else use regular topdirs): Katalog startowy (w przeciwnym razie użyj regularnych topdir): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Pozostaw puste, aby wybrać wszystkie pliki. Możesz użyć wielu wzorców oddzielonych spacjami typu powłoki.<br>Wzory z osadzonymi spacjami powinny być cytowane z podwójnymi cudzysłowami.<br>Może być używane tylko wtedy, gdy ustawiony jest cel początkowy. Selection patterns: Wzory wyboru: Top indexed entity Najczęściej indeksowany obiekt Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Katalog do rekurencyjnego indeksu. Musi to znajdować się wewnątrz regularnego indeksowanego obszaru<br> zgodnie z definicją w pliku konfiguracyjnym (topdirs). Retry previously failed files. Ponów poprzednio nieudane pliki. Start directory. Must be part of the indexed tree. We use topdirs if empty. Katalog startowy. Musi być częścią zindeksowanego drzewa. Jeśli puste, używamy topdirów. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Katalog startowy. Musi być częścią indeksowanego drzewa. Jeśli puste, należy użyć pełnego indeksowanego obszaru. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Przegląd terminów &Expand &Rozszerz Alt+E Alt+E &Close &Zamknij Alt+C Alt+C Term Termin No db info. Brak informacji bd. Doc. / Tot. Dok. / Razem Match Dopasowanie Case Wielkość znaków (Case) Accents Akcenty SpellW Wildcards Wieloznaczniki (wildcards) Regexp Wyrażenie regułowe (regexp) Spelling/Phonetic Pisownia/Fonetyczność Aspell init failed. Aspell not installed? Nieudany start Aspell. Nie zainstalowano Aspell? Aspell expansion error. Błąd rozszerzenia Aspell. Stem expansion Roszerzenie rdzenia (Stem expansion) error retrieving stemming languages Błąd pobierania "reguł ciosania" (ang. stemming languages) No expansion found Nieznalezione rozszerzenie Term Termin Doc. / Tot. Dok. / Razem Index: %1 documents, average length %2 terms Indeks: %1 dokumentów, średnia długość %2 wyrażeń Index: %1 documents, average length %2 terms.%3 results Indeks: %1 dokumenty, średnia długość %2 terminów.%3 wyników %1 results %1 wyników List was truncated alphabetically, some frequent Lista obcięta alfabetycznie, część częsta terms may be missing. Try using a longer root. Terminy mogą zginąć. Użyj dłuższego rdzenia Show index statistics Pokaż statystyki indeksowania Number of documents Liczba dokumentów Average terms per document Średnia terminów na dokument Smallest document length Najmniejsza długość dokumentu Longest document length Największa długość dokumentu Database directory size Rozmiar katalogu bazy danych MIME types: Typy MIME: Item Element Value Wartość Smallest document length (terms) Najmniejsza długość dokumentu (terminy) Longest document length (terms) Najdłuższa długość dokumentu (terminy) Results from last indexing: Wyniki z ostatniego indeksowania: Documents created/updated Dokumenty utworzone/zaktualizowane Files tested Pliki przetestowane Unindexed files Niedeksfekowane pliki List files which could not be indexed (slow) Lista plików, które nie mogą być indeksowane (wolne) Spell expansion error. Błąd rozszerzenia zaklęcia. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Wybrany katalog nie wygląda jak indeks Xapian This is the main/local index! To jest główny|lokalny indeks! The selected directory is already in the index list Wybrany słownik już należy do indeksu Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Wybierz katalog indeksu xapian (np. /home/buddy/.recoll/xapiandb) error retrieving stemming languages Błąd pobierania "reguł ciosania" (ang. stemming languages) Choose Wybierz Result list paragraph format (erase all to reset to default) Format paragrafu listy wyników (usuń wszystko by wróćić do domyślnych) Result list header (default is empty) Nagłówek listy wyników (domyślnie pusty) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Wybierz katalog konfiguracji recoll lub katalog indeksu xapian (np.: /home/ja/.recoll lub /home/ja/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Wybrany katalog wygląda jak katalog konfiguracji Recoll, jednakże kofiguracja nie może być przeczytana At most one index should be selected Co najwyżej jeden indeks powinnien być wyberany Cant add index with different case/diacritics stripping option Nie można dodać indeksu z opcją różnej wielkości-liter/znakach-diakrytycznych Default QtWebkit font Default QtWebkit font Any term Któryś termin All terms Każdy termin File name Nazwa pliku Query language Język zapytań Value from previous program exit Wartość z poprzedniego zakończenia programu Context Kontekst Description Opis Shortcut Skrót Default Domyślny Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Wygląd Number of entries in a result page Liczba wyników na stronie Result list font Czcionka listy wyników Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Otwiera okno wyboru czcionek Reset Reset Resets the result list font to the system default Reset czcionki wyników do domyślnej Auto-start simple search on whitespace entry. Proste szukanie gdy użyto biłych znaków we wpisie. Start with advanced search dialog open. Rozpocznij oknem zaawansowanego szukania. Start with sort dialog open. Rozpocznij z otwartym oknem sortowania. Search parameters Parametry szukania Stemming language Język ciosania Dynamically build abstracts Buduj streszczenia dynamicznie Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Mam budować streszczenie dla wyników po przez użycie kontekstu teminów zapytania? Może zwalniać dla dużych dokumentów. Replace abstracts from documents Zamień streszczenia z dokumentów Do we synthetize an abstract even if the document seemed to have one? Tworzyć sztuczne streszczenie nawet jeśli dokument ma własne? Synthetic abstract size (characters) Rozmiar sztucznego streszczenia (w znakach) Synthetic abstract context words Kontekstowe wyrazy sztucznego streszczenia External Indexes Zewnętrzne indeksy Add index Dodaj indeks Select the xapiandb directory for the index you want to add, then click Add Index Wybierz katalog xapiandb dla indeksu, który chcesz dodać, a następnie kliknij przycisk Dodaj indeks Browse Przeglądaj &OK &Ok Apply changes Zastosuj zmiany &Cancel &Anuluj Discard changes Porzuć zmiany Result paragraph<br>format string Wynik paragrafu<br>w formacie ciągu Automatically add phrase to simple searches Automatycznie dodaj frazę do szukania prostego A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Wyszukanie dla [rolling stones] (2 terminy) zostanie zamienione na [rolling or stones or (rolling phrase 2 stones)]. To powinno dać pierwszeństwo wynikom, dokładnie tak jak zostały wpisane. User preferences Ustawienia Use desktop preferences to choose document editor. Użyj ustawień pulpitu aby wybrać edytor dokumentów. External indexes Indeksy zewnętrzne Toggle selected Odwróc zaznaczenie Activate All Aktywuj wszystko Deactivate All Deaktywuj wszystko Remove selected Usuń zaznaczenie Remove from list. This has no effect on the disk index. Usuń z listy. Brak skutku dla indeksu na dysku. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Określa format dla każdego punktu listy wyników. Użyj formatu qt html i zamienników podobnych do wydruku:<br>%A Abstract<br> %D Date<br> %I Ikona nazwa obrazu<br> %K Słowa kluczowe (jeśli istnieje)<br> %L Podgląd i edycja linków<br> %M Typ Mime<br> %N Numer wyniku<br> %R Procent istotności<br> %S Rozmiar informacji<br> %T Tytuł<br> %U Url<br> Remember sort activation state. Pamiętaj stan sortowania. Maximum text size highlighted for preview (megabytes) Maks. rozmiar tekstu dla wyróżnienia w podglądzie (MB) Texts over this size will not be highlighted in preview (too slow). Teksty powyżej tego rozmiaru będą ukryte w podglądzie (zbyt wolne). Highlight color for query terms Podświetl terminy z zapytania Prefer Html to plain text for preview. Użyj HTML (zamiast czysty tekst) dla podglądu. If checked, results with the same content under different names will only be shown once. Wyświetl tylko raz gdy tak sama zawartość (choć różne nazwy) Hide duplicate results. Ukryj duplikaty w wynikach. Choose editor applications Wybierz edytor aplikacji Display category filter as toolbar instead of button panel (needs restart). Wyświetl filtr kategorii jako pasek zamiast panelu (wymagany restart). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Wyrazy z listy zostaną automatycznie zmienione w klauzule ext:xxx we wpisach języka zapytań. Query language magic file name suffixes. Magiczne przyrostki nazw plików języka zapytań Enable Włącz ViewAction Changing actions with different current values Zmiana akcji z różnymi bieżącymi wartościami Mime type Typ Mime Command Komenda MIME type Typ Mime Desktop Default Domyślnie ustawienia pulpitu Changing entries with different current values Zmiana wpisów o różne obecne wartości ViewActionBase File type Typ pliku Action Akcja Select one or several file types, then click Change Action to modify the program used to open them Wybierz jeden lub kilka typów plików, następnie kliknij przycisk Zmień Akcję, aby zmodyfikować program używany do ich otwarcia Change Action Zmień akcję Close Zamknij Native Viewers Systemowy czytnik Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Wybierz jeden lub kilka typów mime a następnie kliknij "Zmień działanie"<br>Możesz również zamknąć to okno dialogowe i zaznaczyć "Użyj ustawień pulpitu"<br>w panelu głównym, aby zignorować tę listę i użyć domyślnych ustawień pulpitu. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Wybierz jedno lub kilka typów MIME po czym określ jak mają być przetwarzane używając kontrolek na dole ramki Use Desktop preferences by default Użyj domyślnie ustawień Pulpitu Select one or several file types, then use the controls in the frame below to change how they are processed Wybierz jeden lub kilka typów pliku, następnie wskaż w ramce poniżej jak mają zostać przetworzone Exception to Desktop preferences Wyjątki dla ustawień Pulpitu Action (empty -> recoll default) Czyń (pusty -> recoll domyślnie) Apply to current selection Użyj dla obecnego wyboru Recoll action: Recoll zachowanie: current value obecna wartość Select same Wybierz to samo <b>New Values:</b> <b>Nowa wartość:</b> Webcache Webcache editor Edytor webcache Search regexp Szukaj regexp TextLabel WebcacheEdit Copy URL Kopiuj adres URL Unknown indexer state. Can't edit webcache file. Nieznany stan indeksatora. Można't edytować plik pamięci podręcznej. Indexer is running. Can't edit webcache file. Indekser jest uruchomiony. Można't edytować plik webcache Delete selection Usuń zaznaczenie Webcache was modified, you will need to run the indexer after closing this window. Pamięć podręczna została zmodyfikowana, musisz uruchomić indeks po zamknięciu tego okna. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url Adres URL Date Data Size URL Adres URL WinSchedToolW Error Błąd Configuration not initialized Konfiguracja nie została zainicjowana <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Ponowne indeksowanie wsadowego harmonogramu</h3><p>Używamy standardowego harmonogramu zadań systemu Windows. Program zostanie uruchomiony po kliknięciu poniższego przycisku.</p><p>Możesz użyć pełnego interfejsu (<i>Utwórz zadanie</i> w menu po prawej stronie), lub uproszczony kreator <i>Utwórz zadanie podstawowe</i> . W obu przypadkach Kopiuj/Wklej ścieżkę pliku wsadowego wymienioną poniżej jako czynność <i></i> do wykonania.</p> Command already started Polecenie już rozpoczęte Recoll Batch indexing Indeksowanie partii recoll Start Windows Task Scheduler tool Uruchom narzędzie Harmonogramu Zadań Windows Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Kolejka indeksowania kradzieży beagli Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle NIE MUSZĄ być uruchomione. Umożliwia przetwarzanie kolejki beagle w celu indeksowania historii sieci Firefoks.<br>(należy również zainstalować wtyczkę Firefox Beagle) Web cache directory name Nazwa katalogu pamięci podręcznej The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Nazwa katalogu, w którym chcesz przechowywać pamięć podręczną odwiedzanych stron internetowych.<br>Ścieżka nie jest bezwzględna w stosunku do katalogu konfiguracyjnego. Max. size for the web cache (MB) Max. rozmiar pamięci podręcznej (MB) Entries will be recycled once the size is reached Wpisy będą odnowione gdy osiągnie rozmiar Web page store directory name Nazwa katalogu dla trzymania stron web The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Nazwa katalogu, w którym można przechowywać kopie odwiedzanych stron internetowych.<br>Ścieżka nie jest bezwzględna w stosunku do katalogu konfiguracyjnego. Max. size for the web store (MB) Maks. rozmiar dla schowka webowego (MB) Process the WEB history queue Przejdź do kolejki historii web Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Włącza indeksowanie odwiedzonych stron Firefoksu.<br>(musisz również zainstalować wtyczkę Firefoksa Recoll) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Wpisy zostaną poddane recyklingowi po osiągnięciu rozmiaru.<br>Tylko zwiększenie rozmiaru ma sens, ponieważ zmniejszenie wartości nie spowoduje obcięcia istniejącego pliku (tylko miejsce na odpadach). confgui::ConfIndexW Can't write configuration file Nie można pisać w pliku konfiguracji Recoll - Index Settings: Recoll - Ustawienia indeksu: confgui::ConfParamFNW Browse Przeglądaj Choose Wybierz confgui::ConfParamSLW + + - - Add entry Dodaj wpis Delete selected entries Usuń wybrane wpisy ~ ~ Edit selected entries Edytuj wybrane wpisy confgui::ConfSearchPanelW Automatic diacritics sensitivity Automatyczna czułość na diakrytyki <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatyczne wyzwalanie wrażliwości diakrytycznej jeśli wyszukiwane hasło ma znaki akcentowane (nie w unac_except_trans). W innym przypadku musisz użyć języka zapytania i modyfikatora <i>D</i> , aby określić czułość diakrytyczną. Automatic character case sensitivity Automatyczna czułość wielkości znaków <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatyczne wyzwalanie czułości liter, jeśli wpis ma wielkie litery w jednej z pozycji oprócz pierwszej pozycji. W innym przypadku musisz użyć języka zapytania i modyfikatora <i>C</i> , aby określić wielkość liter. Maximum term expansion count Maksymalna liczba rozszerzeń terminu <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximum expansion count for a single term (np.: when use wildcards). Wartość domyślna 10 000 jest rozsądna i pozwoli uniknąć zapytań, które pojawiają się w stanie zamrożonym, gdy silnik porusza się na liście terminów. Maximum Xapian clauses count Maksymalna liczba klauzuli Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Struktura danych sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sdd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sd_sdd W niektórych przypadkach wynik czasowej ekspansji może być mnożnikowy i chcemy uniknąć wykorzystywania nadmiernej pamięci. Domyślna wartość 100 000 powinna być w większości przypadków wystarczająco wysoka i kompatybilna z aktualnymi typowymi konfiguracjami sprzętu. confgui::ConfSubPanelW Global Globalnie Max. compressed file size (KB) Maks. rozmiar skompresowanego pliku (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Wartość progowa od której skompresowane pliki przestają być przetwarzane. Brak limitu to -1, 0 wyłącza przetwarzanie plików skompresowanych. Max. text file size (MB) Maks. rozmiar plików tekstowych (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Wartość progowa po której pliki tekstowe przestają być przetwarzane. Brak limitu to -1. Używaj do wykluczenia gigantycznych plików dziennika systemowego (logs). Text file page size (KB) Rozmiar strony pliku tekstowego (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Indeksując dzieli plik tekstowy na podane kawałki (jeśli różne od -1). Pomocne przy szukaniu w wielkich plikach (np.: dzienniki systemowe). Max. filter exec. time (S) Maks. czas filtrowania (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Filtry zewnętrzne pracujące dłużej niż to zostanie przerwane. Dotyczy to rzadkiego przypadku (np. postscript), w którym dokument może spowodować dodanie filtra do pętli do -1 dla braku limitu. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Przerywa po tym czasie zewnętrzne filtrowanie. Dla rzadkich przypadków (np.: postscript) kiedy dokument może spowodować zapętlenie filtrowania. Brak limitu to -1. Only mime types Tylko typy mime An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Lista ekskluzywnych typów indeksowanych mime.<br>Nic innego nie zostanie zindeksowane. Zwykle puste i nieaktywne Exclude mime types Wyklucz typy mime Mime types not to be indexed Typy Mime nie mają być indeksowane Max. filter exec. time (s) Maks. czas wykonania filtra (s) confgui::ConfTopPanelW Top directories Szczytowe katalogi The list of directories where recursive indexing starts. Default: your home. Lista katalogów rekursywnego indeksowania. Domyślnie: Twój katalog domowy. Skipped paths Wykluczone ścieżki These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Są to nazwy katalogów, które nie zostaną wprowadzone.<br> Może zawierać karty wieloznaczne. Musi odpowiadać ścieżkom obserwowanym przez indeksatora (np. jeśli topdirs zawiera '/home/me' i '/home' jest w rzeczywistości linkiem do '/usr/home', poprawny wpis ścieżki pominiętej będzie '/home/me/tmp*', nie '/usr/home/me/tmp*') Stemming languages Reguły ciosania (ang. stemming languages) The languages for which stemming expansion<br>dictionaries will be built. Języki, dla których zostaną zbudowane dodatkowe<br>słowniki. Log file name Nazwa pliku dziennika (logs) The file where the messages will be written.<br>Use 'stderr' for terminal output Plik w którym będą zapisywane wiadomości.<br>Użyj 'stderr' dla terminali wyjściowych Log verbosity level Poziom stężenia komunikatu This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Ta wartość dostosowuje ilość wiadomości,<br>od tylko błędów do wielu danych debugowania. Index flush megabytes interval Interwał (megabajty) opróżniania indeksowania This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Ta wartość dostosowuje ilość danych, które są indeksowane między spłukiwaniami na dysku.<br>Pomaga to kontrolować użycie pamięci indeksatora. Domyślnie 10MB Max disk occupation (%) Maks. zużycie dysku (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Jest to procent zajęć dysku, gdzie indeksowanie nie powiedzie się i zatrzyma (aby uniknąć wypełniania dysku).<br>0 oznacza brak limitu (domyślnie). No aspell usage Brak użycia Aspell Aspell language Język Aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Język słownika aspektu powinien wyglądać jak 'en' lub 'fr' . .<br>Jeśli ta wartość nie jest ustawiona, środowisko NLS zostanie użyte do jej obliczenia, co zwykle działa. o dostrzec to, co jest zainstalowane w systemie, wpisz 'aspell config' i szukaj . w plikach w katalogu 'data-dir'. Database directory name Nazwa katalogu bazy danych The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Nazwa katalogu, w którym ma być przechowywany indeks<br>Ścieżka niebezwzględna jest przyjmowana w stosunku do katalogu konfiguracyjnego. Domyślnym jest 'xapiandb'. Use system's 'file' command Użyj polecenia systemu's 'plik' Use the system's 'file' command if internal<br>mime type identification fails. Use the system's 'file' command if internal<br>mime type identification fails. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Wyłącza używanie aspektu do generowania przybliżenia pisowni w narzędziu eksploratora terminu.<br> Przydatne jeśli aspekt jest nieobecny lub nie działa. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Język słownika aspektu powinien wyglądać jak 'en' lub 'fr' . .<br>Jeśli ta wartość nie jest ustawiona, środowisko NLS zostanie użyte do jej obliczenia, co zwykle działa. Aby dowiedzieć się, co jest zainstalowane w systemie, wpisz 'aspell config' i szukaj . w plikach w katalogu 'data-dir'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Nazwa katalogu, w którym ma być przechowywany indeks<br>Ścieżka niebezwzględna jest przyjmowana w stosunku do katalogu konfiguracyjnego. Domyślnym jest 'xapiandb'. Unac exceptions Wyjątki niezwiązane <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Są to wyjątki od mechanizmu unac, który domyślnie usuwa wszystkie diakrytyki i wykonuje dekompozycję kanoniczną. Możesz nadpisać nieakcentowanie niektórych znaków, w zależności od języka i określić dodatkowe rozkłady znaków. . dla ligatur. W każdym oddzielonym spacją wpis pierwszy znak jest jednym źródłowym, a reszta jest tłumaczeniem. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Są to nazwy ścieżek katalogów, których indeksowanie nie wprowadzi.<br>Elementy ścieżki mogą zawierać karty wieloznaczne. Wpisy muszą odpowiadać ścieżkom obserwowanym przez indeksatora (np. jeśli topdirs zawiera '/home/me' i '/home' jest w rzeczywistości linkiem do '/usr/home', poprawny wpis ścieżki pominiętej będzie '/home/me/tmp*', nie '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Maksymalne zajęcie dysku (%, 0 oznacza brak limitu) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Jest to procent wykorzystania dysku - całkowite użycie dysku, a nie rozmiar indeksu - przy którym indeksowanie nie będzie udane i zatrzymane.<br>The default value of 0 removes any limit. uiPrefsDialogBase User preferences Ustawienia User interface Wygląd Number of entries in a result page Liczba wyników na stronie If checked, results with the same content under different names will only be shown once. Wyświetl tylko raz gdy tak sama zawartość (choć różne nazwy) Hide duplicate results. Ukryj duplikaty w wynikach. Highlight color for query terms Podświetl terminy z zapytania Result list font Czcionka listy wyników Opens a dialog to select the result list font Otwiera okno wyboru czcionek Helvetica-10 Helvetica-10 Resets the result list font to the system default Reset czcionki wyników do domyślnej Reset Reset Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Określa format dla każdego punktu listy wyników. Użyj formatu qt html i zamienników podobnych do wydruku:<br>%A Abstract<br> %D Date<br> %I Ikona nazwa obrazu<br> %K Słowa kluczowe (jeśli istnieje)<br> %L Podgląd i edycja linków<br> %M Typ Mime<br> %N Numer wyniku<br> %R Procent istotności<br> %S Rozmiar informacji<br> %T Tytuł<br> %U Url<br> Result paragraph<br>format string Wynik paragrafu<br>w formacie ciągu Texts over this size will not be highlighted in preview (too slow). Teksty powyżej tego rozmiaru będą ukryte w podglądzie (zbyt wolne). Maximum text size highlighted for preview (megabytes) Maks. rozmiar tekstu dla wyróżnienia w podglądzie (MB) Use desktop preferences to choose document editor. Użyj ustawień pulpitu aby wybrać edytor dokumentów. Choose editor applications Wybierz edytor aplikacji Display category filter as toolbar instead of button panel (needs restart). Wyświetl filtr kategorii jako pasek zamiast panelu (wymagany restart). Auto-start simple search on whitespace entry. Proste szukanie gdy użyto biłych znaków we wpisie. Start with advanced search dialog open. Rozpocznij oknem zaawansowanego szukania. Start with sort dialog open. Rozpocznij z otwartym oknem sortowania. Remember sort activation state. Pamiętaj stan sortowania. Prefer Html to plain text for preview. Użyj HTML (zamiast czysty tekst) dla podglądu. Search parameters Parametry szukania Stemming language Język ciosania A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Wyszukanie dla [rolling stones] (2 terminy) zostanie zamienione na [rolling or stones or (rolling phrase 2 stones)]. To powinno dać pierwszeństwo wynikom, dokładnie tak jak zostały wpisane. Automatically add phrase to simple searches Automatycznie dodaj frazę do szukania prostego Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Mam budować streszczenie dla wyników po przez użycie kontekstu teminów zapytania? Może zwalniać dla dużych dokumentów. Dynamically build abstracts Buduj streszczenia dynamicznie Do we synthetize an abstract even if the document seemed to have one? Tworzyć sztuczne streszczenie nawet jeśli dokument ma własne? Replace abstracts from documents Zamień streszczenia z dokumentów Synthetic abstract size (characters) Rozmiar sztucznego streszczenia (w znakach) Synthetic abstract context words Kontekstowe wyrazy sztucznego streszczenia The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Wyrazy z listy zostaną automatycznie zmienione w klauzule ext:xxx we wpisach języka zapytań. Query language magic file name suffixes. Magiczne przyrostki nazw plików języka zapytań Enable Włącz External Indexes Zewnętrzne indeksy Toggle selected Odwróc zaznaczenie Activate All Aktywuj wszystko Deactivate All Deaktywuj wszystko Remove from list. This has no effect on the disk index. Usuń z listy. Brak skutku dla indeksu na dysku. Remove selected Usuń zaznaczenie Click to add another index directory to the list Kliknij, aby dodać inny katalog indeksu do listy Add index Dodaj indeks Apply changes Zastosuj zmiany &OK &Ok Discard changes Porzuć zmiany &Cancel &Anuluj Abstract snippet separator Oddzielacz snipetu streszczenia Use <PRE> tags instead of <BR>to display plain text as html. Użyj <PRE> tagów zamiast <BR>, aby wyświetlić zwykły tekst jako html. Lines in PRE text are not folded. Using BR loses indentation. Linie w tekście PRE nie są złożone. Korzystanie z BR traci wcięcie. Style sheet Arkusz stylów Opens a dialog to select the style sheet file Otwiera okno wyboru arkusza stylów Choose Wybierz Resets the style sheet to default Reset arkusza stylów do domyślnych Lines in PRE text are not folded. Using BR loses some indentation. Linie w tekście PRE nie są złożone. Korzystanie z BR traci pewne wcięcia. Use <PRE> tags instead of <BR>to display plain text as html in preview. Użyj <PRE> tagów zamiast <BR>, aby wyświetlić zwykły tekst jako html w podglądzie. Result List Lista wyników Edit result paragraph format string Zmień format paragrafu dla wyniku Edit result page html header insert Zmień nagłówek HTML dla strony wyników Date format (strftime(3)) Format daty (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Próg częstotliowści procentowej dla której terminy wew. autofrazy nie są używane. Częste terminy są powodem słabej wydajności fraz. Pominięte terminy zwiększają rozlużnienie frazy oraz zmniejszanją wydajność autofrazy. Domyślna wartość to 2 (%). Autophrase term frequency threshold percentage Procentowy próg częstości dla terminu Autofrazy Plain text to HTML line style Styl linii czystego tekstu do HTML Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Linie w PRE nie są zwijane. Użycie BR zaciera wcięcia. PRE + Zawijaj styl może być tym co szukasz. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + zawijanie Exceptions Wyjątki Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Typy Mime które nie powinny być przekazywane do xdg-open nawet wtedy, gdy "Ustawienia pulpitu" są ustawione.<br> Przydatne do przekazania numeru strony i opcji ciągu znaków, np. zdarzenia. Disable Qt autocompletion in search entry. Wyłącz podpowiedź Qt dla wpisu szukania Search as you type. Szukaj podczas pisania. Paths translations Ścieżki tłumaczeń Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Kliknij by dodać kolejny katalog do listy. Możesz wybrać zarówno katalog konfiguracji Recoll jak i indeks Xapian. Snippets window CSS file Okno snippetów CSS Opens a dialog to select the Snippets window CSS style sheet file Otwórz okno by wybrać snipet CSS Resets the Snippets window style Reset stylu okna Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Zdecyduj, czy filtry dokumentów są wyświetlane jako przyciski radiowe, komoboks paska narzędzi lub menu. Document filter choice style: Styl filtra dokumentu: Buttons Panel Panel przycisków Toolbar Combobox Pasek narzędzi Comboboks Menu Menu Show system tray icon. Pokaż ikonę zasobnika systemowego. Close to tray instead of exiting. Blisko zasobnika zamiast wyjść. Start with simple search mode Rozpocznij z prostym trybem wyszukiwania Show warning when opening temporary file. Pokaż ostrzeżenie podczas otwierania pliku tymczasowego. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Styl użytkownika do zastosowania w oknie snippet.<br> Uwaga: wstawienie nagłówka strony wyników jest również zawarte w nagłówku okna snippet. Synonyms file Plik synonimów Highlight CSS style for query terms Podświetl styl CSS dla terminów zapytania Recoll - User Preferences Recoll - Ustawienia użytkownika Set path translations for the selected index or for the main one if no selection exists. Ustaw tłumaczenia ścieżki dla wybranego indeksu lub głównego jeśli nie ma wyboru. Activate links in preview. Aktywuj linki w podglądzie. Make links inside the preview window clickable, and start an external browser when they are clicked. Utwórz linki wewnątrz okna podglądu i uruchom zewnętrzną przeglądarkę po kliknięciu. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Zapytaj terminy podświetlające wyniki. <br>Może spróbuj coś takiego "kolor:red;tło:żółty" dla czegoś bardziej ożywionego niż domyślny niebieski... Start search on completer popup activation. Rozpocznij wyszukiwanie na aktywacji kompletnego okienka. Maximum number of snippets displayed in the snippets window Maksymalna liczba snippetów wyświetlanych w oknie fragmentów Sort snippets by page number (default: by weight). Sortuj fragmenty według numeru strony (domyślnie: według wagi). Suppress all beeps. Usuń wszystkie sygnały. Application Qt style sheet Arkusz stylów aplikacji Qt Limit the size of the search history. Use 0 to disable, -1 for unlimited. Ogranicz rozmiar historii wyszukiwania. Użyj 0 aby wyłączyć, -1 dla nieograniczonych. Maximum size of search history (0: disable, -1: unlimited): Maksymalny rozmiar historii wyszukiwania (0: wyłączony, -1: nieograniczony): Generate desktop notifications. Wygeneruj powiadomienia pulpitu. Misc Różne Work around QTBUG-78923 by inserting space before anchor text Pracuj wokół QTBUG-78923 wstawiając spację przed kotwicą tekstu Display a Snippets link even if the document has no pages (needs restart). Wyświetl link Snippets nawet jeśli dokument nie ma żadnych stron (wymaga ponownego uruchomienia). Maximum text size highlighted for preview (kilobytes) Maksymalny rozmiar tekstu podświetlony do podglądu (kilobajty) Start with simple search mode: Rozpocznij z prostym trybem wyszukiwania: Hide toolbars. Ukryj paski narzędzi. Hide status bar. Ukryj pasek stanu. Hide Clear and Search buttons. Ukryj przyciski Wyczyść i Szukaj. Hide menu bar (show button instead). Ukryj pasek menu (zamiast tego pokaż przycisk). Hide simple search type (show in menu only). Ukryj prosty typ wyszukiwania (pokaż tylko w menu). Shortcuts Skróty Hide result table header. Ukryj nagłówek tabeli wyników. Show result table row headers. Pokaż nagłówki tabeli wyników. Reset shortcuts defaults Resetuj domyślne skróty Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Wyłącz skróty Ctrl+[0-9]/[a-z] do skakania do wierszy tabeli. Use F1 to access the manual Użyj F1 aby uzyskać dostęp do podręcznika Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Prosty typ wyszukiwania Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Tryb ciemny Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Tabela wyników Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_it.ts0000644000175000017500000071517314444307651014265 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Tutti i termini Any clause Qualsiasi termine texts testi spreadsheets fogli di calcolo presentations presentazioni media multimediali messages messaggi other altri Bad multiplier suffix in size filter Suffisso moltiplicatore errato nel filtro di dimensione text testo spreadsheet foglio di calcolo presentation presentazione message messaggio Advanced Search Ricerca Avanzata History Next Cronologia Successiva History Prev Storia Precedente Load next stored search Carica la ricerca successiva memorizzata Load previous stored search Carica la ricerca precedente memorizzata AdvSearchBase Advanced search Ricerca avanzata Restrict file types Limita i tipi di file Save as default Salva come default Searched file types Ricerca tipo file All ----> Tutti ----> Sel -----> Sel -----> <----- Sel <----- Sel <----- All <----- Tutti Ignored file types Ignora i file di questo tipo Enter top directory for search Scrivi la directory base per la ricerca Browse Esplora Restrict results to files in subtree: Limita i risultati alla sotto-directory: Start Search Cerca Search for <br>documents<br>satisfying: Cerca i documenti<br>che contengono: Delete clause Elimina condizione Add clause Aggiungi condizione Check this to enable filtering on file types Contrassegna per abilitare la ricerca sul tipo di file By categories Per categorie Check this to use file categories instead of raw mime types Contrassegna per usare le categorie al posto dei tipi mime Close Chiudi All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Tutti i campi non vuoti a destra saranno combinati con AND ("Tutte le clausole" choice) o OR ("Qualsiasi clausola" choice) congiunture. <br>"Qualsiasi" "Tutti" e "Nessuno" tipi di campo può accettare un mix di parole semplici, e frasi racchiuse in virgolette doppie.<br>I campi senza dati vengono ignorati. Invert Invert Minimum size. You can use k/K,m/M,g/G as multipliers Dimensione minima. È possibile utilizzare k/K,m/M,g/G come moltiplicatori Min. Size Min. Size Maximum size. You can use k/K,m/M,g/G as multipliers Dimensione massima. È possibile utilizzare k/K,m/M,g/G come moltiplicatori Max. Size Dimensione Massima Select Seleziona Filter Filtro From Da To A Check this to enable filtering on dates Seleziona questa opzione per abilitare il filtro alle date Filter dates Date del filtro Find Trova Check this to enable filtering on sizes Seleziona questa opzione per abilitare il filtraggio sulle dimensioni Filter sizes Dimensioni filtro Filter birth dates ConfIndexW Can't write configuration file Impossibile scrivere il file di configurazione Global parameters Parametri globali Local parameters Parametri locali Search parameters Parametri per la ricerca Top directories Cartella superiore The list of directories where recursive indexing starts. Default: your home. Lista delle cartelle in cui inizia lìindicizzazione recorsiva. Di default è la tua home. Skipped paths Indirizzi saltati These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Questi sono i pathname delle directory che l'indicizzazione non entrerà.<br>Gli elementi del tracciato possono contenere caratteri jolly. Le voci devono corrispondere ai percorsi visti dall'indicizzatore (ad es. if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', una corretta voce skippedPath sarebbe '/home/me/tmp*', non '/usr/home/me/tmp*') Stemming languages Lingue per la radice The languages for which stemming expansion<br>dictionaries will be built. Lingue per le quali verrà costruito<br>il dizionario delle espansioni radicali. Log file name Nome del file di log The file where the messages will be written.<br>Use 'stderr' for terminal output Il file dove verranno scritti i messaggi.<br>Usa 'stderr' per il terminale Log verbosity level Livello di verbosità del log This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Questo valore regola il numero dei messaggi,>br>dai soli errori a mole indicazioni per il debug. Index flush megabytes interval Intervallo in megabite per il salvataggio intermedio dell'indice This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Questo valore regola il volume di dati da indicizzare tra un salvataggio e l'altro.<br>Aiuta a controllare l'uso della memoria. Di default è post uguale a 10Mb This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Questa è la percentuale di utilizzo del disco - utilizzo totale del disco, dimensione non indice - alla quale l'indicizzazione fallirà e si fermerà.<br>Il valore predefinito di 0 rimuove ogni limite. No aspell usage Non usare aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Disabilita l'uso di aspell per generare approssimazione ortografica nel termine strumento esploratore.<br> Utile se aspell è assente o non funziona. Aspell language Lingua di aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. La lingua per il dizionario aspell. Dovrebbe apparire 'it' o 'fr' . .<br>Se questo valore non è impostato, l'ambiente NLS verrà utilizzato per calcolarlo, che di solito funziona. Per avere un'idea di ciò che è installato sul vostro sistema, digita 'aspell config' e cercare . ai file all'interno della 'data-dir' directory. Database directory name Nome della cartella del database The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Il nome di una directory dove memorizzare l'indice<br>Un percorso non assoluto viene preso rispetto alla directory di configurazione. Il valore predefinito è 'xapiandb'. Unac exceptions Eccezioni di Unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Queste sono eccezioni al meccanismo unac che, per impostazione predefinita, rimuove tutti i diacritici ed esegue decomposizione canonica. È possibile sovrascrivere l'enfasi per alcuni caratteri, a seconda della lingua, e specificare decomposizioni aggiuntive, e. . per le legature. In ogni voce separata da spazio, il primo carattere è quello sorgente, e il resto è la traduzione. Process the WEB history queue Elabora la coda di cronologia WEB Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Abilita l'indicizzazione delle pagine visitate da Firefox.<br>(è necessario installare anche il plugin Firefox Recoll) Web page store directory name Nome directory negozio pagina web The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Il nome di una directory dove archiviare le copie delle pagine web visitate.<br>Un percorso non assoluto è preso rispetto alla directory di configurazione. Max. size for the web store (MB) Dimensione massima per il web store (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Le voci saranno riciclate una volta raggiunta la dimensione.<br>Aumentare la dimensione ha senso solo perché la riduzione del valore non troncerà un file esistente (solo lo spazio di scarto alla fine). Automatic diacritics sensitivity Sensibilità automatica dei diacritici <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Attiva automaticamente la sensibilità dei diacritici se il termine di ricerca ha caratteri accentati (non in unac_except_trans). Altrimenti è necessario utilizzare il linguaggio di query e il modificatore <i>D</i> per specificare la sensibilità dei diacritici. Automatic character case sensitivity Sensibilità automatica delle maiuscole <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Attiva automaticamente la sensibilità delle lettere maiuscole se la voce ha caratteri maiuscoli in qualsiasi ma la prima posizione. Altrimenti è necessario utilizzare la lingua di query e il modificatore <i>C</i> per specificare la sensibilità delle maiuscole e minuscole. Maximum term expansion count Numero massimo di espansione a termine <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Numero massimo di espansione per un singolo termine (ad esempio: quando si usano caratteri jolly). Il valore predefinito di 10 000 è ragionevole ed eviterà le interrogazioni che appaiono congelate mentre il motore sta camminando la lista dei termini. Maximum Xapian clauses count Numero massimo di clausole Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Numero massimo di clausole elementari che aggiungiamo a una singola query Xapiana. In alcuni casi, il risultato di espansione del termine può essere moltiplicativo e vogliamo evitare di usare la memoria eccessiva. Nella maggior parte dei casi il valore predefinito di 100 000 dovrebbe essere sufficientemente elevato e compatibile con le attuali configurazioni hardware tipiche. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... I linguaggi per i quali saranno costruiti dizionari di espansione.<br>Vedi la documentazione degli stemmer di Xapian per i possibili valori. Per esempio inglese, francese, tedesco... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. La lingua per il dizionario aspell. I valori sono codici linguistici a 2 lettere, ad esempio 'it', 'fr' . .<br>Se questo valore non è impostato, l'ambiente NLS verrà utilizzato per calcolarlo, che di solito funziona. Per avere un'idea di ciò che è installato sul vostro sistema, digita 'aspell config' e cercare . ai file all'interno della 'data-dir' directory. Indexer log file name Nome del file log indicizzatore If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Se vuoto, verrà utilizzato il valore del nome del file di registro di cui sopra. Potrebbe essere utile avere un registro separato per scopi diagnostici perché il registro comune verrà cancellato quando<br>verrà avviata la GUI. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Percentuale soglia totale del disco alla quale smettiamo di indicizzare<br>Per esempio il 90% per fermarsi al 90% pieno, 0 o 100 significa nessun limite) Web history Cronologia web Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Solo tipi mime An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Una lista esclusiva di tipi di mime indicizzati.<br>Niente altro sarà indicizzato. Normalmente vuoto e inattivo Exclude mime types Escludi tipi mime Mime types not to be indexed Tipi MIME da non indicizzare Max. compressed file size (KB) Dimensione massima del file compresso (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Questo valore imposta una soglia oltre la quale i file compressi non saranno elaborati. Impostare a -1 per nessun limite, a 0 per nessuna decompressione mai. Max. text file size (MB) Dimensione massima del file di testo (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Questo valore imposta una soglia oltre la quale i file di testo non saranno elaborati. Impostare a -1 per nessun limite. Questo è per escludere i file di registro mostri dall'indice. Text file page size (KB) Dimensione pagina del file di testo (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Se questo valore è impostato (non uguale a -1), i file di testo saranno divisi in pezzi di questa dimensione per l'indicizzazione. Questo aiuterà a cercare file di testo molto grandi (ie: file di registro). Max. filter exec. time (s) Max. filtro tempo esecuzione (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. I filtri esterni che funzionano più a lungo di questo verranno interrotti. Questo è il raro caso (cioè: postscript) in cui un documento potrebbe causare il caricamento di un filtro. Impostare a -1 per nessun limite. Global Globale CronToolW Cron Dialog Cron Dialog <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> pianificazione di indicizzazione batch (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ogni campo può contenere un carattere jolly (*), un unico valore numerico, elenchi separati da virgole (1,3,5) e intervalli (1-7). Più in generale, i campi saranno utilizzati <span style=" font-style:italic;">come è</span> all'interno del file crontab e la sintassi crontab completa può essere utilizzata, vedere crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Per esempio, inserendo <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Giorni, </span><span style=" font-family:'Courier New,courier';">12, 9</span> in <span style=" font-style:italic;">Ore</span> e <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minuti</span> inizierebbe recollindex ogni giorno alle 12:15 e alle 19:15</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Un programma con attivazioni molto frequenti è probabilmente meno efficiente dell'indicizzazione in tempo reale.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Giorni della settimana (* o 0-7, 0 o 7 è la domenica) Hours (* or 0-23) Ore (* o 0-23) Minutes (0-59) Minuti (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Clicca su <span style=" font-style:italic;">Disabilita</span> per fermare l'indicizzazione automatica del batch, <span style=" font-style:italic;">Abilita</span> per attivarlo, <span style=" font-style:italic;">Annulla</span> per non cambiare nulla.</p></body></html> Enable Abilita Disable Disabilita It seems that manually edited entries exist for recollindex, cannot edit crontab Sembra che le voci modificate manualmente esistano per recollindex, non è possibile modificare crontab Error installing cron entry. Bad syntax in fields ? Errore durante l'installazione della voce cron. Sintassi errata nei campi? EditDialog Dialog Dialogo EditTrans Source path Percorso sorgente Local path Percorso locale Config error Errore di configurazione Original path Percorso originale EditTransBase Path Translations Traduzioni Tracciato Setting path translations for Impostazione delle traduzioni del percorso per Select one or several file types, then use the controls in the frame below to change how they are processed Selezionare uno o più tipi di file, quindi utilizzare i controlli nel riquadro sottostante per modificare come vengono elaborati Add Aggiungi Delete Elimina Cancel Annulla Save Salva FirstIdxDialog First indexing setup Prima configurazione di indicizzazione <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Sembra che l'indice per questa configurazione non esista.</span><br /><br />Se vuoi solo indicizzare la tua directory home con un insieme di valori predefiniti ragionevoli, premi il pulsante <span style=" font-style:italic;">Inizia l'indicizzazione ora</span> . Sarete in grado di regolare i dettagli più tardi. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Se vuoi più controllo, utilizzare i seguenti link per regolare la configurazione di indicizzazione e la pianificazione.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Questi strumenti possono essere consultati successivamente dal menu <span style=" font-style:italic;">Preferenze</span> .</p></body></html> Indexing configuration Configurazione indicizzazione This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Questo ti permetterà di regolare le directory che vuoi indicizzare, e altri parametri come percorsi di file o nomi esclusi, set di caratteri predefiniti, ecc. Indexing schedule Schema indicizzazione This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Questo ti permetterà di scegliere tra l'indicizzazione batch e in tempo reale e di impostare una pianificazione automatica per l'indicizzazione batch (usando cron). Start indexing now Inizia l'indicizzazione ora FragButs %1 not found. %1 non trovato. %1: %2 %1: %2 Fragment Buttons Pulsanti Frammento Query Fragments Frammenti Di Interrogazione IdxSchedW Index scheduling setup Configurazione pianificazione indice <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> L'indicizzazione può essere eseguita in modo permanente, l'indicizzazione dei file quando cambiano, o eseguono a intervalli discreti. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Leggere il manuale può aiutarti a decidere tra questi approcci (premere F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Questo strumento può aiutarti a impostare un programma per automatizzare l'esecuzione dell'indicizzazione batch, o inizia l'indicizzazione in tempo reale quando accedi (o entrambi, che raramente ha senso). </p></body></html> Cron scheduling Pianificazione cron The tool will let you decide at what time indexing should run and will install a crontab entry. Lo strumento ti permetterà di decidere in quale momento l'indicizzazione dovrebbe essere eseguita e installerà una voce crontab . Real time indexing start up Indicizzazione in tempo reale avvio Decide if real time indexing will be started when you log in (only for the default index). Decidi se l'indicizzazione in tempo reale verrà avviata quando accedi (solo per l'indice predefinito). ListDialog Dialog Dialogo GroupBox GroupBox Main No db directory in configuration Nessuna directory per il DB di base nella configurazione Could not open database in Impossibile aprire il database in . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. Clicca 'Annulla' se vuoi editare il file di configurazione prima di iniziare l'indicizzazione, oppure 'OK' se vuoi procedere. Configuration problem (dynconf Problema di configurazione (dynconf "history" file is damaged or un(read)writeable, please check or remove it: "cronologia" il file è danneggiato o non (letto) scrivibile, si prega di controllare o rimuoverlo: "history" file is damaged, please check or remove it: "cronologia" file danneggiato, si prega di controllare o rimuoverlo: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Cerca: &Next &Successivo &Previous &Precedente Match &Case Rispetta &Maiuscole/minuscole Clear Cancella Creating preview text Creazione del testo per l'anteprima Loading preview text into editor Caricamento anteprima del testo nell'editor Cannot create temporary directory Impossibile creare directory temporanea Cancel Annulla Close Tab Chiudi Tab Missing helper program: Manca il programma di filtro esterno: Can't turn doc into internal representation for Impossibile tradurre il documento per la rappresentazione interna Cannot create temporary directory: Impossibile creare la directory temporanea: Error while loading file Errore durante il caricamento del file Form Modulo Tab 1 Tab 1 Open Apri Canceled Annullato Error loading the document: file missing. Errore nel caricare il documento: file mancante. Error loading the document: no permission. Errore nel caricamento del documento: nessun permesso. Error loading: backend not configured. Errore nel caricamento: backend non configurato. Error loading the document: other handler error<br>Maybe the application is locking the file ? Errore durante il caricamento del documento: altro errore del gestore<br>Forse l'applicazione sta bloccando il file ? Error loading the document: other handler error. Errore nel caricamento del documento: altro errore del gestore. <br>Attempting to display from stored text. <br>Tentativo di visualizzazione dal testo memorizzato. Could not fetch stored text Impossibile recuperare il testo memorizzato Previous result document Documento di risultato precedente Next result document Prossimo documento di risultato Preview Window Finestra Di Anteprima Close Window Chiudi Finestra Next doc in tab Prossimo documento nella scheda Previous doc in tab Documento precedente nella scheda Close tab Chiudi scheda Print tab Print tab Close preview window Chiudi finestra di anteprima Show next result Mostra il risultato successivo Show previous result Mostra il risultato precedente Print Stampa PreviewTextEdit Show fields Mostra campi Show main text Mostra testo principale Print Stampa Print Current Preview Anteprima Di Stampa Corrente Show image Mostra immagine Select All Seleziona Tutto Copy Copia Save document to file Salva documento su file Fold lines Linee pieghevoli Preserve indentation Preserva rientro Open document Apri documento Reload as Plain Text Reload as HTML QObject Global parameters Parametri globali Local parameters Parametri locali <b>Customised subtrees <b>Ramificazioni personalizzate The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Lista delle sottocartelle nella gerarchia indicizzata<br>ove alcuni parametri devono essere ridefiniti. Predefinita: vuota. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>I parametri che seguono sono postii al livello superiore, se niente <br> o una linea vuota è selezionata nella casella sovrastante, oppure al livello della cartella selezionata.<br> Puoi aggiungere/rimuovere cartelle cliccando i bottoni +/-. Skipped names Nomi saltati These are patterns for file or directory names which should not be indexed. Questi sono modelli per i nomi delle cartelle e/o dei files che non devono vebire indicizzati. Default character set Set di caratteri di default This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Questa è la codifica caratteri usata per leggere i file che non contengono indicazioni interne sulla codifica usata, ad esempio file di testo semplice.<br>Il valore predefinito è vuoto, in modo che venga usata l'impostazione locale del sistema. Follow symbolic links Segue il link simbolico Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Segue il link simbolico durante l'indicizzazione. Di default è no, per evitare la duplicazione dell'indice Index all file names Indicizza tutti i nomi dei files Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indicizza il nome di quei files il cui contenuto non può essere identificato o processato (tipo mime non supportato). Di default è impostato a vero Beagle web history Cronologia web Beagle Search parameters Parametri per la ricerca Web history Cronologia web Default<br>character set Set di caratteri predefinito<br> Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Set di caratteri utilizzato per la lettura di file che non identificano internamente il set di caratteri, ad esempio i file di testo puro.<br>Il valore predefinito è vuoto e viene utilizzato il valore dall'ambiente NLS. Ignored endings Terminazioni ignorate These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Si tratta di terminazioni del nome file per i file che saranno indicizzati solo in base al contenuto (nessun tentativo di identificazione del tipo MIME, nessuna decompressione, nessun indicizzazione del contenuto. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Si tratta di terminazioni del nome del file per i file che saranno indicizzati solo per nome (nessun tentativo di identificazione del tipo MIME, nessuna decompressione, nessun indicizzazione del contenuto). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>I parametri che seguono sono impostati al livello superiore, se non viene selezionata niente o una riga vuota nella lista qui sopra, o per la sottocartella selezionata. È possibile aggiungere o rimuovere directory facendo clic sui pulsanti +/. QWidget Create or choose save directory Crea o scegli la directory di salvataggio Choose exactly one directory Scegli esattamente una directory Could not read directory: Impossibile leggere la directory: Unexpected file name collision, cancelling. Collisione del nome del file inattesa, annullamento. Cannot extract document: Impossibile estrarre il documento: &Preview &Anteprima &Open &Apri Open With Apri Con Run Script Esegui Script Copy &File Name Copia il nome del &File Copy &URL Copia l'&Url &Write to File &Scrivi su file Save selection to files Salva la selezione sui file Preview P&arent document/folder Anteprima documento/cartella P&arent &Open Parent document/folder &Apri documento/cartella padre Find &similar documents Trova documenti &simili Open &Snippets window Apri finestra &snippet Show subdocuments / attachments Mostra sotto-documenti / allegati &Open Parent document &Apri documento padre &Open Parent Folder &Apri Cartella Padre Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Non mostrare di nuovo. RTIToolW Real time indexing automatic start Indicizzazione automatica in tempo reale <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> L'indicizzazione può essere impostata per essere eseguita come demone, aggiornare l'indice come i file cambiano, in tempo reale. Guadagni un indice sempre aggiornato, ma le risorse del sistema vengono utilizzate in modo permanente.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Avvia l'indicizzazione del demone con la sessione del desktop. Also start indexing daemon right now. Inizia anche l'indicizzazione del demone in questo momento. Replacing: Sostituendo: Replacing file Sostituzione del file Can't create: Può't creare: Warning Attenzione Could not execute recollindex Impossibile eseguire recollindex Deleting: Eliminazione: Deleting file Eliminazione file Removing autostart Rimozione avvio automatico Autostart file deleted. Kill current process too ? Avvio automatico eliminato. Uccidi anche il processo corrente? RclCompleterModel Hits RclMain About Recoll Informazioni su Recoll Executing: [ Esecuzione di: [ Cannot retrieve document info from database Impossibile caricare informazioni del documento dal database Warning Attenzione Can't create preview window Non posso creare la finestra di anteprima Query results Risultati della ricerca Document history Cronologia dei documenti History data Cronologia dei dati Indexing in progress: Indicizzazione in corso: Files File Purge Pulisco Stemdb Database espansioni Closing Chiusura Unknown Sconosciuto This search is not active any more Questa ricerca non e' piu' attiva Can't start query: Non posso iniziare la ricerca: Bad viewer command line for %1: [%2] Please check the mimeconf file Errata linea di comando per %1: [%2] Verifica il file mimeconf Cannot extract document or create temporary file Non posso estrarre il documento o creare il file temporaneo (no stemming) (nessuna espansione) (all languages) (tutte le lingue) error retrieving stemming languages errore nel recupero delle lingue per l'espansione Update &Index Aggiorna Indice Indexing interrupted Indicizzazione interrotta Stop &Indexing Ferma &Indicizzazione All Tutti media multimediali message messaggio other altri presentation presentazione spreadsheet foglio di calcolo text testo sorted ordinati filtered filtrato External applications/commands needed and not found for indexing your file types: Applicazioni/comandi esterni necessari e non trovati per indicizzare i tipi di file: No helpers found missing Nessun aiutante trovato mancante Missing helper programs Programmi helper mancanti Save file dialog Finestra di salvataggio file Choose a file name to save under Scegli un nome file in cui salvare Document category filter Filtro categoria documento No external viewer configured for mime type [ Nessun visualizzatore esterno configurato per il tipo mime [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Il visualizzatore specificato in mimeview per %1: %2 non è stato trovato. Vuoi avviare la finestra di dialogo delle preferenze? Can't access file: Può't file di accesso: Can't uncompress file: Può't decomprimere il file: Save file Salva file Result count (est.) Conteggio dei risultati (est.) Query details Dettagli ricerca Could not open external index. Db not open. Check external indexes list. Impossibile aprire l'indice esterno. Db non è aperto. Controlla l'elenco degli indici esterni. No results found Nessun risultato trovato None Nessuno Updating Aggiornamento Done Fatto Monitor Monitor Indexing failed Indicizzazione fallita The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Il processo di indicizzazione corrente non è stato avviato da questa interfaccia. Fare clic su Ok per ucciderlo comunque, o Annulla per lasciarlo da solo Erasing index Cancellazione indice Reset the index and start from scratch ? Ripristinare l'indice e iniziare da zero ? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Interrogazione in corso.<br>A causa delle limitazioni della libreria di indicizzazione,<br>l'annullamento esce dal programma Error Errore Index not open Indice non aperto Index query error Errore di query dell'indice Indexed Mime Types Tipi Mime Indicizzati Content has been indexed for these MIME types: Il contenuto è stato indicizzato per questi tipi MIME: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Indice non aggiornato per questo file. Rifiutare il rischio di mostrare la voce sbagliata. Fare clic su Ok per aggiornare l'indice per questo file, quindi rieseguire la query quando l'indicizzazione è finita. Altrimenti, annulla. Can't update index: indexer running Can't update index: indexer in esecuzione Indexed MIME Types Tipi Mime Indicizzati Bad viewer command line for %1: [%2] Please check the mimeview file Linea di comando visualizzatore errata per %1: [%2] Si prega di controllare il file mimeview Viewer command line for %1 specifies both file and parent file value: unsupported Il visualizzatore riga di comando per %1 specifica sia il valore del file che il valore del file superiore: non supportato Cannot find parent document Impossibile trovare il documento genitore Indexing did not run yet L'indicizzazione non è ancora stata eseguita External applications/commands needed for your file types and not found, as stored by the last indexing pass in Applicazioni/comandi esterni necessari per i tipi di file e non trovati, come memorizzati dall'ultimo pass di indicizzazione in Index not up to date for this file. Refusing to risk showing the wrong entry. Indice non aggiornato per questo file. Rifiutare il rischio di mostrare la voce sbagliata. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Fare clic su Ok per aggiornare l'indice per questo file, quindi rieseguire la query quando l'indicizzazione è finita. Altrimenti, annulla. Indexer running so things should improve when it's done Indexer in esecuzione così le cose dovrebbero migliorare quando's fatto Sub-documents and attachments Sotto-documenti e allegati Document filter Filtro documento Index not up to date for this file. Refusing to risk showing the wrong entry. Indice non aggiornato per questo file. Rifiutare il rischio di mostrare la voce sbagliata. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Clic Ok per aggiornare l'indice per questo file, quindi sarà necessario ri-eseguire la query quando l'indicizzazione è fatto. The indexer is running so things should improve when it's done. L'indicizzatore è in esecuzione quindi le cose dovrebbero migliorare quando's fatto. The document belongs to an external indexwhich I can't update. Il documento appartiene ad un indice esterno che posso't aggiornare. Click Cancel to return to the list. Click Ignore to show the preview anyway. Fare clic su Annulla per tornare all'elenco. Fare clic su Ignora per visualizzare comunque l'anteprima. Duplicate documents Duplica documenti These Urls ( | ipath) share the same content: Questi URL (<unk> ipath) condividono lo stesso contenuto: Bad desktop app spec for %1: [%2] Please check the desktop file Specc app desktop errata per %1: [%2] Si prega di controllare il file desktop Bad paths Percorsi errati Bad paths in configuration file: Percorsi errati nel file di configurazione: Selection patterns need topdir I motivi di selezione richiedono topdir Selection patterns can only be used with a start directory I modelli di selezione possono essere usati solo con una directory iniziale No search Nessuna ricerca No preserved previous search Nessuna ricerca precedente conservata Choose file to save Scegli il file da salvare Saved Queries (*.rclq) Query Salvate (*.rclq) Write failed Scrittura fallita Could not write to file Impossibile scrivere sul file Read failed Lettura fallita Could not open file: Impossibile aprire il file: Load error Errore di caricamento Could not load saved query Impossibile caricare la query salvata Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Aprire una copia temporanea. Le modifiche andranno perse se non le salvate't<br/>in una posizione permanente. Do not show this warning next time (use GUI preferences to restore). Non mostrare questo avviso la prossima volta (usa le preferenze GUI per ripristinare). Disabled because the real time indexer was not compiled in. Disabilitato perché l'indicizzatore in tempo reale non è stato compilato. This configuration tool only works for the main index. Questo strumento di configurazione funziona solo per l'indice principale. The current indexing process was not started from this interface, can't kill it Il processo di indicizzazione corrente non è stato avviato da questa interfaccia, può't ucciderlo The document belongs to an external index which I can't update. Il documento appartiene ad un indice esterno che posso't aggiornare. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Fare clic su Annulla per tornare alla lista. <br>Fare clic su Ignora per mostrare comunque l'anteprima (e ricordarsi per questa sessione). Index scheduling Indice di programmazione Sorry, not available under Windows for now, use the File menu entries to update the index Siamo spiacenti, non disponibile in Windows per ora, utilizzare le voci del menu File per aggiornare l'indice Can't set synonyms file (parse error?) Può't impostare il file sinonimi (errore di interpretazione?) Index locked Indice bloccato Unknown indexer state. Can't access webcache file. Stato indicizzatore sconosciuto. Può't accedere al file webcache. Indexer is running. Can't access webcache file. Indexer è in esecuzione. Può't accedere al file webcache. with additional message: con messaggio aggiuntivo: Non-fatal indexing message: Messaggio di indicizzazione non fatale: Types list empty: maybe wait for indexing to progress? Tipi di lista vuota: forse attendere l'indicizzazione per progredire? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Il visualizzatore riga di comando per %1 specifica il file genitore ma l'URL è http[s]: non supportato Tools Strumenti Results Risultati (%d documents/%d files/%d errors/%d total files) (%d documenti/%d file/%d errori/%d file totali) (%d documents/%d files/%d errors) (%d documenti/%d file/%d errori) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Percorsi vuoti o inesistenti nel file di configurazione. Fare clic su Ok per iniziare comunque l'indicizzazione (i dati assenti non verranno eliminati dall'indice): Indexing done Indicizzazione eseguita Can't update index: internal error Può't aggiornare l'indice: errore interno Index not up to date for this file.<br> Indice non aggiornato per questo file.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Inoltre, sembra che l'ultimo aggiornamento dell'indice per il file non sia riuscito.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Fare clic su Ok per provare ad aggiornare l'indice di questo file. Sarà necessario eseguire nuovamente la query quando l'indicizzazione è fatta.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Fare clic su Annulla per tornare alla lista.<br>Fare clic su Ignora per mostrare comunque l'anteprima (e ricordarsi per questa sessione). C'è il rischio di mostrare la voce sbagliata.<br/> documents documenti document documento files file file file errors errori error errore total files) file totali) No information: initial indexing not yet performed. Nessuna informazioni: indicizzazione iniziale non ancora eseguita. Batch scheduling Programmazione lotti The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Lo strumento ti permetterà di decidere in quale momento l'indicizzazione dovrebbe essere eseguita. Utilizza il programma delle attività di Windows. Confirm Conferma Erasing simple and advanced search history lists, please click Ok to confirm Cancellare liste di cronologia di ricerca semplici ed avanzate, fare clic su Ok per confermare Could not open/create file Impossibile aprire/creare il file F&ilter F&ilter Could not start recollindex (temp file error) Impossibile avviare recollindex (errore del file temp) Could not read: Impossibile leggere: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Questo sostituirà il contenuto corrente della stringa dell'intestazione della lista dei risultati e il nome del file qss. Continuare? You will need to run a query to complete the display change. È necessario eseguire una query per completare il cambiamento del display. Simple search type Tipo di ricerca semplice Any term Qualsiasi All terms Tutti File name Nome file Query language Linguaggio di interrogazione Stemming language Linguaggio per l'espansione Main Window Finestra Principale Focus to Search Focus alla ricerca Focus to Search, alt. Concentrati su Ricerca, alt. Clear Search Cancella Ricerca Focus to Result Table Focus alla tabella dei risultati Clear search Cancella ricerca Move keyboard focus to search entry Sposta il focus della tastiera nella voce di ricerca Move keyboard focus to search, alt. Sposta il focus della tastiera per cercare, alt. Toggle tabular display Attiva/Disattiva visualizzazione tabellare Move keyboard focus to table Sposta il focus della tastiera nella tabella Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Pagina precedente Next page Pagina seguente &File &File E&xit &Esci &Tools &Strumenti &Help &Aiuto &Preferences &Preferenze Search tools Strumenti di ricerca Result list Lista risultati &About Recoll &Informazioni su Recoll Document &History C&ronologia documenti Document History Visualizza la cronologia dei documenti &Advanced Search Ricerca &Avanzata Advanced/complex Search Mostra la finestra di Ricerca avanzata &Sort parameters &Parametri ordinamento Sort parameters Configurazione dei parametri di ordinamento Next page of results Pagina seguente Previous page of results Pagina precedente &Query configuration &Configurazione ricerca &User manual &Manuale utente Recoll Ricoll Ctrl+Q Ctrl+Q Update &index Aggiorna &indice Term &explorer &Esplora l'indice Term explorer tool Strumento di esplorazione indice External index dialog Configurazione indici esterni &Erase document history &Cancella la cronologia dei documenti First page Prima pagina Go to first page of results Vai alla prima pagina dei risultati &Indexing configuration Conf&igurazione indicizzazione All Tutti &Show missing helpers &Mostra gli aiutanti mancanti PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Maiusc+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen &Schermo Intero F11 F11 Full Screen Schermo Intero &Erase search history &Cancella cronologia di ricerca sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Ordina per date dal più vecchio al più recente sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Ordina per date dal più recente al più vecchio Show Query Details Mostra Dettagli Query Show results as table Mostra i risultati come tabella &Rebuild index &Ricostruisci indice &Show indexed types &Mostra tipi indicizzati Shift+PgUp Maiusc+PgUp &Indexing schedule &Indicizzazione pianificazione E&xternal index dialog Finestra dell'indice &xternal &Index configuration &Configurazione indice &GUI configuration &Configurazione GUI &Results &Risultati Sort by date, oldest first Ordina per data, prima più vecchio Sort by date, newest first Ordina per data, prima più recente Show as table Mostra come tabella Show results in a spreadsheet-like table Mostra risultati in una tabella simile a foglio di calcolo Save as CSV (spreadsheet) file Salva come file CSV (foglio di calcolo) Saves the result into a file which you can load in a spreadsheet Salva il risultato in un file che puoi caricare in un foglio di calcolo Next Page Pagina Successiva Previous Page Pagina Precedente First Page Prima Pagina Query Fragments Frammenti Di Interrogazione With failed files retrying Riprova con i file falliti Next update will retry previously failed files Il prossimo aggiornamento riproverà i file precedentemente falliti Save last query Salva l'ultima query Load saved query Carica query salvata Special Indexing Indicizzazione Speciale Indexing with special options Indicizzazione con opzioni speciali Indexing &schedule &Pianificazione Indicizzazione Enable synonyms Abilita sinonimi &View &Visualizza Missing &helpers &Aiutanti Mancanti Indexed &MIME types Tipi &MIME indicizzati Index &statistics &Statistiche Indice Webcache Editor Editor Webcache Trigger incremental pass Passaggio incrementale innesco E&xport simple search history E&mporta una semplice cronologia di ricerca Use default dark mode Usa la modalità scura predefinita Dark mode Modalità scura &Query &Interroga Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Date del filtro Assisted complex search Filter birth dates RclTrayIcon Restore Ripristina Quit Esci RecollModel Abstract Astratto Author Autore Document size Dimensione del documento Document date Data documento File size Dimensione file File name Nome file File date Data del file Ipath Ipath Keywords Parole Chiave Mime type Tipo MIME Original character set Set di caratteri originale Relevancy rating Valutazione di pertinenza Title Titolo URL URL Mtime Mtime Date Data Date and time Data e ora Ipath Ipath MIME type Tipo MIME Can't sort by inverse relevance Può't ordinare per rilevanza inversa ResList Result list Lista dei risultati Unavailable document Documento inaccessible Previous Precedente Next Successivo <p><b>No results found</b><br> <p><b>Nessun risultato</b><br> &Preview &Anteprima Copy &URL Copia l'&Url Find &similar documents Trova documenti &simili Query details Dettagli ricerca (show query) (mostra dettagli di ricerca) Copy &File Name Copia il nome del &File filtered filtrato sorted ordinati Document history Cronologia dei documenti Preview Anteprima Open Apri <p><i>Alternate spellings (accents suppressed): </i> <p><i>Alternativi ortografia (accenti soppressi): </i> &Write to File &Scrivi su file Preview P&arent document/folder Anteprima documento/cartella P&arent &Open Parent document/folder &Apri documento/cartella padre &Open &Apri Documents Risultati out of at least totale di almeno for per <p><i>Alternate spellings: </i> <p><i>Ortografia alternativa: </i> Open &Snippets window Apri finestra &snippet Duplicate documents Duplica documenti These Urls ( | ipath) share the same content: Questi URL (<unk> ipath) condividono lo stesso contenuto: Result count (est.) Conteggio dei risultati (est.) Snippets Snippet This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Ripristina ordinamento &Delete column &Elimina colonna Add " Aggiungi " " column " colonna Save table to CSV file Salva tabella su file CSV Can't open/create file: Può't aprire/creare il file: &Preview &Anteprima &Open &Apri Copy &File Name Copia il nome del &File Copy &URL Copia l'&Url &Write to File &Scrivi su file Find &similar documents Trova documenti &simili Preview P&arent document/folder Anteprima documento/cartella P&arent &Open Parent document/folder &Apri documento/cartella padre &Save as CSV &Salva come CSV Add "%1" column Aggiungi "%1" colonna Result Table Tabella Dei Risultati Open Apri Open and Quit Apri ed esci Preview Anteprima Show Snippets Mostra Snippet Open current result document Apre il documento di risultato corrente Open current result and quit Apri il risultato corrente ed esci Show snippets Mostra snippet Show header Mostra intestazione Show vertical header Mostra intestazione verticale Copy current result text to clipboard Copia il testo del risultato corrente negli appunti Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Anteprima &Open &Apri Copy &File Name Copia il nome del &File Copy &URL Copia l'&Url &Write to File &Scrivi su file Find &similar documents Trova documenti &simili Preview P&arent document/folder Anteprima documento/cartella P&arent &Open Parent document/folder &Apri documento/cartella padre ResultPopup &Preview &Anteprima &Open &Apri Copy &File Name Copia il nome del &File Copy &URL Copia l'&Url &Write to File &Scrivi su file Save selection to files Salva la selezione sui file Preview P&arent document/folder Anteprima documento/cartella P&arent &Open Parent document/folder &Apri documento/cartella padre Find &similar documents Trova documenti &simili Open &Snippets window Apri finestra &snippet Show subdocuments / attachments Mostra sotto-documenti / allegati Open With Apri Con Run Script Esegui Script SSearch Any term Qualsiasi All terms Tutti File name Nome file Completions Espansione Select an item: Seleziona una voce: Too many completions Troppe possibilita' di espansione Query language Linguaggio di interrogazione Bad query string Stringa di ricerca malformata Out of memory Memoria esaurita Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Inserisci l'espressione della lingua di interrogazione. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in qualsiasi campo.<br> <i>campo:term1</i> : 'term1' nel campo 'campo'.<br> Nomi di campo standard/sinonimi:<br> titolo/soggetto/didascalia, autore/da, destinatario/a, nome file, estro.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Due date range: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> Nessuna parentesi consentita.<br> <i>"term1 term2"</i> : frase (deve avvenire esattamente). Possibili modificatori:<br> <i>"term1 term2"p</i> : ricerca di prossimità non ordinata con distanza predefinita.<br> Usa <b>Mostra il link Query</b> quando hai dubbi sul risultato e vedi il manuale (&lt; 1>) per maggiori dettagli. Enter file name wildcard expression. Inserisci il nome del file espressione. Enter search terms here. Type ESC SPC for completions of current term. Inserisci qui i termini di ricerca. Premi ESC Spazio per il completamento automatico dei termini. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Inserisci l'espressione della lingua di interrogazione. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in qualsiasi campo.<br> <i>campo:term1</i> : 'term1' nel campo 'campo'.<br> Nomi di campo standard/sinonimi:<br> titolo/soggetto/didascalia, autore/da, destinatario/a, nome file, estro.<br> Pseudo-fields: dir, mime/format, type/rclcat, data, dimensione.<br> Esempi di intervallo di due date: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> Puoi usare le parentesi per rendere le cose più chiare.<br> <i>"term1 term2"</i> : frase (deve avvenire esattamente). Possibili modificatori:<br> <i>"term1 term2"p</i> : ricerca di prossimità non ordinata con distanza predefinita.<br> Usa <b>Mostra il link Query</b> quando hai dubbi sul risultato e vedi il manuale (&lt; 1>) per maggiori dettagli. Stemming languages for stored query: Lingue di stemming per la query memorizzata: differ from current preferences (kept) differiscono dalle preferenze correnti (mantenute) Auto suffixes for stored query: Suffissi automatici per la query memorizzata: External indexes for stored query: Indici esterni per la query memorizzata: Autophrase is set but it was unset for stored query Autophrase è impostata ma non è stata impostata per la query memorizzata Autophrase is unset but it was set for stored query Autophrase non è impostata ma è stata impostata per la query memorizzata Enter search terms here. Inserisci qui i termini di ricerca. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border-collapse: collasso; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Query lingua cheat-sheet. In dubbio: clicca <b>Mostra Query</b>.&nbsp; You should really look at the manual (F1)</p> Dovresti davvero guardare il manuale (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Che</th><th>Esempi</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>E</td><td>uno due&nbsp;&nbsp;&nbsp;uno E due&nbsp;&nbsp;&nbsp;uno && due</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>O</td><td>uno o due&nbsp;&nbsp;&nbsp;uno <unk> due</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Complesso booleano. O ha la priorità, usa parentesi&nbsp; where needed</td><td>(one AND two) OR three</td></tr> se necessario</td><td>(uno E due) O tre</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Non</td><td>termine</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Frase</td><td>"orgoglio e pregiudizio"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Prox non ordinato. (default slack=10)</td><td>"prejudice&nbsp;orgoglio"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Nessuna espansione dello stelo: capitalizza</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Nomi dei campi</td><td>titolo/soggetto/didascalia&nbsp;&nbsp;autore/da<br>destinatario/a&nbsp;&nbsp;nome_file&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Filtro percorso directory</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Filtro tipo MIME</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Intervalli di data</td><td>data:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Can't open index Could not restore external indexes for stored query:<br> Impossibile ripristinare gli indici esterni per la query memorizzata:<br> ??? ??? Using current preferences. Usare le preferenze correnti. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Cancella Ctrl+S Ctrl+S Erase search entry Cancella voce di ricerca Search Cerca Start query Inizia ricerca Enter search terms here. Type ESC SPC for completions of current term. Inserisci qui i termini di ricerca. Premi ESC Spazio per il completamento automatico dei termini. Choose search type. Scegli il tipo di ricerca. Show query history Mostra cronologia query Enter search terms here. Inserisci qui i termini di ricerca. Main menu Menu principale SearchClauseW SearchClauseW SearchClauseW Any of these Qualsiasi parola All of these Tutte le parole None of these Nessuna di queste This phrase Questa frase Terms in proximity Parole in prossimita' File name matching Nome del file Select the type of query that will be performed with the words Seleziona il tipo di ricerca da effettuare con i termini indicati Number of additional words that may be interspersed with the chosen ones Numero di parole che possono frapporsi tra i termini di ricerca indicati In field In campo No field Nessun campo Any Qualsiasi All Tutti None Nessuno Phrase Frase Proximity Prossimità File name Nome file Snippets Snippets Snippet X X Find: Trova: Next Successivo Prev Precedente SnippetsW Search Cerca <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Siamo spiacenti, nessuna corrispondenza esatta è stata trovata entro i limiti. Probabilmente il documento è molto grande e il generatore di snippet si è perso in un labirinto...</p> Sort By Relevance Ordina Per Rilevanza Sort By Page Ordina Per Pagina Snippets Window Finestra Snippet Find Trova Find (alt) Trova (Alto) Find Next Trova Successivo Find Previous Trova Precedente Hide Nascondi Find next Trova successivo Find previous Trova precedente Close window Chiudi finestra SortForm Date Data Mime type Tipo MIME SortFormBase Sort Criteria Criterio di ordinamento Sort the Ordina i most relevant results by: risultati piu' rilevanti per: Descending Discendente Close Chiudi Apply Applica SpecIdxW Special Indexing Indicizzazione Speciale Do not retry previously failed files. Non riprovare i file precedentemente falliti. Else only modified or failed files will be processed. Verranno elaborati solo i file modificati o non riusciti. Erase selected files data before indexing. Cancella i dati dei file selezionati prima dell'indicizzazione. Directory to recursively index Directory all'indice ricorsivo Browse Esplora Start directory (else use regular topdirs): Directory iniziale (altrimenti usa topdirs regolari): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Lasciare vuoto per selezionare tutti i file. È possibile utilizzare più modelli di tipo shell separati da spazio.<br>I modelli con spazi incorporati devono essere citati con virgolette doppie.<br>Può essere usato solo se è impostato l'obiettivo iniziale. Selection patterns: Modelli di selezione: Top indexed entity Principale entità indicizzata Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Directory all'indice ricorsivo. Deve essere all'interno dell'area indicizzata regolare<br> come definita nel file di configurazione (topdir). Retry previously failed files. Riprova i file precedentemente falliti. Start directory. Must be part of the indexed tree. We use topdirs if empty. Cartella di avvio. Deve essere parte dell'albero indicizzato. Usiamo le topdirs se vuote. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Directory di avvio. Deve essere parte dell'albero indicizzato. Usare l'area indicizzata completa se vuota. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Esplorazione dei termini &Expand &Espandi Alt+E Alt+E &Close &Chiudi Alt+C Alt+C Term Termine No db info. Nessuna informazione db. Doc. / Tot. Doc. / Tot. Match Partita Case Caso Accents Accenti SpellW Wildcards Caratteri jolly Regexp Espressione regolare Spelling/Phonetic Ortografia/Fonetica Aspell init failed. Aspell not installed? Errore di inizializzazione aspell. Aspell e' installato? Aspell expansion error. Errore di espansione di Aspell. Stem expansion Espansione grammaticale error retrieving stemming languages Impossibile formare la lista di espansione per la lingua No expansion found Nessun epansione trovata Term Termine Doc. / Tot. Doc. / Tot. Index: %1 documents, average length %2 terms Indice: %1 documenti, lunghezza media %2 termini Index: %1 documents, average length %2 terms.%3 results Indice: %1 documenti, lunghezza media %2 termini.%3 risultati %1 results %1 risultati List was truncated alphabetically, some frequent La lista è stata troncata alfabeticamente, alcuni frequenti terms may be missing. Try using a longer root. i termini potrebbero mancare. Prova ad usare una radice più lunga. Show index statistics Mostra statistiche indice Number of documents Numero di documenti Average terms per document Termini medi per documento Smallest document length Lunghezza del documento più piccola Longest document length Lunghezza più lunga del documento Database directory size Dimensione directory database MIME types: Tipi MIME: Item Elemento Value Valore Smallest document length (terms) Lunghezza del documento più piccola (termini) Longest document length (terms) Lunghezza del documento più lunga (termini) Results from last indexing: Risultati dell'ultima indicizzazione: Documents created/updated Documenti creati/aggiornati Files tested File testati Unindexed files File non indicizzati List files which could not be indexed (slow) Elenca i file che non possono essere indicizzati (lento) Spell expansion error. Errore di espansione ortografica. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index La directory selezionata non sembra essera un indice Xapian This is the main/local index! Questo e' l'indice principale! The selected directory is already in the index list La directory selezionata e' gia' nella lista Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Seleziona la directory indice Xapian (es.: /home/ciccio/.recoll/xapiandb) error retrieving stemming languages Impossibile formare la lista delle lingue per l'espansione grammaticale Choose Scegli Result list paragraph format (erase all to reset to default) Formato del paragrafo dell'elenco dei risultati (cancella tutto per resettare al predefinito) Result list header (default is empty) Intestazione della lista dei risultati (predefinita vuota) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Seleziona la directory di configurazione di recoll o la directory di indice xapian (es.: /home/me/.recoll o /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read La directory selezionata sembra una directory di configurazione Recoll ma la configurazione non può essere letta At most one index should be selected Al massimo un indice dovrebbe essere selezionato Cant add index with different case/diacritics stripping option Cant add index with different case/diacritics stripping option Default QtWebkit font Default QtWebkit font Any term Qualsiasi All terms Tutti File name Nome file Query language Linguaggio di interrogazione Value from previous program exit Valore dall'uscita del programma precedente Context Contesto Description Descrizione Shortcut Scorciatoia Default Predefinito Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Interfaccia utente Number of entries in a result page Numero di risultati per pagina Result list font Fonts per la lista dei risultati Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Apre una finestra di dialogo per selezionare i fonts della lista dei risultati Reset Ripristina Resets the result list font to the system default Ripristina i font della lista dei risultati Auto-start simple search on whitespace entry. Inizia automaticamente una ricerca semplice digitando uno spazio. Start with advanced search dialog open. Inizia aprendo la finestra di ricerca avanzata. Start with sort dialog open. Inizia con la finestra di ordinamento aperta. Search parameters Parametri per la ricerca Stemming language Linguaggio per l'espansione Dynamically build abstracts Costruisci dinamicamente i riassunti Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Devo cercare di costruire i riassunti per le voci nell'elenco dei risultati usando il contesto dei termini di ricerca? Puo' essere lento per grossi documenti.. Replace abstracts from documents Sostituisci i riassunti dei documenti Do we synthetize an abstract even if the document seemed to have one? Devo sintetizzare un riassunto anche se il documento sembra ne abbia uno? Synthetic abstract size (characters) Numero caratteri per il riassunto Synthetic abstract context words Numero di parole di contesto per il riassunto External Indexes Indici esterni Add index Aggiungi indice Select the xapiandb directory for the index you want to add, then click Add Index Seleziona nella directory Xapiandb l'indice che vuoi aggiungere e clicca su 'Aggiungi indice' Browse Esplora &OK &OK Apply changes Applica modifiche &Cancel &Annulla Discard changes Annulla modifiche Result paragraph<br>format string Stringa di formattazione<br>dei risultati Automatically add phrase to simple searches Aggiungi automaticamente frase alle ricerche semplici A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Una ricerca per [vino rosso] (2 parole) sara' completata come [vino O rosso O (vino FRASE 2 rosso)]. Questo dovrebbe dare la precedenza ai risultati che contengono i termini esattamente come sono stati scritti. User preferences Preferenze utente Use desktop preferences to choose document editor. Usa le preferenze del desktop per scegliere l'editor dei documenti. External indexes Indici esterni Toggle selected Commuta selezionati Activate All Seleziona tutti Deactivate All Deseleziona tutti Remove selected Rimuovi selezionati Remove from list. This has no effect on the disk index. Rimuovi dalla lista. Non ha effetto sull'indice del disco. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definisce il formato per ogni paragrafo dell'elenco dei risultati. Usare il formato qt html e le sostituzioni tipo printf:<br>%A Riassunto<br> %D Data<br> %I Icona<br> %K Parole chiave (se esistono)<br> %L Link per anteprima e modifica<br> %M Tipo MIME<br> %N Numero del risultato<br> %R Percentuale di rilevanza<br> %S Informazioni sulla dimensione<br> %T Titolo<br> %U Url<br> Remember sort activation state. Ricorda lo stato dell'impostazione di ordinamento. Maximum text size highlighted for preview (megabytes) Dimensione massima del testo da evidenziare nell'anteprima (megabytes) Texts over this size will not be highlighted in preview (too slow). Testi di lunghezza superiore a questa non vengono evidenziati nella preview (troppo lento). Highlight color for query terms Evidenzia il colore per i termini della query Prefer Html to plain text for preview. Preferisci HTML al testo semplice per l'anteprima. If checked, results with the same content under different names will only be shown once. Se selezionato, i risultati con lo stesso contenuto sotto nomi diversi verranno visualizzati solo una volta. Hide duplicate results. Nascondi i risultati duplicati. Choose editor applications Scegli le applicazioni dell'editor Display category filter as toolbar instead of button panel (needs restart). Mostra il filtro categoria come barra degli strumenti invece del pannello dei pulsanti (richiede il riavvio). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Le parole nella lista saranno automaticamente trasformate in clausole ext:xxx nella voce di lingua di interrogazione. Query language magic file name suffixes. Suffissi del nome del file magico della lingua dell'interrogazione. Enable Abilita ViewAction Changing actions with different current values Modifica di azioni con valori differenti da quelli attuali Mime type Tipo MIME Command Comando MIME type Tipo MIME Desktop Default Desktop Predefinito Changing entries with different current values Cambiare voci con valori correnti diversi ViewActionBase File type Tipo di file Action Azione Select one or several file types, then click Change Action to modify the program used to open them Seleziona uno o piu' tipi di file e poi clicca su 'Cambia Azione' per modificare il programma usato per aprirli Change Action Cambia Azione Close Chiudi Native Viewers Applicazione di visualizzazione Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Selezionare uno o più tipi mime quindi fare clic su "Cambia azione"<br>È anche possibile chiudere questa finestra di dialogo e controllare "Usa preferenze desktop"<br>nel pannello principale per ignorare questa lista e utilizzare le impostazioni predefinite del desktop. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Selezionare uno o più tipi mime quindi utilizzare i controlli nel riquadro inferiore per cambiare come vengono elaborati. Use Desktop preferences by default Usa le preferenze del desktop per impostazione predefinita Select one or several file types, then use the controls in the frame below to change how they are processed Selezionare uno o più tipi di file, quindi utilizzare i controlli nel riquadro sottostante per modificare come vengono elaborati Exception to Desktop preferences Eccezione alle preferenze del desktop Action (empty -> recoll default) Azione (vuota -> valore predefinito di recoll) Apply to current selection Applica alla selezione corrente Recoll action: Recoll action: current value valore attuale Select same Seleziona lo stesso <b>New Values:</b> <b>Nuovi valori:</b> Webcache Webcache editor Editor Webcache Search regexp Cerca regexp TextLabel WebcacheEdit Copy URL Copia URL Unknown indexer state. Can't edit webcache file. Stato indicizzatore sconosciuto. Può't modificare il file webcache. Indexer is running. Can't edit webcache file. Indexer è in esecuzione. Può't modificare il file webcache. Delete selection Elimina selezione Webcache was modified, you will need to run the indexer after closing this window. Webcache è stata modificata, è necessario eseguire l'indicizzatore dopo aver chiuso questa finestra. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url Url Date Data Size URL URL WinSchedToolW Error Errore Configuration not initialized Configurazione non inizializzata <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexing batch scheduling</h3><p>Utilizziamo lo standard di Windows task scheduler per questo. Il programma verrà avviato quando si fa clic sul pulsante qui sotto.</p><p>È possibile utilizzare l'interfaccia completa (<i>Crea attività</i> nel menu a destra), o la procedura guidata semplificata <i>Crea attività base</i> . In entrambi i casi Copia/Incolla il percorso del file batch elencato qui sotto come <i>Azione</i> da eseguire.</p> Command already started Comando già avviato Recoll Batch indexing Ricoll lotto indicizzazione Start Windows Task Scheduler tool Avvia lo strumento Pianificazione Attività di Windows Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Ruba coda di indicizzazione Beagle Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle NON DEVE essere in esecuzione. Abilita l'elaborazione della coda beagle per indicizzare la cronologia web di Firefox.<br>(dovresti anche installare il plugin di Firefox Beagle) Web cache directory name Nome directory cache Web The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Il nome di una directory dove memorizzare la cache per le pagine web visitate.<br>Un percorso non assoluto è preso rispetto alla directory di configurazione. Max. size for the web cache (MB) Dimensione massima per la cache web (MB) Entries will be recycled once the size is reached Le voci saranno riciclate una volta raggiunta la dimensione Web page store directory name Nome directory negozio pagina web The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Il nome di una directory dove archiviare le copie delle pagine web visitate.<br>Un percorso non assoluto è preso rispetto alla directory di configurazione. Max. size for the web store (MB) Dimensione massima per il web store (MB) Process the WEB history queue Elabora la coda di cronologia WEB Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Abilita l'indicizzazione delle pagine visitate da Firefox.<br>(è necessario installare anche il plugin Firefox Recoll) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Le voci saranno riciclate una volta raggiunta la dimensione.<br>Aumentare la dimensione ha senso solo perché la riduzione del valore non troncerà un file esistente (solo lo spazio di scarto alla fine). confgui::ConfIndexW Can't write configuration file Impossibile scrivere il file di configurazione Recoll - Index Settings: Ricoll - Impostazioni Indice confgui::ConfParamFNW Browse Esplora Choose Scegli confgui::ConfParamSLW + + - - Add entry Aggiungi voce Delete selected entries Elimina le voci selezionate ~ ~ Edit selected entries Modifica le voci selezionate confgui::ConfSearchPanelW Automatic diacritics sensitivity Sensibilità automatica dei diacritici <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Attiva automaticamente la sensibilità dei diacritici se il termine di ricerca ha caratteri accentati (non in unac_except_trans). Altrimenti è necessario utilizzare il linguaggio di query e il modificatore <i>D</i> per specificare la sensibilità dei diacritici. Automatic character case sensitivity Sensibilità automatica delle maiuscole <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Attiva automaticamente la sensibilità delle lettere maiuscole se la voce ha caratteri maiuscoli in qualsiasi ma la prima posizione. Altrimenti è necessario utilizzare la lingua di query e il modificatore <i>C</i> per specificare la sensibilità delle maiuscole e minuscole. Maximum term expansion count Numero massimo di espansione a termine <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Numero massimo di espansione per un singolo termine (ad esempio: quando si usano caratteri jolly). Il valore predefinito di 10 000 è ragionevole ed eviterà le interrogazioni che appaiono congelate mentre il motore sta camminando la lista dei termini. Maximum Xapian clauses count Numero massimo di clausole Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Numero massimo di clausole elementari che aggiungiamo a una singola query Xapiana. In alcuni casi, il risultato di espansione del termine può essere moltiplicativo e vogliamo evitare di usare la memoria eccessiva. Nella maggior parte dei casi il valore predefinito di 100 000 dovrebbe essere sufficientemente elevato e compatibile con le attuali configurazioni hardware tipiche. confgui::ConfSubPanelW Global Globale Max. compressed file size (KB) Dimensione massima del file compresso (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Questo valore imposta una soglia oltre la quale i file compressi non saranno elaborati. Impostare a -1 per nessun limite, a 0 per nessuna decompressione mai. Max. text file size (MB) Dimensione massima del file di testo (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Questo valore imposta una soglia oltre la quale i file di testo non saranno elaborati. Impostare a -1 per nessun limite. Questo è per escludere i file di registro mostri dall'indice. Text file page size (KB) Dimensione pagina del file di testo (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Se questo valore è impostato (non uguale a -1), i file di testo saranno divisi in pezzi di questa dimensione per l'indicizzazione. Questo aiuterà a cercare file di testo molto grandi (ie: file di registro). Max. filter exec. time (S) Max. filtro tempo di esecuzione (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. I filtri esterni che funzionano più a lungo di questo verranno interrotti. Questo è il raro caso (cioè: postscript) in cui un documento potrebbe causare un filtro loopSet a -1 per nessun limite. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. I filtri esterni che funzionano più a lungo di questo verranno interrotti. Questo è il raro caso (cioè: postscript) in cui un documento potrebbe causare il caricamento di un filtro. Impostare a -1 per nessun limite. Only mime types Solo tipi mime An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Una lista esclusiva di tipi di mime indicizzati.<br>Niente altro sarà indicizzato. Normalmente vuoto e inattivo Exclude mime types Escludi tipi mime Mime types not to be indexed Tipi MIME da non indicizzare Max. filter exec. time (s) Max. filtro tempo esecuzione (s) confgui::ConfTopPanelW Top directories Cartella superiore The list of directories where recursive indexing starts. Default: your home. Lista delle cartelle in cui inizia lìindicizzazione recorsiva. Di default è la tua home. Skipped paths Indirizzi saltati These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Questi sono i nomi delle cartelle in cui l'indicizzazione non entra<br>Possono contenere caratteri speciali. Devono corrispondere agli indirizzi visti dal motore di indicizzazione (ad esempio, se la cartella superiore include '/home/io' e '/home' è in realtà un link a '/usr/home', l'indirizzo corretto che si vuole sltare dovrebbe essere '/home/me/tmp*' e non ì/home/usr/tmp*') Stemming languages Lingue per la radice The languages for which stemming expansion<br>dictionaries will be built. Lingue per le quali verrà costruito<br>il dizionario delle espansioni radicali. Log file name Nome del file di log The file where the messages will be written.<br>Use 'stderr' for terminal output Il file dove verranno scritti i messaggi.<br>Usa 'stderr' per il terminale Log verbosity level Livello di verbosità del log This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Questo valore regola il numero dei messaggi,>br>dai soli errori a mole indicazioni per il debug. Index flush megabytes interval Intervallo in megabite per il salvataggio intermedio dell'indice This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Questo valore regola il volume di dati da indicizzare tra un salvataggio e l'altro.<br>Aiuta a controllare l'uso della memoria. Di default è post uguale a 10Mb Max disk occupation (%) Massima occupazione del disco fisso (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Questa è la percentuale fi occupazione del disco fisso oltre la quale l'indicizzazione si ferma con un errore (per evitare di riempire il disco).<br>0 significa nessun limite (questo è il valore di default). No aspell usage Non usare aspell Aspell language Lingua di aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Lingua per il dizionario aspell. Dovrebbe essere simile a 'en' o 'it' ...<br>Se questo valore non è impostato verrà usato l'ambiente NLS per calcolarlo, cosa che generalmente funziona. Per avere un'idea di cosa sia installato sul tuo sistema, dai il comando 'aspell config' e guarda il nome dei files .dat nella cartella 'data-dir'. Database directory name Nome della cartella del database The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Nome della cartella in cui salvare l'indice<br>Un indirizzo non assoluto viene interpretato come relativo alla cartella di congigurazione. Di default è 'xapiandb'. Use system's 'file' command Usa il comando di sistema 'file' Use the system's 'file' command if internal<br>mime type identification fails. Usa il comando di sistema 'file' se fallisce<br>l'identificazione interna del tipo mime. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Disabilita l'uso di aspell per generare approssimazione ortografica nel termine strumento esploratore.<br> Utile se aspell è assente o non funziona. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. La lingua per il dizionario aspell. Dovrebbe apparire 'it' o 'fr' . .<br>Se questo valore non è impostato, l'ambiente NLS verrà utilizzato per calcolarlo, che di solito funziona. Per avere un'idea di ciò che è installato sul vostro sistema, digita 'aspell config' e cercare . ai file all'interno della 'data-dir' directory. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Il nome di una directory dove memorizzare l'indice<br>Un percorso non assoluto viene preso rispetto alla directory di configurazione. Il valore predefinito è 'xapiandb'. Unac exceptions Eccezioni di Unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Queste sono eccezioni al meccanismo unac che, per impostazione predefinita, rimuove tutti i diacritici ed esegue decomposizione canonica. È possibile sovrascrivere l'enfasi per alcuni caratteri, a seconda della lingua, e specificare decomposizioni aggiuntive, e. . per le legature. In ogni voce separata da spazio, il primo carattere è quello sorgente, e il resto è la traduzione. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Questi sono i pathname delle directory che l'indicizzazione non entrerà.<br>Gli elementi del tracciato possono contenere caratteri jolly. Le voci devono corrispondere ai percorsi visti dall'indicizzatore (ad es. if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', una corretta voce skippedPath sarebbe '/home/me/tmp*', non '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Occupazione massima su disco (%, 0 significa nessun limite) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Questa è la percentuale di utilizzo del disco - utilizzo totale del disco, dimensione non indice - alla quale l'indicizzazione fallirà e si fermerà.<br>Il valore predefinito di 0 rimuove ogni limite. uiPrefsDialogBase User preferences Preferenze utente User interface Interfaccia utente Number of entries in a result page Numero di risultati per pagina If checked, results with the same content under different names will only be shown once. Se selezionato, i risultati con lo stesso contenuto sotto nomi diversi verranno visualizzati solo una volta. Hide duplicate results. Nascondi i risultati duplicati. Highlight color for query terms Evidenzia il colore per i termini della query Result list font Fonts per la lista dei risultati Opens a dialog to select the result list font Apre una finestra di dialogo per selezionare i fonts della lista dei risultati Helvetica-10 Helvetica-10 Resets the result list font to the system default Ripristina i font della lista dei risultati Reset Ripristina Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definisce il formato per ogni paragrafo dell'elenco dei risultati. Usare il formato qt html e le sostituzioni tipo printf:<br>%A Riassunto<br> %D Data<br> %I Icona<br> %K Parole chiave (se esistono)<br> %L Link per anteprima e modifica<br> %M Tipo MIME<br> %N Numero del risultato<br> %R Percentuale di rilevanza<br> %S Informazioni sulla dimensione<br> %T Titolo<br> %U Url<br> Result paragraph<br>format string Stringa di formattazione<br>dei risultati Texts over this size will not be highlighted in preview (too slow). Testi di lunghezza superiore a questa non vengono evidenziati nella preview (troppo lento). Maximum text size highlighted for preview (megabytes) Dimensione massima del testo da evidenziare nell'anteprima (megabytes) Use desktop preferences to choose document editor. Usa le preferenze del desktop per scegliere l'editor dei documenti. Choose editor applications Scegli le applicazioni dell'editor Display category filter as toolbar instead of button panel (needs restart). Mostra il filtro categoria come barra degli strumenti invece del pannello dei pulsanti (richiede il riavvio). Auto-start simple search on whitespace entry. Inizia automaticamente una ricerca semplice digitando uno spazio. Start with advanced search dialog open. Inizia aprendo la finestra di ricerca avanzata. Start with sort dialog open. Inizia con la finestra di ordinamento aperta. Remember sort activation state. Ricorda lo stato dell'impostazione di ordinamento. Prefer Html to plain text for preview. Preferisci HTML al testo semplice per l'anteprima. Search parameters Parametri per la ricerca Stemming language Linguaggio per l'espansione A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Una ricerca per [vino rosso] (2 parole) sara' completata come [vino O rosso O (vino FRASE 2 rosso)]. Questo dovrebbe dare la precedenza ai risultati che contengono i termini esattamente come sono stati scritti. Automatically add phrase to simple searches Aggiungi automaticamente frase alle ricerche semplici Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Devo cercare di costruire i riassunti per le voci nell'elenco dei risultati usando il contesto dei termini di ricerca? Puo' essere lento per grossi documenti.. Dynamically build abstracts Costruisci dinamicamente i riassunti Do we synthetize an abstract even if the document seemed to have one? Devo sintetizzare un riassunto anche se il documento sembra ne abbia uno? Replace abstracts from documents Sostituisci i riassunti dei documenti Synthetic abstract size (characters) Numero caratteri per il riassunto Synthetic abstract context words Numero di parole di contesto per il riassunto The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Le parole nella lista saranno automaticamente trasformate in clausole ext:xxx nella voce di lingua di interrogazione. Query language magic file name suffixes. Suffissi del nome del file magico della lingua dell'interrogazione. Enable Abilita External Indexes Indici esterni Toggle selected Commuta selezionati Activate All Seleziona tutti Deactivate All Deseleziona tutti Remove from list. This has no effect on the disk index. Rimuovi dalla lista. Non ha effetto sull'indice del disco. Remove selected Rimuovi selezionati Click to add another index directory to the list Fare clic per aggiungere un'altra directory indice alla lista Add index Aggiungi indice Apply changes Applica modifiche &OK &OK Discard changes Annulla modifiche &Cancel &Annulla Abstract snippet separator Separatore snippet astratto Use <PRE> tags instead of <BR>to display plain text as html. Usa <PRE> tag invece di <BR>per visualizzare il testo semplice come html. Lines in PRE text are not folded. Using BR loses indentation. Le righe nel testo PRE non sono piegate. L'uso di BR perde l'indentazione. Style sheet Foglio di stile Opens a dialog to select the style sheet file Apre una finestra di dialogo per selezionare il foglio di stile Choose Scegli Resets the style sheet to default Ripristina il foglio di stile predefinito Lines in PRE text are not folded. Using BR loses some indentation. Le righe nel testo PRE non sono ripiegate. L'uso di BR perde qualche rientro. Use <PRE> tags instead of <BR>to display plain text as html in preview. Usa <PRE> tag invece di <BR>per visualizzare il testo semplice come html nell'anteprima. Result List Elenco Dei Risultati Edit result paragraph format string Modifica la stringa di formato del paragrafo del risultato Edit result page html header insert Modifica l'inserimento dell'intestazione html della pagina dei risultati Date format (strftime(3)) Date format (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. I termini saltati aumentano la mancanza di frase e riducono l'efficienza dell'autofasi. Il valore predefinito è 2 (percentuale). Autophrase term frequency threshold percentage Percentuale soglia di frequenza termine Autophrase Plain text to HTML line style Testo semplice in stile riga HTML Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Le righe nel testo PRE non sono piegate. L'uso di BR perde un po' di indentazione. Lo stile PRE + Wrap potrebbe essere quello che vuoi. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + a capo Exceptions Eccezioni Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Tipi MIME che non devono essere passati a xdg-open anche quando "Usa le preferenze del desktop" è impostato.<br> Utile per passare il numero di pagina e cercare le opzioni di stringa, ad esempio evince. Disable Qt autocompletion in search entry. Disabilita completamento automatico di Qt nella voce di ricerca. Search as you type. Cerca mentre scrivi. Paths translations Tracciati traduzioni Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Fare clic per aggiungere un'altra directory indice all'elenco. È possibile selezionare una directory di configurazione Recoll o un indice Xapian. Snippets window CSS file File CSS finestra snippet Opens a dialog to select the Snippets window CSS style sheet file Apre una finestra di dialogo per selezionare il file di foglio CSS della finestra Snippet Resets the Snippets window style Ripristina lo stile della finestra Snippet Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Decidi se i filtri del documento sono mostrati come pulsanti radio, combobox della barra degli strumenti o menu. Document filter choice style: Stile scelta filtro documento: Buttons Panel Pannello Pulsanti Toolbar Combobox Combobox Barra Strumenti Menu Menu Show system tray icon. Mostra icona nel vassoio di sistema. Close to tray instead of exiting. Vicino al vassoio invece di uscire. Start with simple search mode Avvia con una semplice modalità di ricerca Show warning when opening temporary file. Mostra avviso all'apertura del file temporaneo. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Stile utente da applicare alla finestra snippet.<br> Nota: l'inserimento dell'intestazione della pagina dei risultati è anche incluso nell'intestazione della finestra snippet. Synonyms file File sinonimi Highlight CSS style for query terms Evidenzia lo stile CSS per i termini di interrogazione Recoll - User Preferences Ricoll - Preferenze Utente Set path translations for the selected index or for the main one if no selection exists. Imposta le traduzioni del percorso per l'indice selezionato o per quello principale se non esiste selezione. Activate links in preview. Attiva i collegamenti nell'anteprima. Make links inside the preview window clickable, and start an external browser when they are clicked. Fare clic sui link all'interno della finestra di anteprima e avviare un browser esterno quando vengono cliccati. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Interroga i termini che evidenziano nei risultati. <br>Forse prova qualcosa come "color:red;background:yellow" per qualcosa di più vivace del blu predefinito... Start search on completer popup activation. Avvia la ricerca all'attivazione del popup completo. Maximum number of snippets displayed in the snippets window Numero massimo di snippet visualizzate nella finestra snippet Sort snippets by page number (default: by weight). Ordina pezzetti di codice per numero di pagina (predefinito: per peso). Suppress all beeps. Sopprimi tutti i beep. Application Qt style sheet Foglio di stile Qt dell'applicazione Limit the size of the search history. Use 0 to disable, -1 for unlimited. Limita la dimensione della cronologia di ricerca. Usa 0 per disabilitare, -1 per illimitati. Maximum size of search history (0: disable, -1: unlimited): Dimensione massima della cronologia di ricerca (0: disabilitare, -1: illimitata): Generate desktop notifications. Genera notifiche desktop. Misc Varie Work around QTBUG-78923 by inserting space before anchor text Lavorare intorno a QTBUG-78923 inserendo spazio prima del testo di ancoraggio Display a Snippets link even if the document has no pages (needs restart). Mostra un link Snippet anche se il documento non ha pagine (richiede il riavvio). Maximum text size highlighted for preview (kilobytes) Dimensione massima del testo evidenziata per l'anteprima (kilobyte) Start with simple search mode: Inizia con una semplice modalità di ricerca: Hide toolbars. Nascondi barre degli strumenti. Hide status bar. Nascondi barra di stato. Hide Clear and Search buttons. Nascondi i pulsanti Pulizia e Ricerca. Hide menu bar (show button instead). Nascondi la barra dei menu (mostra invece il pulsante). Hide simple search type (show in menu only). Nascondi il semplice tipo di ricerca (mostra solo nel menu). Shortcuts Scorciatoie Hide result table header. Nascondi intestazione tabella risultati. Show result table row headers. Mostra le intestazioni delle righe della tabella dei risultati. Reset shortcuts defaults Ripristina le scorciatoie predefinite Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Disabilita le scorciatoie Ctrl+[0-9]/[a-z] per saltare alle righe della tabella. Use F1 to access the manual Usa F1 per accedere al manuale Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Tipo di ricerca semplice Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Modalità scura Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Tabella Dei Risultati Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_nl.ts0000644000175000017500000070554314444307651014262 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Alle termen Any clause Elke term texts teksten spreadsheets werkbladen presentations presentaties media Media messages berichten other andere Bad multiplier suffix in size filter Geen juist achtervoegsel in grootte filter text tekst spreadsheet rekenblad presentation presentatie message bericht Advanced Search Geavanceerd Zoeken History Next Volgende geschiedenis History Prev Vorige geschiedenis Load next stored search Zoeken op volgende opslag laden Load previous stored search Laad vorige opgeslagen zoekopdracht AdvSearchBase Advanced search geavanceerd zoeken Restrict file types beperk tot bestandstype Save as default Sla op als standaard Searched file types Gezochte bestands type All ----> Alle ----> Sel -----> Sel ----> <----- Sel <----- Zel <----- All alle Ignored file types negeer bestandstype Enter top directory for search voer de top bestandsmap in om te doorzoeken Browse doorbladeren Restrict results to files in subtree: Beperk de resultaten tot de bestanden in de subtak Start Search Begin met zoeken Search for <br>documents<br>satisfying: Zoek naar<br>documenten<br> die bevatten: Delete clause verwijder term Add clause voeg term toe Check this to enable filtering on file types vink dit aan om filetype filtering te activeren By categories Per categorie Check this to use file categories instead of raw mime types Vink dit aan om bestands catergorie te gebruiken in plaats van raw mime Close sluit All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Elk niet lege veld aan de rechterzijde zal worden gecombineerd met En ("Alle clausules" keuze) en Of ("Bepalingen" keuze) voegwoorden. <br> "Elk", "en" Of "Geen" veldtypen kan een mix van eenvoudige woorden en uitdrukkingen tussen dubbele aanhalingstekens te accepteren. <br> Velden zonder gegevens worden genegeerd. Invert omkeren Minimum size. You can use k/K,m/M,g/G as multipliers Minimummaat. U kunt k / K, m / M gebruiken, g / G als multipliers Min. Size Min Grootte Maximum size. You can use k/K,m/M,g/G as multipliers Maximale grootte. U kunt k / K, m / M gebruiken, g / G als multipliers Max. Size Max grootte Select Selecteren Filter Filteren From Van To Tot Check this to enable filtering on dates Vink dit aan om op datum te kunnen filteren Filter dates Filter datums Find Vind Check this to enable filtering on sizes Vink dit aan om te filteren op grootte Filter sizes Filter grootte Filter birth dates ConfIndexW Can't write configuration file Kan configuratie bestand niet lezen Global parameters Globale parameters Local parameters Lokale parameters Search parameters Zoek parameters Top directories Top mappen The list of directories where recursive indexing starts. Default: your home. Een lijst van mappen waar de recursive indexering gaat starten. Standaard is de thuismap. Skipped paths Paden overgeslagen These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Dit zijn padnamen van mappen die niet zullen worden geïndexeerd.<br>Pad elementen kunnen wildcards bevatten. De items moeten overeenkomen met de paden die door de indexeerder worden gezien (bijv. als de uren op de voorgrond zijn '/home/me' en '/home' een link is naar '/usr/home'een juist skippedPath item zou '/home/me/tmp*'zijn, niet '/usr/home/me/tmp*') Stemming languages Stam talen The languages for which stemming expansion<br>dictionaries will be built. De talen waarvoor de stam uitbreidings<br>wooordenboeken voor zullen worden gebouwd. Log file name Log bestandsnaam The file where the messages will be written.<br>Use 'stderr' for terminal output Het bestand waar de boodschappen geschreven zullen worden.<br>Gebruik 'stderr' voor terminal weergave Log verbosity level Log uitgebreidheids nivo This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Deze waarde bepaald het aantal boodschappen,<br>van alleen foutmeldingen tot een hoop debugging data. Index flush megabytes interval Index verversings megabyte interval This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Deze waarde past de hoeveelheid data die zal worden geindexeerd tussen de flushes naar de schijf.<br> Dit helpt bij het controleren van het gebruik van geheugen. Standaad 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Dit is het percentage van schijfgebruik - totaal schijfgebruik, niet indexgrootte - waarop de indexering zal mislukken en stoppen.<br>De standaardwaarde van 0 verwijdert elke limiet. No aspell usage Gebruik aspell niet Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Schakelt het gebruik van aspell uit om spellings gissingen in het term onderzoeker gereedschap te genereren. <br> Handig als aspell afwezig is of niet werkt. Aspell language Aspell taal The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Taal instelling voor het aspell woordenboek. Dit zou er uit moeten zien als 'en'of 'nl'...<br> als deze waarde niet is ingesteld, zal de NLS omgeving gebruikt worden om het te berekenen, wat meestal werkt. Om een idee te krijgen wat er op uw systeem staat, type 'aspell config' en zoek naar .dat bestanden binnen de 'data-dir'map. Database directory name Database map naam The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. De naam voor een map om de index in op te slaan<br> Een niet absoluut pad ten opzichte van het configuratie bestand is gekozen. Standaard is het 'xapian db'. Unac exceptions Unac uitzonderingen <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Dit zijn uitzonderingen op het unac mechanisme dat, standaard, alle diakritische tekens verwijderd, en voert canonische ontbinding door. U kunt unaccenting voor sommige karakters veranderen, afhankelijk van uw taal, en extra decomposities specificeren, bijv. voor ligaturen. In iedere ruimte gescheiden ingave , waar het eerste teken is de bron is, en de rest de vertaling. Process the WEB history queue Verwerk de WEB geschiedenis wachtrij Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Zet het indexeren van firefox bezochte paginas aan. <br> (hiervoor zal ook de Firefox Recoll plugin moeten worden geinstalleerd door uzelf) Web page store directory name Web pagina map naam om op te slaan The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. De naam voor een map waarin de kopieen van de bezochte webpaginas opgeslagen zullen worden.<br>Een niet absoluut pad zal worden gekozen ten opzichte van de configuratie map Max. size for the web store (MB) Max. grootte voor het web opslaan (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Invoeringen zullen worden gerecycled zodra de groote is bereikt. <br> Het verhogen van de groote heeft zin omdat het beperken van de waarde de bestaande waardes niet zal afkappen ( er is alleen afval ruimte aan het einde). Automatic diacritics sensitivity Automatische diakritische tekens gevoeligheid <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <P> Automatisch activeren diakritische tekens gevoeligheid als de zoekterm tekens zijn geaccentueerd (niet in unac_except_trans). Wat je nodig hebt om de zoek taal te gebruiken en de <i> D</i> modifier om diakritische tekens gevoeligheid te specificeren. Automatic character case sensitivity Automatische karakter hoofdletter gevoeligheid <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <P> Automatisch activeren hoofdletters gevoeligheid als de vermelding hoofdletters heeft in elke, behalve de eerste positie. Anders moet u zoek taal gebruiken en de <i>C</i> modifier karakter-hoofdlettergevoeligheid opgeven. Maximum term expansion count Maximale term uitbreidings telling <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p> Maximale uitbreidingstelling voor een enkele term (bijv.: bij het gebruik van wildcards) Een standaard van 10.000 is redelijk en zal zoekpodrachten die lijken te bevriezen terwijl de zoekmachine loopt door de termlijst vermijden. Maximum Xapian clauses count Maximaal Xapian clausules telling <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p> Maximale aantal elementaire clausules die we kunnen toevoegen aan een enkele Xapian zoeken. In sommige gevallen kan het resultaatvan de term uitbreiding multiplicatief zijn, en we willen voorkomen dat er overmatig gebruik word gemaakt van het werkgeheugen. De standaard van 100.000 zou hoog genoeg moeten zijn in beidde gevallen en compatible zijn met moderne hardware configuraties. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... De talen waarvoor de uitbreidingswoordenboeken zullen worden gebouwd.<br>Zie de Xapiaanse stammer documentatie voor mogelijke waarden. Bijv. dutse, french, duitsch... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. De taal van het aspell woordenboek. De waarden zijn 2-letter taalcodes, bijv. 'nr', 'fr' . .<br>Als deze waarde niet is ingesteld, zal de NLS-omgeving worden gebruikt om het te berekenen, wat meestal werkt. Om een idee te krijgen van wat is geïnstalleerd op uw systeem, typ 'aspell config' en zoek naar . op bestanden in de map 'data-dir' Indexer log file name Indexer log bestandsnaam If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Indien leeg, wordt de bovenstaande waarde van de bestandsnaam gebruikt. Het kan handig zijn om een apart logboek voor diagnostische doeleinden te hebben, omdat het algemene logboek wordt gewist wanneer<br>de GUI opstart. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Schijf volledige drempelpercentage waarmee we stoppen met het indexeren van<br>Bijv. 90% om 90% volledig, 0 of 100 betekent geen limiet) Web history Web geschiedenis Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Alleen mime types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Een exclusieve lijst van geïndexeerde typen mime. <br> Niets anders zal worden geïndexeerd. Normaal gesproken leeg en inactief Exclude mime types Sluit mime types uit Mime types not to be indexed Mime types die niet geindexeerd zullen worden Max. compressed file size (KB) Maximaal gecomprimeerd bestands formaat (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Deze waarde stelt een drempel waarboven gecomprimeerde bestanden niet zal worden verwerkt. Ingesteld op -1 voor geen limiet, op 0 voor geen decompressie ooit. Max. text file size (MB) Max. tekstbestand groote (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Deze waarde stelt een drempel waarboven tekstbestanden niet zal worden verwerkt. Ingesteld op -1 voor geen limiet. Dit is voor het uitsluiten van monster logbestanden uit de index. Text file page size (KB) Tekst bestand pagina grootte (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Als deze waarde is ingesteld (niet gelijk aan -1), zal tekstbestanden worden opgedeeld in blokken van deze grootte voor indexering. Dit zal helpen bij het zoeken naar zeer grote tekstbestanden (bijv: log-bestanden). Max. filter exec. time (s) Max. uitvoertijd filter (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Externe filters die langer dan dit werken worden afgebroken. Dit is voor het zeldzame geval (bijv: postscript) wanneer een document een filterlus zou kunnen veroorzaken. Stel in op -1 voor geen limiet. Global Globaal CronToolW Cron Dialog Cron dialoogvenster <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexeer schema (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> Een enkele numerieke waarde, door komma's gescheiden lijsten (1,3,5) en reeksen (1-7). Meer in het algemeen zullen de velden worden gebruikt <span style=" font-style:italic;">als </span> in het crontab bestand, en het volledige crontab syntax kan worden gebruikt, zie crontab (5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Bijvoorbeeld invoeren <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Dagen, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Uren</span> en <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minuten</span> zal recollindex starten op elke dag om 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Een schema met zeer frequent activering is waarschijnlijk minder efficiënt dan real time indexeren.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Dagen van de week (* of 0-7, of 7 is Zondag) Hours (* or 0-23) Uren (*of 0-23 Minutes (0-59) Minuten (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Klik <span style=" font-style:italic;">Uitzetten</span> om automatisch batch indexeren uit te zetten, <span style=" font-style:italic;">Aanzetten</span> om het te activeren, <span style=" font-style:italic;">Annuleren</span> om niets te doen</p></body></html> Enable Aanzetten Disable Uitzetten It seems that manually edited entries exist for recollindex, cannot edit crontab Het lijkt erop dat met de hand bewerkt ingaves bestaan voor recollindex, kan niet crontab bewerken Error installing cron entry. Bad syntax in fields ? Fout bij het instellen van cron job. Slechte syntax in de ingave? EditDialog Dialog Dialoog EditTrans Source path bronpad Local path lokaal pad Config error Configuratie fout Original path Oorspronkelijk pad EditTransBase Path Translations Pad vertalingen Setting path translations for zet vertalingspad voor Select one or several file types, then use the controls in the frame below to change how they are processed Selecteer één of meerdere bestandstypen, gebruik dan de bediening in het kader hieronder om te veranderen hoe ze worden verwerkt Add toevoegen Delete Verwijderen Cancel Annuleer Save Bewaar FirstIdxDialog First indexing setup Setup van eerste indexering <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Het blijkt dat de index voor deze configuratie niet bestaat.</span><br /><br />Als u gewoon uw home directory wilt indexeren met een set van redelijke standaardinstellingen, drukt u op de<span style=" font-style:italic;">Start indexeer nu</span>knop. Je zult in staat zijn om de details later aan te passen.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Als u meer controle wil, gebruik dan de volgende links om de indexering configuratie en het schema aan te passen.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Deze tools kunnen later worden geopend vanuit het<span style=" font-style:italic;">Voorkeuren</span> menu.</p></body></html> Indexing configuration Configuratie inedexering This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Dit laat u de mappen die u wilt indexeren, en andere parameters aan passen, zoals uitgesloten bestandspaden of namen, standaard character sets, enz. Indexing schedule Indexerings schema This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Dit zal u laten kiezen tussen batch en real-time indexering, en het opzetten van een automatisch schema voor batch indexeren (met behulp van cron) Start indexing now Begin nu met indexering FragButs %1 not found. %1 niet gevonden. %1: %2 %1: %2 Fragment Buttons Fragment knoppen Query Fragments Zoekterm fragmenten IdxSchedW Index scheduling setup indexing schema setup <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span>indexering kan permanent draaien, het indexeren van bestanden als ze veranderen, of lopen op vaste intervallen.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Het lezen van de handleiding kan helpen om te beslissen tussen deze benaderingen (druk op F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Deze tool kan u helpen bij het opzetten van een schema om batch indexeren runs te automatiseren, of het starten van real time indexeren wanneer u zich aanmeldt (of beide, dat is echter zelden zinvol). </p></body></html> Cron scheduling Cron schema The tool will let you decide at what time indexing should run and will install a crontab entry. Deze tool zal u laten beslissen op welk tijdstip het indexeren moet worden uitgevoerd en zal een crontab installeren. Real time indexing start up Real time indexering opstart Decide if real time indexing will be started when you log in (only for the default index). Beslis of real time indexeren wordt gestart wanneer u inlogt (alleen voor de standaard-index). ListDialog Dialog Dialoog venster GroupBox GroepVenster Main No db directory in configuration Geen db bestand in configuratie Could not open database in Kon de database niet openen in . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Klik op Annuleren als u het configuratiebestand wilt bewerken voordat u het indexeren start, of Ok om het verder te laten gaan. Configuration problem (dynconf Configuratieprobleem (dynconf "history" file is damaged or un(read)writeable, please check or remove it: Het "Geschiedenis" bestand is beschadigd of on(lees)schrijfbaar geworden, graag controleren of verwijderen: "history" file is damaged, please check or remove it: "geschiedenis" bestand is beschadigd, controleer of verwijder het: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Zoek naar: &Next &Volgende &Previous &Vorige Match &Case Hoofd/kleine letter Clear Wissen Creating preview text preview tekst aan het maken Loading preview text into editor Preview tekst in editor aan het laden Cannot create temporary directory Kan tijdelijke map niet aanmaken Cancel Annuleer Close Tab Sluit tab Missing helper program: Help programma ontbreekt Can't turn doc into internal representation for Kan doc omzetten in een interne representatie Cannot create temporary directory: Kan tijdelijke map niet aanmaken: Error while loading file Fout bij het laden van bestand Form Vorm Tab 1 Tab 1 Open Openen Canceled Geannuleerd Error loading the document: file missing. Fout bij het laden van het document: bestand ontbreekt. Error loading the document: no permission. Fout bij laden van document: geen toestemming Error loading: backend not configured. Fout laden: backend niet geconfigureerd. Error loading the document: other handler error<br>Maybe the application is locking the file ? Fout bij het laden van het document: andere handler-fout<br>Vergrendelt de applicatie het bestand? Error loading the document: other handler error. Fout bij het laden van het document: andere verwerkingsfout. <br>Attempting to display from stored text. <br>Probeert weer te geven van opgeslagen tekst. Could not fetch stored text Opgeslagen tekst kon niet worden opgehaald Previous result document Vorig resultaat document Next result document Volgend resultaat document Preview Window Voorbeeld venster Close Window Sluit venster Next doc in tab Volgende doc op tabblad Previous doc in tab Vorige verwijzing naar tabblad Close tab Tabblad sluiten Print tab Print tab Close preview window Voorbeeld sluiten Show next result Toon het volgende resultaat Show previous result Toon vorig resultaat Print Druk af PreviewTextEdit Show fields Toon veld Show main text Toon hoofd tekst Print Druk af Print Current Preview Druk huidige Preview af Show image Toon afbeelding Select All Selecteer alles Copy Kopieer Save document to file Bewaar document als bestand Fold lines Vouw lijnen Preserve indentation Behoud inspringing Open document Document openen Reload as Plain Text Reload as HTML QObject Global parameters Globale parameters Local parameters Lokale parameters <b>Customised subtrees <b>Aangepaste substructuur The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. De lijst van de submappen in de geïndexeerde hiërarchie <br> waar sommige parameters moeten worden geherdefinieerd. Standaard: leeg. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>De parameters die volgen zijn ingesteld, hetzij op het hoogste niveau, als er niets <br>of een lege regel is geselecteerd in de keuzelijst boven, of voor de geselecteerde submap.<br> U kunt mappen toevoegen of verwijderen door op de +/- knoppen te klikken. Skipped names Overgeslagen namen These are patterns for file or directory names which should not be indexed. Dit zijn patronen voor bestand of de mappen namen die niet mogen worden geïndexeerd. Default character set Standaard tekenset This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Dit is de tekenset die gebruikt wordt voor het lezen van bestanden die de tekenset intern niet identificeren, bijvoorbeeld zuivere tekstbestanden.<br>De standaardwaarde is leeg en de waarde van de NLS-omgeving wordt gebruikt. Follow symbolic links Volg symbolische links Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Volg symbolische links tijdens het indexeren. De standaard is niet volgen, om dubbele indexering te voorkomen Index all file names Indexeer alle bestandsnamen Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indexeer de namen van bestanden waarvan de inhoud niet kan worden geïdentificeerd of verwerkt (geen of niet-ondersteunde MIME-type). standaard true Beagle web history Artsen internetgeschiedenis Search parameters Zoek parameters Web history Web geschiedenis Default<br>character set Standaard<br>karakter set Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Tekenset die wordt gebruikt voor het lezen van bestanden die het intern tekenset niet worden herkend, bijvoorbeeld pure tekstbestanden. Ondernemingen De standaard waarde is leeg en de waarde van de NLS-omgeving wordt gebruikt. Ignored endings Genegeerde eindes These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Dit zijn eindpunten voor bestanden die alleen worden geïndexeerd door inhoud (geen MIME-type identificatiepoging, geen decompressie, geen indexering van inhoud. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Dit zijn bestandsnaam eindes voor bestanden die zullen worden geïndexeerd door alleen de naam (geen MIME-type identificatie poging, geen decompressie, geen inhoud indexering). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>De volgende parameters worden ingesteld op het bovenste niveau als niets of een lege regel is geselecteerd in de lijst hierboven, of voor de geselecteerde submap. Je kunt mappen toevoegen of verwijderen door te klikken op de +/- knoppen. QWidget Create or choose save directory Maak of kies een bestandsnaam om op te slaan Choose exactly one directory Kies exact een map Could not read directory: kon map niet lezen Unexpected file name collision, cancelling. Onverwachte bestandsnaam botsing, annuleren. Cannot extract document: Kan het document niet uitpakken &Preview Voorvertoning &Open &Openen Open With Open met Run Script Voer script uit Copy &File Name Kopieer &Bestands Naam Copy &URL Kopieer &URL &Write to File &Schijf naar Bestand Save selection to files Bewaar selektie naar bestanden Preview P&arent document/folder Preview B&ovenliggende document/map &Open Parent document/folder &Open Bovenliggend document/map Find &similar documents Vindt &gelijksoortige documenten Open &Snippets window Open &Knipsel venster Show subdocuments / attachments Toon subdocumenten / attachments &Open Parent document &Bovenliggend document openen &Open Parent Folder &Bovenliggende map openen Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Niet nogmaals tonen RTIToolW Real time indexing automatic start Automatisch Starten realtime-indexeren <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span>indexering kan worden ingesteld om te draaien als een daemon, het bijwerken van de index als bestanden veranderen, in real time. Je krijgt dan een altijd up-to-date index, maar systeembronnen worden permanent gebruikt.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Start met indexeren bij mijn desktop-sessie Also start indexing daemon right now. start nu ook de indexatie daemon Replacing: Vervanging Replacing file Vervang bestand Can't create: Kan niet aanmaken Warning Waarschuwing Could not execute recollindex Kon recollindex niet starten Deleting: Verwijderen Deleting file Verwijder bestand Removing autostart Verwijder autostart Autostart file deleted. Kill current process too ? Autostart ongedaan gemaakt proces ook stoppen ? RclCompleterModel Hits RclMain About Recoll Over Recoll Executing: [ Uitvoeren: [ Cannot retrieve document info from database kan info van het document uit database niet lezen Warning Waarschuwing Can't create preview window kan preview venster niet maken Query results Zoekresultaat Document history Document geschiedenis History data Geschiedenis data Indexing in progress: Indexering is bezig Files Bestanden Purge Wissen Stemdb Stemdb Closing Sluiten Unknown Onbekend This search is not active any more Deze zoekopdracht is niet meer aktief Can't start query: Kan'starten met zoekopdracht: Bad viewer command line for %1: [%2] Please check the mimeconf file Verkeerde viewer opdrachtregel voor %1: [%2] Controleer het mimeconf bestand Cannot extract document or create temporary file kan het document niet uitpakken of een tijdelijk bestand maken (no stemming) Geen taal (all languages) alle talen error retrieving stemming languages Fout bij het ophalen van de stam talen Update &Index Indexeren &bijwerken Indexing interrupted Indexering onderbroken Stop &Indexing Stop &indexeren All Alle media Media message bericht other anders presentation presentatie spreadsheet rekenblad text tekst sorted gesorteerd filtered gefilterd External applications/commands needed and not found for indexing your file types: Externe applicaties/commando's nodig en niet gevonden voor het indexeren van uw bestandstypen: No helpers found missing Alle hulpprogrammas zijn aanwezig Missing helper programs Missende hulp programmas Save file dialog Bestand dialoogvenster opslaan Choose a file name to save under Kies een bestandsnaam om onder op te slaan Document category filter Document categorie filter No external viewer configured for mime type [ Geen externe viewer voor dit mime type geconfigureerd [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? De viewer gespecificeerd in mimeview voor %1: %2 is niet gevonden Wilt u het dialoogvenster voorkeuren openen? Can't access file: Geen toegang tot het bestand Can't uncompress file: Kan het bestand niet uitpakken Save file Bestand opslaan Result count (est.) Telresultaat(est.) Query details Zoekopdracht details Could not open external index. Db not open. Check external indexes list. kon externe index niet openen. Db niet geopend. Controleer externe indexlijst No results found Geen resultaten gevonden None Geen Updating Bijwerken Done afgerond Monitor Monitoren Indexing failed Indexering mislukt The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Het huidige indexering proces werdt niet gestart vanaf deze interface. Klik Ok om het toch te stoppen, of annuleren om het zo te laten Erasing index Wis index Reset the index and start from scratch ? De index resetten en geheel opnieuw beginnen? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Bezig met opdracht <br>Vanwege beperkingen van de indexeerder zal bij,<br>stop het programma in zijn geheel sluiten! Error Fout Index not open Index is niet open Index query error Index vraag fout Indexed Mime Types Geïndexeerde MIME-types Content has been indexed for these MIME types: Inhoud is geïndexeerd voor deze MIME-types: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Index niet up to date voor dit bestand. Hergebruik om het risico te lopen de verkeerde invoer te tonen. Klik Ok om de index voor dit bestand bij te werken, en voer daarna de query opnieuw uit als indexering is voltooid. Oké, Annuleren. Can't update index: indexer running kan het index niet bijwerken:indexeren is al aktief Indexed MIME Types Geindexeerd MIME Types Bad viewer command line for %1: [%2] Please check the mimeview file Verkeerde command line voor viewer %1:[%2'] controleer mimeview van bestand Viewer command line for %1 specifies both file and parent file value: unsupported Viewer command line voor %1 specificeerd zowel het bestandtype als het parentfile type waarde: niet ondersteund Cannot find parent document kan parent van document niet vinden Indexing did not run yet Indexering is nog niet bezig External applications/commands needed for your file types and not found, as stored by the last indexing pass in Externe toepassingen / commandos die nodig zijn voor dit bestandstype en niet gevonden, zoals opgeslagen in de laatste indexerings poging Index not up to date for this file. Refusing to risk showing the wrong entry. Index niet up to date voor dit bestand. Hergebruik om het risico te lopen de verkeerde invoer te tonen. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Klik Ok om de index voor dit bestand bij te werken, en voer daarna de query opnieuw uit als indexering is voltooid. Oké, Annuleren. Indexer running so things should improve when it's done Indexeren draaien, dus dingen moeten verbeteren wanneer'is voltooid Sub-documents and attachments Sub-documenten en attachments Document filter Document filter Index not up to date for this file. Refusing to risk showing the wrong entry. Index voor dit bestand is niet op tu date. geweigerd om verkeerde inforamtie te tonen te riskeren Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Klik Ok om de index voor dit bestand bij te werken, daarna moet u de opdracht opnieuw uitvoeren na het indexeren The indexer is running so things should improve when it's done. De indexeerder is bezig dus er zou een verbetering moeten optreden als hij klaar is. The document belongs to an external indexwhich I can't update. Het document behoort tot een externe indexering die ik'kan bijwerken. Click Cancel to return to the list. Click Ignore to show the preview anyway. Klik op Annuleren om terug te keren naar de lijst. Klik op Negeren om de voorbeeldweergave toch weer te geven. Duplicate documents Vermenigvuldig documenten These Urls ( | ipath) share the same content: Deze Urls (ipath) hebben dezelfde inhoud: Bad desktop app spec for %1: [%2] Please check the desktop file Verkeerde desktop snelkoppeling for %1:[%2] Graag de desktop snelkoppeling controleren Bad paths Pad verkeerd Bad paths in configuration file: Verkeerd pad in configuratie bestand Selection patterns need topdir Patronen selecteren vraagt een begin folder Selection patterns can only be used with a start directory Patronen selecteren kan alleen gebruikt worden met een start folder No search Niets gezocht No preserved previous search Geen opgeslagen vorige zoekresultaten Choose file to save Kies bestand om op te slaan Saved Queries (*.rclq) Bewaarde Zoekopdrachten (*.rclq) Write failed Schrijf fout Could not write to file Kan niet schrijven naar bestand Read failed Lees fout Could not open file: Kan bestand niet openen Load error Laad fout Could not load saved query Kon bewaarde zoekopdracht niet laden Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Openen van tijdelijke kopie.Alle bewerkingen zullen verloren gaan als u ze niet opslaat naar een permanente lokatie Do not show this warning next time (use GUI preferences to restore). Laat deze waarschuwing niet meer zien (gebruik GUI voorkeuren om te herstellen) Disabled because the real time indexer was not compiled in. Uitgeschakeld omdat real-time indexering niet ingeschakeld is This configuration tool only works for the main index. Deze configuratie tool werkt alleen voor de hoofdindex The current indexing process was not started from this interface, can't kill it Het huidige indexerings proces werdt niet gestart vanaf deze interface, kan het niet stoppen The document belongs to an external index which I can't update. Het document hoort bij een externe index die niet up te daten is Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Klik op annuleren om terug te keren naar de lijst. <br>Klik negeren om het voorbeeld toch te tonen( en te onthouden voor deze sessie) Index scheduling Index schema Sorry, not available under Windows for now, use the File menu entries to update the index Het spijt ons, dit is nog niet beschikbaar voor het windows platform, gebruik het bestands ingave menu om de index te updaten Can't set synonyms file (parse error?) kan synomiemen bestand niet instellen ( parse error?) Index locked index geblokkeerd Unknown indexer state. Can't access webcache file. De staat van de indexer is onbekend. Kan geen toegang krijgen tot het webcache bestand. Indexer is running. Can't access webcache file. De indexeerder is bezig. Geen toegang tot webcache with additional message: met extra bericht: Non-fatal indexing message: Bericht bij niet-fatale indexering: Types list empty: maybe wait for indexing to progress? Types lijst leeg: wacht misschien tot indexering doorgaat? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Bekijken opdrachtregel voor %1 geeft het bovenliggende bestand aan, maar de URL is http[s]: niet ondersteund Tools Gereedschappen Results Resultaten (%d documents/%d files/%d errors/%d total files) (%d documenten/%d bestanden/%d fouten /%d totale bestanden) (%d documents/%d files/%d errors) (%d documenten/%d bestanden/%d fouten) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Lege of niet-bestaande paden in het configuratiebestand. Klik OK om te beginnen met indexeren (afwezige gegevens zullen niet verwijderd worden uit de indexering): Indexing done Indexeren voltooid Can't update index: internal error Kan'bij te werken index: interne fout Index not up to date for this file.<br> Index niet up-to-date voor dit bestand.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Ook lijkt het erop dat de laatste index update voor het bestand is mislukt.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Klik op Ok om de index voor dit bestand bij te werken. U moet de query opnieuw uitvoeren wanneer de indexering is voltooid.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Klik op Annuleren om terug te keren naar de lijst.<br>Klik op Negeren om het voorbeeld toch te tonen (en vergeet niet voor deze sessie). Het risico bestaat dat de verkeerde inzending wordt getoond.<br/> documents documenten document document files bestanden file bestand errors fouten error fout total files) totale bestanden) No information: initial indexing not yet performed. Geen informatie: initiële indexering nog niet uitgevoerd. Batch scheduling Batch planning The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. De tool laat u beslissen op welk tijdstip het indexeren moet worden uitgevoerd. Het gebruikt de taakplanner. Confirm Bevestigen Erasing simple and advanced search history lists, please click Ok to confirm Verwijder eenvoudige en geavanceerde zoekgeschiedenislijsten, klik op OK om te bevestigen Could not open/create file Kon bestand niet openen/aanmaken F&ilter F&ilter Could not start recollindex (temp file error) Kan recoldex niet starten (tijdelijke bestandsfout) Could not read: Kan niet lezen: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Dit zal de huidige inhoud van de lijst met koptekst en de GUI qss bestandsnaam vervangen. Doorgaan? You will need to run a query to complete the display change. U moet een query uitvoeren om de weergavewijziging te voltooien. Simple search type Eenvoudig zoektype Any term Elke term All terms Alle termen File name Bestandsnaam Query language Zoek taal Stemming language Stam taal Main Window Hoofd venster Focus to Search Focus om te zoeken Focus to Search, alt. Focus om te zoeken, alt. Clear Search Zoekopdracht wissen Focus to Result Table Focus op resultaat tabel Clear search Zoekopdracht wissen Move keyboard focus to search entry Verplaats toetsenbord focus om item te zoeken Move keyboard focus to search, alt. Verplaats toetsenbord focus om te zoeken, alt. Toggle tabular display Tabulaire weergave in-/uitschakelen Move keyboard focus to table Verplaats toetsenbord focus naar tabel Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Vorige pagina Next page Volgende pagina &File &Bestand E&xit V&erlaten &Tools &Gereedschappen &Help &Hulp &Preferences &Voorkeuren Search tools Zoek gereedschappen Result list Resultaatslijst &About Recoll &Over Recoll Document &History Document & Geschiedenis Document History Document geschiedenis &Advanced Search &Geavanceerd Zoeken Advanced/complex Search Uitgebreid/ Geavanceerd Zoeken &Sort parameters &Sorteer parameters Sort parameters Sorteer parameters Next page of results Volgende resultaten pagina Previous page of results Vorige pagina met resultaten &Query configuration &Query configuratie &User manual &Gebruiks handleiding Recoll Herstellen Ctrl+Q Crtl+ Q Update &index Update &indexeerder Term &explorer Term &onderzoeker Term explorer tool Termen onderzoekers gereedschap External index dialog Extern index dialoog &Erase document history &Wis bestands geschiedenis First page Eerste pagina Go to first page of results Ga naar de eerste pagina van resultaten &Indexing configuration &Configuratie inedexering All Alle &Show missing helpers Ontbrekende helpers weergeven PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen &Volledig Scherm F11 F11 Full Screen Volledig Scherm &Erase search history &Wis zoekgeschiedenis sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Sorteer op datum van oud naar nieuw sortByDateDesc sorteerByDateDesc Sort by dates from newest to oldest Sorteer op datum van oud naar nieuw Show Query Details Toon zoek detials Show results as table Resultaten als tabel weergeven &Rebuild index &Vernieuw de gehele index &Show indexed types Geïndexeerde types weergeven Shift+PgUp Shift+PgUp &Indexing schedule &Indexerings schema E&xternal index dialog E&xternal index dialoog &Index configuration &Index configuratie &GUI configuration &GUI configuratie &Results &Resultaten Sort by date, oldest first Sorteer op datume, oudste eerst Sort by date, newest first Sorteer op datum, nieuwste eerst Show as table Toon als tabel Show results in a spreadsheet-like table Toon het resultaat in een spreadsheet achtig tabel Save as CSV (spreadsheet) file Bewaar als CVS ( spreadsheet) bestand Saves the result into a file which you can load in a spreadsheet Bewaar het resultaat naar een bestand die te laden is in een spreadsheet Next Page Volgende Pagina Previous Page Vorige Pagina First Page Eerste Pagina Query Fragments Zoek fragmenten With failed files retrying Opnieuw proberen met mislukte bestand Next update will retry previously failed files De volgende update zal de eerder mislukte bestanden opnieuw proberen Save last query Bewaar laatste zoekopdracht Load saved query Laad bewaarde zoekopdracht Special Indexing Speciale Indexering Indexing with special options Indexeren met speciale opties Indexing &schedule Indexing &schema Enable synonyms Schakel synoniemen in &View &Bekijken Missing &helpers Missend & Hulpprogrammas Indexed &MIME types Geindexeerd &MIME types Index &statistics Index & statistieken Webcache Editor Webcache Editor Trigger incremental pass Trigger incrementele pas E&xport simple search history E&xporteer eenvoudige zoekgeschiedenis Use default dark mode Gebruik standaard donkere modus Dark mode Donkere modus &Query &Query Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Filter datums Assisted complex search Filter birth dates RclTrayIcon Restore Herstellen Quit Afsluiten RecollModel Abstract Uittreksel Author Auteur Document size Bestands grootte Document date Bestands datum File size Bestands grootte File name Bestands naam File date Bestands datum Ipath Ipad Keywords Sleutelwoorden Mime type Mime type Original character set Origineel karakter set Relevancy rating relevantiewaarde Title Titel URL URL Mtime Mtijd Date Datum Date and time Datum en tijd Ipath Ipad MIME type MIME-type Can't sort by inverse relevance Kan't sorteren op inverse relevantie ResList Result list Resultaatslijst Unavailable document Document niet beschikbaar Previous Vorige Next Volgende <p><b>No results found</b><br> <p><b>Geen resultaat gevonden</b><br> &Preview &Bekijken Copy &URL Kopieer &URL Find &similar documents Vindt &gelijksoortige documenten Query details Zoekopdracht details (show query) (toon zoekopdracht) Copy &File Name Kopieer &Bestands Naam filtered gefilterd sorted gesorteerd Document history Document historie Preview Bekijken Open Openen <p><i>Alternate spellings (accents suppressed): </i> <p><i>Alternatieve spellingen (accenten onderdrukken): </i> &Write to File &Schijf naar Bestand Preview P&arent document/folder Preview B&ovenliggende document/map &Open Parent document/folder &Open Bovenliggend document/map &Open &Openen Documents Documenten out of at least van tenminste for voor <p><i>Alternate spellings: </i> <p><i>Alternatieve spelling: </i> Open &Snippets window Open &Knipsel venster Duplicate documents Vermenigvuldig documenten These Urls ( | ipath) share the same content: Deze Urls (ipath) hebben dezelfde inhoud: Result count (est.) Resultaten telling (est.) Snippets Knipsel This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Opnieuw sorteren &Delete column &Verwijder kolom Add " Toevoegen " " column " kolom Save table to CSV file Bewaar lijst als cvs bestand Can't open/create file: Kan bestand niet openen/ bewaren: &Preview &Bekijken &Open &Openen Copy &File Name Kopieer &Bestands Naam Copy &URL Kopieer &URL &Write to File &Schijf naar Bestand Find &similar documents Vindt &gelijksoortige documenten Preview P&arent document/folder Preview B&ovenliggende document/map &Open Parent document/folder &Open Bovenliggend document/map &Save as CSV &Bewaar als CVS Add "%1" column Voeg "%1" kolom toe Result Table Resultaat tabel Open Openen Open and Quit Openen en afsluiten Preview Bekijken Show Snippets Tekstfragmenten tonen Open current result document Open huidig resultaat document Open current result and quit Huidige resultaat openen en afsluiten Show snippets Tekstbouwstenen tonen Show header Toon koptekst Show vertical header Toon verticale koptekst Copy current result text to clipboard Kopieer huidige resultaattekst naar klembord Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Bekijken &Open &Openen Copy &File Name Kopieer &Bestands Naam Copy &URL Kopieer &URL &Write to File &Schijf naar Bestand Find &similar documents Vindt &gelijksoortige documenten Preview P&arent document/folder Preview B&ovenliggende document/map &Open Parent document/folder &Open Bovenliggend document/map ResultPopup &Preview &Bekijken &Open &Openen Copy &File Name Kopieer &Bestands Naam Copy &URL Kopieer &URL &Write to File &Schijf naar Bestand Save selection to files Bewaar selektie naar bestanden Preview P&arent document/folder Preview B&ovenliggende document/map &Open Parent document/folder &Open Bovenliggend document/map Find &similar documents Vindt &gelijksoortige documenten Open &Snippets window Open &Knipsel venster Show subdocuments / attachments Toon subdocumenten / attachments Open With Open met Run Script Voer script uit SSearch Any term Elke term All terms Alle termen File name Bestandsnaam Completions Voltooiing Select an item: Selecteer een item: Too many completions Te veel aanvullingen Query language Zoek taal Bad query string Foute zoekterm Out of memory Geen geheugen meer Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Voer query taalexpressie in. Cheat sheet:<br> <i>term1 term2</i> : 'term1' en 'term2' in elk veld.<br> <i>field:term1</i> : 'term1' in veld 'veld'.<br> Standaard veldnamen/synoniemen:<br> titel/onderwerp/ondertiteling, auteur/van, ontvanger/tot, bestandsnaam, ext.<br> Pseudo-velden: dir, mime/format, type/rclcat, date.<br> Twee datum interval exemplaars: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 EN (term2 OR term3).<br> haakjes zijn niet toegestaan.<br> <i>"term1 term2"</i> : phrase (moet precies gebeuren). Mogelijke wijzigingen:<br> <i>"term1"per</i> : ongeordende nabijheidszoekopdracht met standaard afstand.<br> Gebruik <b>Toon Query</b> link bij twijfel over het resultaat en zie de handleiding (&lt; 1>) voor meer detail. Enter file name wildcard expression. Voer bestandsnaam wildcard uitdrukking in. Enter search terms here. Type ESC SPC for completions of current term. Voer zoekterm hier in. Type ESC SPC als aanvulling voor huidige term Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Zoekterm'taal expressie. Cheat sheet: <br> <i> term1 term2 </i>. 'Term1' en 'term2' op elk gebied <br> <i> veld: term1 </i>. 'Term1' in 'het veld' veld <br> Standaard veldnamen / synoniemen: <br> titel / onderwerp / titel, auteur / uit, ontvanger / to, filename, ext. <br> Pseudo-velden: dir, mime / format, het type / rclcat, datum, grootte <br>. Twee datuminterval Voorbeelden: 2009-03-01 / 2009-05-20 2009-03-01 / P2M <br>. <i> term1 term2 OR term3 </i>: term1 AND (term2 OR term3) <br>. U kunt haakjes gebruiken om dingen duidelijker te maken. <br> <i> "term1 term2" </i>: zin (moet precies gebeuren). Mogelijke modifiers: <br> <i> "term1 term2" p </i>. Ongeordende nabijheid zoeken met de standaard afstand <br> Gebruik <b> Toon Zoekterm </b> in geval van twijfel over de uitslag en zie handleiding (& lt; F1>) voor meer informatie. Stemming languages for stored query: Stam taal voor opgeslagen zoekopdrachten: differ from current preferences (kept) Afwijken van de uidig (bewaarde) voorkeuren Auto suffixes for stored query: Automatische aanvullingen voor opgeslagen zoeken External indexes for stored query: External indexen voor opgeslagen zoekopdrachten: Autophrase is set but it was unset for stored query Auto aanvullen is ingesteld, maar het was uitgeschakeld voor de opgeslagen zoekopdracht Autophrase is unset but it was set for stored query Automatisch aanvullen is uitgeschakeld maar was ingesteld voor opegeslagen zoekopdracht Enter search terms here. Vul hier zoektermen in. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border-collapse: instorten; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Query language cheat-sheet . Klik in twijfel: <b>Toon query</b>.&nbsp; You should really look at the manual (F1)</p> Je zou echt naar de handleiding moeten kijken (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Wat</th><th>Voorbeelden</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>En</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Of</td><td>een OF twee&nbsp;&nbsp;&nbsp;een vijand twee</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Complex Boolean. OF heeft prioriteit, gebruik haakjes&nbsp; where needed</td><td>(one AND two) OR three</td></tr> waar je</td><td>(één EN twee) OF 3</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Niet</td><td>-term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Zin</td><td>"trots en vooroordeel"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Ongeordende prox. (standaard slack=10)</td><td>"vooroordelen&nbsp;trots"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Geen stamexpansie: hoofdletter</td><td>vloer</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Velden-specifiek</td><td>auteur:austen&nbsp;&nbsp;titel:vooroor</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>EN binnen het veld (geen volgorde)</td><td>auteur:jane, austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OF binnen het veld</td><td>auteur:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>veldnamen</td><td>titel/onderwerp/onderschrift&nbsp;&nbsp;auteur/van<br>ontvanger/&nbsp;&nbsp;bestandsnaam&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Map pad filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME-type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Datumintervallen</td><td>datum:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> datum:2018&nbsp;&nbsp;datum:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Kan'de index openen Could not restore external indexes for stored query:<br> Externe indexen konden niet worden hersteld voor opgeslagen zoekopdracht:<br> ??? ??? Using current preferences. Gebruik huidige voorkeuren. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SZoekBasis Clear Wissen Ctrl+S Crtl+S Erase search entry Wis zoekopdracht Search Zoeken Start query Start zoekopdracht Enter search terms here. Type ESC SPC for completions of current term. Voer de zoekopdracht term hier in. Type ESC SPC om huidige termen aan te vullen Choose search type. Kies zoektype. Show query history Query geschiedenis weergeven Enter search terms here. Vul hier zoektermen in. Main menu Hoofd menu SearchClauseW SearchClauseW ClauseW zoeken Any of these Een van deze All of these Al deze None of these Geen van deze This phrase Deze zin Terms in proximity Voorwaarden in nabijheid File name matching Bestandsnaam komt overeen Select the type of query that will be performed with the words Selecteer het type zoekopdracht dat zal worden uitgevoerd met de woorden: Number of additional words that may be interspersed with the chosen ones Aantal extra woorden die kunnen worden ingevoegd met de gekozen woorden In field In veld No field Geen veld Any Elke All Alle None Geen Phrase Frase Proximity Ongeveer File name Bestandsnaam Snippets Snippets Knipsels X X Find: Vindt: Next Volgende Prev Vorige SnippetsW Search Zoek <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <P> Sorry, niet iets precies kunnen vinden. Waarschijnlijk is het document zeer groot en is de knipsels generator verdwaald in een doolhof ... </ p> Sort By Relevance Sorteren op relevantie Sort By Page Sorteren op pagina Snippets Window Snippets venster Find Vind Find (alt) Zoeken (alternatief) Find Next Volgende zoeken Find Previous Vorige zoeken Hide Verbergen Find next Volgende zoeken Find previous Vorige zoeken Close window Sluit venster SortForm Date Datum Mime type Mime type SortFormBase Sort Criteria Sorteer criteria Sort the Sorteer de most relevant results by: meest relevante resultaten door: Descending Aflopend Close sluit Apply Toepassen SpecIdxW Special Indexing Speciale indexering Do not retry previously failed files. Probeerniet nog eens de vorig niet gelukte bestanden Else only modified or failed files will be processed. Anders zullen alleen de veranderende of gefaalde bestanden verwerkt worden Erase selected files data before indexing. Wis de geselecteerde bestandens data voor de indexering Directory to recursively index Map naar recursieve index Browse Bladeren Start directory (else use regular topdirs): Begin Map (anders de normale hoofdmappen gebruiken) Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Laat dit leeg om alle bestanden te kunnen selecteren. U kunt meerdere spaties gescheiden shell-type patronen gebruiken. <br> Patronen met ingesloten ruimtes moeten aangeduid worden met dubbele aanhalingstekens. <br> Kan alleen worden gebruikt als het hoofddoel is ingesteld Selection patterns: Selecteer patronen Top indexed entity Hoofd index identiteit Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Map om recursief te indexeren. Dit moet binnen het reguliere geindexeerde gebied zijn<br>zoals ingesteld in het configuratiebestand (hoofdmappen) Retry previously failed files. Probeer eerder mislukte bestanden opnieuw Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Moet deel uitmaken van de geïndexeerde boom. We gebruiken topdaturen indien leeg. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Start directory. Moet deel uitmaken van de geïndexeerde boom. Gebruik volledig geïndexeerd gebied indien leeg. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Term onderzoeker &Expand &Uitvouwen Alt+E Alt+E &Close &Sluiten Alt+C Alt+C Term Termijn No db info. Geen db info. Doc. / Tot. Doc./Tot. Match Gelijk Case Hoofdletter Accents Accenten SpellW Wildcards wildcards Regexp Regexp Spelling/Phonetic Spelling/Phonetisch Aspell init failed. Aspell not installed? Aspell init faalt. Is Aspell niet geinstalleerd? Aspell expansion error. Aspell expansie fout. Stem expansion Stam expansie error retrieving stemming languages Fout bij het ophalen van woordstam talen No expansion found Geen expansie gevonden Term Termijn Doc. / Tot. Doc./Tot. Index: %1 documents, average length %2 terms Index: %1 documenten, gemiddelde lengte %2 termen Index: %1 documents, average length %2 terms.%3 results Index: %1 documenten, wisselende lengte %2 termen.%3 resultaten %1 results %1 resultaten List was truncated alphabetically, some frequent De lijst is alfabetisch afgebroken, sommige frequenter terms may be missing. Try using a longer root. Er kunnen termen ontbreken. Probeer gebruik te maken van een langere root Show index statistics Toon indexeer statistieken Number of documents Aantal documenten Average terms per document Gemiddelde termen per document Smallest document length Kleinste documentlengte Longest document length Langste documentlengte Database directory size Database map grootte MIME types: MIME types Item Artikel Value Waarde Smallest document length (terms) Kleinste document lengte (termen) Longest document length (terms) Langste document lengte (termen) Results from last indexing: resultaten van vorige indexering Documents created/updated Documenten gemaakt/bijgewerkt Files tested Bestanden getest Unindexed files Ongeindexeerde bestanden List files which could not be indexed (slow) Bestanden weergeven die niet geïndexeerd konden worden (langzaam) Spell expansion error. Spel uitbreidingsfout. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index De geselecteerde map schijnt geen Xapian index te zijn This is the main/local index! Dit is de hoofd/lokale index! The selected directory is already in the index list De geselecteerde map bestaat al in de index lijst Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Selecteer xapiaanse index map (bv: /home/buddy/.recoll/xapiandb) error retrieving stemming languages fout bij het ophalen van de stam talen Choose Kies Result list paragraph format (erase all to reset to default) Resultaten lijst paragrafen formaat (wist alles en reset naar standaard) Result list header (default is empty) Resultaten koppen lijst ( is standaard leeg) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Selecteer recoll config map of xapian index map (bijv.: /home/me/.recoll of /home/me/.recoll/xapian db) The selected directory looks like a Recoll configuration directory but the configuration could not be read De geselecteerde map ziet eruit als een Recoll configuratie map, maar de configuratie kon niet worden gelezen At most one index should be selected Tenminste moet er een index worden geselecteerd Cant add index with different case/diacritics stripping option Kan index met verschillende hoofdletters/ diakritisch tekens opties niet toevoegen Default QtWebkit font Standaard QtWebkit lettertype Any term Elke term All terms Alle termen File name Bestandsnaam Query language Zoek taal Value from previous program exit Waarde van vorige programma afsluiting Context Context Description Beschrijving Shortcut Snelkoppeling Default Standaard Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Gebruikers interface Number of entries in a result page Opgegeven aantal van weergaves per resultaten pagina Result list font Resultaten lijst lettertype Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Opent een dialoog om de resultaten lijst lettertype te selecteren Reset Herstel Resets the result list font to the system default Reset het resultaten lijst lettertype naar systeem standaardwaarde Auto-start simple search on whitespace entry. Autostart eenvoudige zoekopdracht bij ingave in de witruimte. Start with advanced search dialog open. Start met geavanceerd zoek dialog open. Start with sort dialog open. Start met open sorteerdialoogvenster. Search parameters Zoek parameters Stemming language Stam taal Dynamically build abstracts Dynamisch abstracten bouwen Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Moeten we proberen om abstracten voor resultatenlijst invoering op te bouwen met behulp van de context van de zoektermen? Kan traag zijn met grote documenten. Replace abstracts from documents Vervang abstracten van documenten Do we synthetize an abstract even if the document seemed to have one? Moeten we een abstract maken, zelfs als het document er al een blijkt te hebben? Synthetic abstract size (characters) Synthetische abstractie grootte (tekens) Synthetic abstract context words Synthetische abstract context woorden External Indexes Externe indexen Add index Index toevoegen Select the xapiandb directory for the index you want to add, then click Add Index Selecteer de xapiandb map voor de index die je wilt toevoegen, klik daarna op Index toevoegen. Browse Bladeren &OK &Oké Apply changes Veranderingen doorvoeren &Cancel &Annuleren Discard changes Veranderingen ongedaan maken Result paragraph<br>format string Resultaat alinea<br>opmaak tekenreeks Automatically add phrase to simple searches Automatisch aanvullen van eenvoudige zoekopdrachten A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Een zoekopdracht naar '[rollende stenen] (2 termen) wordt gewijzigd in [rollen of stenen of (rollende frase 2 stenen)]. Dit zou een hogere prioriteit moeten geven aan de resultaten, waar de zoektermen precies zoals ingevoerd moeten verschijnen. User preferences Gebruikers voorkeuren Use desktop preferences to choose document editor. Gebruik de desktopvoorkeuren om documentbewerker te kiezen. External indexes Externe indexen Toggle selected Toggle geselecteerde Activate All Alles Activeren Deactivate All Alles Deactiveren Remove selected Geselecteerde verwijderen Remove from list. This has no effect on the disk index. Verwijder van de lijst. Dit heeft geen effect op de schijf index. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definieert het formaat voor elke alinea met resultatenlijst. Gebruik qt html-formaat en printf-achtige vervangingen:<br>%A Abstract<br> %D Datum<br> %I Naam van pictogramafbeelding<br> %K Sleutelwoorden (indien aanwezig)<br> %LVoorbeeld bekijken en links bewerken<br> %M Mime type<br> %N Resultaat nummer<br> %R Relevantiepercentage<br> %S Maat informatie<br> %T Titel<br> %U Url<br> Remember sort activation state. Onthoud sorteer activatie status Maximum text size highlighted for preview (megabytes) Maximale tekst groote highlighted voor preview (megabytes) Texts over this size will not be highlighted in preview (too slow). Teksten groter dan dit zullen niet worden highlighted in previews (te langzaam). Highlight color for query terms Highlight kleur voor zoektermen Prefer Html to plain text for preview. Html voorkeur in plaats van gewoon tekst als preview If checked, results with the same content under different names will only be shown once. Indien aangevinkt, zullen de resultaten met dezelfde inhoud onder verschillende namen slecht eenmaal worden getoond. Hide duplicate results. Verberg duplicaat resultaten. Choose editor applications Kies editor toepassingen Display category filter as toolbar instead of button panel (needs restart). Toon categorie filter als werkbalk in plaats van knop paneel (vereist herstart). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. De woorden in de lijst zal automatisch omgezet worden naar ext:xxx clausules in de zoektaal ingave. Query language magic file name suffixes. Zoek taal magic bestandsnaam achtervoegsel Enable Aanzetten ViewAction Changing actions with different current values Acties met verschillende huidige waarden wijzigen Mime type Mime type Command Opdracht MIME type MIME-type Desktop Default Desktop Standaard Changing entries with different current values invoering van verschillende huidige waardes veranderd ViewActionBase File type Soort bestand Action actie Select one or several file types, then click Change Action to modify the program used to open them Selecteer een of meerdere bestandstypes en klik vervolgens op 'Wijzigen' actie om het programma te wijzigen dat wordt gebruikt om ze te openen Change Action Wijzig actie Close Afsluiten Native Viewers Standaard Viewers Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Selecteer een of meerdere MIME-types en klik vervolgens op "Actie wijzigen"<br>U kunt dit dialoogvenster ook sluiten en controleren "Gebruik desktopvoorkeuren"<br>in het hoofdvenster om deze lijst te negeren en uw bureaublad standaardwaarden te gebruiken. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Slecteer een of meerdere mime types gebruik vervolgens de instellingen onderin het venster om de verwerkingen aan te passen Use Desktop preferences by default Gebruik Desktop voorkeuren als standaard Select one or several file types, then use the controls in the frame below to change how they are processed Selecteer een of meerdere bestandstypes, gebruik vervolgens de instellingen onderin het venster hoe ze verwerkt worden Exception to Desktop preferences Uitzonderingen op Desktop voorkeuren Action (empty -> recoll default) Aktie (leeg -> recoll standaard) Apply to current selection Toepassen op huidige selectie Recoll action: Recoll acties current value huidige waarde Select same Selecteer dezelfde <b>New Values:</b> <b>Nieuwe Waardes:</b> Webcache Webcache editor Webcache bewerker Search regexp Zoek regexp TextLabel WebcacheEdit Copy URL Kopieer URL Unknown indexer state. Can't edit webcache file. Status van indexer onbekend. Kan webcache bestand niet bewerken. Indexer is running. Can't edit webcache file. Indexer is aan het werken. Kan webcache bestand niet bewerken. Delete selection Verwijder selectie Webcache was modified, you will need to run the indexer after closing this window. Webcache is gewijzigd, u zult de indexer opnieuw moeten uitvoeren na het sluiten van dit venster Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIJN Url URL Date Datum Size URL URL WinSchedToolW Error Fout Configuration not initialized Configuratie niet geïnitialiseerd <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexeren van batchplanning</h3><p>We gebruiken hiervoor de standaard Windows taakplanner. Het programma wordt gestart wanneer je op de knop hieronder klikt.</p><p>U kunt ofwel de volledige interface gebruiken (<i>taak aanmaken</i> in het menu aan de rechterkant) of de vereenvoudigde <i>Maak de basistaak</i> wizard aan. In beide gevallen kopieer en plak je het bestandspad hieronder als de <i>actie</i> om uit te voeren.</p> Command already started Commando is al gestart Recoll Batch indexing Recoll batchindexering Start Windows Task Scheduler tool Start Windows Taakplanner tool Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Steel Beagle indexeer wachtrij Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Nou, waarom moet je dat NIET doen. Maakt het mogelijk om de rijen van Firefox de geschiedenis te indexeren.<br>(je zou ook de Firefox Beagle plugin moeten installeren) Web cache directory name Webcache map naam The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. De naam voor een map waar de cache opgeslagen moet worden voor bezochte webpagina's.<br>Een niet-absoluut pad is genomen relatief aan de configuratiemap. Max. size for the web cache (MB) Max. grootte voor de webcache (MB) Entries will be recycled once the size is reached Invoer zal worden gerecycled zodra de grootte is bereikt Web page store directory name Web pagina map naam om op te slaan The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. De naam voor een map waarin de kopieen van de bezochte webpaginas opgeslagen zullen worden.<br>Een niet absoluut pad zal worden gekozen ten opzichte van de configuratie map Max. size for the web store (MB) Max. grootte voor het web opslaan (MB) Process the WEB history queue Verwerk de WEB geschiedenis wachtrij Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Zet het indexeren van firefox bezochte paginas aan. <br> (hiervoor zal ook de Firefox Recoll plugin moeten worden geinstalleerd door uzelf) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Invoeringen zullen worden gerecycled zodra de groote is bereikt. <br> Het verhogen van de groote heeft zin omdat het beperken van de waarde de bestaande waardes niet zal afkappen ( er is alleen afval ruimte aan het einde). confgui::ConfIndexW Can't write configuration file Kan configuratie bestand niet lezen Recoll - Index Settings: Recoll - Index instellingen: confgui::ConfParamFNW Browse Bladeren Choose Kies confgui::ConfParamSLW + + - - Add entry Invoer toevoegen Delete selected entries Geselecteerde invoergegevens wissen ~ ~ Edit selected entries Geselecteerde items bewerken confgui::ConfSearchPanelW Automatic diacritics sensitivity Automatische diakritische tekens gevoeligheid <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <P> Automatisch activeren diakritische tekens gevoeligheid als de zoekterm tekens zijn geaccentueerd (niet in unac_except_trans). Wat je nodig hebt om de zoek taal te gebruiken en de <i> D</i> modifier om diakritische tekens gevoeligheid te specificeren. Automatic character case sensitivity Automatische karakter hoofdletter gevoeligheid <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <P> Automatisch activeren hoofdletters gevoeligheid als de vermelding hoofdletters heeft in elke, behalve de eerste positie. Anders moet u zoek taal gebruiken en de <i>C</i> modifier karakter-hoofdlettergevoeligheid opgeven. Maximum term expansion count Maximale term uitbreidings telling <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p> Maximale uitbreidingstelling voor een enkele term (bijv.: bij het gebruik van wildcards) Een standaard van 10.000 is redelijk en zal zoekpodrachten die lijken te bevriezen terwijl de zoekmachine loopt door de termlijst vermijden. Maximum Xapian clauses count Maximaal Xapian clausules telling <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p> Maximale aantal elementaire clausules die we kunnen toevoegen aan een enkele Xapian zoeken. In sommige gevallen kan het resultaatvan de term uitbreiding multiplicatief zijn, en we willen voorkomen dat er overmatig gebruik word gemaakt van het werkgeheugen. De standaard van 100.000 zou hoog genoeg moeten zijn in beidde gevallen en compatible zijn met moderne hardware configuraties. confgui::ConfSubPanelW Global Globaal Max. compressed file size (KB) Maximaal gecomprimeerd bestands formaat (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Deze waarde stelt een drempel waarboven gecomprimeerde bestanden niet zal worden verwerkt. Ingesteld op -1 voor geen limiet, op 0 voor geen decompressie ooit. Max. text file size (MB) Max. tekstbestand groote (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Deze waarde stelt een drempel waarboven tekstbestanden niet zal worden verwerkt. Ingesteld op -1 voor geen limiet. Dit is voor het uitsluiten van monster logbestanden uit de index. Text file page size (KB) Tekst bestand pagina grootte (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Als deze waarde is ingesteld (niet gelijk aan -1), zal tekstbestanden worden opgedeeld in blokken van deze grootte voor indexering. Dit zal helpen bij het zoeken naar zeer grote tekstbestanden (bijv: log-bestanden). Max. filter exec. time (S) Max. filter executie tijd (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Externe filters die langer werken dan dit zullen worden afgebroken. Dit is voor de zeldzame zaak (bijv.: postscript) waar een document een filter kan veroorzaken om te loopsen op -1 voor geen limiet. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Externe filters die langer dan dit werken worden afgebroken. Dit is voor het zeldzame geval (bijv: postscript) wanneer een document een filterlus zou kunnen veroorzaken. Stel in op -1 voor geen limiet. Only mime types Alleen mime types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Een exclusieve lijst van geïndexeerde typen mime. <br> Niets anders zal worden geïndexeerd. Normaal gesproken leeg en inactief Exclude mime types Sluit mime types uit Mime types not to be indexed Mime types die niet geindexeerd zullen worden Max. filter exec. time (s) Max. uitvoertijd filter (s) confgui::ConfTopPanelW Top directories Top mappen The list of directories where recursive indexing starts. Default: your home. Een lijst van mappen waar de recursive indexering gaat starten. Standaard is de thuismap. Skipped paths Paden overgeslagen These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Dit zijn de namen van de mappen die indexering niet zal doorzoeken. <br> Kan wildcards bevatten. Moet overeenkomen met de paden gezien door de indexer (bijv: als topmappen zoals '/ home/me en '/ home' is eigenlijk een link naar '/usr/home', een correcte overgeslagen pad vermelding zou zijn '/home/me/tmp * ', niet' /usr/home/me/tmp * ') Stemming languages Stam talen The languages for which stemming expansion<br>dictionaries will be built. De talen waarvoor de stam uitbreidings<br>wooordenboeken voor zullen worden gebouwd. Log file name Log bestandsnaam The file where the messages will be written.<br>Use 'stderr' for terminal output Het bestand waar de boodschappen geschreven zullen worden.<br>Gebruik 'stderr' voor terminal weergave Log verbosity level Log uitgebreidheids nivo This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Deze waarde bepaald het aantal boodschappen,<br>van alleen foutmeldingen tot een hoop debugging data. Index flush megabytes interval Index verversings megabyte interval This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Deze waarde past de hoeveelheid data die zal worden geindexeerd tussen de flushes naar de schijf.<br> Dit helpt bij het controleren van het gebruik van geheugen. Standaad 10MB Max disk occupation (%) maximale schijf gebruik This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Dit is het precentage van schijfgebruike waar indexering zal falen en stoppen (om te vermijden dat uw schijf volraakt.<br>0 betekend geen limit (dit is standaard). No aspell usage Gebruik aspell niet Aspell language Aspell taal The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. De taal van het aspell woordenboek. Dit zou er als 'en' of 'fr' moeten uitzien. .<br>Als deze waarde niet is ingesteld, zal de NLS-omgeving worden gebruikt om het te berekenen, wat meestal werkt. o krijgt een idee van wat is geïnstalleerd op uw systeem, typ 'aspell config' en zoek naar . op bestanden in de map 'data-dir' Database directory name Database map naam The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. De naam voor een map waar het opslaan van de index<br>Een niet-absoluut pad is genomen ten opzichte van de configuratiemap. Standaard is 'xapiandb'. Use system's 'file' command Systeem's 'bestand' opdracht gebruiken Use the system's 'file' command if internal<br>mime type identification fails. Gebruik het systeem's 'bestand' opdracht als interne<br>MIME-type identificatie niet werkt. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Schakelt het gebruik van aspell uit om spellings gissingen in het term onderzoeker gereedschap te genereren. <br> Handig als aspell afwezig is of niet werkt. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Taal instelling voor het aspell woordenboek. Dit zou er uit moeten zien als 'en'of 'nl'...<br> als deze waarde niet is ingesteld, zal de NLS omgeving gebruikt worden om het te berekenen, wat meestal werkt. Om een idee te krijgen wat er op uw systeem staat, type 'aspell config' en zoek naar .dat bestanden binnen de 'data-dir'map. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. De naam voor een map om de index in op te slaan<br> Een niet absoluut pad ten opzichte van het configuratie bestand is gekozen. Standaard is het 'xapian db'. Unac exceptions Unac uitzonderingen <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Dit zijn uitzonderingen op het unac mechanisme dat, standaard, alle diakritische tekens verwijderd, en voert canonische ontbinding door. U kunt unaccenting voor sommige karakters veranderen, afhankelijk van uw taal, en extra decomposities specificeren, bijv. voor ligaturen. In iedere ruimte gescheiden ingave , waar het eerste teken is de bron is, en de rest de vertaling. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Dit zijn padnamen van mappen die niet zullen worden geïndexeerd.<br>Pad elementen kunnen wildcards bevatten. De items moeten overeenkomen met de paden die door de indexeerder worden gezien (bijv. als de uren op de voorgrond zijn '/home/me' en '/home' een link is naar '/usr/home'een juist skippedPath item zou '/home/me/tmp*'zijn, niet '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Max schijf bezetting (%, 0 betekent geen limiet) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Dit is het percentage van schijfgebruik - totaal schijfgebruik, niet indexgrootte - waarop de indexering zal mislukken en stoppen.<br>De standaardwaarde van 0 verwijdert elke limiet. uiPrefsDialogBase User preferences Gebruikers voorkeuren User interface Gebruikers interface Number of entries in a result page Opgegeven aantal van weergaves per resultaten pagina If checked, results with the same content under different names will only be shown once. Indien aangevinkt, zullen de resultaten met dezelfde inhoud onder verschillende namen slecht eenmaal worden getoond. Hide duplicate results. Verberg duplicaat resultaten. Highlight color for query terms Highlight kleur voor zoektermen Result list font Resultaten lijst lettertype Opens a dialog to select the result list font Opent een dialoog om de resultaten lijst lettertype te selecteren Helvetica-10 Helvetica-10 Resets the result list font to the system default Reset het resultaten lijst lettertype naar systeem standaardwaarde Reset Herstel Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definieert het formaat voor elke alinea met resultatenlijst. Gebruik qt html-formaat en printf-achtige vervangingen:<br>%A Abstract<br> %D Datum<br> %I Naam van pictogramafbeelding<br> %K Sleutelwoorden (indien aanwezig)<br> %LVoorbeeld bekijken en links bewerken<br> %M Mime type<br> %N Resultaat nummer<br> %R Relevantiepercentage<br> %S Maat informatie<br> %T Titel<br> %U Url<br> Result paragraph<br>format string Resultaat alinea<br>opmaak tekenreeks Texts over this size will not be highlighted in preview (too slow). Teksten groter dan dit zullen niet worden highlighted in previews (te langzaam). Maximum text size highlighted for preview (megabytes) Maximale tekst groote highlighted voor preview (megabytes) Use desktop preferences to choose document editor. Gebruik de desktopvoorkeuren om documentbewerker te kiezen. Choose editor applications Kies editor toepassingen Display category filter as toolbar instead of button panel (needs restart). Toon categorie filter als werkbalk in plaats van knop paneel (vereist herstart). Auto-start simple search on whitespace entry. Autostart eenvoudige zoekopdracht bij ingave in de witruimte. Start with advanced search dialog open. Start met geavanceerd zoek dialog open. Start with sort dialog open. Start met open sorteerdialoogvenster. Remember sort activation state. Onthoud sorteer activatie status Prefer Html to plain text for preview. Html voorkeur in plaats van gewoon tekst als preview Search parameters Zoek parameters Stemming language Stam taal A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Een zoekopdracht naar '[rollende stenen] (2 termen) wordt gewijzigd in [rollen of stenen of (rollende frase 2 stenen)]. Dit zou een hogere prioriteit moeten geven aan de resultaten, waar de zoektermen precies zoals ingevoerd moeten verschijnen. Automatically add phrase to simple searches Automatisch aanvullen van eenvoudige zoekopdrachten Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Moeten we proberen om abstracten voor resultatenlijst invoering op te bouwen met behulp van de context van de zoektermen? Kan traag zijn met grote documenten. Dynamically build abstracts Dynamisch abstracten bouwen Do we synthetize an abstract even if the document seemed to have one? Moeten we een abstract maken, zelfs als het document er al een blijkt te hebben? Replace abstracts from documents Vervang abstracten van documenten Synthetic abstract size (characters) Synthetische abstractie grootte (tekens) Synthetic abstract context words Synthetische abstract context woorden The words in the list will be automatically turned to ext:xxx clauses in the query language entry. De woorden in de lijst zal automatisch omgezet worden naar ext:xxx clausules in de zoektaal ingave. Query language magic file name suffixes. Zoek taal magic bestandsnaam achtervoegsel Enable Aanzetten External Indexes Externe indexen Toggle selected Toggle geselecteerde Activate All Alles Activeren Deactivate All Alles Deactiveren Remove from list. This has no effect on the disk index. Verwijder van de lijst. Dit heeft geen effect op de schijf index. Remove selected Geselecteerde verwijderen Click to add another index directory to the list Klik om een andere indexmap aan de lijst toe te voegen Add index Index toevoegen Apply changes Veranderingen doorvoeren &OK &Oké Discard changes Veranderingen ongedaan maken &Cancel &Annuleren Abstract snippet separator Abstract knipsel scheiding Use <PRE> tags instead of <BR>to display plain text as html. Gebruik <PRE> tags in plaats van <BR>om platte tekst als html weer te geven. Lines in PRE text are not folded. Using BR loses indentation. Regels in PRE tekst zijn niet gevouwen met behulp van BR inspringing. Style sheet Stijl blad Opens a dialog to select the style sheet file Opend een dialoog venster om style sheet te selecteren Choose Kies Resets the style sheet to default Reset de style sheet naar standaard Lines in PRE text are not folded. Using BR loses some indentation. Regels in PRE tekst zijn niet geplooid. Het gebruik van BR verliest wat inspringen. Use <PRE> tags instead of <BR>to display plain text as html in preview. Gebruik <PRE> tags in plaats van <BR>om platte tekst als html in het voorbeeld weer te geven. Result List Resultaten lijst Edit result paragraph format string Bewerk resultaten paragraaf formaat string Edit result page html header insert Bewerk resultaat pagina html header invoeg Date format (strftime(3)) Datum notatie (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Frequentie percentage drempel waarover wij geen termen gebruiken binnen autofrase. Frequente termen zijn een belangrijk prestatie probleem met zinnen en frases. Overgeslagen termen vergroten de zins verslapping, en verminderen de autofrase doeltreffendheid. De standaardwaarde is 2 (procent). Autophrase term frequency threshold percentage Autofrase term frequentie drempelwaarde percentage Plain text to HTML line style Platte tekst naar HTML lijn stijl Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Lijnen in PRE tekst worden niet gevouwen. Met behulp van BR kan inspringen verwijderen. PRE + Wrap stijl zou wenselijk kunnen zijn. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + pauze Exceptions Uitzonderingen Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Mime types die niet mogen worden doorgegeven aan xdg-open, zelfs niet wanneer "gebruik desktopvoorkeuren" is ingesteld.<br> Nuttig om paginanummer door te geven en om tekst-opties te zoeken, bijv. evince. Disable Qt autocompletion in search entry. Schakel Qt auto-aanvullen uit in zoek invoegveld Search as you type. Zoek terwijl u typed. Paths translations Paden vertalingen Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Klik hier om een andere index map toe te voegen aan de lijst. U kunt een Recoll configuratie map of een Xapian index te selecteren. Snippets window CSS file Knipsel venster CSS bestand Opens a dialog to select the Snippets window CSS style sheet file Opent een dailoog venster om het knipsel venster CSS stijl sheet bestand te selecteren Resets the Snippets window style Herstel de Knipsel venster stijl Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Bepaal of document mappen moeten worden weergegeven als keuzerondjes, gereedschap combinatiebox of menu. Document filter choice style: Document filter keuze stijl: Buttons Panel Knoppen Paneel Toolbar Combobox Gereedschaps-menu combinatiebox Menu Menu Show system tray icon. Toon pictogram in het systeemvak. Close to tray instead of exiting. Sluit naar systeemvak in plaats van sluiten. Start with simple search mode Start met een eenvoudige zoek modus Show warning when opening temporary file. Toon waarschuwing bij het openen van een temp bestand. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Gebruiker stijl toe te passen op het knipsel-venster <br>. Let op: het resultaat pagina header invoegen is ook opgenomen in het'kop knipsel-venster . Synonyms file Synoniemen bestand Highlight CSS style for query terms CSS-stijl voor zoektermen markeren Recoll - User Preferences Recoll - Gebruikersvoorkeuren Set path translations for the selected index or for the main one if no selection exists. Stel padvertalingen in voor de geselecteerde index of voor de hoofdindex als er geen selectie bestaat. Activate links in preview. Links activeren in preview. Make links inside the preview window clickable, and start an external browser when they are clicked. Maak links in het voorbeeld venster klikbaar en start een externe browser wanneer er op wordt geklikt. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Query termen markeren in resultaten. <br>Probeer iets als "color:red;background:yellow" voor iets levendigers dan het standaard blauw... Start search on completer popup activation. Start zoeken bij completer popup activering. Maximum number of snippets displayed in the snippets window Maximum aantal snippets weergegeven in het snippets venster Sort snippets by page number (default: by weight). Snippets sorteren op paginanummer (standaard: bij gewicht). Suppress all beeps. Onderdruk alle piepen. Application Qt style sheet Toepassing Qt style sheet Limit the size of the search history. Use 0 to disable, -1 for unlimited. Limiteer de grootte van de zoekgeschiedenis. Gebruik 0 om uit te schakelen, -1 voor onbeperkt. Maximum size of search history (0: disable, -1: unlimited): Maximale grootte van zoekgeschiedenis (0: uitschakelen, -1: ongelimiteerd): Generate desktop notifications. Bureaubladmeldingen genereren. Misc Diversen Work around QTBUG-78923 by inserting space before anchor text Om QTBUG-78923 te gebruiken voor ankertekst Display a Snippets link even if the document has no pages (needs restart). Toon een tekstbouwstenenslink zelfs als het document geen pagina's heeft (opnieuw opstarten vereist). Maximum text size highlighted for preview (kilobytes) Maximale tekstgrootte gemarkeerd voor preview (kilobytes) Start with simple search mode: Start met een eenvoudige zoek modus: Hide toolbars. Werkbalken verbergen. Hide status bar. Statusbalk verbergen. Hide Clear and Search buttons. Verberg leeg en zoek knoppen. Hide menu bar (show button instead). Menubalk verbergen (toon knop) Hide simple search type (show in menu only). Verberg eenvoudig zoektype (alleen in het menu weergeven). Shortcuts Snelkoppelingen Hide result table header. Resultatentabel header verbergen. Show result table row headers. Resultatentabel rij koppen weergeven. Reset shortcuts defaults Standaardinstellingen voor snelkoppelingen herstellen Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Schakel de Ctrl+[0-9]/[a-z] snelkoppelingen uit om naar tabelrijen te springen. Use F1 to access the manual Gebruik F1 voor toegang tot de handleiding Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Eenvoudig zoektype Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Donkere modus Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Resultaat tabel Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_de.ts0000644000175000017500000071506414444307651014240 00000000000000 ActSearchDLG Menu search AdvSearch All clauses alle Ausdrücke Any clause irgendeinen Ausdruck texts Texte spreadsheets Tabellen presentations Präsentationen media Medien messages Nachrichten other Andere Bad multiplier suffix in size filter Ungültiger Multiplikator-Suffix im Größen-Filter text Text spreadsheet Tabelle presentation Präsentation message Nachricht Advanced Search Erweiterte Suche History Next Historie Nächste History Prev Historie Zurück Load next stored search Nächste gespeicherte Suche laden Load previous stored search Vorherige gespeicherte Suche laden AdvSearchBase Advanced search Erweiterte Suche Restrict file types Dateitypen einschränken Save as default Als Standard speichern Searched file types Durchsuchte Dateitypen All ----> Alle ----> Sel -----> Auswahl ----> <----- Sel <---- Auswahl <----- All <---- Alle Ignored file types Nicht durchsuchte Dateitypen Enter top directory for search Geben Sie das Startverzeichnis für die Suche ein Browse Durchsuchen Restrict results to files in subtree: Ergebnisse auf Dateien in folgendem Verzeichnisbaum einschränken: Start Search Suche starten Search for <br>documents<br>satisfying: Suche nach Dokumenten, <br>die Folgendes erfüllen: Delete clause Ausdruck entfernen Add clause Ausdruck hinzufügen Check this to enable filtering on file types Auswählen, um Filterung nach Dateitypen einzuschalten By categories Nach Kategorien Check this to use file categories instead of raw mime types Auswählen, um Dateikategorien statt Mime-Typen zu verwenden Close Schließen All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Alle nicht-leeren Felder rechts werden mit UND ("alle Ausdrücke") oder ODER ("irgendeinen Ausdruck") verknüpft. <br>Felder des Typs "Irgendeines", "Alle" und "Keines" können eine Mischung aus Wörtern und in Anführungszeichen eingeschlossenen Phrasen enthalten. <br>Nicht gefüllte Felder werden ignoriert. Invert Invertieren Minimum size. You can use k/K,m/M,g/G as multipliers Minimale Größe. Sie können k/K, m/M, g/G als Multiplikatoren verwenden Min. Size Min. Größe Maximum size. You can use k/K,m/M,g/G as multipliers Maximale Größe. Sie können k/K, m/M, g/G als Multiplikatoren verwenden Max. Size Max. Größe Select Auswählen Filter Filtern From Von To Bis Check this to enable filtering on dates Auswählen, um Filterung nach Datum einzuschalten Filter dates Nach Datum filtern Find Finden Check this to enable filtering on sizes Auswählen, um Filterung nach Dateigröße einzuschalten Filter sizes Nach Größe filtern Filter birth dates ConfIndexW Can't write configuration file Fehler beim Schreiben der Konfigurationsdatei Global parameters Globale Parameter Local parameters Lokale Parameter Search parameters Suchparameter Top directories Start-Verzeichnisse The list of directories where recursive indexing starts. Default: your home. Die Liste der Verzeichnisse, in denen die rekursive Indizierung startet. Standard: Home-Verzeichnis. Skipped paths Auszulassende Pfade These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Dies sind Pfadnamen von Verzeichnissen, welche die Indizierung nicht betritt.<br>Pfad-Elemente können Wildcards enthalten. Die Einträge müssen den Pfaden des Indexers entsprechen (z.B.: wenn das Startverzeichnis '/home/me' beinhaltet und '/home' tatsächlich ein Link zu '/usr/home' ist, würde ein korrekt ausgelassener Pfadeintrag '/home/me/tmp*' und nicht '/usr/home/me/tmp*' sein) Stemming languages Stemming-Sprachen The languages for which stemming expansion<br>dictionaries will be built. Die Sprachen, für die Worstammerweiterungsverzeichnisse erstellt werden. Log file name Log-Dateiname The file where the messages will be written.<br>Use 'stderr' for terminal output Die Datei, in die Ausgaben geschrieben werden.<br>Für Ausgaben auf dem Terminal 'stderr' benutzen. Log verbosity level Ausführlichkeitsstufe des Logs This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Dieser Wert steuert die Menge der Meldungen<br>(nur Fehler oder viele Debugging Ausgaben). Index flush megabytes interval Interval (MB) für Speicherleerung This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Dieser Wert steuert, wieviel Daten indiziert werden bevor die Indexinformationen auf die Festplatte geschrieben werden.<br>Hierdurch kann der Speicherverbrauch des Indizierers gesteuert werden. Standardwert: 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Dies ist der Prozentsatz der Festplattennutzung - Gesamtfestplattennutzung, nicht Indexgröße - bei dem die Indexierung ausfällt und stoppt.<br> Der Standardwert von 0 entfernt jede Grenze. No aspell usage Aspell nicht benutzen Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Deaktiviert die Verwendung von Aspell für die Erzeugung von Schreibweisen-Näherungen im Ausdruck-Explorer-Werkzeug. <br>Nützlich, wenn Aspell nicht vorhanden ist oder nicht funktioniert. Aspell language Sprache für Aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Die Sprache des Aspell-Wörterbuchs (z.B. 'en' oder 'de' ...)<br>Wenn dieser Wert nicht gesetzt ist, wird die NLS-Umgebung verwendet, um die Sprache festzustellen, was im Allgemeinen funktioniert. Um eine Vorstellung zu bekommen, was auf Ihrem System installiert ist, geben Sie 'aspell config' ein und schauen Sie nach .dat Dateien im Verzeichnis 'data-dir'. Database directory name Verzeichnisname für die Datenbank The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Der Name eines Verzeichnisses, in dem der Index gespeichert werden soll.<br>Ein nicht-absoluter Pfad ist dabei relativ zum Konfigurationsverzeichnis. Der Standard ist 'xapiandb'. Unac exceptions Unac Ausnahmen <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Dies sind Ausnahmen für den unac Mechanismus, der standardmäßig alle diakritischen Zeichen entfernt und sie durch kanonische Entsprechungen ersetzt. Sie können (abhängig von Ihrer Sprache) dieses Entfernen von Akzenten für einige Zeichen übersteuern und zusätzliche Ersetzungen angeben, z.B. für Ligaturen. Bei jedem durch Leerzeichen getrennten Eintrag ist das erste Zeichen das Ausgangszeichen und der Rest die Ersetzung. Process the WEB history queue Verarbeite die Web-Chronik-Warteschlange Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Ermöglicht die Indexierung der von Firefox besuchten Seiten.<br>(Sie müssen auch das Firefox-Recoll-Plugin installieren) Web page store directory name Verzeichnisname zur Ablage von Webseiten The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Der Name eines Verzeichnisses, in dem Kopien der besuchten Webseiten gespeichert werden sollen.<br>Ein nicht-absoluter Pfad ist dabei relativ zum Konfigurationsverzeichnis. Max. size for the web store (MB) Maximale Größe für Ablage von Webseiten (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Die Einträge werden nach dem Erreichen der Größe wiederverwertet.<br>Nur die Größe zu erhöhen macht wirklich Sinn, weil die Verringerung des Wertes nicht eine bestehende Datei verkürzt (nur verschwendeten Platz am Ende). Automatic diacritics sensitivity Automatisch diakritische Zeichen beachten <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p> Automatisch die Beachtung von diakritischen Zeichen einschalten, wenn der Suchbegriff Zeichen mit Akzenten enthält (nicht in unac_except_trans). Ansonsten müssen Sie dafür die Abfrageprache und den <i>D</i> Modifikator verwenden um die Beachtung von diakritischen Zeichen anzugeben. Automatic character case sensitivity Automatisch Groß-/Kleinschreibung beachten <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p> Automatisch die Beachtung von Groß-/Kleinschreibung einschalten, wenn der Eintrag Großbuchstaben enthält (außer an erster Stelle). Ansonsten müssen Sie dafür die Abfragesprache und den <i>C</i> Modifikator verwenden um die Groß- und Kleinschreibung der Zeichen anzugeben. Maximum term expansion count Maximale Anzahl von Ausdruck-Erweiterungen <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximale Anzahl von Erweiterungen für einen einzelnen Ausdruck (z.B. bei der Verwendung von Wildcards). Der Standardwert 10 000 ist vernünftig und verhindert, dass Suchanfragen scheinbar einfrieren, während die Liste der Begriffe durchlaufen wird. Maximum Xapian clauses count Maximale Anzahl von Xapian-Ausdrücken <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maximale Anzahl von elementaren Ausdrücken, die wir zu einer einzelnen Xapian Abfrage hinzufügen. In manchen Fällen können die Ergebnisse von Ausdruck-Erweiterungen sich ausmultiplizieren, und wir wollen übermäßigen Speicherverbrauch vermeiden. Der Standardwert 100 000 sollte in den meisten Fällen hoch genug sein und zugleich zu typischen derzeitigen Hardware-Ausstattungen passen. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... Die Sprachen, für die Erweiterungswörterbücher erstellt werden.<br>Siehe die Xapian stemmer Dokumentation für mögliche Werte. Z.B. Englisch, Französisch, Deutsch... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Die Sprache für das Aspells-Wörterbuch. Die Werte sind 2-Buchstaben-Sprachcodes, z.B. 'en', 'fr' ...<br>Wenn dieser Wert nicht festgelegt ist, wird die NLS-Umgebung verwendet, um sie zu berechnen, was in der Regel funktioniert. Um eine Vorstellung davon zu erhalten, was auf Ihrem System installiert ist, geben Sie 'aspell config' ein und suchen Sie nach .dat-Dateien im 'data-dir'-Verzeichnis. Indexer log file name Name der Indexer-Protokolldatei If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Wenn leer, wird der obige Log-Dateinamen-Wert verwendet. Es kann nützlich sein, ein separates Protokoll für diagnostische Zwecke zu haben, da das gemeinsame Protokoll gelöscht wird, wenn <br>die GUI hochfährt. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Prozentsatz des vollen Schwellenwerts für die Festplatte, bei dem die Indizierung beendet wird<br>z.B. 90% um bei 90% voll zu stoppen, 0 oder 100 bedeutet keine Begrenzung) Web history Web-Chronik Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Nur Mime-Typen An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Eine exklusive Liste der indizierten Mimetypen.<br>Sonst wird nichts indiziert. Normalerweise leer und inaktiv Exclude mime types Auszuschließende Mime-Typen Mime types not to be indexed Mime-Typen, die nicht indiziert werden Max. compressed file size (KB) Max. Größe kompr. Dateien (kB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Dies ist eine Obergrenze; komprimierte Dateien jenseits dieser Größe werden nicht verarbeitet. Auf -1 setzen, um keine Obergrenze zu haben, auf 0, um nie zu dekomprimieren. Max. text file size (MB) Max. Größe Textdateien (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Dies ist eine Obergrenze; Textdateien jenseits dieser Größe werden nicht verarbeitet Auf -1 setzen, um keine Obergrenze zu haben. Dies dient dazu, riesige Log-Dateien vom Index auszuschließen. Text file page size (KB) Seitengröße Textdateien (kB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Wenn dieser Wert gesetzt ist (ungleich -1), werden Textdateien zur Indizierung in Stücke dieser Größe aufgeteilt. Das hilft bei der Suche in sehr großen Textdateien (z.B. Log-Dateien). Max. filter exec. time (s) Max. Ausführungszeit der Filter (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Externe Filter, die länger als diese Zeit laufen, werden abgebrochen. Das ist für den seltenen Fall (Postscript), in dem ein Dokument eine unendliche Schleife auslöst. Auf -1 setzen, um keine Obergrenze zu haben. Global Globale CronToolW Cron Dialog Cron-Zeitplan <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> Zeitplan für periodische Indizierung (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jedes Feld kann eine Wildcard (*), eine einzelne Zahl, eine mit Kommata getrennte Liste (1,3,5) oder einen Bereich (1-7) enthalten. Die Felder werden <span style=" font-style:italic;">so wie sie sind</span> in der crontab-Datei verwendet und die gesamte crontab Syntax kann verwendet werden, siehe crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Beispielsweise startet die Eingabe <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Wochentage, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Stunden</span> und <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minuten</span> recollindex jeden Tag um 12:15 Uhr und 19:15 Uhr.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ein Zeitplan mit sehr häufigen Aktivierungen ist wahrscheinlich weniger effizient als Echtzeit-Indizierung.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Wochentage (* oder 0-7, 0/7 ist Sonntag) Hours (* or 0-23) Stunden (* oder 0-23) Minutes (0-59) Minuten (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wählen Sie <span style=" font-style:italic;">Deaktivieren</span>, um die periodische Indizierung auszuschalten, <span style=" font-style:italic;">Aktivieren</span>, um sie einzuschalten, <span style=" font-style:italic;">Abbruch</span>, um nichts zu verändern.</p></body></html> Enable Aktivieren Disable Deaktivieren It seems that manually edited entries exist for recollindex, cannot edit crontab Offenbar gibt es manuelle Einträge für recollindex, crontab kann nicht angepasst werden. Error installing cron entry. Bad syntax in fields ? Fehler beim Erstellen des cron Eintrags. Falsche Syntax in Feldern? EditDialog Dialog Dialog EditTrans Source path Quellpfad Local path Lokaler Pfad Config error Konfigurationsfehler Original path Originalpfad EditTransBase Path Translations Pfadumwandlungen Setting path translations for Setze Pfadumwandlungen für Select one or several file types, then use the controls in the frame below to change how they are processed Wählen Sie einen oder mehrere Dateitypen aus. Nutzen Sie dann die Bedienelemente unten, um einzustellen wie sie verarbeitet werden. Add Hinzufügen Delete Entfernen Cancel Abbrechen Save Speichern FirstIdxDialog First indexing setup Einrichten für die erste Indizierung <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Es existiert noch kein Index für diese Konfiguration.</span><br /><br />Wenn Sie nur Ihr Home-Verzeichnis mit sinnvollen Voreinstellungen indizieren wollen, wählen Sie die Schaltfläche <span style=" font-style:italic;">Indizierung jetzt starten</span>. Sie können die Details später anpassen.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn Sie das Verhalten genauer festlegen wollen, verwenden Sie die folgenden Verknüpfungen, um Einstellungen und Zeitplan für die Indizierung anzupassen.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Diese Werkzeuge können Sie später im Menü <span style=" font-style:italic;">Einstellungen</span> erreichen.</p></body></html> Indexing configuration Einstellungen für Indizierung This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Hier können Sie die zu indizierenden Verzeichnisse und andere Einstellungen (wie auszuschließende Dateipfade oder -namen, Standard-Zeichensatz usw.) anpassen. Indexing schedule Zeitplan für Indizierung This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Hier können Sie zwischen Batch-Indizierung und Echtzeit-Indizierung wählen und einen automatischen Zeitplan für die Batch-Indizierung einrichten (mit cron). Start indexing now Indizierung jetzt starten FragButs %1 not found. %1 nicht gefunden. %1: %2 %1: %2 Fragment Buttons Fragmenttasten Query Fragments Abfrage Fragmente IdxSchedW Index scheduling setup Einrichtung des Zeitplans für die Indizierung <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> Indizierung kann ständig laufen und Datein indizieren sobald sie verändert werden, oder aber nur zu bestimmten Zeitpunkten ablaufen.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Im Handbuch finden Sie Informationen, anhand derer Sie sich für einen der Ansätze entscheiden können (drücken Sie F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Dieses Werkzeug hilft Ihnen, einen Zeitplan für periodische Indizierungs-Läufe einzurichten oder die Echtzeit-Indizierung zu starten, wenn Sie sich anmelden (oder beides, was aber selten sinnvoll sein dürfte). </p></body></html> Cron scheduling Cron-Zeitplan The tool will let you decide at what time indexing should run and will install a crontab entry. Mit diesem Werkzeug können Sie festlegen, zu welchen Zeiten die Indizierung laufen soll, und einen crontab Eintrag anlegen. Real time indexing start up Start der Echtzeit-Indizierung Decide if real time indexing will be started when you log in (only for the default index). Entscheiden Sie, ob die Echtzeit-Indizierung beim Anmelden gestartet wird (nur für den Standard-Index). ListDialog Dialog Dialog GroupBox GruppenBox Main No db directory in configuration Kein Datenbankverzeichnis konfiguriert Could not open database in Fehler beim Öffnen der Datenbank in . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Drücken Sie Abbrechen, um die Konfigurationsdatei vor dem Start der Indizierung anzupassen oder OK um mit der Indizierung zu beginnen. Configuration problem (dynconf Konfigurationsproblem (dynconf) "history" file is damaged or un(read)writeable, please check or remove it: "history" Datei ist beschädigt oder nicht les-/schreibbar, bitte überprüfen oder entfernen Sie sie: "history" file is damaged, please check or remove it: Die "History"-Datei ist beschädigt, bitte überprüfe oder entferne sie: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Suche nach: &Next &Nächstes &Previous &Vorheriges Match &Case Groß-/Kleinschreibung &beachten Clear Leeren Creating preview text Erzeuge Vorschautext Loading preview text into editor Lade Vorschautext in den Editor Cannot create temporary directory Fehler beim Anlegen des temporären Verzeichnisses Cancel Abbrechen Close Tab Tab schließen Missing helper program: Fehlendes Hilfsprogramm: Can't turn doc into internal representation for Überführung in interne Darstellung nicht möglich für Cannot create temporary directory: Fehler beim Anlegen des temporären Verzeichnisses: Error while loading file Fehler beim Lesen der Datei Form Formular Tab 1 Tab 1 Open Öffnen Canceled Abgebrochen Error loading the document: file missing. Fehler beim Laden des Dokumentes: Datei fehlt. Error loading the document: no permission. Fehler beim Laden des Dokumentes: Keine Berechtigung. Error loading: backend not configured. Fehler beim Laden: Backend nicht konfiguriert. Error loading the document: other handler error<br>Maybe the application is locking the file ? Fehler beim Laden des Dokumentes: Anderer Bedienprogrammfehler<br>Vielleicht sperrt die Anwendung die Datei ? Error loading the document: other handler error. Fehler beim Laden des Dokumentes: Anderer Bedienprogrammfehler. <br>Attempting to display from stored text. <br> Versuche aus gespeichertem Text anzuzeigen. Could not fetch stored text Gespeicherter Text konnte nicht abgerufen werden Previous result document Vorheriges Ergebnisdokument Next result document Nächste Ergebnisdokument Preview Window Vorschaufenster Close Window Fenster schließen Next doc in tab Nächstes Dokument im Tab Previous doc in tab Vorheriges Dokument im Tab Close tab Tab schließen Print tab Drucke Tab Close preview window Vorschaufenster schließen Show next result Nächstes Ergebnis anzeigen Show previous result Vorheriges Ergebnis anzeigen Print Drucken PreviewTextEdit Show fields Felder zeigen Show main text Vorschautext zeigen Print Drucken Print Current Preview Aktuelle Vorschau drucken Show image Zeige Bild Select All Alles auswählen Copy Kopieren Save document to file Dokument in Datei sichern Fold lines Zeilen umbrechen Preserve indentation Einrückung erhalten Open document Dokument öffnen Reload as Plain Text Reload as HTML QObject Global parameters Globale Parameter Local parameters Lokale Parameter <b>Customised subtrees <b>Angepasste<br> Unterverzeichnisse The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Die Liste der Unterverzeichnisse in der indizierten Hierarchie, in denen einige Parameter anders gesetzt werden müssen. Voreinstellung: leer. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Die folgenden Parameter werden entweder global gesetzt (wenn nichts oder eine leere<br> Zeile in der Liste oben ausgewählt ist) oder für das ausgewählte Unterverzeichnis.<br> Sie können Verzeichnisse durch Anklicken von +/- hinzufügen oder entfernen.<br> Skipped names Auszulassende Namen These are patterns for file or directory names which should not be indexed. Dies sind Muster für Dateien oder Verzeichnisse, die nicht indiziert werden sollen. Default character set Standard-Zeichensatz This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. DIes ist der Zeichensatz, der für Dateien benutzt wird, die ihren Zeichensatz nicht intern definieren, z.B. Textdateien. Der Standardwert ist leer und der Wert der NLS-Umgebung wird benutzt. Follow symbolic links Folge symbolischen Links Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Folge symbolischen Links bei der Indizierung. Der Standardwert ist "Nein", um doppelte Indizierung zu vermeiden. Index all file names Indiziere alle Dateinamen Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indiziere die Namen von Dateien, deren Inhalt nicht erkannt oder verarbeitet werden kann (kein oder nicht unterstützter Mime-Typ). Der Standardwert ist "Ja". Beagle web history Beagle Web-Chronik Search parameters Suchparameter Web history Web-Chronik Default<br>character set Standard-<br>Zeichensatz Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Zeichensatz zum Lesen von Dateien, die den Zeichensatz nicht intern identifizieren, zum Beispiel reine Textdateien.<br>Der Standardwert ist leer und der Wert der NLS Umgebung wird verwendet. Ignored endings Ignorierte Endungen These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Dies sind Dateiendungen für Dateien, die nur durch Inhalte indiziert werden (kein MIME-Typ-Identifikationsversuch, keine Dekompression, keine Inhaltsindizierung. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Dies sind Dateinamen-Endungen für Dateien, die nur nach Namen indexiert werden (keine MIME-Typ-Identifikationsversuch, keine Dekompression, keine Inhaltsindizierung). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>Die folgenden Parameter werden entweder global gesetzt (wenn nichts oder eine leere Zeile in der Liste oben ausgewählt ist) oder für das ausgewählte Unterverzeichnis. Sie können Verzeichnisse durch Anklicken von +/- hinzufügen oder entfernen. QWidget Create or choose save directory Speicherverzeichnis erstellen oder auswählen Choose exactly one directory Wähle genau ein Verzeichnis aus Could not read directory: Kann das Verzeichnis nicht lesen: Unexpected file name collision, cancelling. Unerwartete Dateinamenkollision, Stornierung, es wird abgebrochen. Cannot extract document: Das Dokument kann nicht extrahiert werden: &Preview &Vorschau &Open &Öffnen Open With Öffnen mit Run Script Skripte ausführen Copy &File Name &Dateinamen kopieren Copy &URL &URL kopieren &Write to File &Schreibe in Datei Save selection to files Auswahl in Dateien sichern Preview P&arent document/folder Vorschau des &übergeordneten Dokuments/Ordners &Open Parent document/folder Ö&ffnen des übergeordneten Dokuments/Ordners Find &similar documents &Ähnliche Dokumente finden Open &Snippets window Öffne &Schnipsel-Fenster Show subdocuments / attachments Untergeordnete Dokumente / Anhänge anzeigen &Open Parent document Ö&ffnen des übergeordneten Dokuments &Open Parent Folder Ö&ffnen des übergeordneten Ordners Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Nicht erneut zeigen. RTIToolW Real time indexing automatic start Automatischer Start der Echtzeit-Indizierung <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> Indizierung kann im Hintergrund laufen und den Index in Echtzeit aktualisieren sobald sich Dateien ändern. Sie erhalten so einen Index, der stets aktuell ist, aber die System-Resourcen werden ununterbrochen beansprucht.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></htm Start indexing daemon with my desktop session. Indizierungs-Dämon mit Desktop-Sitzung starten Also start indexing daemon right now. Indizierungs-Dämon jetzt sofort starten Replacing: Ersetze: Replacing file Ersetze Datei Can't create: Fehler beim Erzeugen von: Warning Warnung Could not execute recollindex Fehler beim Ausführen von recollindex Deleting: Lösche: Deleting file Lösche Datei Removing autostart Autostart wird entfernt Autostart file deleted. Kill current process too ? Autotstart-Datei wurde entfernt. Soll auch der laufende Prozess beendet werden? RclCompleterModel Hits RclMain About Recoll Über Recoll Executing: [ Ausführen: [ Cannot retrieve document info from database Keine Informationen zum Dokument in der Datenbank Warning Warnung Can't create preview window Fehler beim Erzeugen des Vorschaufensters Query results Suchergebnisse Document history Dokumenten-Chronik History data Chronik-Daten Indexing in progress: Indizierung läuft: Files Dateien Purge Säubern Stemdb Wortstämme Closing Schließen Unknown Unbekannt This search is not active any more Diese Suche ist nicht mehr aktiv Can't start query: Kann die Suche nicht starten: Bad viewer command line for %1: [%2] Please check the mimeconf file Fehlerhafter Anzeigebefehl für %1: [%2] Überprüfen Sie die Datei mimeconf. Cannot extract document or create temporary file Fehler beim Extrahieren des Dokuments oder beim Erzeugen der temporären Datei (no stemming) (keine Stammformreduktion/ Stemming) (all languages) (alle Sprachen) error retrieving stemming languages Fehler beim Holen der Stemming-Sprachen Update &Index Index &aktualisieren Indexing interrupted Indizierung unterbrochen Stop &Indexing &Indizierung stoppen All Alle media Medien message Nachricht other Andere presentation Präsentation spreadsheet Tabelle text Text sorted sortiert filtered gefiltert External applications/commands needed and not found for indexing your file types: Externe Anwendungen/Befehle, die zur Indizierung Ihrer Dateitypen gebraucht werden und nicht gefunden wurden: No helpers found missing Keine fehlenden Hilfsprogramme Missing helper programs Fehlende Hilfsprogramme Save file dialog Dateidialog speichern Choose a file name to save under Wählen Sie einen Dateinamen zum Speichern unter Document category filter Filter für Dokumenten-Kategorie No external viewer configured for mime type [ Kein externes Anzeigeprogramm konfiguriert für Mime-Typ [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Das in mimeview angegebene Anzeigeprogramm für %1: %2 wurde nicht gefunden. Wollen Sie den Einstellungs-Dialog starten? Can't access file: Fehler beim Zugriff auf Datei: Can't uncompress file: Fehler beim Dekomprimieren von Datei: Save file Datei sichern Result count (est.) Anzahl Ergebnisse (ca.) Query details Details zur Suchanfrage Could not open external index. Db not open. Check external indexes list. Externer Index konnte nicht geöffnet werden. Datenbank nicht offen. Überprüfen Sie die Liste der externen Indizes. No results found Keine Ergebnisse gefunden None Keine Updating Aktualisiere Done Fertig Monitor Überwachen Indexing failed Indizierung gescheitert The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Der laufende Indizierungs-Prozess wurde nicht aus diesem Programm gestartet. Drücken SIe OK, um ihn dennoch zu stoppen oder Abbrechen, um ihn unverändert zu lassen. Erasing index Lösche Index Reset the index and start from scratch ? Index zurücksetzen und ganz neu aufbauen? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Suche läuft.<br>Aufgrund von Einschränkungen der Indizierungs-Bibliothek<br>führt ein Abbruch zur Beendigung des Programms. Error Fehler Index not open Index nicht geöffnet Index query error Fehler beim Abfragen des Index Indexed Mime Types Indexierte Mime Typen Content has been indexed for these MIME types: Für diese MIME-Typen wurde der Inhalte indiziert: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Der Index ist für diese Datei nicht auf dem neuesten Stand. Es soll nicht das Risiko eingegangen werden, den falschen Eintrag anzuzeigen. Drücken SIe OK, um den Index für diese Datei zu aktualisieren und starten Sie die Suchanfrage erneut, wenn die Indizierung abgeschlossen ist. Drücken Sie ansonsten Abbrechen. Can't update index: indexer running Fehler beim Aktualisieren des Index: Indizierung läuft Indexed MIME Types Indizierte Mime-Typen Bad viewer command line for %1: [%2] Please check the mimeview file Fehlerhafter Anzeigebefehl für %1: [%2] Überprüfen Sie die Datei mimeview. Viewer command line for %1 specifies both file and parent file value: unsupported Anzeigebefehl für %1 legt Datei und übergeordnete Datei fest: nicht unterstützt Cannot find parent document Übergeordnetes Dokument nicht gefunden Indexing did not run yet Indizierung ist noch nicht durchgeführt worden External applications/commands needed for your file types and not found, as stored by the last indexing pass in Externe Anwendungen/Befehle, die zur Indizierung Ihrer Dateitypen gebraucht werden und nicht gefunden wurden - vom letzten Indizierungslauf hinterlegt unter Index not up to date for this file. Refusing to risk showing the wrong entry. Der Index ist für diese Datei nicht mehr aktuell. Einträge könnten fehlerhaft sein und werden nicht angezeigt. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Drücken Sie Ok, um den Index für diese Datei zu aktualisieren und die Suche daraufhin zu wiederholen. Ansonsten drücken Sie auf Abbrechen. Indexer running so things should improve when it's done Indizierung ist im Gange. Die Resultate sollten sich nach der Fertigstelltung verbessert haben Sub-documents and attachments Untergeordnete Dokumente und Anhänge Document filter Dokumentenfilter Index not up to date for this file. Refusing to risk showing the wrong entry. Der Index ist für diese Datei nicht mehr aktuell. Einträge könnten fehlerhaft sein und werden nicht angezeigt. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Klicken Sie auf Ok, um den Index für diese Datei zu aktualisieren, dann müssen Sie die Abfrage erneut ausführen, wenn die Indizierung abgeschlossen ist. The indexer is running so things should improve when it's done. Der Indexer läuft, daher sollten sich die Dinge verbessern, wenn er fertig ist. The document belongs to an external indexwhich I can't update. Das Dokument gehört zu einem externen Index, den ich't aktualisieren kann. Click Cancel to return to the list. Click Ignore to show the preview anyway. Klicken Sie auf Abbrechen, um zur Liste zurückzukehren. Klicken Sie auf Ignorieren, um die Vorschau trotzdem anzuzeigen. Duplicate documents Doppelte Dokumente These Urls ( | ipath) share the same content: Diese URLs ( | ipath) sind inhaltsgleich: Bad desktop app spec for %1: [%2] Please check the desktop file Falsche Desktop-App-Spezifikation für %1: [%2] Bitte überprüfen Sie die Desktop-Datei Bad paths Falsche Pfade Bad paths in configuration file: Falsche Pfade in der Konfigurationsdatei: Selection patterns need topdir Auswahlmuster benötigen Topdir Selection patterns can only be used with a start directory Auswahlmuster können nur mit einem Startverzeichnis verwendet werden No search Keine Suche No preserved previous search Keine erhaltene vorherige Suche Choose file to save Wählen Sie eine Dateie zum Speichern Saved Queries (*.rclq) Gespeicherte Abfragen (*.rclq) Write failed Schreiben gescheitert Could not write to file Kann nicht in Datei schreiben Read failed Lesen fehlgeschlagen Could not open file: Kann Datei nicht Öffnen: Load error Ladefehler Could not load saved query Die gespeicherte Abfrage kann nicht geladen werden Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Öffnen einer temporären Kopie. Änderungen werden verloren gehen, wenn du sie nicht <<br/> an einen dauerhaften Ort speicherst. Do not show this warning next time (use GUI preferences to restore). Diese Warnung beim nächsten Mal nicht mehr anzeigen (Die GUI-Einstellungen zum Wiederherzustellen verwenden). Disabled because the real time indexer was not compiled in. Deaktiviert, weil der Echtzeit-Indexer nicht einkompiliert wurde. This configuration tool only works for the main index. Dieses Konfigurationstool funktioniert nur für den Hauptindex. The current indexing process was not started from this interface, can't kill it Der aktuelle Indizierungsprozess wurde nicht von dieser Schnittstelle aus gestartet, kann ihn't beenden The document belongs to an external index which I can't update. Das Dokument gehört zu einem externen Index, den ich nicht aktualisieren kann. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Klicken Sie auf Abbrechen, um zur Liste zurückzukehren. <br>Klicken Sie auf Ignorieren, um die Vorschau trotzdem anzuzeigen (und sich für diese Sitzung merken). Index scheduling Indexplanung Sorry, not available under Windows for now, use the File menu entries to update the index Entschuldigung, momentan unter Windows nicht verfügbar, verwenden Sie die Datei-Menü-Einträge um den Index zu aktualisieren Can't set synonyms file (parse error?) Kann't Synonyms-Datei setzen (Parse-Fehler?) Index locked Index gesperrt Unknown indexer state. Can't access webcache file. Unbekannter Indexer Zustand. Kann nicht auf die Webcache-Datei zugreifen. Indexer is running. Can't access webcache file. Indexer läuft. Kann nicht auf Webcache-Datei zugreifen. with additional message: mit zusätzlicher Nachricht: Non-fatal indexing message: Nicht schwerwiegende Indexierungsnachricht: Types list empty: maybe wait for indexing to progress? Liste der Typen leer: Warten Sie vielleicht auf den Fortschritt der Indexierung? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Viewer Kommandozeile für %1 gibt die übergeordnete Datei an, aber URL ist http[s]: nicht unterstützt Tools Werkzeuge Results Ergebnisse (%d documents/%d files/%d errors/%d total files) (%d Dokumente/%d Dateien/%d Fehler/%d Gesamtdateien) (%d documents/%d files/%d errors) (%d Dokumente/%d Dateien/%d Fehler) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Leere oder nicht vorhandene Pfade in der Konfigurationsdatei. Klicken Sie auf Ok, um die Indexierung trotzdem zu starten (abwesende Daten werden nicht aus dem Index gelöscht): Indexing done Indizierung erledigt Can't update index: internal error Der Index kann nicht aktualisiert werden: interner Fehler Index not up to date for this file.<br> Der Index ist für diese Datei nicht aktuell.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Ebenso scheint es, dass das letzte Index-Update für die Datei fehlgeschlagen ist.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Klicken Sie auf Ok, um den Index für diese Datei zu aktualisieren. Sie müssen die Abfrage erneut ausführen, wenn die Indexierung erfolgt ist.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Klicken Sie auf Abbrechen, um zur Liste zurückzukehren.<br>Klick Ignorieren, um die Vorschau trotzdem anzuzeigen (und sich für diese Sitzung einzuprägen). Es besteht die Gefahr, den falschen Eintrag anzuzeigen.<br/> documents Dokumente document Dokument files Dateien file Datei errors Fehler error Fehler total files) Gesamte Dateien) No information: initial indexing not yet performed. Keine Informationen: Die Erstindizierung wurde noch nicht durchgeführt. Batch scheduling Stapelplanung The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Mit dem Tool können Sie entscheiden, wann die Indexierung laufen soll. Es verwendet den Windows-Taskplaner. Confirm Bestätigen Erasing simple and advanced search history lists, please click Ok to confirm Einfache und erweiterte Suchhistorielisten löschen, zum Bestätigen bitte auf Ok klicken Could not open/create file Kann Datei nicht Öffnen/Erzeugen F&ilter F&ilter Could not start recollindex (temp file error) Konnte den Recollindex nicht starten (temporäre Datei Fehler) Could not read: Kann nicht gelesen werden: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Dies ersetzt den aktuellen Inhalt der Ergebnislisten-Header-Zeichenkette und GUI qss Dateinamen. Fortfahren ? You will need to run a query to complete the display change. Sie müssen eine Abfrage ausführen, um die Anzeigeänderung abzuschließen. Simple search type Einfache Suchart Any term Irgendein Ausdruck All terms Alle Ausdrücke File name Dateiname Query language Abfragesprache Stemming language Stemming-Sprache Main Window Hauptfenster Focus to Search Fokus auf Suche Focus to Search, alt. Fokus auf Suche, alternativ Clear Search Suche säubern Focus to Result Table Fokus auf Ergebnistabelle Clear search Suche löschen Move keyboard focus to search entry Tastaturfokus in den Sucheintrag verschieben Move keyboard focus to search, alt. Tastaturfokus auf Suche verschieben, alt. Toggle tabular display Tabellenanzeige umschalten Move keyboard focus to table Tastaturfokus in Tabelle verschieben Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Vorherige Seite Next page Nächste Seite &File &Datei E&xit &Beenden &Tools &Werkzeuge &Help &Hilfe &Preferences Ein&stellungen Search tools Suchwerkzeuge Result list Ergebnisliste &About Recoll &Über Recoll Document &History &Dokumenten-Chronik Document History Dokumenten-Chronik &Advanced Search &Erweiterte Suche Advanced/complex Search Erweiterte/komplexe Suche &Sort parameters &Sortierparameter Sort parameters Sortierparameter Next page of results Nächste Ergebnisseite Previous page of results Vorherige Ergebnisseite &Query configuration Einstellungen für &Suche &User manual &Benutzerhandbuch Recoll Rekoll Ctrl+Q Strg+Q Update &index &Index aktualisieren Term &explorer &Ausdruck-Explorer Term explorer tool Ausdruck-Explorer-Werkzeug External index dialog Dialog für externe Indizes &Erase document history Lösche &Dokumenten-Chronik First page Erste Seite Go to first page of results Gehe zur ersten Ergebnisseite &Indexing configuration &Einstellungen für Indizierung All Alle &Show missing helpers Zeige fehlende &Hilfsprogramme PgDown PgRunter Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Umschalt+Home, Strg+S, Strg+Q, Strg+S PgUp PgUp &Full Screen &Vollbild F11 F11 Full Screen Vollbild &Erase search history Lösche &Such-Chronik sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Nach Datum sortieren (von alt nach neu) sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Nach Datum sortieren (von neu nach alt) Show Query Details Zeige Details zur Suchanfrage Show results as table Zeige Ergebnisse als Tabelle &Rebuild index Index &neu aufbauen &Show indexed types Zeige indizierte &Typen Shift+PgUp Umschalt+PgUp &Indexing schedule &Zeitplan für Indizierung E&xternal index dialog Dialog für externe &Indizes &Index configuration &Index-Einstellungen &GUI configuration &GUI-Einstellungen &Results &Ergebnisse Sort by date, oldest first Nach Datum sortieren (von alt nach neu) Sort by date, newest first Nach Datum sortieren (von neu nach alt) Show as table Als Tabelle anzeigen Show results in a spreadsheet-like table Zeigt Ergebnisse als Tabelle an Save as CSV (spreadsheet) file Tabelle als CSV Datei speichern Saves the result into a file which you can load in a spreadsheet Speichert Resultate als Tabellenkalkulations-kompatible CSV-Datei ab Next Page Nächste Seite Previous Page Vorherige Seite First Page Erste Seite Query Fragments Abfrage-Fragmente With failed files retrying Bei fehlgeschlagenen Dateien erneut versuchen Next update will retry previously failed files Beim nächsten Update werden zuvor fehlgeschlagene Dateien erneut versucht Save last query Letzte Abfrage speichern Load saved query Gespeicherte Abfrage laden Special Indexing Spezielle Indizierung Indexing with special options Indizierung mit speziellen Optionen Indexing &schedule Indizierungs&plan Enable synonyms Synonyme aktivieren &View &Ansicht Missing &helpers Fehlende &Hilfsprogramme Indexed &MIME types Indizierte &MIME-Typen Index &statistics Index-&Statistiken Webcache Editor Webcache Editor Trigger incremental pass Inkrementeller Auslöser E&xport simple search history E&xportiere einfache Suchhistorie Use default dark mode Verwende den Standard-Dunkelmodus Dark mode Dunkler Modus &Query Ab&fragen Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Nach Datum filtern Assisted complex search Filter birth dates RclTrayIcon Restore Wiederherstellen Quit Verlassen RecollModel Abstract Auszug Author Autor Document size Größe des Dokuments Document date Datum des Dokuments File size Größe der Datei File name Dateiname File date Datum der Datei Ipath Interner Pfad Keywords Schlagworte Mime type Mime Type Original character set Ursprünglicher Zeichensatz Relevancy rating Relevanz-Bewertung Title Titel URL URL Mtime Änderungszeitpunkt Date Datum Date and time Datum und Uhrzeit Ipath Interner Pfad MIME type MIME-Typ Can't sort by inverse relevance Kann nicht nach inverser Relevanz sortieren ResList Result list Ergebnisliste Unavailable document Dokument nicht verfügbar Previous Zurück Next Weiter <p><b>No results found</b><br> <p><b>Keine Ergebnisse gefunden</b><br> &Preview &Vorschau Copy &URL &URL kopieren Find &similar documents &Ähnliche Dokumente finden Query details Suchdetails (show query) (Suchanfrage zeigen) Copy &File Name &Dateinamen kopieren filtered gefiltert sorted sortiert Document history Dokumenten-Chronik Preview Vorschau Open Öffnen <p><i>Alternate spellings (accents suppressed): </i> <p><i>Alternative Schreibweisen (Akzente unterdrückt): </i> &Write to File &Schreibe in Datei Preview P&arent document/folder Vorschau des &übergeordneten Dokuments/Ordners &Open Parent document/folder Ö&ffnen des übergeordneten Dokuments/Ordners &Open &Öffnen Documents Dokumente out of at least von mindestens for für <p><i>Alternate spellings: </i> <p><i>Alternative Schreibweisen: </i> Open &Snippets window Öffne &Schnipsel-Fenster Duplicate documents Doppelte Dokumente These Urls ( | ipath) share the same content: Diese URLs ( | ipath) sind inhaltsgleich: Result count (est.) Anzahl Ergebnisse (ca.) Snippets Schnipsel This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort Sortierung &zurücksetzen &Delete column Spalte &löschen Add " " hinzufügen " column " Spalte Save table to CSV file Tabelle als CSV Datei speichern Can't open/create file: Fehler beim Öffnen/Erzeugen von Datei: &Preview &Vorschau &Open &Öffnen Copy &File Name &Dateinamen kopieren Copy &URL &URL kopieren &Write to File &Schreibe in Datei Find &similar documents &Ähnliche Dokumente finden Preview P&arent document/folder Vorschau des &übergeordneten Dokuments/Ordners &Open Parent document/folder Ö&ffnen des übergeordneten Dokuments/Ordners &Save as CSV Als CSV &speichern Add "%1" column Spalte "%1" hinzufügen Result Table Ergebnistabelle Open Öffnen Open and Quit Öffnen und Verlassen Preview Vorschau Show Snippets Schnipsel anzeigen Open current result document Aktuelles Ergebnisdokument öffnen Open current result and quit Aktuelles Ergebnis öffnen und beenden Show snippets Snippets anzeigen Show header Header anzeigen Show vertical header Zeige vertikale Kopfzeile Copy current result text to clipboard Aktuellen Ergebnistext in Zwischenablage kopieren Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Vorschau &Open &Öffnen Copy &File Name &Dateinamen kopieren Copy &URL &URL kopieren &Write to File &Schreibe in Datei Find &similar documents &Ähnliche Dokumente finden Preview P&arent document/folder Vorschau des &übergeordneten Dokuments/Ordners &Open Parent document/folder Ö&ffnen des übergeordneten Dokuments/Ordners ResultPopup &Preview &Vorschau &Open &Öffnen Copy &File Name &Dateinamen kopieren Copy &URL &URL kopieren &Write to File &Schreibe in Datei Save selection to files Auswahl in Dateien sichern Preview P&arent document/folder Vorschau des &übergeordneten Dokuments/Ordners &Open Parent document/folder Ö&ffnen des übergeordneten Dokuments/Ordners Find &similar documents &Ähnliche Dokumente finden Open &Snippets window Öffne &Schnipsel-Fenster Show subdocuments / attachments Untergeordnete Dokumente / Anhänge anzeigen Open With Öffnen mit Run Script Skripte ausführen SSearch Any term Irgendein Ausdruck All terms Alle Ausdrücke File name Dateiname Completions Vervollständigungen Select an item: Wählen Sie ein Element: Too many completions Zu viele Vervollständigungen Query language Abfragesprache Bad query string Fehlerhafte Suchanfrage Out of memory Kein Speicher mehr verfügbar Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Geben Sie einen Abfragesprachen-Ausdruck ein. Spickzettel:<br> <i>Begriff1 Begriff2</i> : 'Begriff1' und 'Begriff2' in irgendeinem Feld.<br> <i>field:Begriff1</i> : 'Begriff1' im Feld 'field'.<br> Standard-Feldnamen/Synonyme:<br> title/subject/caption, author/from, recipient/to, filename, ext<br> Pseudo-Felder: dir, mime/format, type/rclcat, date<br> Zwei Beispiele für Datumsintervalle: 2009-03-01/2009-05-20 2009-03-01/P2M<br> <i>Begriff1 Begriff2 OR Begriff3</i> : Begriff1 AND (Begriff2 OR Begriff3)<br> Klammern sind nicht erlaubt.<br> <i>"Begriff1 Begriff2"</i> : Phrase (muss genaus so vorkommen). Mögliche Modifikatoren:<br> <i>"Begriff1 Begriff2"p</i> : ungeordnete Nähen-Suche mit voreingestelltem Abstand.<br> Im Zweifelsfalle verwenden Sie den Link <b>Suchanfrage zeigen</b> und finden im Handbuch (&lt;F1>) weitere Details. Enter file name wildcard expression. Geben Sie einen Wildcard-Ausdruck für Dateinamen ein. Enter search terms here. Type ESC SPC for completions of current term. Suchbegriffe hier eingeben. Drücken Sie ESC+Leerzeichen für Vervollständigungen des aktuellen Begriffs. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Geben Sie den Sprachausdruck ein. Cheat Sheet:<br> <i>term1 term2</i> : 'term1' und 'term2' in jedem Feld.<br> <i>Feld:term1</i> : 'term1' im Feld 'Feld'.<br> Standardfeldnamen/Synonyme:<br> title/subject/caption, author/von, recipient/to, filename, ext.<br> Pseudo-Felder: dir, mime/Format, type/rclcat, Datum, Größe.<br> Zwei Datumsintervallbeispiele: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 ODER term3</i> : term1 UND (term2 ODER term3).<br> Sie können Klammern verwenden, um die Dinge klarer zu machen.<br> <i>"term1 term2"</i> : phrase (muss genau auftreten). Mögliche Modifikatoren:<br> <i>"term1 term2"p</i> : ungeordnete Näherungssuche mit Standardabstand.<br> Benutze <b>Abfrage anzeigen</b> Link bei Zweifeln am Ergebnis und siehe Handbuch (&lt; 1>) für mehr Details. Stemming languages for stored query: Stemming Sprachen für gespeicherte Abfrage: differ from current preferences (kept) von den aktuellen Einstellungen unterscheiden (beibehalten) Auto suffixes for stored query: Automatische Suffixe für gespeicherte Abfrage: External indexes for stored query: Externe Indizes für gespeicherte Abfrage: Autophrase is set but it was unset for stored query Autophrase ist gesetzt, aber für gespeicherte Abfrage nicht gesetzt Autophrase is unset but it was set for stored query Autophrase ist nicht gesetzt, aber für gespeicherte Abfrage gesetzt Enter search terms here. Hier den Suchbegriffe eingeben. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px fest schwarz; border-collapse: collapse; border-collapse: zusammenbruch; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Anfrage Sprache Cheat-Sheet. Im Zweifel: Klicken Sie <b>Abfrage anzeigen</b>.&nbsp; You should really look at the manual (F1)</p> Sie sollten sich wirklich das Handbuch ansehen (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Was</th><th>Beispiele</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Und</td><td>ein zwei&nbsp;&nbsp;&nbsp;ein UND zwei&nbsp;&nbsp;&nbsp;ein && zwei</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Komplexe Boolean. ODER hat Priorität, verwende Klammern&nbsp; where needed</td><td>(one AND two) OR three</td></tr> wenn benötigt</td><td>(ein UND zwei) ODER drei</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Nicht</td><td>-Begriff</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Phrase</td><td>"Stolz und Vorurteile"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Unsortierter Prox. (default slack=10)</td><td>"Vorurteil&nbsp;Stolz"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Keine Stammexpansion: Kapital</td><td>Stockwerk</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Feldspezifisch</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>UND innen Feld (keine Bestellung)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>ODER im Feld</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Feldnamen</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Verzeichnispfadfilter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME Typ Filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Datumsintervalle</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Kann den Index nicht öffnen Could not restore external indexes for stored query:<br> Konnte die externen Indizes für die gespeicherte Abfrage nicht wiederherstellen:<br> ??? ??? Using current preferences. Benutze die aktuellen Einstellungen. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Löschen Ctrl+S Strg+S Erase search entry Sucheintrag löschen Search Suchen Start query Suche starten Enter search terms here. Type ESC SPC for completions of current term. Suchbegriffe hier eingeben. Drücken Sie ESC+Leerzeichen für Vervollständigungen des aktuellen Begriffs. Choose search type. Wählen Sie die Art der Suche Show query history Zeige Suchanfragen-Chronik Enter search terms here. Hier den Suchbegriffe eingeben. Main menu Hauptmenü SearchClauseW SearchClauseW Suche ClauseW Any of these Irgendeins dieser All of these Alle diese None of these Keins dieser This phrase diese Wörter Terms in proximity ähnliche Ausdrücke File name matching passende Dateinamen Select the type of query that will be performed with the words Wählen Sie die Art der Suche aus, die mit den Wörtern gestartet wird. Number of additional words that may be interspersed with the chosen ones Anzahl der Wörter, die sich zwischen den angegebenen befinden dürfen In field Im Feld No field Kein Feld Any Irgendeines All Alle None Keines Phrase Ausdrücke Proximity Nähe (proximity) File name Dateiname Snippets Snippets Schnipsel X X Find: Finden: Next Weiter Prev Zurück SnippetsW Search Suchen <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Entschuldigung,es wurde keine genaue Übereinstimmung innerhalb der Grenzen gefunden. Wahrscheinlich ist das Dokument sehr groß und der Schnipsel-Generator hat sich in einem Labyrinth verirrt...</p> Sort By Relevance Sortieren nach Relevanz Sort By Page Sortieren nach Seite Snippets Window Schnipselfenster Find Finden Find (alt) Finden (alternativ) Find Next Finde Nächste Find Previous Finde Vorherige Hide Verstecken Find next Nächstes suchen Find previous Vorherige suchen Close window Fenster schließen SortForm Date Datum Mime type Mime Type SortFormBase Sort Criteria Sortierkriterium Sort the Zeige die most relevant results by: relevantesten Ergebnisse sortiert nach: Descending Absteigend Close Schließen Apply Übernehmen SpecIdxW Special Indexing Spezielle Indexierung Do not retry previously failed files. Vorher fehlgeschlagene Dateien nicht wiederholen. Else only modified or failed files will be processed. Andernfalls werden nur modifizierte oder fehlgeschlagene Dateien verarbeitet. Erase selected files data before indexing. Löschen der Daten der ausgewählte Dateien vor der Indexierung. Directory to recursively index Verzeichnis zu rekursiv indizieren Browse Durchblättern Start directory (else use regular topdirs): Startverzeichnis (sonst normale Topdirs): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Leer lassen, um alle Dateien auszuwählen. Sie können mehrere Leerzeichen getrennte Shell-Typ Muster verwenden.<br>Muster mit eingebetteten Leerzeichen sollten mit doppelten Zitaten zitiert werden.<br> Kann nur verwendet werden, wenn das Startziel eingestellt ist. Selection patterns: Auswahlmuster: Top indexed entity Top indizierte Entität Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Verzeichnis zur rekursiven Indexierung. Dies muss sich im regulären indizierten Bereich<br> befinden, wie in der Konfigurationsdatei (topdirs) definiert. Retry previously failed files. Zuvor gescheitert Dateien nochmal versuchen. Start directory. Must be part of the indexed tree. We use topdirs if empty. Startverzeichnis. Muss Teil des indizierten Baumes sein. Wir verwenden Topdirs, wenn leer. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Startverzeichnis. Muss Teil des indexierten Baumes sein. Benutzen Sie den vollen Indexbereich, wenn es leer ist. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Ausdruck-Explorer &Expand &Vervollständigen Alt+E Alt+V &Close &Schließen Alt+C Alt+S Term Ausdruck No db info. Keine Datenbank-Information Doc. / Tot. Dok. / Ges. Match Beachte Case Groß-/Kleinschreibung Accents Betonungszeichen SpellW Wildcards Platzhalter Regexp Regulärer Ausdruck Spelling/Phonetic Phonetisch Aspell init failed. Aspell not installed? Fehler bei der Initialisierung von Aspell. Ist Aspell nicht installiert? Aspell expansion error. Aspell Vervollständigungsfehler Stem expansion Wortstamm-Erweiterung error retrieving stemming languages Fehler beim Holen der Stemming-Sprachen No expansion found Keine Erweiterung gefunden Term Begriff Doc. / Tot. Dok. / Ges. Index: %1 documents, average length %2 terms Index: %1 Dokumente mit durchschnittlicher Länge von %2 Begriffen Index: %1 documents, average length %2 terms.%3 results Index: %1 Dokumente mit durchschnittlicher Länge von %2 Begriffen. %3 Ergebnisse %1 results %1 Ergebnisse List was truncated alphabetically, some frequent Liste wurde alphabetisch abgeschnitten, einige häufige Begriffe terms may be missing. Try using a longer root. können fehlen. Versuchen Sie es mit einer längeren Wurzel. Show index statistics Indexstatistiken anzeigen Number of documents Dokumentenzahl Average terms per document Durchschnittliche Zahl von Ausdrücken pro Dokument Smallest document length Minimale Zahl von Ausdrücken Longest document length Maximale Zahl von Ausdrücken Database directory size Größe des Datenbankordners MIME types: MIME-Typen: Item Eintrag Value Wert Smallest document length (terms) Kleinste Dokumentlänge (Begriffe) Longest document length (terms) Längste Dokumentlänge (Begriffe) Results from last indexing: Ergebnisse der letzten Indexierung: Documents created/updated Dokumente wurden erstellt/aktualisiert Files tested Dateien getestet Unindexed files Unindexierte Dateien List files which could not be indexed (slow) Liste Dateien auf, die nicht indexiert werden konnten (langsam) Spell expansion error. Zaubererweiterungsfehler. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Das ausgewählte Verzeichnis scheint kein Xapian-Index zu sein. This is the main/local index! Das ist der Hauptindex! The selected directory is already in the index list Das ausgewählte Verzeichnis ist bereits in der Indexliste. Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Wählen Sie das Xapian-Indexverzeichnis (z.B. /home/benutzer/.recoll/xapiandb) error retrieving stemming languages Fehler beim Holen der Stemming-Sprachen Choose Auswählen Result list paragraph format (erase all to reset to default) Format für Ergebnis-Absatz (alles löschen, um auf Standard zurück zu setzen) Result list header (default is empty) Header der Ergebnisliste (Standard ist leer) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Wählen Sie den Recoll-Konfigurationsordner oder das Xapian-Indexverzeichnis aus (z.B. /home/ich/.recoll oder /home/ich/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Der ausgewählten Ordner handelt scheint Recoll-Konfigurationsordner zu sein, aber die Konfiguration konnte nicht ausgelesen werden At most one index should be selected Bitte wählen Sie maximal einen Index aus Cant add index with different case/diacritics stripping option Indices mit unterschiedlichen Einstellungen zum Umgang mit Groß/-Kleinschreibung und diakritischen Zeichen können nicht hinzugefügt werden Default QtWebkit font Standard QtWebkit Schriftart Any term Irgendein Ausdruck All terms Alle Ausdrücke File name Dateiname Query language Abfragesprache Value from previous program exit Wert vom vorherigem Programmausgang Context Kontext Description Beschreibung Shortcut Verknüpfung Default Standard Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Benutzeroberfläche Number of entries in a result page Anzahl der Ergebnisse pro Seite Result list font Schriftart für Ergebnisliste Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Öffnet einen Dialog zur Auswahl der Schriftart für die Ergebnisliste Reset Zurücksetzen Resets the result list font to the system default Setzt die Schriftart für die Ergebnisliste zurück auf den Standardwert Auto-start simple search on whitespace entry. Automatisch eine einfache Suche starten, wenn ein Worttrenner im Sucheingabefeld eingegeben wird. Start with advanced search dialog open. Nach dem Start automatisch den Dialog für die erweiterte Suche öffnen. Start with sort dialog open. Nach dem Start automatisch den Sortierdialog öffnen. Search parameters Suchparameter Stemming language Stemming Sprache Dynamically build abstracts Zusammenfassungen dynamisch erzeugen Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Festlegung ob Zusammenfassungen für Ergebnisse im Kontext der Suchparameter erzeugt werden (kann bei großen Dokumenten langsam sein). Replace abstracts from documents Ersetzen der Zusammenfassungen in den Dokumenten Do we synthetize an abstract even if the document seemed to have one? Festlegung ob eine Zusammenfassung auch dann erzeugt wird, wenn das Dokument schon eine Zusammenfassung enthält Synthetic abstract size (characters) Länge der erzeugten Zusammenfassung (Zeichen) Synthetic abstract context words Anzahl der Kontextworte in der Zusammenfassung External Indexes externe Indizes Add index Index hinzufügen Select the xapiandb directory for the index you want to add, then click Add Index Wählen Sie das xapiandb-Verzeichnis des zuzufügenden Indizes und klicken Sie auf Index hinzufügen Browse Auswahl &OK &Ok Apply changes Änderungen übernehmen &Cancel &Abbrechen Discard changes Änderungen verwerfen Result paragraph<br>format string Formatstring für Ergebnisse Automatically add phrase to simple searches Automatisches Zufügen von Sätzen zu einfachen Suchen A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Eine Suche nach [Jürgen Klinsmann] wird geändert nach [Jürgen OR Klinsmann OR (Jürgen PHRASE 2 Klinsmann)]. Dadurch sollten Ergebnisse, die exakte Übereinstimmungen der Suchworte enthalten, stärker gewichtet werden. User preferences Benutzereinstellungen Use desktop preferences to choose document editor. Die Einstellung des Dokumenteneditors erfolgt in den Desktopvoreinstellungen. External indexes Externe Indizes Toggle selected Auswahl umkehren Activate All Alle Auswählen Deactivate All Alle Abwählen Remove selected Ausgewählte entfernen Remove from list. This has no effect on the disk index. Aus der Liste entfernen. Dies hat keinen Einfluss auf den gespeicherten Index. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definiert das Format für jeden Absatz der Ergebnisliste. Verwenden Sie qt HTML-Format und druckähnliche Ersetzungen:<br>%A Abstrakt<br> %D Datum<br> %I Icon Bildname<br> %K Keywords (falls vorhanden)<br> %L Vorschau und Bearbeiten von Links<br> %M Mime Typ<br> %N Ergebnisnummer<br> %R Relevanz Prozentsatz<br> %S Größen Informationen<br> %T Titel<br> %U Url<br> Remember sort activation state. Speichern, ob Sortieren aktiviert ist Maximum text size highlighted for preview (megabytes) Maximale Textgröße für Vorschau-Hervorhebung Texts over this size will not be highlighted in preview (too slow). Texte über dieser Größe werden in der Vorschau nicht mit Hervorhebungen versehen (zu langsam). Highlight color for query terms Farbe zur Hervorhebung von Suchbegriffen Prefer Html to plain text for preview. Bei Vorschau HTML gegenüber reinem Text bevorzugen If checked, results with the same content under different names will only be shown once. Bei Auswahl werden Ergebnisse mit dem gleichen Inhalt unter verschiedenen Namen nur einmal gezeigt. Hide duplicate results. Verstecke doppelte Ergebnisse Choose editor applications Standardanwendungen auswählen Display category filter as toolbar instead of button panel (needs restart). Kategorie-Filter in Werkzeugleiste statt als Radio-Buttons (Neustart erforderlich) The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Die Worte in dieser Liste werden automatisch zu ext:xxx Ausdrücken im Abfragesprachen-Eintrag umgewandelt. Query language magic file name suffixes. Magische Dateinamen-Erweiterungen für Abfragesprache Enable Aktivieren ViewAction Changing actions with different current values Aktionen mit anderen Werten ändern Mime type Mime Type Command Befehl MIME type MIME-Typ Desktop Default Desktopvoreinstellung Changing entries with different current values Einträge mit anderen Werten ändern ViewActionBase File type Dateityp Action Aktion Select one or several file types, then click Change Action to modify the program used to open them Wählen Sie einen oder mehrere Dateitypen und klicken Sie auf "Ändere Aktion", um das Programm zum Öffnen anzupassen. Change Action Ändere Aktion Close Schließen Native Viewers Anzeigeprogramme Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Wählen Sie einen oder mehrere Mime-Typen und klicken Sie auf "Ändere Aktion".<br>Sie können diesen Dialog auch schließen und stattdessen "Die Einstellung des<br> Dokumenteneditors erfolgt in den Desktopeinstellungen" auswählen.<br> Die Liste wird dann igoriert und es werden die Desktopeinstellungen verwendet. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Wählen Sie einen oder mehrere MIME-Typen aus und nutzen Sie dann die Bedienelemente unten, um das Programm zum Öffnen anzupassen. Use Desktop preferences by default Standardmäßig Desktopvoreinstellungen nutzen Select one or several file types, then use the controls in the frame below to change how they are processed Wählen Sie einen oder mehrere Dateitypen aus. Nutzen Sie dann die Bedienelemente unten, um das Programm zum Öffnen anzupassen Exception to Desktop preferences Von Desktopvoreinstellungen abweichende Ausnahme Action (empty -> recoll default) Aktion (leer → Recoll-Voreinstellung) Apply to current selection Auf aktuelle Auswahl anwenden Recoll action: Recoll-Aktion: current value aktueller Wert Select same Das Selbe wählen <b>New Values:</b> <b>Neuer Wert</b> Webcache Webcache editor Webcache Editor Search regexp Suche Regexp TextLabel WebcacheEdit Copy URL URL kopieren Unknown indexer state. Can't edit webcache file. Unbekannter Indexer Zustand. Kann die Webcache-Datei nicht bearbeiten. Indexer is running. Can't edit webcache file. Indexer läuft. Kann die Webcache-Datei nicht bearbeiten. Delete selection Auswahl löschen Webcache was modified, you will need to run the indexer after closing this window. Der Webcache wurde geändert, Sie müssen den Indexer nach dem Schließen dieses Fensters ausführen. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url Url Date Datum Size URL URL WinSchedToolW Error Fehler Configuration not initialized Konfiguration nicht initialisiert <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Batch-Planung für Recoll-Indexierung</h3><p>Wir verwenden dafür den Standard-Windows Task-Planer. Das Programm wird gestartet, wenn Sie auf den Button unten klicken.</p><p>Sie können entweder die vollständige Schnittstelle verwenden (<i>Aufgabe erstellen</i> im Menü rechts), oder der vereinfachte <i>Basic Task</i> Assistent erstellen. In beiden Fällen Kopieren/Einfügen des Batch-Datei-Pfades, der unten als <i>Aktion</i> aufgelistet ist.</p> Command already started Befehl bereits gestartet Recoll Batch indexing Recoll Batch Indizierung Start Windows Task Scheduler tool Starte Windows Task Scheduler Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Indizierungs-Warteschlange von Beagle übernehmen Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle darf NICHT laufen. Ermöglicht die Abarbeitung der Beagle-Warteschlange, um die Firefox Web-Chronik zu indizieren.<br>(Sie sollten auch das Beagle-Plugin für Firefox installieren.) Web cache directory name Web-Cache-Verzeichnisname The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Der Name für ein Verzeichnis, in dem der Cache für besuchte Webseiten gespeichert werden soll.<br>Ein unabsoluter Pfad wird relativ zum Konfigurationsverzeichnis verwendet. Max. size for the web cache (MB) Max. Größe für den Webcache (MB) Entries will be recycled once the size is reached Einträge werden wiederverwendet sobald die Größe erreicht ist. Web page store directory name Verzeichnis zur Ablage von Webseiten The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Der Name eines Verzeichnisses, in dem Kopien der besuchten Webseiten gespeichert werden sollen.<br>Ein nicht-absoluter Pfad ist dabei relativ zum Konfigurationsverzeichnis. Max. size for the web store (MB) Maximale Größe für Ablage von Webseiten (MB) Process the WEB history queue Web-Chronik Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Ermöglicht die Indexierung der von Firefox besuchten Seiten.<br>(Sie müssen auch das Firefox-Recoll-Plugin installieren) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Die Einträge werden nach dem Erreichen der Größe wiederverwertet.<br>Nur die Größe zu erhöhen macht wirklich Sinn, weil die Verringerung des Wertes nicht eine bestehende Datei verkürzt (nur verschwendeten Platz am Ende). confgui::ConfIndexW Can't write configuration file Fehler beim Schreiben der Konfigurationsdatei Recoll - Index Settings: Recoll - Index-Einstellungen: confgui::ConfParamFNW Browse Durchblättern Choose Auswählen confgui::ConfParamSLW + + - - Add entry Eintrag hinzufügen Delete selected entries Ausgewählte Einträge löschen ~ ~ Edit selected entries Ausgewählte Eiinträge bearbeiten confgui::ConfSearchPanelW Automatic diacritics sensitivity Automatisch diakritische Zeichen beachten <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p> Automatisch die Beachtung von diakritischen Zeichen einschalten, wenn der Suchbegriff Zeichen mit Akzenten enthält (nicht in unac_except_trans). Ansonsten müssen Sie dafür die Abfrageprache und den <i>D</i> Modifikator verwenden. Automatic character case sensitivity Automatisch Groß-/Kleinschreibung beachten <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p> Automatisch die Beachtung von Groß-/Kleinschreibung einschalten, wenn der Eintrag Großbuchstaben enthält (außer an erster Stelle). Ansonsten müssen Sie dafür die Abfragesprache und den <i>C</i> Modifikator verwenden. Maximum term expansion count Maximale Anzahl von Ausdruck-Erweiterungen <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximale Anzahl von Erweiterungen für einen einzelnen Ausdruck (z.B. bei der Verwendung von Wildcards). Der Standardwert 10 000 ist vernünftig und verhindert, dass Suchanfragen scheinbar einfrieren, während die Liste der Begriffe durchlaufen wird. Maximum Xapian clauses count Maximale Anzahl von Xapian-Ausdrücken <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maximale Anzahl von elementaren Ausdrücken, die wir zu einer einzelnen Xapian Abfrage hinzufügen. In manchen Fällen können die Ergebnisse von Ausdruck-Erweiterungen sich ausmultiplizieren, und wir wollen übermäßigen Speicherverbrauch vermeiden. Der Standardwert 100 000 sollte in den meisten Fällen hoch genug sein und zugleich zu typischen derzeitigen Hardware-Ausstattungen passen. confgui::ConfSubPanelW Global Globale Max. compressed file size (KB) Max. Größe kompr. Dateien (kB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Dies ist eine Obergrenze; komprimierte Dateien jenseits dieser Größe werden nicht verarbeitet. Auf -1 setzen, um keine Obergrenze zu haben, auf 0, um nie zu dekomprimieren. Max. text file size (MB) Max. Größe Textdateien (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Dies ist eine Obergrenze; Textdateien jenseits dieser Größe werden nicht verarbeitet Auf -1 setzen, um keine Obergrenze zu haben. Dies dient dazu, riesige Log-Dateien vom Index auszuschließen. Text file page size (KB) Seitengröße Textdateien (kB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Wenn dieser Wert gesetzt ist (ungleich -1), werden Textdateien zur Indizierung in Stücke dieser Größe aufgeteilt. Das hilft bei der Suche in sehr großen Textdateien (z.B. Log-Dateien). Max. filter exec. time (S) Max. Zeit für Filter (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Externe Filter, die länger als diese Zeit laufen, werden abgebrochen. Das ist für den seltenen Fall (Postscript), in dem ein Dokument eine unendliche Schleife auslöst. Auf -1 setzen, um keine Obergrenze zu haben. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Externe Filter, die länger als diese Zeit laufen, werden abgebrochen. Das ist für den seltenen Fall (Postscript), in dem ein Dokument eine unendliche Schleife auslöst. Auf -1 setzen, um keine Obergrenze zu haben. Only mime types Nur Mime-Typen An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Eine exklusive Liste der indizierten Mimetypen.<br>Sonst wird nichts indiziert. Normalerweise leer und inaktiv Exclude mime types Auszuschließende Mime-Typen Mime types not to be indexed Mime-Typen, die nicht indiziert werden Max. filter exec. time (s) Max. Ausführungszeit der Filter (s) confgui::ConfTopPanelW Top directories Start-Verzeichnisse The list of directories where recursive indexing starts. Default: your home. Die Liste der Verzeichnisse, in denen die rekursive Indizierung startet. Standard: Home-Verzeichnis. Skipped paths Auszulassende Pfade These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Die Namen der Verzeichnisse, die nicht indiziert werden.<br>Kann Wildcards enthalten. Muss den Pfaden entsprechen, die der Indizierer sieht (d.h.. wenn '/home/me' in den Start-Verzeichnissen steht und '/home' eigentlich ein Link zu '/usr/home' ist, dann wäre ein korrekter Eintrag '/home/me/tmp*' und nicht '/usr/home/me/tmp*') Stemming languages Stemming-Sprachen The languages for which stemming expansion<br>dictionaries will be built. Die Sprachen, für die Worstammerweiterungsverzeichnisse erstellt werden. Log file name Log-Datei The file where the messages will be written.<br>Use 'stderr' for terminal output Die Datei, in die Ausgaben geschrieben werden.<br>Für Ausgaben auf dem Terminal 'stderr' benutzen. Log verbosity level Ausführlichkeit des Logs This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Dieser Wert steuert die Menge der Meldungen<br>(nur Fehler oder viele Debugging Ausgaben). Index flush megabytes interval Interval (MB) für Speicherleerung This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Dieser Wert steuert, wieviel Daten indiziert werden bevor die Indexinformationen auf Festplatte geschrieben werden.<br>Hierdurch kann der Speicherverbrauch des Indizierers gesteuert werden. Standardwert: 10MB Max disk occupation (%) Max. Festplattenbelegung (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Dies ist der Prozentsatz der Festplattenbelegung, ab dem die Indizierung gestoppt wird (um das Füllen der Festplatte zu vermeiden).<br>0 bedeutet keine Begrenzung (das ist der Standardwert). No aspell usage Aspell nicht benutzen Aspell language Sprache für Aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Die Sprache des Aspell-Wörterbuchs (z.B. 'en' oder 'de' ...)<br>Wenn dieser Wert nicht gesetzt ist, wird die NLS-Umgebung verwendet, um die Sprache festzustellen, was im Allgemeinen funktioniert. Um eine Vorstellung zu bekommen, was auf Ihrem System installiert ist, geben Sie 'aspell config' ein und schauen Sie nach .dat Dateien im Verzeichnis 'data-dir'. Database directory name Verzeichnis für Index-Datenbank The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Der Name eines Verzeichnisses, in dem der Index gespeichert werden soll.<br>Ein nicht-absoluter Pfad ist dabei relativ zum Konfigurationsverzeichnis. Der Standard ist 'xapiandb'. Use system's 'file' command 'file' Kommando benutzen Use the system's 'file' command if internal<br>mime type identification fails. Benutze das 'file' Kommando, wenn die interne Erkennung des Mime-Typs fehlschlägt. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Deaktiviert die Verwendung von Aspell für die Erzeugung von Schreibweisen-Näherungen im Ausdruck-Explorer-Werkzeug. <br>Nützlich, wenn Aspell nicht vorhanden ist oder nicht funktioniert. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Die Sprache des Aspell-Wörterbuchs (z.B. 'en' oder 'de' ...)<br>Wenn dieser Wert nicht gesetzt ist, wird die NLS-Umgebung verwendet, um die Sprache festzustellen, was im Allgemeinen funktioniert. Um eine Vorstellung zu bekommen, was auf Ihrem System installiert ist, geben Sie 'aspell config' ein und schauen Sie nach .dat Dateien im Verzeichnis 'data-dir'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Der Name eines Verzeichnisses, in dem der Index gespeichert werden soll.<br>Ein nicht-absoluter Pfad ist dabei relativ zum Konfigurationsverzeichnis. Der Standard ist 'xapiandb'. Unac exceptions Unac Ausnahmen <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Dies sind Ausnahmen für den unac Mechanismus, der standardmäßig alle diakritischen Zeichen entfernt und sie durch kanonische Entsprechungen ersetzt. Sie können (abhängig von Ihrer Sprache) dieses Entfernen von Akzenten für einige Zeichen übersteuern und zusätzliche Ersetzungen angeben, z.B. für Ligaturen. Bei jedem durch Leerzeichen getrennten Eintrag ist das erste Zeichen das Ausgangszeichen und der Rest die Ersetzung. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Dies sind Pfadnamen von Verzeichnissen, welche die Indizierung nicht betritt.<br>Pfad-Elemente können Wildcards enthalten. Die Einträge müssen den Pfaden des Indexers entsprechen (z.B.: wenn das Startverzeichnis '/home/me' beinhaltet und '/home' tatsächlich ein Link zu '/usr/home' ist, würde ein korrekt ausgelassener Pfadeintrag '/home/me/tmp*' und nicht '/usr/home/me/tmp*' sein) Max disk occupation (%, 0 means no limit) Maximale Plattenbeschäftigung (%, 0 bedeutet kein Limit) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Dies ist der Prozentsatz der Festplattennutzung - Gesamtfestplattennutzung, nicht Indexgröße - bei dem die Indexierung ausfällt und stoppt.<br> Der Standardwert von 0 entfernt jede Grenze. uiPrefsDialogBase User preferences Benutzereinstellungen User interface Benutzeroberfläche Number of entries in a result page Anzahl der Ergebnisse pro Seite If checked, results with the same content under different names will only be shown once. Bei Auswahl werden Ergebnisse mit dem gleichen Inhalt unter verschiedenen Namen nur einmal gezeigt. Hide duplicate results. Verstecke doppelte Ergebnisse Highlight color for query terms Farbe zur Hervorhebung von Suchbegriffen Result list font Schriftart für Ergebnisliste Opens a dialog to select the result list font Öffnet einen Dialog zur Auswahl der Schriftart für die Ergebnisliste Helvetica-10 Helvetica-10 Resets the result list font to the system default Setzt die Schriftart für die Ergebnisliste auf den Standardwert zurück Reset Zurücksetzen Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definiert das Format für jeden Absatz der Ergebnisliste. Verwenden Sie qt HTML-Format und druckähnliche Ersetzungen:<br>%A Abstrakt<br> %D Datum<br> %I Icon Bildname<br> %K Keywords (falls vorhanden)<br> %L Vorschau und Bearbeiten von Links<br> %M Mime Typ<br> %N Ergebnisnummer<br> %R Relevanz Prozentsatz<br> %S Größen Informationen<br> %T Titel<br> %U Url<br> Result paragraph<br>format string Formatstring für Ergebnisse Texts over this size will not be highlighted in preview (too slow). Texte über dieser Größe werden in der Vorschau nicht mit Hervorhebungen versehen (zu langsam). Maximum text size highlighted for preview (megabytes) Maximale Textgröße für Vorschau-Hervorhebung Use desktop preferences to choose document editor. Einstellung des Dokumenteneditors erfolgt in den Desktopeinstellungen Choose editor applications Standardanwendungen auswählen Display category filter as toolbar instead of button panel (needs restart). Kategorie-Filter in Werkzeugleiste statt als Radio-Buttons (Neustart erforderlich) Auto-start simple search on whitespace entry. Automatisch eine einfache Suche starten, wenn ein Worttrenner eingegeben wird Start with advanced search dialog open. Nach dem Start automatisch den Dialog für die erweiterte Suche öffnen Start with sort dialog open. Nach dem Start automatisch den Sortierdialog öffnen. Remember sort activation state. Speichern, ob Sortierung aktiviert ist Prefer Html to plain text for preview. Bei Vorschau HTML gegenüber reinem Text bevorzugen Search parameters Suchparameter Stemming language Stemming-Sprache A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Eine Suche nach [Rolling Stones] wird geändert zu [Rolling OR Stones OR (Rolling PHRASE 2 Stones)]. Dadurch sollten Ergebnisse, in denen die Suchworte genau wie eingegeben auftreten, stärker gewichtet werden. Automatically add phrase to simple searches Automatisches Hinzufügen von Phrasen zu einfachen Suchen Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Versuchen wir, Zusammenfassungen für Ergebnisse aus den Fundstellen zu erzeugen? Dies kann bei großen Dokumenten langsam sein. Dynamically build abstracts Zusammenfassungen dynamisch erzeugen Do we synthetize an abstract even if the document seemed to have one? Erzeugen wir eine Zusammenfassung auch dann, wenn das Dokument schon eine Zusammenfassung enthält? Replace abstracts from documents Ersetzen der Zusammenfassungen aus Dokumenten Synthetic abstract size (characters) Länge der erzeugten Zusammenfassung (in Zeichen) Synthetic abstract context words Anzahl der Kontextworte in der Zusammenfassung The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Die Worte in dieser Liste werden automatisch zu ext:xxx Ausdrücken im Abfragesprachen-Eintrag umgewandelt. Query language magic file name suffixes. Magische Dateinamen-Erweiterungen für Abfragesprache Enable Aktivieren External Indexes Externe Indizes Toggle selected Auswahl umkehren Activate All Alle auswählen Deactivate All Alle abwählen Remove from list. This has no effect on the disk index. Aus der Liste entfernen. Dies hat keinen Einfluss auf den gespeicherten Index. Remove selected Ausgewählte entfernen Click to add another index directory to the list Anklicken, um ein weiteres Index-Verzeichnis zur Liste hinzuzufügen Add index Index hinzufügen Apply changes Änderungen übernehmen &OK &Ok Discard changes Änderungen verwerfen &Cancel &Abbrechen Abstract snippet separator Trenner für Zusammenfassungs-Teile Use <PRE> tags instead of <BR>to display plain text as html. Verwenden Sie <PRE> Tags anstelle von <BR>um Klartext als HTML anzuzeigen. Lines in PRE text are not folded. Using BR loses indentation. Zeilen im PRE-Text werden nicht gefaltet. BR verliert Einrückung. Style sheet Style Sheet Opens a dialog to select the style sheet file Öffnet einen Dialog zur Auswahl der Style Sheet Datei Choose Auswählen Resets the style sheet to default Setzt das Style Sheet auf den Standardwert zurück Lines in PRE text are not folded. Using BR loses some indentation. Zeilen in PRE-Text werden nicht umgebrochen. Bei Verwendung von BR gehen manche Einrückungen verloren. Use <PRE> tags instead of <BR>to display plain text as html in preview. <PRE> Tags statt <BR> verwenden, um Texte in der Vorschau als HTML anzuzeigen Result List Ergebnisliste Edit result paragraph format string Format-String für Ergebnis-Absatz editieren Edit result page html header insert HTML-Header der Ergebnisseite ergänzen Date format (strftime(3)) Datumsformat (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Häufigkeitsschwellwert in Prozent, über dem Begriffe nicht beim automatischen Hinzufügen von Phrasen verwendet werden. Häufige Begriffe beeinträchtigen die Performance bei Phrasen stark. Weggelassene Begriffe erhöhen den Phrasen-Slack und vermindern den Nutzender automatischen Phrasen. Der Standardwert ist 2. Autophrase term frequency threshold percentage Häufigkeitsschwellwert für automatische Phrasen Plain text to HTML line style Zeilen-Stil für Umwandlung von Text in HTML Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Zeilen in PRE-Text werden nicht umgebrochen. Bei Verwendung von BR gehen manche Einrückungen verloren. Möglicherweise ist der Stil 'PRE + Umbruch' das, was Sie wollen. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + Umbruch Exceptions Ausnahmen Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Mime-Typen, die nicht an xdg-open übergeben werden sollen, selbst wenn Desktopvoreinstellungen gewählt wurden.<br> Nützlich, um Seitenzahl und Suchstring zu übergebn, z.B. an evince. Disable Qt autocompletion in search entry. Qt-Autovervollständigung in Suchleiste deaktivieren. Search as you type. Suche beim Eintippen starten. Paths translations Pfadumwandlung Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Klicken Sie hier um einen weiteren Indexordner zur Liste hinzuzufügen. Sie können entweder einen Recoll-Konfigurationsordner oder einen Xapian-Index auswählen. Snippets window CSS file Schnipsel-Fenster CSS Datei Opens a dialog to select the Snippets window CSS style sheet file Öffnet einen Dialog zur Auswahl der Schnipsel-Fenster CSS Style Sheet Datei Resets the Snippets window style Setzt das Schnipsel-Fenster Style Sheet auf den Standardwert zurück Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Entscheide ob die Dokumentfilter als Optionsfeld, Auswahllisten-Kombinationsfeld oder Menü angezeigt werden. Document filter choice style: Stil der Dokumentfilterauswahl: Buttons Panel Tasten Panel Toolbar Combobox Auswahllisten-Kombinationsfeld Menu Menü Show system tray icon. Zeige das Icon im Benachrichtigungsbereich. Close to tray instead of exiting. Schließe es in den Benachrichtigungsbereich, anstatt es zu verlassen. Start with simple search mode Starten mit einfachem Suchmodus Show warning when opening temporary file. Eine Warnung anzeigen, wenn eine temporäre Datei geöffnet wird. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Benutzerstil, der auf das Snippet-Fenster angewendet werden soll.<br> Hinweis: Die Ergebnis-Seiten-Kopfzeile ist auch im Snippet-Fensterheader enthalten. Synonyms file Symonyme-Datei (bedeutungsgleiche Wörter-Datei) Highlight CSS style for query terms Hervorheben von Abfragebegriffe im CSS-Stil Recoll - User Preferences Recoll - Benutzereinstellungen Set path translations for the selected index or for the main one if no selection exists. Lege die Pfadübersetzungen für den ausgewählten Index oder für den Hauptindex fest, wenn keine Auswahl vorhanden ist. Activate links in preview. Links in der Vorschau aktivieren. Make links inside the preview window clickable, and start an external browser when they are clicked. Mache Links innerhalb des Vorschaufensters anklickbar und starte einen externen Browser, wenn sie angeklickt werden. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Abfragebedingungen in den Ergebnissen hervorheben. <br>Versuchen vielleicht etwas wie "color:red;background:yellow" für etwas lebendigeres als das Standard-Blau... Start search on completer popup activation. Suche bei vollständiger Popup-Aktivierung starten. Maximum number of snippets displayed in the snippets window Maximale Anzahl von Schnipsel die im Schnipselfenster angezeigt werden Sort snippets by page number (default: by weight). Sortiere die Schnipsel nach der Seitennummer (Standard: nach Wichtigkeit). Suppress all beeps. Unterdrücke alles Piepen. Application Qt style sheet Anwendungs-Qt-Stylesheet Limit the size of the search history. Use 0 to disable, -1 for unlimited. Die Größe der Suchhistorie beschränken. Verwenden 0 zum deaktivieren, -1 für unbegrenzt. Maximum size of search history (0: disable, -1: unlimited): Maximale Größe der Suchhistorie (0: deaktivieren, -1: unbegrenzt): Generate desktop notifications. Erzeuge Desktop-Benachrichtigungen. Misc Sonstiges Work around QTBUG-78923 by inserting space before anchor text Umgehen Sie QTBUG-78923, indem Sie vor dem Ankertext ein Leerzeichen einfügen Display a Snippets link even if the document has no pages (needs restart). Zeige einen Snippets-Link an, auch wenn das Dokument keine Seiten hat (Restart erforderlich). Maximum text size highlighted for preview (kilobytes) Maximale Textgröße der hervorgehoben Vorschau (Kilobytes) Start with simple search mode: Starten mit einfachem Suchmodus: Hide toolbars. Verstecke Werkzeugleisten. Hide status bar. Verstecke Statusleiste. Hide Clear and Search buttons. Verstecke Löschen- und Suchtaste. Hide menu bar (show button instead). Verstecke Menüleiste (zeige stattdessen Buttons). Hide simple search type (show in menu only). Verstecke einfache Suchart (nur im Menü anzeigen). Shortcuts Verknüpfungen Hide result table header. Ergebnis-Tabellen-Header ausblenden. Show result table row headers. Ergebniszeilen-Kopfzeilen anzeigen. Reset shortcuts defaults Shortcuts zurücksetzen Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Deaktivieren Sie die Strg+[0-9]/[a-z] Verknüpfungen um zu Tabellenzeilen zu springen. Use F1 to access the manual F1 verwenden, um auf das Handbuch zuzugreifen Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Einfache Suchart Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Dunkler Modus Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Ergebnistabelle Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_fr.ts0000644000175000017500000073316114444307651014255 00000000000000 ActSearchDLG Menu search Recherche dans les menus AdvSearch All clauses Toutes les clauses Any clause Une des clauses texts textes spreadsheets feuilles de calcul presentations présentations media multimédia messages messages other autres Bad multiplier suffix in size filter Suffixe multiplicateur incorrect dans un filtre de taille (k/m/g/t) text texte spreadsheet feuille de calcul presentation présentation message message Advanced Search Recherche avancée History Next Historique, suivant History Prev Historique, Precedent Load next stored search Afficher la recherche sauvegardée suivante Load previous stored search Afficher la recherche sauvegardée précédente AdvSearchBase Advanced search Recherche avancée Restrict file types Restreindre les types de fichier Save as default Sauver comme valeur initiale Searched file types Types de fichier recherchés All ----> Tout ----> Sel -----> Sel -----> <----- Sel <----- Sel <----- All <----- Tout Ignored file types Types de fichiers ignorés Enter top directory for search Entrer le répertoire où démarre la recherche Browse Parcourir Restrict results to files in subtree: Restreindre les résultats aux fichiers de l'arborescence : Start Search Lancer la recherche Search for <br>documents<br>satisfying: Rechercher les <br>documents<br>vérifiant : Delete clause Enlever une clause Add clause Ajouter une clause Check this to enable filtering on file types Cocher pour permettre le filtrage des types de fichiers By categories Par catégories Check this to use file categories instead of raw mime types Cocher pour utiliser les catégories de fichiers au lieu des types mimes Close Fermer All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Tous les champs de droite non vides seront combinés par une conjonction ET (choix "Toutes les clauses") ou OU (choix "Une des clauses"). <br> Les champs de type "Un de ces mots", "Tous ces mots" et "Aucun de ces mots" acceptent un mélange de mots et de phrases contenues dans des apostrophes "une phrase".<br>Les champs non renseignés sont ignorés. Invert Inverser Minimum size. You can use k/K,m/M,g/G as multipliers Taille minimum. Vous pouvez utiliser un suffixe multiplicateur : k/K, m/M, g/G Min. Size Taille Min Maximum size. You can use k/K,m/M,g/G as multipliers Taille Maximum. Vous pouvez utiliser un suffixe multiplicateur : k/K, m/M, g/G Max. Size Taille Max Select Sélectionner Filter Filtrer From À partir de To Jusqu'à Check this to enable filtering on dates Cocher pour activer le filtrage sur les dates Filter dates Filtrer sur les dates Find Trouver Check this to enable filtering on sizes Cocher pour activer le filtrage sur taille fichier Filter sizes Filtrer les tailles Filter birth dates Filtrer par date de création ConfIndexW Can't write configuration file Impossible d'écrire le fichier de configuration Global parameters Paramètres globaux Local parameters Paramètres locaux Search parameters Paramètres pour la recherche Top directories Répertoires de départ The list of directories where recursive indexing starts. Default: your home. La liste des répertoires où l'indexation récursive démarre. Défaut: votre répertoire par défaut. Skipped paths Chemins ignorés These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Ce sont les chemins des répertoires où l'indexation n'ira pas.<br>Les éléments peuvent contenir des caractères joker. Les entrés doivent correspondre aux chemins vus par l'indexeur (ex.: si topdirs comprend '/home/me' et que '/home' est en fait un lien vers '/usr/home', un élément correct pour skippedPaths serait '/home/me/tmp*', et non '/usr/home/me/tmp*') Stemming languages Langue pour l'expansion des termes The languages for which stemming expansion<br>dictionaries will be built. Les langages pour lesquels les dictionnaires d'expansion<br>des termes seront construits. Log file name Nom du fichier journal The file where the messages will be written.<br>Use 'stderr' for terminal output Le nom du fichier ou les messages seront ecrits.<br>Utiliser 'stderr' pour le terminal Log verbosity level Niveau de verbosité This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Cette valeur ajuste la quantite de messages emis,<br>depuis uniquement les erreurs jusqu'a beaucoup de donnees de debug. Index flush megabytes interval Intervalle d'écriture de l'index en mégaoctets This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Ajuste la quantité de données lues entre les écritures sur disque.<br>Contrôle l'utilisation de la mémoire. Défaut 10 Mo This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. C'est le pourcentage d'utilisation disque - utilisation totale, et non taille de l'index - où l'indexation s'arrêtera en erreur.<br>La valeur par défaut de 0 désactive ce test. No aspell usage Pas d'utilisation d'aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Désactiver l'utilisation d'aspell pour générer les approximations orthographiques.<br> Utile si aspell n'est pas installé ou ne fonctionne pas. Aspell language Langue pour Aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Langue pour le dictionnaire aspell. La valeur devrait ressembler à 'en' ou 'fr'... <br>Si cette valeur n'est pas positionnée, l'environnement national sera utilisé pour la calculer, ce qui marche bien habituellement. Pour avoir une liste des valeurs possibles sur votre système, entrer 'aspell config' sur une ligne de commande et regarder les fichiers '.dat' dans le répertoire 'data-dir'. Database directory name Répertoire de stockage de l'index The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Le nom d'un répertoire pour stocker l'index<br>Un chemin relatif sera interprété par rapport au répertoire de configuration. La valeur par défaut est 'xapiandb'. Unac exceptions Exceptions Unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Ce sont les exceptions au mécanisme de suppression des accents, qui, par défaut et en fonction de la configuration de l'index, supprime tous les accents et effectue une décomposition canonique Unicode. Vous pouvez inhiber la suppression des accents pour certains caractères, en fonction de votre langue, et préciser d'autres décompositions, par exemple pour des ligatures. Dans la liste séparée par des espaces, le premier caractères d'un élément est la source, le reste est la traduction. Process the WEB history queue Traiter la file des pages WEB Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Permet d'indexer les pages Web visitées avec Firefox <br>(il vous faut également installer l'extension Recoll pour Firefox) Web page store directory name Répertoire de stockage des pages Web The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Le nom d'un répertoire où stocker les copies des pages visitées.<br>Un chemin relatif se réfère au répertoire de configuration. Max. size for the web store (MB) Taille maximale pour le cache Web (Mo) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Les entrées seront recyclées quand la taille sera atteinte.<br>Seule l'augmentation de la taille a un sens parce que réduire la valeur ne tronquera pas un fichier existant (mais gachera de l'espace à la fin). Automatic diacritics sensitivity Sensibilité automatique aux accents <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Activer automatiquement la sensibilité aux accents si le terme recherché contient des accents (saufs pour ceux de unac_except_trans). Sans cette option, il vous faut utiliser le langage de recherche et le drapeau <i>D</i> pour activer la sensibilité aux accents. Automatic character case sensitivity Sensibilité automatique aux majuscules <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Activer automatiquement la sensibilité aux majuscules si le terme de recherche contient des majuscules ailleurs qu'en première lettre. Sans cette option, vous devez utiliser le langage de recherche et le drapeau <i>C</i> pour activer la sensibilité aux majuscules. Maximum term expansion count Taille maximum de l'expansion d'un terme <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Nombre maximum de termes de recherche résultant d'un terme entré (par exemple expansion par caractères jokers). La valeur par défaut de 10000 est raisonnable et évitera les requêtes qui paraissent bloquées pendant que le moteur parcourt l'ensemble de la liste des termes. Maximum Xapian clauses count Compte maximum de clauses Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Nombre maximum de clauses Xapian élémentaires générées pour une requête. Dans certains cas, le résultat de l'expansion des termes peut ere multiplicatif, et utiliserait trop de mémoire. La valeur par défaut de 100000 devrait être à la fois suffisante et compatible avec les configurations matérielles typiques. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... Langues pour lesquelles les dictionnaires d'expansion des racines.<br>Consulter la liste des valeurs possibles dans documentation Xapian (stemmers). Ex: english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Langue du dictionnaire aspell. Les valeurs sont des codes en deux lettres, par exemple 'en', 'fr'...<br>Si cette valeur n'est pas renseignée, l'environnement système sera utilisé pour la déterminer, ce qui marche bien le plus souvent. Pour avoir une idée de ce qui est installé sur votre système, utiliser la commande 'aspell --help' qui affiche entre autres la liste des dictionnaires disponibles. Indexer log file name Nom du fichier journal de l'indexeur If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Si la valeur est vide, la valeur du journal commun est utilisée. Il peut être utile d'avoir un fichier séparé pour l'indexeur parce que le fichier commun est effacé quand<br>l'interface graphique démarre. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Pourcentage d'occupation du disque provoquant l'arrêt de l'indexation<br>Ex: 90% pour arrêter quand le disque est 90% plein. 0 ou 100 signifient pas de limite Web history Historique Web Process the Web history queue Traiter la file d'attente de l'historique Web (by default, aspell suggests mispellings when a query has no results). (par défaut, aspell suggère des erreurs lorsqu'une requête n'a pas de résultats). Page recycle interval Intervalle de recyclage de la page <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. <p>Par défaut, une seule instance d'une URL est conservée dans le cache. Cela peut être modifié en définissant cette valeur à une valeur déterminant à quelle fréquence nous gardons plusieurs instances ('day', 'semaine', 'mois', 'année'). Notez qu'augmenter l'intervalle n'effacera pas les entrées existantes. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Remarque : les anciennes pages seront effacées pour faire de l'espace pour les nouvelles lorsque la taille maximale est atteinte. Taille actuelle : %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) Pourcentage d'occupation du disque provoquant l'arrêt de l'indexation<br>(Ex: 90% pour arrêter quand le disque est 90% plein. 0 ou 100 signifient pas de limite) ConfSubPanelW Only mime types Seulement ces types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Une liste exclusive des types MIME à indexer.<br>Rien d'autre ne sera indexé. Normalement vide et inactif Exclude mime types Types exclus Mime types not to be indexed Types MIME à ne pas indexer Max. compressed file size (KB) Taille maximale pour les fichiers à décomprimer (ko) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Cette valeur définit un seuil au delà duquel les fichiers comprimés ne seront pas traités. Utiliser -1 pour désactiver la limitation, 0 pour ne traiter aucun fichier comprimé. Max. text file size (MB) Taille maximale d'un fichier texte (Mo) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Cette valeur est un seuil au delà duquel les fichiers de texte pur ne seront pas indexés. Spécifier -1 pour supprimer la limite. Utilisé pour éviter d'indexer des fichiers monstres. Text file page size (KB) Taille de page pour les fichiers de texte pur (ko) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Si cette valeur est spécifiée et positive, les fichiers de texte pur seront découpés en tranches de cette taille pour l'indexation. Ceci diminue les ressources consommées par l'indexation et aide le chargement pour prévisualisation. Max. filter exec. time (s) Temps d'exécution maximum pour un filtre (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Un filtre externe qui prend plus de temps sera arrêté. Traite le cas rare (possible avec postscript par exemple) où un document pourrait amener un filtre à boucler sans fin. Mettre -1 pour complètement supprimer la limite. Global Global CronToolW Cron Dialog Dialogue Cron <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span>: planification de l'indexation périodique (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Chaque champ peut contenir un joker (*), une simple valeur numérique , des listes ponctuées par des virgules (1,3,5) et des intervalles (1-7). Plus généralement, les champs seront utilisés <span style=" font-style:italic;">tels quels</span> dans le fichier crontab, et la syntaxe générale crontab peut être utilisée, voir la page de manuel crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Par exemple, en entrant <span style=" font-family:'Courier New,courier';">*</span> dans <span style=" font-style:italic;">Jours, </span><span style=" font-family:'Courier New,courier';">12,19</span> dans <span style=" font-style:italic;">Heures</span> et <span style=" font-family:'Courier New,courier';">15</span> dans <span style=" font-style:italic;">Minutes</span>, recollindex démarrerait chaque jour à 12:15 et 19:15</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Un planning avec des activations très fréquentes est probablement moins efficace que l'indexation au fil de l'eau.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Jours de la semaine (* ou 0-7, 0 ou 7 signifie Dimanche) Hours (* or 0-23) Heures (* ou 0-23) Minutes (0-59) Minutes (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Cliquer <span style=" font-style:italic;">Désactiver</span> pour arrêter l'indexation automatique périodique, <span style=" font-style:italic;">Activer</span> pour la démarrer, <span style=" font-style:italic;">Annuler</span> pour ne rien changer.</p></body></html> Enable Activer Disable Désactiver It seems that manually edited entries exist for recollindex, cannot edit crontab Il semble que des entrées créées manuellement existent pour recollindex. Impossible d´éditer le fichier Cron Error installing cron entry. Bad syntax in fields ? Erreur durant l'installation de l'entrée cron. Mauvaise syntaxe des champs ? EditDialog Dialog Dialogue EditTrans Source path Chemin source Local path Chemin local Config error Erreur config Original path Chemin Originel EditTransBase Path Translations Traductions de chemins Setting path translations for Ajustement des traductions de chemins pour Select one or several file types, then use the controls in the frame below to change how they are processed Sélectionner un ou plusieurs types de fichiers, puis utiliser les contrôles dans le cadre ci-dessous pour changer leur traitement Add Ajouter Delete Supprimer Cancel Annuler Save Sauvegarder FirstIdxDialog First indexing setup Paramétrage de la première indexation <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Il semble que l'index pour cette configuration n'existe pas encore.</span><br /><br />Si vous voulez simplement indexer votre répertoire avec un jeu raisonnable de valeurs par défaut, cliquer le bouton <span style=" font-style:italic;">Démarrer l'indexation maintenant</span>. Vous pourrez ajuster les détails plus tard. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si vous voulez plus de contrôle, utilisez les liens qui suivent pour ajuster la configuration et le planning d'indexation.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ces outils peuvent être accédés plus tard à partir du menu <span style=" font-style:italic;">Preferences</span>.</p></body></html> Indexing configuration Indexation This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Vous pourrez ajuster les répertoires que vous voulez indexer, et d'autres paramètres comme les schémas de noms ou chemins de fichiers exclus, les jeux de caractères par défaut, etc. Indexing schedule Planning de l'indexation This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Vous pourrez choisir entre l'indexation à intervalles fixes ou au fil de l'eau, et définir un planning pour la première (basé sur l'utilitaire cron). Start indexing now Démarrer l'indexation maintenant FragButs %1 not found. %1 non trouvé %1: %2 %1 : %2 Fragment Buttons Boutons de fragment Query Fragments Fragments de recherche IdxSchedW Index scheduling setup Paramétrage du planning d'indexation <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">L'indexation <span style=" font-weight:600;">Recoll</span> peut fonctionner en permanence, traitant les fichiers dès qu'ils sont modifiés, ou être exécutée à des moments prédéterminés. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Une lecture du manuel peut vous aider à choisir entre ces approches (presser F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Cet outil peut vous aider à planifier l'indexation périodique, ou configurer un démarrage automatique de l'indexation au fil de l'eau quand vous vous connectez (ou les deux, ce qui est rarement pertinent). </p></body></html> Cron scheduling Planning Cron The tool will let you decide at what time indexing should run and will install a crontab entry. Le dialogue vous permettra de déterminer à quelle heure l'indexation devra démarrer et installera une entrée crontab. Real time indexing start up Démarrage de l'indexation au fil de l'eau Decide if real time indexing will be started when you log in (only for the default index). Déterminer si l'indexation au fil de l'eau démarre quand vous vous connectez (pour l'index par défaut). ListDialog Dialog Dialogue GroupBox GroupBox Main No db directory in configuration Répertoire de la base de données non défini dans la configuration Could not open database in Impossible d'ouvrir la base de données dans . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Cliquez sur Annuler si vous voulez modifier le fichier de configuration avant le début de l'indexation, ou sur Ok pour le laisser continuer. Configuration problem (dynconf Problème de configuration (dynconf "history" file is damaged or un(read)writeable, please check or remove it: Le fichier d'historique est illisible, le verifier ou l'effacer : "history" file is damaged, please check or remove it: Le fichier "history" est corrompu. Le detruire : Needs "Show system tray icon" to be set in preferences! Nécessite la préférence "Afficher l'icone dans la barre d'état système¨! Preview &Search for: &Rechercher : &Next &Suivant &Previous &Précédent Match &Case Respecter la &casse Clear Effacer Creating preview text Création du texte pour la prévisualisation Loading preview text into editor Chargement du texte de la prévisualisation Cannot create temporary directory Impossible de créer le répertoire temporaire Cancel Annuler Close Tab Fermer l'onglet Missing helper program: Programmes filtres externes manquants : Can't turn doc into internal representation for Impossible de traduire le document en représentation interne pour Cannot create temporary directory: Impossible de créer le répertoire temporaire: Error while loading file Erreur de chargement du fichier Form Ecran Tab 1 Tab 1 Open Ouvrir Canceled Annulé Error loading the document: file missing. Erreur de chargement : fichier manquant. Error loading the document: no permission. Erreur de chargement : accès refusé. Error loading: backend not configured. Erreur de chargement : gestionnaire de stockage non configuré. Error loading the document: other handler error<br>Maybe the application is locking the file ? Erreur de chargement : erreur indéterminée<br>Fichier verrouillé par l'application ? Error loading the document: other handler error. Erreur de chargement : erreur indéterminée. <br>Attempting to display from stored text. <br>Essai d'affichage à partir du texte stocké. Could not fetch stored text Impossible de récupérer le texte stocké Previous result document Prévisualisation du résultat Next result document Résultat suivant Preview Window Fenêtre d'aperçu Close Window Fermer la fenêtre Next doc in tab Résultat suivant Previous doc in tab Résultat précédent Close tab Fermer l'onglet Print tab Imprimer l'onglet Close preview window Fermer la fenêtre Show next result Afficher le résultat suivant Show previous result Afficher le résultat précédent Print Imprimer PreviewTextEdit Show fields Afficher les valeurs des champs Show main text Afficher le corps du texte Print Imprimer Print Current Preview Imprimer la fenêtre de prévisualisation Show image Afficher l'image Select All Tout sélectionner Copy Copier Save document to file Sauvegarder le document Fold lines Replier les lignes Preserve indentation Préserver l'indentation Open document Ouvrir le document Reload as Plain Text Afficher comme du texte pur Reload as HTML Afficher comme du HTML QObject Global parameters Paramètres globaux Local parameters Paramètres locaux <b>Customised subtrees <b>Répertoires avec paramètres spécifiques The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. La liste des sous-répertoires de la zone indexée<br>où certains paramètres sont redéfinis. Défaut : vide. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Les paramètres qui suivent sont définis soit globalement, si la sélection dans la liste ci-dessus est vide ou réduite à la ligne vide, soit pour le répertoire sélectionné. Vous pouvez ajouter et enlever des répertoires en cliquant les boutons +/-. Skipped names Noms ignorés These are patterns for file or directory names which should not be indexed. Canevas définissant les fichiers ou répertoires qui ne doivent pas etre indexés. Default character set Jeu de caractères par défaut This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Il s'agit du jeu de caractères utilisé pour lire des fichiers qui n'identifient pas le jeu de caractères en interne, par exemple des fichiers texte purs.<br>La valeur par défaut est vide, et la valeur du NLS environnement est utilisée. Follow symbolic links Suivre les liens symboliques Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Indexer les fichiers et répertoires pointés par les liens symboliques. Pas fait par défaut pour éviter les indexations multiples Index all file names Indexer tous les noms de fichiers Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indexer les noms des fichiers dont le contenu n'est pas identifié ou traité (pas de type mime, ou type non supporté). Vrai par défaut Beagle web history Historique du web Beagle Search parameters Paramètres pour la recherche Web history Historique Web Default<br>character set Jeu de caractères<br>par défaut Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Jeu de caractères utilisé pour lire les fichiers qui n'identifient pas de manière interne leur encodage, par exemple les fichiers texte purs.<br>La valeur par défaut est vide, et la valeur obtenue à partir de l'environnement est utilisée dans ce cas. Ignored endings Suffixes ignorés These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Suffixes sélectionnant des fichiers qui seront indexés uniquement sur leur nom (pas d' identification de type MIME, pas de décompression, pas d' indexation du contenu. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Suffixes sélectionnant des fichiers qui seront indexés uniquement sur leur nom (pas d'identification de type MIME, pas de décompression, pas d'indexation du contenu). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>Les paramètres qui suivent sont définis soit globalement, si la sélection dans la liste ci-dessus est vide ou réduite à la ligne vide, soit pour le répertoire sélectionné. Vous pouvez ajouter et enlever des répertoires en cliquant les boutons +/-. QWidget Create or choose save directory Créer ou choisir un répertoire d'écriture Choose exactly one directory Choisir exactement un répertoire Could not read directory: Impossible de lire le répertoire : Unexpected file name collision, cancelling. Collision de noms inattendue, abandon. Cannot extract document: Impossible d'extraire le document : &Preview &Voir contenu &Open &Ouvrir Open With Ouvrir Avec Run Script Exécuter le Script Copy &File Name Copier le nom de &Fichier Copy &URL Copier l'&Url &Write to File &Sauver sous Save selection to files Sauvegarder la sélection courante dans des fichiers Preview P&arent document/folder Prévisualiser le document p&arent &Open Parent document/folder &Ouvrir le document/dossier parent Find &similar documents Chercher des documents &similaires Open &Snippets window Ouvrir la fenêtre des e&xtraits Show subdocuments / attachments Afficher les sous-documents et attachements &Open Parent document &Ouvrir le document parent &Open Parent Folder Ouvrir le dossier parent Copy Text Copier le texte Copy &File Path Copier le chemin du fichier Copy File Name Copier le nom du fichier QxtConfirmationMessage Do not show again. Ne plus afficher. RTIToolW Real time indexing automatic start Démarrage automatique de l'indexation au fil de l'eau <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">L'indexation <span style=" font-weight:600;">Recoll</span> peut être configurer pour s'exécuter en arrière plan, mettant à jour l'index au fur et à mesure que des documents sont modifiés. Vous y gagnez un index toujours à jour, mais des ressources systême (mémoire et processeur) sont consommées en permanence.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Démarrer le démon d'indexation quand je me connecte. Also start indexing daemon right now. Également démarrer le démon maintenant. Replacing: Remplacement de : Replacing file Remplacement du fichier Can't create: Impossible de créer : Warning Attention Could not execute recollindex Impossible d'exécuter recollindex Deleting: Effacement : Deleting file Effacement du fichier Removing autostart Enlèvement de l'autostart Autostart file deleted. Kill current process too ? Fichier autostart détruit. Arrêter le process en cours ? RclCompleterModel Hits Résultats RclMain About Recoll À propos de Recoll Executing: [ Exécution de : [ Cannot retrieve document info from database Impossible d'accéder au document dans la base Warning Attention Can't create preview window Impossible de créer la fenêtre de visualisation Query results Résultats de la recherche Document history Historique des documents consultés History data Données d'historique Indexing in progress: Indexation en cours : Files Fichiers Purge Nettoyage Stemdb Base radicaux Closing Fermeture Unknown Inconnue This search is not active any more Cette recherche n'est plus active Can't start query: Peut't démarrer la requête : Bad viewer command line for %1: [%2] Please check the mimeconf file Mauvaise ligne de commande du visualiseur pour %1: [%2] Veuillez vérifier le fichier mimeconf Cannot extract document or create temporary file Impossible d'extraire le document ou de créer le fichier temporaire (no stemming) (pas d'expansion) (all languages) (tous les langages) error retrieving stemming languages impossible de trouver la liste des langages d'expansion Update &Index Mettre à jour l'&index Indexing interrupted Indexation interrompue Stop &Indexing Arrêter l'&Indexation All Tout media multimédia message message other autres presentation présentation spreadsheet feuille de calcul text texte sorted trié filtered filtré External applications/commands needed and not found for indexing your file types: Applications/commandes externes nécessaires et non trouvées pour l'indexation de vos types de fichiers : No helpers found missing Pas d'applications manquantes Missing helper programs Applications manquantes Save file dialog Boîte de dialogue de sauvegarde des fichiers Choose a file name to save under Choisissez un nom de fichier sous lequel enregistrer Document category filter Filtre de catégorie de document No external viewer configured for mime type [ Pas de visualiseur configuré pour le type MIME [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Le visualiseur spécifié dans mimeview pour %1 : %2 est introuvable. Voulez vous démarrer le dialogue de préférences ? Can't access file: Impossible d'accéder au fichier : Can't uncompress file: Impossible de décomprimer le fichier : Save file Sauvegarder le fichier Result count (est.) Nombre de résultats (estimation) Query details Détail de la recherche Could not open external index. Db not open. Check external indexes list. Impossible d'ouvrir un index externe. Base non ouverte. Verifier la liste des index externes. No results found Aucun résultat trouvé None Rien Updating Mise à jour Done Fini Monitor Moniteur Indexing failed L'indexation a échoué The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Le processus d'indexation en cours n'a pas été démarré depuis cette interface. Cliquer OK pour le tuer quand même, ou Annuler pour le laisser tranquille. Erasing index Effacement de l'index Reset the index and start from scratch ? Effacer l'index et redémarrer de zéro ? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Requête en cours.<br>En raison de restrictions internes, <br>annuler terminera l'exécution du programme Error Erreur Index not open Index pas ouvert Index query error Erreur de la recherche sur l'index Indexed Mime Types Types Mime indexés Content has been indexed for these MIME types: Du contenu a été indexé pour ces types MIME : Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. L'index n'est pas à jour pour ce fichier. Refuser pour risquer d'afficher la mauvaise entrée. Cliquez sur OK pour mettre à jour l'index de ce fichier, puis ré-exécuter la requête lorsque l'indexation est terminée. Sinon, Annuler. Can't update index: indexer running Impossible de mettre à jour l'index : un indexeur est déjà actif Indexed MIME Types Types MIME indexés Bad viewer command line for %1: [%2] Please check the mimeview file Ligne de commande incorrecte pour %1 : [%2]. Vérifier le fichier mimeview. Viewer command line for %1 specifies both file and parent file value: unsupported La ligne de commande pour %1 spécifie à la fois le fichier et son parent : non supporté Cannot find parent document Impossible de trouver le document parent Indexing did not run yet L'indexation n'a pas encore eu lieu External applications/commands needed for your file types and not found, as stored by the last indexing pass in Applications et commandes externes nécessaires pour vos types de documents, et non trouvées, telles qu'enregistrées par la dernière séquence d'indexation dans. Index not up to date for this file. Refusing to risk showing the wrong entry. L'index n'est pas à jour pour ce fichier. Refuser pour risquer d'afficher la mauvaise entrée. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Cliquez sur OK pour mettre à jour l'index de ce fichier, puis ré-exécuter la requête lorsque l'indexation est terminée. Sinon, Annuler. Indexer running so things should improve when it's done Indexer en cours d'exécution donc les choses devraient s'améliorer quand cela's est fait Sub-documents and attachments Sous-documents et attachements Document filter Filtre de documents Index not up to date for this file. Refusing to risk showing the wrong entry. Index pas à jour pour ce fichier. Je ne veux pas risquer d'afficher la mauvaise entrée. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Cliquer OK pour mettre à jour l'index pour ce fichier, puis attendez la fin de l'indexation pour relancer la recherche. The indexer is running so things should improve when it's done. L'indexeur est actif, les choses devraient aller mieux quand il aura fini. The document belongs to an external indexwhich I can't update. Le document appartient à un index externe que je peux't mettre à jour. Click Cancel to return to the list. Click Ignore to show the preview anyway. Cliquez sur Annuler pour retourner à la liste. Cliquez sur Ignorer pour afficher l'aperçu quand même. Duplicate documents Documents identiques These Urls ( | ipath) share the same content: Ces URLs(| ipath) partagent le même contenu : Bad desktop app spec for %1: [%2] Please check the desktop file Mauvaise spécification d'application pour %1 : [%2] Merci de vérifier le fichier desktop Bad paths Chemins inexistants Bad paths in configuration file: Chemins inexistants définis dans le fichier de configuration : Selection patterns need topdir Les schémas de sélection nécessitent un répertoire de départ Selection patterns can only be used with a start directory Les schémas de sélection ne peuvent être utilisés qu'avec un répertoire de départ No search Pas de recherche No preserved previous search Pas de recherche sauvegardée Choose file to save Choisir un fichier pour sauvegarder Saved Queries (*.rclq) Recherches Sauvegardées (*.rclq) Write failed Échec d'écriture Could not write to file Impossible d'écrire dans le fichier Read failed Erreur de lecture Could not open file: Impossible d'ouvrir le fichier : Load error Erreur de chargement Could not load saved query Le chargement de la recherche sauvegardée a échoué Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Ouverture d'un fichier temporaire. Les modification seront perdues<br/>si vous ne les sauvez pas dans un emplacement permanent. Do not show this warning next time (use GUI preferences to restore). Ne plus afficher ce message (utiliser le dialogue de préférences pour rétablir). Disabled because the real time indexer was not compiled in. Désactivé parce que l'indexeur au fil de l'eau n'est pas disponible dans cet exécutable. This configuration tool only works for the main index. Cet outil de configuration ne travaille que sur l'index principal. The current indexing process was not started from this interface, can't kill it Le processus d'indexation en cours n'a pas été démarré depuis cette interface, impossible de l'arrêter The document belongs to an external index which I can't update. Le document appartient à un index externe que je ne peux pas mettre à jour. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Cliquer Annulation pour retourner à la liste.<br>Cliquer Ignorer pour afficher la prévisualisation de toutes facons (mémoriser l'option pour la session). Index scheduling Programmation de l'indexation Sorry, not available under Windows for now, use the File menu entries to update the index Désolé, pas disponible pour Windows pour le moment, utiliser les entrées du menu fichier pour mettre à jour l'index Can't set synonyms file (parse error?) Impossible d'ouvrir le fichier des synonymes (erreur dans le fichier?) Index locked L'index est verrouillé Unknown indexer state. Can't access webcache file. État de l'indexeur inconnu. Impossible d'accéder au fichier webcache. Indexer is running. Can't access webcache file. L'indexeur est actif. Impossible d'accéder au fichier webcache. with additional message: avec le message complémentaire : Non-fatal indexing message: Erreur d'indexation non fatale : Types list empty: maybe wait for indexing to progress? Liste vide : attendre que l'indexation progresse ? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported La ligne de commande pour %1 specifie l'utilisation du fichier parent, mais l'URL est http[s] : ne peut pas marcher Tools Outils Results Résultats (%d documents/%d files/%d errors/%d total files) (%d documents/%d fichiers/%d erreurs/%d fichiers en tout) (%d documents/%d files/%d errors) (%d documents/%d fichiers/%d erreurs) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Chemins vides ou non existants dans le fichier de configuration. Cliquer sur Ok pour démarrer l'indexation (les données absentes ne seront pas éliminées de l'index) : Indexing done Indexation terminée Can't update index: internal error Impossible de mettre à jour l'index : erreur interne Index not up to date for this file.<br> L'index n'est pas à jour pour ce fichier.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Par ailleurs, il semble que la dernière mise à jour pour ce fichier a échoué.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Cliquer Ok pour essayer de mettre à jour l'index. Vous devrez lancer la recherche à nouveau quand l'indexation sera terminée.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Cliquer Annuler pour retourner à la liste.<br>Cliquer Ignorer pour afficher la prévisualisation (et enregister l'option pour cette session). Il y a un risque d'afficher le mauvais document.<br/> documents documents document document files fichiers file fichier errors erreurs error erreur total files) fichiers totaux) No information: initial indexing not yet performed. Pas de données : l'indexation initiale n'est pas faite. Batch scheduling Programmation du traitement par lots The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Utilisez cet outil pour déterminer à quelle heure l'indexation doit s'exécuter. Il est basé sur l'outile de programmation de tâches Windows. Confirm Confirmer Erasing simple and advanced search history lists, please click Ok to confirm Effacement des historique de recherche, cliquer Ok pour confirmer Could not open/create file Impossible d'ouvrir ou créer le fichier F&ilter F&iltre Could not start recollindex (temp file error) Impossible de démarrer l'indexeur (erreur de fichier temporaire) Could not read: Impossible de lire: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Ceci remplacera le contenu courant le la préférence "en-tete de liste de résultats" et le nom du fichier qss. Continuer ? You will need to run a query to complete the display change. Exécutez une recherche pour compléter le changement d'affichage. Simple search type Type de recherche simple Any term Certains termes All terms Tous les termes File name Nom de fichier Query language Language d'interrogation Stemming language Langue pour l'expansion des termes Main Window Fenêtre principale Focus to Search Retourner à la zone du texte de recherche Focus to Search, alt. Retourner à la zone du texte de recherche, alt. Clear Search Effacer la recherche Focus to Result Table Activer la table Clear search Effacer la recherche Move keyboard focus to search entry Cibler l'entrée clavier sur la zone de recherche Move keyboard focus to search, alt. Cibler l'entrée clavier vers la zone de recherche, autre. Toggle tabular display Basculer l'affichage en mode table Move keyboard focus to table Cibler l'entrée clavier vers la table Flushing Écriture de l'index Show menu search dialog Afficher le dialogue de recherche dans les menus Duplicates Doublons Filter directories Filtrer par répertoire Main index open error: Erreur d'ouverture de l'index principal: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. L'index est peut-être corrompu. Essayer d'exécuter xapian-check, ou reconstruire l'index. This search is not active anymore Cette recherche n'est plus active Viewer command line for %1 specifies parent file but URL is not file:// : unsupported La ligne de commande pour %1 specifie l'utilisation du fichier parent, mais l'URL n'est pas file:// : ne peut pas marcher RclMainBase Previous page Page précédente Next page Page suivante &File &Fichier E&xit &Quitter &Tools &Outils &Help &Aide &Preferences &Préférences Search tools Outils de recherche Result list Liste de résultats &About Recoll &A propos de Recoll Document &History &Historique des documents Document History Historique des documents &Advanced Search Recherche &Avancée Advanced/complex Search Recherche Avancée &Sort parameters Paramètres pour le &tri Sort parameters Paramètres pour le tri Next page of results Page suivante Previous page of results Page précédente &Query configuration Configuration des &requêtes &User manual &Manuel Recoll Recoll Ctrl+Q Ctrl+Q Update &index &Indexer Term &explorer &Exploration de l'index Term explorer tool Outil d'exploration de l'index External index dialog Index externes &Erase document history &Effacer l'historique des documents First page Première page Go to first page of results Aller à la première page de résultats &Indexing configuration &Indexation All Tout &Show missing helpers &Afficher les aides manquantes PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Maj+Accueil, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen &Plein écran F11 F11 Full Screen Plein écran &Erase search history &Effacer l'historique des recherches sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Trier par date des plus anciennes aux plus récentes sortByDateDesc trier par description de la date Sort by dates from newest to oldest Trier par date des plus récentes aux plus anciennes Show Query Details Afficher la requête en détails Show results as table Afficher les résultats sous forme de tableau &Rebuild index &Reconstruire l'index &Show indexed types &Afficher les types indexés Shift+PgUp Shift+PgUp &Indexing schedule &Planning de l'indexation E&xternal index dialog Index e&xternes &Index configuration &Index &GUI configuration Interface utilisateur &Results &Résultats Sort by date, oldest first Trier par date, le plus ancien en premier Sort by date, newest first Trier par date, le plus récent en premier Show as table Afficher comme un tableau Show results in a spreadsheet-like table Montrer les résultats dans un tableau Save as CSV (spreadsheet) file Sauver en format CSV (fichier tableur) Saves the result into a file which you can load in a spreadsheet Sauvegarde les résultats dans un fichier qu'il sera possible de charger dans un tableur Next Page Page suivante Previous Page Page précédente First Page Première page Query Fragments Fragments de recherche With failed files retrying Avec re-traitement des fichiers en échec Next update will retry previously failed files La prochaine mise à jour de l'index essaiera de traiter les fichiers actuellement en échec Save last query Sauvegarder la dernière recherche Load saved query Charger une recherche sauvegardée Special Indexing Indexation spéciale Indexing with special options Indexation avec des options spéciales Indexing &schedule Programme d'indexation Enable synonyms Activer les synonymes &View &Voir Missing &helpers &Traducteurs manquants Indexed &MIME types Types &MIME indexés Index &statistics &Statistiques de l'index Webcache Editor Editeur &Webcache Trigger incremental pass Déclencher une indexation incrémentale E&xport simple search history E&xporter l'historique de recherches simples Use default dark mode Utiliser le mode sombre standard Dark mode Mode sombre &Query R&echerche Increase results text font size Augmenter la taille de la police de texte des résultats Increase Font Size Augmenter la taille de police Decrease results text font size Diminuer la taille de la police de texte des résultats Decrease Font Size Réduire la taille de police Start real time indexer Démarrage de l'indexation au fil de l'eau Query Language Filters Filtres de recherche (mode language) Filter dates Filtrer sur les dates Assisted complex search Recherche complexe assistée Filter birth dates Filtrer par date de création RclTrayIcon Restore Restaurer Quit Quitter RecollModel Abstract Extrait Author Auteur Document size Taille document Document date Date document File size Taille fichier File name Nom de fichier File date Date fichier Ipath Ipath Keywords Mots clef Mime type Type Mime Original character set Jeu de caractères d'origine Relevancy rating Pertinence Title Titre URL URL Mtime Mtime Date Date Date and time Date et heure Ipath Ipath MIME type Type MIME Can't sort by inverse relevance Impossible de trier par pertinence inverse ResList Result list Liste de résultats Unavailable document Document inaccessible Previous Précédent Next Suivant <p><b>No results found</b><br> <p><b>Aucun résultat</b><br> &Preview &Voir contenu Copy &URL Copier l'&Url Find &similar documents Chercher des documents &similaires Query details Détail de la recherche (show query) (requête) Copy &File Name Copier le nom de &Fichier filtered filtré sorted trié Document history Historique des documents consultés Preview Prévisualisation Open Ouvrir <p><i>Alternate spellings (accents suppressed): </i> <p><i>Orthographes proposés (sans accents) : </i> &Write to File &Sauver sous Preview P&arent document/folder Prévisualiser le document p&arent &Open Parent document/folder &Ouvrir le document/dossier parent &Open &Ouvrir Documents Documents out of at least parmi au moins for pour <p><i>Alternate spellings: </i> <p><i>Orthographes proposés : </i> Open &Snippets window Ouvrir la fenêtre des e&xtraits Duplicate documents Documents identiques These Urls ( | ipath) share the same content: Ces URLs(| ipath) partagent le même contenu : Result count (est.) Nombre de résultats (est.) Snippets Extraits This spelling guess was added to the search: Approximation orthographique ajoutée à la recherche: These spelling guesses were added to the search: Approximations orthographiques ajoutées à la recherche: ResTable &Reset sort &Revenir au tri par pertinence &Delete column &Enlever la colonne Add " Ajouter " " column " colonne Save table to CSV file Sauvegarder dans un fichier CSV Can't open/create file: Impossible d'ouvrir ou créer le fichier : &Preview &Voir contenu &Open &Ouvrir Copy &File Name Copier le nom de &Fichier Copy &URL Copier l'&Url &Write to File &Sauver sous Find &similar documents Chercher des documents &similaires Preview P&arent document/folder Prévisualiser le document p&arent &Open Parent document/folder &Ouvrir le document/dossier parent &Save as CSV &Sauvegarder en CSV Add "%1" column Ajouter une colonne "%1" Result Table Tableau de résultats Open Ouvrir Open and Quit Ouvrir et quitter Preview Prévisualisation Show Snippets Afficher les extraits Open current result document Ouvrir le document résultat courant Open current result and quit Ouvrir le document résultat courant puis quitter le programme Show snippets Afficher les extraits Show header Afficher l'en tête de table Show vertical header Afficher les en-têtes de ligne Copy current result text to clipboard Copier le texte du résultat courant vers le presse-papier Use Shift+click to display the text instead. Utilisez Maj+clic pour afficher le texte à la place. %1 bytes copied to clipboard %1 octets copiés vers le presse-papiers Copy result text and quit Copier le texte du résultat et quitter le programme ResTableDetailArea &Preview &Voir contenu &Open &Ouvrir Copy &File Name Copier le nom de &Fichier Copy &URL Copier l'&Url &Write to File &Sauver sous Find &similar documents Chercher des documents &similaires Preview P&arent document/folder Prévisualiser le document p&arent &Open Parent document/folder &Ouvrir le document/dossier parent ResultPopup &Preview &Voir contenu &Open &Ouvrir Copy &File Name Copier le nom de &Fichier Copy &URL Copier l'&Url &Write to File &Sauver sous Save selection to files Sauvegarder la sélection courante dans des fichiers Preview P&arent document/folder Prévisualiser le document p&arent &Open Parent document/folder &Ouvrir le document/dossier parent Find &similar documents Chercher des documents &similaires Open &Snippets window Ouvrir la fenêtre des e&xtraits Show subdocuments / attachments Afficher les sous-documents et attachements Open With Ouvrir Avec Run Script Exécuter le Script SSearch Any term Certains termes All terms Tous les termes File name Nom de fichier Completions Complètement Select an item: Sélectionnez un élément: Too many completions Trop de complétions Query language Language d'interrogation Bad query string Requête non reconnue Out of memory Plus de mémoire disponible Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Entrer une expression du langage de recherche. Antisèche :<br> <i>term1 term2</i> : 'term1' ET 'term2' champ non spécifié.<br> <i>field:term1</i> : 'term1' recherche dans le champ 'field'.<br> Noms de champs standards (utiliser les mots anglais)/alias:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-champs: dir, mime/format, type/rclcat, date.<br> Examples d'intervalles de dates: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> NE PAS mettre les parenthèses.<br> <i>"term1 term2"</i> : phrase exacte. Options::<br> <i>"term1 term2"p</i> : proximité (pas d'ordre).<br> Utiliser le lien <b>Afficher la requête en détail</b> en cas de doute sur les résultats et consulter le manuel (en anglais) (&lt;F1>) pour plus de détails. Enter file name wildcard expression. Entrer un nom de fichier (caractères jokers possibles) Enter search terms here. Type ESC SPC for completions of current term. Entrer les termes recherchés ici. Taper ESC SPC pour afficher les mots commençant par l'entrée en cours. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Entrer une expression du langage de recherche. Antisèche :<br> <i>term1 term2</i> : 'term1' ET 'term2' champ non spécifié.<br> <i>field:term1</i> : 'term1' recherche dans le champ 'field'.<br> Noms de champs standards (utiliser les mots anglais)/alias:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-champs: dir, mime/format, type/rclcat, date.<br> Examples d'intervalles de dates: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> NE PAS mettre les parenthèses.<br> <i>"term1 term2"</i> : phrase exacte. Options::<br> <i>"term1 term2"p</i> : proximité (pas d'ordre).<br> Utiliser le lien <b>Afficher la requête en détail</b> en cas de doute sur les résultats et consulter le manuel (en anglais) (&lt;F1>) pour plus de détails. Stemming languages for stored query: Les langages d'expansion pour la recherche sauvegardée : differ from current preferences (kept) diffèrent des préférences en cours (conservées) Auto suffixes for stored query: L'option de suffixe automatique pour la recherche sauvegardée : External indexes for stored query: Les index externes pour la recherche sauvegardée : Autophrase is set but it was unset for stored query L'option autophrase est positionnée, mais ne l'était pas pour la recherche sauvegardée Autophrase is unset but it was set for stored query L'option autophrase est désactivée mais était active pour la recherche sauvegardée Enter search terms here. Entrer les termes recherchés ici. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border: 1px solid black; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Langue de requête feuille de triche. En doute : cliquez sur <b>Afficher la requête</b>.&nbsp; You should really look at the manual (F1)</p> Vous devriez vraiment consulter le manuel (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Quoi</th><th>Exemples</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Boolean complexe. OR a priorité, utilisez des parenthèses&nbsp; where needed</td><td>(one AND two) OR three</td></tr> si nécessaire</td><td>(un AND deux) OR trois</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Noms de champs</td><td>titre/sujet/légende&nbsp;&nbsp;auteur/de<br>destinataire/à&nbsp;&nbsp;nom de fichier&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Filtre du chemin du répertoire</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Filtre de type MIME</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Intervalle de date</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Taille</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Impossible d'ouvrir l'index Could not restore external indexes for stored query:<br> Impossible de restaurer les index externes utilisés pour la requête d'origine:<br> ??? ??? Using current preferences. Utilisation des préférences actuelles. Simple search Type de recherche simple History Historique <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <p>Langue de requête feuille de triche. En doute : cliquez sur <b>Afficher la requête en détails</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> <tr><td>Capitaliser pour une recherche littérale</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Effacer Ctrl+S Ctrl+S Erase search entry Effacer l'entrée Search Rechercher Start query Démarrer la recherche Enter search terms here. Type ESC SPC for completions of current term. Entrer les termes recherchés ici. Taper ESC SPC pour afficher les mots commençant par l'entrée en cours. Choose search type. Choisir le type de recherche. Show query history Afficher l'historique des recherches Enter search terms here. Entrer les termes recherchés ici. Main menu Menu principal SearchClauseW SearchClauseW Recherche dans ClauseW Any of these Tous ces éléments All of these Tous ces éléments None of these Aucun de ces éléments This phrase Cette phrase Terms in proximity Termes à proximité File name matching Nom du fichier correspondant Select the type of query that will be performed with the words Sélectionner le type de requête à effectuer avec les mots Number of additional words that may be interspersed with the chosen ones Nombre de mots additionnels qui peuvent se trouver entre les termes recherchés In field Dans le champ No field Sans champ Any Certains All Tout None Rien Phrase Phrase Proximity Proximité File name Nom de fichier Snippets Snippets Extraits X X Find: Trouver : Next Suivant Prev Précédent SnippetsW Search Rechercher <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Désolé, aucun résultat trouvé dans les limites de recherche. Peut-être que le document est très gros et que le générateur d'extraits s'est perdu...<p> Sort By Relevance Trier Par Pertinence Sort By Page Trier Par Page Snippets Window Fenêtre des extraits Find Trouver Find (alt) Trouver (alt) Find Next Trouver le suivant Find Previous Trouver le précédent Hide Fermer Find next Trouver le suivant Find previous Trouver le précédent Close window Fermer la fenêtre SortForm Date Date Mime type Type Mime SortFormBase Sort Criteria Critères de tri Sort the Trier le most relevant results by: résultats les plus pertinents par: Descending Descendant Close Fermer Apply Appliquer SpecIdxW Special Indexing Indexation spéciale Do not retry previously failed files. Ne pas réessayer les fichiers en erreur. Else only modified or failed files will be processed. Sinon, seulement les fichiers modifiés ou en erreur seront traités. Erase selected files data before indexing. Effacer les données pour les fichiers sélectionnés avant de réindexer. Directory to recursively index Répertoire d'index récursif Browse Parcourir Start directory (else use regular topdirs): Répertoire de départ (sinon utiliser la variable normale topdirs) : Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Laisser vide pour sélectionner tous les fichiers. Vous pouvez utiliser plusieurs schémas séparés par des espaces.<br>Les schémas contenant des espaces doivent ere enclos dans des apostrophes doubles.<br>Ne peut être utilisé que si le répertoire de départ est positionné. Selection patterns: Schémas de sélection : Top indexed entity Objet indexé de démarrage Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Répertoire à indexer récursivement. Il doit être à l'intérieur de la zone normale<br>définie par la variable topdirs. Retry previously failed files. Ne pas réessayer les fichiers en erreur. Start directory. Must be part of the indexed tree. We use topdirs if empty. Répertoire de départ. Doit faire partie de la zone indexée. topdirs est utilisé si non renseigné. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Répertoire de départ. Doit faire partie de la zone indexée. Traite toute la zone si non renseigné. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Fichier de sortie de diagnostics. Il sera tronqué et recevra des diagnostics d'indexation (raisons pour lesquelles les fichiers ne sont pas indexés). Diagnostics file Fichier de diagnostics SpellBase Term Explorer Explorateur d'index &Expand &Dérivés Alt+E Alt+D &Close &Fermer Alt+C Alt+F Term Terme No db info. Pas d'information sur la base. Doc. / Tot. Doc. / Tot. Match Faire correspondre Case Majuscules/Minuscules Accents Accents SpellW Wildcards Jokers Regexp Expression régulière Spelling/Phonetic Orthographe/Phonétique Aspell init failed. Aspell not installed? Erreur d'initialisation aspell. Il n'est peut-être pas installé? Aspell expansion error. Erreur aspell. Stem expansion Expansion grammaticale error retrieving stemming languages Impossible de former la liste des langages d'expansion No expansion found Pas de résultats Term Terme Doc. / Tot. Doc. / Tot. Index: %1 documents, average length %2 terms Index: %1 documents, longueur moyenne %2 termes Index: %1 documents, average length %2 terms.%3 results Index : %1 documents, longueur moyenne %2 termes. %3 résultats %1 results %1 résultats List was truncated alphabetically, some frequent La liste a été tronquée par ordre alphabétique. Certains termes fréquents terms may be missing. Try using a longer root. pourraient être absents. Essayer d'utiliser une racine plus longue Show index statistics Afficher les statistiques de l'index Number of documents Nombre de documents Average terms per document Nombre moyen de termes par document Smallest document length Longueur de document la plus petite Longest document length Longueur la plus longue du document Database directory size Taille occupee par l'index MIME types: Types MIME : Item Element Value Valeur Smallest document length (terms) Taille minimale document (termes) Longest document length (terms) Taille maximale document (termes) Results from last indexing: Résultats de la dernière indexation : Documents created/updated Documents créés ou mis à jour Files tested Fichiers testés Unindexed files Fichiers non indexés List files which could not be indexed (slow) Lister les fichiers qui n'ont pas pu être traités (lent) Spell expansion error. Erreur dans les suggestions orthographiques. Spell expansion error. Erreur dans les suggestions orthographiques. UIPrefsDialog The selected directory does not appear to be a Xapian index Le répertoire sélectionné ne semble pas être un index Xapian This is the main/local index! C'est l'index principal ! The selected directory is already in the index list Le répertoire sélectionné est déjà dans la liste Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Sélectionnez le répertoire d'index xapian (ie: /home/buddy/.recoll/xapiandb) error retrieving stemming languages Impossible de former la liste des langues pour l'expansion grammaticale Choose Choisir Result list paragraph format (erase all to reset to default) Format de paragraphe de la liste de résultats (tout effacer pour revenir à la valeur par défaut) Result list header (default is empty) En-tête HTML (la valeur par défaut est vide) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Sélection un répertoire de configuration Recoll ou un répertoire d'index Xapian (Ex : /home/moi/.recoll ou /home/moi/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Le repertoire selectionne ressemble a un repertoire de configuration Recoll mais la configuration n'a pas pu etre chargee At most one index should be selected Selectionner au plus un index Cant add index with different case/diacritics stripping option Impossible d'ajouter un index avec une option differente de sensibilite a la casse et aux accents Default QtWebkit font Fonte par défaut de QtWebkit Any term Certains termes All terms Tous les termes File name Nom de fichier Query language Language d'interrogation Value from previous program exit Valeur obtenue de la dernière exécution Context Contexte Description Libellé Shortcut Raccourci Default Défaut Choose QSS File Choisir un fichier QSS Can't add index with different case/diacritics stripping option. Impossible d'ajouter un index avec une option differente de sensibilite a la casse et aux accents. UIPrefsDialogBase User interface Interface utilisateur Number of entries in a result page Nombre de résultats par page Result list font Fonte pour la liste de résultats Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Ouvre une fenêtre permettant de changer la fonte Reset Réinitialiser Resets the result list font to the system default Réinitialiser la fonte à la valeur par défaut Auto-start simple search on whitespace entry. Démarrer automatiquement une recherche simple sur entrée d'un espace. Start with advanced search dialog open. Panneau de recherche avancée ouvert au démarrage. Start with sort dialog open. Commencer par ouvrir la boîte de dialogue de tri. Search parameters Paramètres pour la recherche Stemming language Langue pour l'expansion des termes Dynamically build abstracts Construire dynamiquement les résumés Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Décide si des résumés seront construits à partir du contexte des termes de recherche. Peut ralentir l'affichage si les documents sont gros. Replace abstracts from documents Remplacer les résumés existant dans les documents Do we synthetize an abstract even if the document seemed to have one? Est-ce qu'un résumé doit etre synthétisé meme dans le cas ou le document original en avait un? Synthetic abstract size (characters) Taille du résumé synthétique (caractères) Synthetic abstract context words Nombre de mots de contexte par occurrence de terme dans le résumé External Indexes Index externes Add index Ajouter un index Select the xapiandb directory for the index you want to add, then click Add Index Sélectionnez le répertoire xapiandb pour l'index que vous souhaitez ajouter, puis cliquez sur Ajouter un index Browse Parcourir &OK &OK Apply changes Appliquer les modifications &Cancel &Annuler Discard changes Abandonner les modifications Result paragraph<br>format string Format de texte<br>du paragraphe résultat Automatically add phrase to simple searches Ajouter automatiquement une phrase aux recherches simples A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Une recherche pour [vin rouge] (2 mots) sera complétée comme [vin OU rouge OU (vin PHRASE 2 rouge)].<br> Ceci devrait donner une meilleure pertinence aux résultats où les termes recherchés apparaissent exactement et dans l'ordre. User preferences Préférences utilisateur Use desktop preferences to choose document editor. Utilisez les préférences du bureau pour choisir l'éditeur de document. External indexes Index externes Toggle selected Changer l'état pour les entrées sélectionnées Activate All Tout activer Deactivate All Tout désactiver Remove selected Effacer la sélection Remove from list. This has no effect on the disk index. Oter de la liste. Sans effet sur les données stockées. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Définit le format de chaque paragraphe de la liste de résultats. Utiliser le format qt html et les remplacements de type d'impression :<br>%A Résumé<br> %D Date<br> %I Nom de l'image de l'icône<br> %K Mots-clés (le cas échéant)<br> %L Aperçu et modification des liens<br> %M Type MIME<br> %N Nombre de résultats<br> %R Pourcentage de pertinence<br> %S Informations sur la taille<br> %T Titre<br> %U Url<br> Remember sort activation state. Mémoriser l'état d'activation du tri Maximum text size highlighted for preview (megabytes) Taille maximum des textes surlignés avant prévisualisation (Mo) Texts over this size will not be highlighted in preview (too slow). Les textes plus gros ne seront pas surlignés dans la prévisualisation (trop lent). Highlight color for query terms Couleur de mise en relief des termes recherchés Prefer Html to plain text for preview. Utiliser le format Html pour la previsualisation. If checked, results with the same content under different names will only be shown once. N'afficher qu'une entrée pour les résultats de contenu identique. Hide duplicate results. Cacher les doublons Choose editor applications Choisir les éditeurs pour les différents types de fichiers Display category filter as toolbar instead of button panel (needs restart). Afficher le filtre de catégorie comme barre d'outils au lieu du panneau des boutons (redémarrage nécessaire). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Les mots de la liste seront automatiquement changés en clauses ext:xxx dans les requêtes en langage d'interrogation. Query language magic file name suffixes. Suffixes automatiques pour le langage d'interrogation Enable Activer ViewAction Changing actions with different current values Changement des actions avec des valeurs actuelles différentes Mime type Type Mime Command Commande MIME type Type MIME Desktop Default Défaut du bureau Changing entries with different current values Nous changeons des éléments avec des valeurs actuelles différentes ViewActionBase File type Type de fichier Action Action Select one or several file types, then click Change Action to modify the program used to open them Sélectionnez un ou plusieurs types de fichiers, puis cliquez sur Modifier l'action pour modifier le programme utilisé pour les ouvrir Change Action Modifier l'action Close Fermer Native Viewers Applications de visualisation Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Sélectionnez un ou plusieurs types mime, puis cliquez sur "Changer d'action"<br>Vous pouvez également fermer cette boîte de dialogue et cocher "Utiliser les préférences du bureau"<br>dans le panneau principal pour ignorer cette liste et utiliser les paramètres par défaut de votre bureau. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Sélectionner un ou plusieurs types MIME, puis utiliser les contrôles dans le cadre du bas pour changer leur traitement Use Desktop preferences by default Utiliser les préférences du bureau Select one or several file types, then use the controls in the frame below to change how they are processed Sélectionner un ou plusieurs types de fichiers, puis utiliser les contrôles dans le cadre du bas pour changer leur traitement Exception to Desktop preferences Exception aux préférences du bureau Action (empty -> recoll default) Action (vide -> utiliser le defaut recoll) Apply to current selection Appliquer à la sélection courante Recoll action: Action current value valeur actuelle Select same S&eacute;lectionner par valeur <b>New Values:</b> <b>Nouveaux param&egrave;tres</b> Webcache Webcache editor Editeur Webcache Search regexp Recherche (regexp) TextLabel TextLabel WebcacheEdit Copy URL Copier l'URL Unknown indexer state. Can't edit webcache file. État indexeur inconnu. Impossible d'éditer le fichier webcache. Indexer is running. Can't edit webcache file. L'indexeur est actif. Impossible d'accéder au fichier webcache. Delete selection Détruire les entrées sélectionnées Webcache was modified, you will need to run the indexer after closing this window. Le fichier webcache a été modifié, il faudra redémarrer l'indexation après avoir fermé cette fenêtre. Save to File Enregistrer sous File creation failed: La création du fichier a échoué : Maximum size %1 (Index config.). Current size %2. Write position %3. Taille maximum %1 (config. Index). Taille courante %2. Position d'écriture %3. WebcacheModel MIME MIME Url Url Date Date Size Taille URL URL WinSchedToolW Error Erreur Configuration not initialized Configuration non initialisée <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Programmation de l'indexation par lots</h3><p>Recoll utilise l'outil standard Windows de programmation de tâches. Ce programme sera démarré quand vous cliquerez le bouton ci-dessous.</p><p>Vous pouvez utiliser soit l'interface complète (<i>Créer</i> dans le menu à droite), ou l'interface simplifiée <i>Créer une tâche basique</i>. Dans les deux cas, Copier/Coller le chemin du fichier de tâche listé ci-dessous comme l'<i>Action</i> à exécuter.</p> Command already started Commande déjà démarrée Recoll Batch indexing Indexation par lots Start Windows Task Scheduler tool Démarrer l'outil de programmation de tâches Could not create batch file Impossible de créer le fichier de commandes confgui::ConfBeaglePanelW Steal Beagle indexing queue Vol de la file d'indexation des Beagles Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle NE DOIT PAS être en cours d'exécution. Active le traitement de la file d'attente de beagle pour indexer l'historique web de Firefox.<br>(Vous devriez également installer le plugin Firefox Beagle) Web cache directory name Nom du répertoire du cache Web The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Le nom d'un répertoire où stocker le cache pour les pages Web visitées.<br>Un chemin non absolu est pris par rapport au répertoire de configuration. Max. size for the web cache (MB) Taille maximale du cache web (MB) Entries will be recycled once the size is reached Les entrées seront recyclées une fois la taille atteinte Web page store directory name Répertoire de stockage des pages WEB The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Le nom d'un répertoire où stocker les copies des pages visitées.<br>Un chemin relatif se réfère au répertoire de configuration. Max. size for the web store (MB) Taille maximale pour le cache Web (Mo) Process the WEB history queue Traiter la file des pages WEB Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Permet d'indexer les pages Web visitées avec Firefox <br>(il vous faut également installer l'extension Recoll pour Firefox) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Les entrées seront recyclées quand la taille sera atteinte.<br>Seule l'augmentation de la taille a un sens parce que réduire la valeur ne tronquera pas un fichier existant (mais gachera de l'espace à la fin). confgui::ConfIndexW Can't write configuration file Impossible d'ecrire le fichier de configuration Recoll - Index Settings: Recoll - Paramètres de l'index : confgui::ConfParamFNW Browse Parcourir Choose Choisir confgui::ConfParamSLW + + - - Add entry Ajouter une entrée Delete selected entries Détruire les entrées sélectionnées ~ ~ Edit selected entries Modifier les entrées sélectionnées confgui::ConfSearchPanelW Automatic diacritics sensitivity Sensibilité automatique aux accents <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Activer automatiquement la sensibilité aux accents si le terme recherché contient des accents (saufs pour ceux de unac_except_trans). Sans cette option, il vous faut utiliser le langage de recherche et le drapeau <i>D</i> pour activer la sensibilité aux accents. Automatic character case sensitivity Sensibilité automatique aux majuscules <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Activer automatiquement la sensibilité aux majuscules si le terme de recherche contient des majuscules (sauf en première lettre). Sans cette option, vous devez utiliser le langage de recherche et le drapeau <i>C</i> pour activer la sensibilité aux majuscules. Maximum term expansion count Taille maximum de l'expansion d'un terme <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Nombre maximum de termes de recherche résultant d'un terme entré (par exemple expansion par caractères jokers). La valeur par défaut de 10000 est raisonnable et évitera les requêtes qui paraissent bloquées pendant que le moteur parcourt l'ensemble de la liste des termes. Maximum Xapian clauses count Compte maximum de clauses Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Nombre maximum de clauses Xapian élémentaires générées pour une requête. Dans certains cas, le résultat de l'expansion des termes peut ere multiplicatif, et utiliserait trop de mémoire. La valeur par défaut de 100000 devrait être à la fois suffisante et compatible avec les configurations matérielles typiques. confgui::ConfSubPanelW Global Global Max. compressed file size (KB) Taille maximale pour les fichiers à décomprimer (ko) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Cette valeur définit un seuil au delà duquel les fichiers comprimés ne seront pas traités. Utiliser -1 pour désactiver la limitation, 0 pour ne traiter aucun fichier comprimé. Max. text file size (MB) Taille maximale d'un fichier texte (Mo) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Cette valeur est un seuil au delà duquel les fichiers de texte pur ne seront pas indexés. Spécifier -1 pour supprimer la limite. Utilisé pour éviter d'indexer des fichiers monstres. Text file page size (KB) Taille de page pour les fichiers de texte pur (ko) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Si cette valeur est spécifiée et positive, les fichiers de texte pur seront découpés en tranches de cette taille pour l'indexation. Ceci diminue les ressources consommées par l'indexation et aide le chargement pour prévisualisation. Max. filter exec. time (S) Temps d'exécution maximum pour un filtre (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Les filtres externes fonctionnant plus longtemps que cela seront abandonnés. Ceci est pour le cas rare (ex: postscript) où un document pourrait faire boucler un filtre à -1 pour pas de limite. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Un filtre externe qui prend plus de temps sera arrêté. Traite le cas rare (possible avec postscript par exemple) où un document pourrait amener un filtre à boucler sans fin. Mettre -1 pour complètement supprimer la limite (déconseillé). Only mime types Seulement ces types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Une liste exclusive des types MIME à indexer.<br>Rien d'autre ne sera indexé. Normalement vide et inactif Exclude mime types Types exclus Mime types not to be indexed Types MIME à ne pas indexer Max. filter exec. time (s) Temps d'exécution maximum pour un filtre (s) confgui::ConfTopPanelW Top directories Répertoires de départ The list of directories where recursive indexing starts. Default: your home. La liste des répertoires où l'indexation récursive démarre. Défault: votre répertoire par défaut. Skipped paths Chemins ignorés These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Ce sont des noms de répertoires où l'indexation n'entrera pas.<br>Ils peuvent contenir des caractères jokers. Les chemins doivent correspondre à ceux vus par l'indexeur (par exemple: si un des répertoires de départ est '/home/me' et que '/home' est un lien sur '/usr/home', une entrée correcte ici serait '/home/me/tmp*' , pas '/usr/home/me/tmp*') Stemming languages Langue pour l'expansion des termes The languages for which stemming expansion<br>dictionaries will be built. Les langages pour lesquels les dictionnaires d'expansion<br>des termes seront construits. Log file name Nom du fichier journal The file where the messages will be written.<br>Use 'stderr' for terminal output Le nom du fichier ou les messages seront ecrits.<br>Utiliser 'stderr' pour le terminal Log verbosity level Niveau de verbosité This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Cette valeur ajuste la quantite de messages emis,<br>depuis uniquement les erreurs jusqu'a beaucoup de donnees de debug. Index flush megabytes interval Intervalle d'écriture de l'index en mégaoctets This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Ajuste la quantité de données lues entre les écritures sur disque.<br>Contrôle l'utilisation de la mémoire. Défaut 10 Mo Max disk occupation (%) Occupation disque maximum (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Niveau d'occupation du disque ou l'indexation s'arrête (pour eviter un remplissage excessif).<br>0 signifie pas de limite (defaut). No aspell usage Pas d'utilisation d'aspell Aspell language Langue pour aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. La langue du dictionnaire aspell. Cela devrait ressembler à 'en' ou 'fr' . .<br>Si cette valeur n'est pas définie, l'environnement NLS sera utilisé pour le calculer, ce qui fonctionne. o obtenez une idée de ce qui est installé sur votre système, tapez 'config aspell' et cherchez . dans le répertoire 'data-dir'. Database directory name Répertoire de stockage de l'index The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Le nom d'un répertoire où stocker l'index<br>Un chemin non absolu est pris par rapport au répertoire de configuration. La valeur par défaut est 'xapiandb'. Use system's 'file' command Utiliser la commande système's 'file' Use the system's 'file' command if internal<br>mime type identification fails. Utilisez la commande système's 'file' si l'identification interne de type mime<br>échoue. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Désactiver l'utilisation d'aspell pour générer les approximations orthographiques.<br> Utile si aspell n'est pas installé ou ne fonctionne pas. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Langue pour le dictionnaire aspell. La valeur devrait ressembler à 'en' ou 'fr'... <br>Si cette valeur n'est pas positionnée, l'environnement national sera utilisé pour la calculer, ce qui marche bien habituellement. Pour avoir une liste des valeurs possibles sur votre système, entrer 'aspell config' sur une ligne de commande et regarder les fichiers '.dat' dans le répertoire 'data-dir'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Le nom d'un répertoire pour stocker l'index<br>Un chemin relatif sera interprété par rapport au répertoire de configuration. La valeur par défaut est 'xapiandb'. Unac exceptions Exceptions Unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Ce sont les exceptions au mécanisme de suppression des accents, qui, par défaut et en fonction de la configuration de l'index, supprime tous les accents et effectue une décomposition canonique Unicode. Vous pouvez inhiber la suppression des accents pour certains caractères, en fonction de votre langue, et préciser d'autres décompositions, par exemple pour des ligatures. Dans la liste séparée par des espaces, le premier caractères d'un élément est la source, le reste est la traduction. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Ce sont les chemins des répertoires où l'indexation n'ira pas.<br>Les éléments peuvent contenir des caractères joker. Les entrés doivent correspondre aux chemins vus par l'indexeur (ex.: si topdirs comprend '/home/me' et que '/home' est en fait un lien vers '/usr/home', un élément correct pour skippedPaths serait '/home/me/tmp*', et non '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Utilisation disque maximale (%, 0 signifie pas de limite) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. C'est le pourcentage d'utilisation disque - utilisation totale, et non taille de l'index - où l'indexation s'arrêtera en erreur.<br>La valeur par défaut de 0 désactive ce test. uiPrefsDialogBase User preferences Préférences utilisateur User interface Interface utilisateur Number of entries in a result page Nombre de résultats par page If checked, results with the same content under different names will only be shown once. N'afficher qu'une entrée pour les résultats de contenu identique. Hide duplicate results. Cacher les doublons Highlight color for query terms Couleur de mise en relief des termes recherchés Result list font Fonte pour la liste de résultats Opens a dialog to select the result list font Ouvre une fenêtre permettant de changer la fonte Helvetica-10 Helvetica-10 Resets the result list font to the system default Réinitialiser la fonte à la valeur par défaut Reset Réinitialiser Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Définit le format de chaque paragraphe de la liste de résultats. Utiliser le format qt html et les remplacements de type d'impression :<br>%A Résumé<br> %D Date<br> %I Nom de l'image de l'icône<br> %K Mots-clés (le cas échéant)<br> %L Aperçu et modification des liens<br> %M Type MIME<br> %N Nombre de résultats<br> %R Pourcentage de pertinence<br> %S Informations sur la taille<br> %T Titre<br> %U Url<br> Result paragraph<br>format string Format de texte<br>du paragraphe résultat Texts over this size will not be highlighted in preview (too slow). Les textes plus gros ne seront pas surlignés dans la prévisualisation (trop lent). Maximum text size highlighted for preview (megabytes) Taille maximum des textes surlignés avant prévisualisation (Mo) Use desktop preferences to choose document editor. Utilisez les préférences du bureau pour choisir l'éditeur de document. Choose editor applications Choisir les éditeurs pour les différents types de fichiers Display category filter as toolbar instead of button panel (needs restart). Afficher le filtre de catégorie comme barre d'outils au lieu du panneau des boutons (redémarrage nécessaire). Auto-start simple search on whitespace entry. Démarrer automatiquement une recherche simple sur entrée d'un espace. Start with advanced search dialog open. Panneau de recherche avancée ouvert au démarrage. Start with sort dialog open. Commencer par ouvrir la boîte de dialogue de tri. Remember sort activation state. Mémoriser l'état d'activation du tri Prefer Html to plain text for preview. Utiliser le format Html pour la previsualisation. Search parameters Paramètres pour la recherche Stemming language Langue pour l'expansion des termes A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Une recherche pour [vin rouge] (2 mots) sera complétée comme [vin OU rouge OU (vin PHRASE 2 rouge)].<br> Ceci devrait donner une meilleure pertinence aux résultats où les termes recherchés apparaissent exactement et dans l'ordre. Automatically add phrase to simple searches Ajouter automatiquement une phrase aux recherches simples Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Décide si des résumés seront construits à partir du contexte des termes de recherche. Peut ralentir l'affichage si les documents sont gros. Dynamically build abstracts Construire dynamiquement les résumés Do we synthetize an abstract even if the document seemed to have one? Est-ce qu'un résumé doit etre synthétisé meme dans le cas ou le document original en avait un? Replace abstracts from documents Remplacer les résumés existant dans les documents Synthetic abstract size (characters) Taille du résumé synthétique (caractères) Synthetic abstract context words Nombre de mots de contexte par occurrence de terme dans le résumé The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Les mots de la liste seront automatiquement changés en clauses ext:xxx dans les requêtes en langage d'interrogation. Query language magic file name suffixes. Suffixes automatiques pour le langage d'interrogation Enable Activer External Indexes Index externes Toggle selected Changer l'état pour les entrées sélectionnées Activate All Tout activer Deactivate All Tout désactiver Remove from list. This has no effect on the disk index. Oter de la liste. Sans effet sur les données stockées. Remove selected Effacer la sélection Click to add another index directory to the list Cliquez pour ajouter un autre répertoire d'index à la liste Add index Ajouter un index Apply changes Appliquer les modifications &OK &OK Discard changes Abandonner les modifications &Cancel &Annuler Abstract snippet separator Séparateur d'extrait Use <PRE> tags instead of <BR>to display plain text as html. Utilisez les balises <PRE> au lieu de <BR>pour afficher du texte brut en html. Lines in PRE text are not folded. Using BR loses indentation. Les lignes dans le texte PRE ne sont pas pliées. L'utilisation de BR perd l'indentation. Style sheet Feuille de style Opens a dialog to select the style sheet file Ouvre un dialogue pour choisir un fichier feuille de style Choose Choisir Resets the style sheet to default Restore la valeur par défaut pour la feuille de style Lines in PRE text are not folded. Using BR loses some indentation. Les lignes dans le texte PRE ne sont pas pliées. L'utilisation de BR perd une certaine indentation. Use <PRE> tags instead of <BR>to display plain text as html in preview. Utilisez les balises <PRE> au lieu de <BR>pour afficher du texte brut comme html dans la prévisualisation. Result List Liste de résultats Edit result paragraph format string Editer le format du paragraphe de résultat Edit result page html header insert Editer le fragment à insérer dans l'en-tête HTML Date format (strftime(3)) Format de date (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Seuil de fréquence (pourcentage) au delà duquel les termes ne seront pas utilisés. Les phrases contenant des termes trop fréquents posent des problèmes de performance. Les termes ignorés augmentent la distance de phrase, et réduisent l'efficacité de la fonction de recherche de phrase automatique. La valeur par défaut est 2% Autophrase term frequency threshold percentage Seuil de fréquence de terme (pourcentage) pour la génération automatique de phrases Plain text to HTML line style Style de traduction texte ordinaire vers HTML Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Les lignes dans une balise PRE ne sont pas repliées. Utiliser BR conduit à perdre une partie des tabulations. Le style PRE + WRAP peut être le meilleurs compromis mais son bon fonctionnement dépend des versions Qt. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + repliement Exceptions Exception Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Types de Mime qui ne devraient pas être passés à xdg-open même lorsque "Utiliser les préférences du bureau" est défini.<br> Utile pour passer le numéro de page et les options de recherche, par exemple évince. Disable Qt autocompletion in search entry. Désactiver l'autocomplétion Qt dans l'entrée de recherche Search as you type. Lancer la recherche a chaque caractere entre Paths translations Traductions de chemins Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Cliquer pour ajouter un autre index a la liste. Vous pouvez sélectionner soit un répertoire de configuration Recoll soit un index Xapian Snippets window CSS file Feuille de style CSS pour le popup de fragments Opens a dialog to select the Snippets window CSS style sheet file Ouvre un dialogue permettant de sélectionner la feuille de style CSS pour le popup des fragments Resets the Snippets window style Réinitialise le style de la fenêtre des fragments Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Décide si les filtres de documents sont affichés comme des radio-boutons, un menu déroulant dans la barre d'outils, ou un menu. Document filter choice style: Style de choix des filtres de documents : Buttons Panel Panneau de boutons Toolbar Combobox Menu déroulant dans le panneau d'outils Menu Menu Show system tray icon. Afficher l'icone dans la barre d'état système Close to tray instead of exiting. Réduire dans la barre d'état au lieu de quitter Start with simple search mode Démarrer en mode recherche simple Show warning when opening temporary file. Afficher un avertissement quand on édite une copie temporaire User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Style utilisateur à appliquer à la fenêtre "snippets".<br>Note : l'en tête de page de résultat est aussi inclus dans la fenêtre "snippets". Synonyms file Fichier de synonymes Highlight CSS style for query terms Style CSS de mise en avant pour les termes de la recherche Recoll - User Preferences Recoll - Préférences utilisateur Set path translations for the selected index or for the main one if no selection exists. Créer les traductions de chemins d'accès pour l'index selectionné, ou pour l'index principal si rien n'est sélectionné. Activate links in preview. Activer les liens dans la prévisualisation Make links inside the preview window clickable, and start an external browser when they are clicked. Rendre clicquables les liens dans la fenêtre de prévisualisation et démarrer un navigateur extérieur quand ils sont activés. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Mise en évidence des termes de recherche. <br>Si le bleu utilisé par défaut est trop discret, essayer peut-être : "color:red;background:yellow"... Start search on completer popup activation. Démarrer la recherche quand un choix est fait dans les suggestions Maximum number of snippets displayed in the snippets window Nombre maximum d'extraits affichés dans la fenêtre des extraits Sort snippets by page number (default: by weight). Trier les extraits par numéro de page (défaut: par pertinence). Suppress all beeps. Mode silencieux. Application Qt style sheet Feuille de style Qt pour l'application Limit the size of the search history. Use 0 to disable, -1 for unlimited. Limiter la taille de l'historique de recherche. Entrer 0 pour ne pas avoir d'historique, et -1 pour ne pas avoir de limite de taille. Maximum size of search history (0: disable, -1: unlimited): Taille maximum de l'historique de recherche (0:inactivé, -1: sans limite): Generate desktop notifications. Générer des notifications bureau. Misc Divers Work around QTBUG-78923 by inserting space before anchor text Contourner le QTBUG-78923 en inseŕant un espace devant le texte du lien Display a Snippets link even if the document has no pages (needs restart). Afficher un lien Extraits me quand le document n'est pas paginé (vous devrez redémarrer Recoll). Maximum text size highlighted for preview (kilobytes) Taille maximum de texte surligné pour la prévisualisation (kilo-octets) Start with simple search mode: Démarrer avec le type de recherche simple qui suit: Hide toolbars. Cacher les barres d'outils. Hide status bar. Cacher la barre de status. Hide Clear and Search buttons. Cacher les boutons Effacer et Recherche. Hide menu bar (show button instead). Cacher la barre de menu (afficher un bouton à la place). Hide simple search type (show in menu only). Cacher le type de recherche simple (afficher uniquement dans le menu). Shortcuts Raccourcis Hide result table header. Cacher l'en-tête de table de résultats Show result table row headers. Afficher les en-têtes de ligne. Reset shortcuts defaults Rétablir la configuration par défaut des raccourcis Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Désactive les raccourcis Ctrl+[0-9]/[a-z] pour sauter sur les lignes du tableau. Use F1 to access the manual Utilisez F1 pour accéder au manuel Hide some user interface elements. Masquer certains éléments de l'interface utilisateur. Hide: Fermer: Toolbars Barres d'outils Status bar Barre d'état Show button instead. Afficher le bouton à la place. Menu bar Barre de menu Show choice in menu only. Afficher le choix dans le menu seulement. Simple search type Type de recherche simple Clear/Search buttons Boutons d'effacement/recherche Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. Désactive les raccourcis Ctrl+[0-9]/[a-z] pour sauter vers les lignes du tableau. None (default) Aucun (par défaut) Uses the default dark mode style sheet Utilise la feuille de style de mode sombre par défaut Dark mode Mode sombre Choose QSS File Choisir un fichier QSS To display document text instead of metadata in result table detail area, use: Pour afficher le texte du document au lieu des métadonnées dans la zone de détail de la table de résultats, utilisez : left mouse click clic gauche de la souris Shift+click Maj+clic Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Ouvre une boîte de dialogue pour sélectionner le fichier de feuille de style.<br>Regardez /usr/share/recoll/examples/recoll[-dark].qss pour un exemple. Result Table Tableau de résultats Do not display metadata when hovering over rows. Ne pas afficher les détails quand on passe sur un rang avec la souris. Work around Tamil QTBUG-78923 by inserting space before anchor text Contourner le bug QTBUG-78923 pour le Tamil en inseŕant un espace devant le texte du lien The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Ce bug provoque l'apparition d'un caractère étrange en forme de cercle à l'intérieur de mots en Tamil. Le contournement insère un caractère d'espace additionnel supplémentaire qui semble corriger le problème. Depth of side filter directory tree Profondeur de l'arbre du filtre de répertoires Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Facteur de zoom pour l'interface utilisateur. Utile si l'affichage par défaut ne convient pas à votre résolution d'écran. Display scale (default 1.0): Échelle d'affichage (défaut 1.0): Enable spelling approximations for terms not found in the index. Activer l'ajout automatique d'approximations orthographiques aux recherches. Automatic spelling approximation. Approximation orthographique automatique. Max spelling distance Distance orthographique maximale Add common spelling approximations for rare terms. Ajouter des termes fréquents et proches orthographiquement des termes rares de la recherche. Maximum number of history entries in completer list Nombre maximum d'entrées dans la liste de complétions Number of history entries in completer: Nombre d'entrées historiques dans la liste de complétion: Displays the total number of occurences of the term in the index Affiche le nombre total d'occurences du terme dans l'index Show hit counts in completer popup. Afficher le nombre de correspondances dans la liste de complétion. Prefer HTML to plain text for preview. Utiliser le format HTML pour la previsualisation. recoll-1.36.1/qtgui/i18n/recoll_da.ts0000644000175000017500000070137414444307651014233 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Alle sætninger Any clause Vilkårlig sætning texts tekster spreadsheets regneark presentations præsentationer media medier messages beskeder other andet Bad multiplier suffix in size filter Forkert multiplikator suffiks i størrelsefilter text tekst spreadsheet regneark presentation præsentation message besked Advanced Search Avanceret søgning History Next Næste Historik History Prev Historik Forrige Load next stored search Indlæs næste gemte søgning Load previous stored search Indlæs tidligere gemt søgning AdvSearchBase Advanced search Avanceret søgning Restrict file types Begræns filtyper Save as default Gem som standard Searched file types Søgte filtyper All ----> Alle ----> Sel -----> Valg -----> <----- Sel <----- Valg <----- All <----- Alle Ignored file types Ignorerede filtyper Enter top directory for search Indtast øverste mappe for søgning Browse Gennemse Restrict results to files in subtree: Begræns resultater til filer i undermapper: Start Search Start søgning Search for <br>documents<br>satisfying: Søg efter <br>dokumenter<br>der opfylder: Delete clause Slet sætning Add clause Tilføj sætning Check this to enable filtering on file types Afkryds dette for at aktivere filtrering på filtyper By categories Efter kategorier Check this to use file categories instead of raw mime types Afkryds dette for at bruge filkategorier i stedet for rå mime-typer Close Luk All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Alle felter med indhold til højre vil blive kombineret med AND ("Alle sætninger" valgt) eller OR ("Vilkårlig sætning" valgt) bindeord. <br>"Enhver" "Alle" og "Ingen" felttyper kan acceptere en blanding af simple ord, og fraser i dobbelte anførselstegn.<br>Felter uden data ignoreres. Invert Inverter Minimum size. You can use k/K,m/M,g/G as multipliers Mindste størrelse. Du kan bruge k/K,m/M,g/G som multiplikatorer Min. Size Min. størrelse Maximum size. You can use k/K,m/M,g/G as multipliers Maksimal størrelse. Du kan bruge k/K,m/M g/G som multiplikatorer Max. Size Maks. størrelse Select Vælg Filter Filtrer From Fra To Til Check this to enable filtering on dates Afkryds dette for at aktivere filtrering på datoer Filter dates Filtrer datoer Find Find Check this to enable filtering on sizes Afkryds dette for at aktivere filtrering på størrelser Filter sizes Filtrer størrelser Filter birth dates ConfIndexW Can't write configuration file Kan ikke skrive konfigurationsfil Global parameters Globale parametre Local parameters Lokale parametre Search parameters Søgeparametre Top directories Øverste mapper The list of directories where recursive indexing starts. Default: your home. Listen over mapper hvor rekursiv indeksering starter. Standard: din hjemme-mappe (home). Skipped paths Udeladte stier These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Disse er stinavne på mapper som indeksering ikke vil komme ind.<br>Sti elementer kan indeholde jokerkort. Indgangene skal matche stierne set af indekseringen (f.eks. hvis topdirs omfatter '/home/me' og '/home' er faktisk et link til '/usr/home', en korrekt sprunget ind i stien ville være '/home/me/tmp*', ikke '/usr/home/me/tmp*') Stemming languages Ordstammer for sprogene The languages for which stemming expansion<br>dictionaries will be built. De sprog, hvor ordstamme-udvidelses<br>ordbøger vil blive bygget. Log file name Navn på logfil The file where the messages will be written.<br>Use 'stderr' for terminal output Filen hvor meddelelser vil blive skrevet.<br>Brug 'stderr' for terminal output Log verbosity level Log informationsniveau This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Denne værdi justerer mængden af meddelelser,<br>fra kun fejl til en masse fejlretningsdata. Index flush megabytes interval Megabyte interval for skrivning af Index This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Denne værdi justere mængden af data, der er indekseret mellem skrivning til disken.<br>Dette hjælper med at kontrollere indekseringsprogrammets brug af hukommelse. Standard 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Dette er procentdelen af diskforbrug - samlet diskforbrug, ikke indeksstørrelse - hvor indeksering vil mislykkes og stoppe.<br>Standardværdien på 0 fjerner enhver grænse. No aspell usage Brug ikke aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Deaktiver brug af aspell til at generere stavnings-tilnærmelse i værktøj for søgning efter ord. <br> Nyttigt hvis aspell er fraværende eller ikke virker. Aspell language Aspell sprog The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Sproget for aspell ordbog. Det skal se ud som "en" eller "fr" ...<br>Hvis denne værdi ikke er angivet, så vil NLS omgivelser blive brugt til at finde det, det fungerer normalt. For at få en idé om, hvad der er installeret på dit system, kan du skrive 'aspell konfig "og se efter .dat filer inde i 'data-dir' mappen. Database directory name Databasens mappenavn The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Navnet på en mappe hvor du vil gemme indekset<br>En relativ sti er taget i forhold til konfigurationsmappen. Standard er "xapiandb. Unac exceptions Unac-undtagelser <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Disse er undtagelser fra unac mekanismen, der, som standard, fjerner alle diakritiske tegn, og udfører kanonisk nedbrydning. Du kan tilsidesætte fjernelse af accent for nogle tegn, afhængigt af dit sprog, og angive yderligere nedbrydninger, f.eks. for ligaturer. I hver indgang adskilt af mellemrum, er det første tegn kildedelen, og resten er oversættelsen. Process the WEB history queue Behandl køen for WEB-historik Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Aktiverer indeksering af sider besøgt af Firefox.<br>(Du skal også installere Firefox Recoll plugin) Web page store directory name Mappenavn for lageret til Websider The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Navnet på en mappe hvor du vil gemme kopier af besøgte websider.<br>En relativ sti er taget i forhold til konfigurationsmappen. Max. size for the web store (MB) Max. størrelse til web-lager (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Indgangene vil blive genbrugt, når størrelsen er nået.<br>Kun en øgning af størrelsen giver god mening, da en reducering af værdien ikke vil afkorte en eksisterende fil (kun spildplads i slutningen). Automatic diacritics sensitivity Automatisk følsomhed over for diakritiske tegn <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Udløser automatisk følsomhed over for diakritiske tegn, hvis søgeordet har accent tegn (ikke i unac_except_trans). Ellers er du nød til bruge forespørgselssproget og <i>D</i> modifikatoren, for at angive følsomhed over for diakritiske tegn. Automatic character case sensitivity Automatisk følsomhed over for store/små bogstaver <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Udløser automatisk følsomhed over for store/små bogstaver, hvis indgangen har store bogstaver i andet end den første position. Ellers er du nød til bruge forespørgselssproget og <i>C</i> modifikatoren, for at angive følsomhed over for store/små bogstaver. Maximum term expansion count Maksimale antal ordudvidelser <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maksimal antal udvidelser-for et enkelt ord (fx: når der bruges jokertegn). Standarden på 10 000 er rimeligt og vil undgå forespørgsler, der synes at fryse mens motoren arbejder sig igennem ordlisten. Maximum Xapian clauses count Maksimale antal Xapiansætninger <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maksimalt antal grundlæggende sætninger vi føjer til en enkel Xapian forespørgsel. I nogle tilfælde kan resultatet af ordudvidelse være multiplikativ, og vi ønsker at undgå at bruge overdreven hukommelse. Standarden på 100 000 bør være både høj nok i de fleste tilfælde og kompatibel med de nuværende typiske hardware konfigurationer. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... De sprog, for hvilke der vil blive bygget ekspansionsordbøger.<br>Se dokumentationen for Xapian stemmer for mulige værdier. F.eks. engelsk, fransk, tysk... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Sproget for aspell ordbog. Værdierne er 2-bogstavs sprogkoder, fx 'da', 'fr' . .<br>Hvis denne værdi ikke er angivet, vil NLS- miljøet blive brugt til at beregne den, som normalt virker. For at få en idé om, hvad der er installeret på dit system, skriv 'aspell config' og kig efter . på filer inde i 'data-dir' mappe. Indexer log file name Indekserings logfilnavn If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Hvis tom, vil ovenstående log filnavn værdi blive brugt. Det kan være nyttigt at have en separat log til diagnostiske formål, fordi den fælles log vil blive slettet, når<br>GUI starter. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Disk fuld tærskel procentdel, hvor vi stopper indeksering<br>F.eks. 90% at stoppe ved 90% fuld, 0 eller 100 betyder ingen grænse) Web history Webhistorik Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Kun mime-typer An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive En eksklusiv liste over indekserede MIME-typer.<br>Intet andet vil blive indekseret. Normalt tom og inaktiv Exclude mime types Udeluk mime-typer Mime types not to be indexed Mime-typer der ikke skal indekseres Max. compressed file size (KB) Maks. komprimeret filstørrelse (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Denne værdi angiver en grænse for, hvornår komprimerede filer ikke vil blive behandlet. Indstil til -1 for ingen grænse, til 0 for ingen dekomprimering nogensinde. Max. text file size (MB) Maks. størrelse på tekstfil (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Denne værdi angiver en grænse for, hvornår tekstfiler ikke vil blive behandlet. Indstil til -1 for ingen grænse. Dette er for at udelukke monster logfiler fra indekset. Text file page size (KB) Sidestørrelse på tekstfil (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Hvis denne værdi er angivet (ikke lig med -1), vil tekstfiler opdeles i bidder af denne størrelse for indeksering. Dette vil hjælpe søgning i meget store tekstfiler (dvs.: log-filer). Max. filter exec. time (s) Maks. filter eksekveringstid (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Eksterne filtre der arbejder længere end dette vil blive afbrudt. Dette er for det sjældne tilfælde (dvs.: postscript) hvor et dokument kan forårsage, at et filter laver et loop. Indstil til -1 for ingen grænse. Global Globalt CronToolW Cron Dialog Cron vindue <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indekseringstidsplan (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hvert felt kan indeholde et jokertegn (*), en enkelt numerisk værdi, kommaseparerede lister (1,3,5) og intervaller (1-7). Mere generelt vil felterne blive brugt <span style=" font-style:italic;"> som de er</span> inde i crontabfilen, og den fulde crontab syntaks kan bruges, se crontab (5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For eksempel, indtastning af <span style=" font-family:'Courier New,courier';">*</span> i <span style=" font-style:italic;">Dage, </span><span style=" font-family:'Courier New,courier';">12,19</span> i <span style=" font-style:italic;">Timer</span> og <span style=" font-family:'Courier New,courier';">15</span> i <span style=" font-style:italic;">Minutter</span> ville starte recollindex hver dag kl. 00:15 og 19:15 </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">En tidsplan med meget hyppige aktiveringer er formentlig mindre effektiv end realtid indeksering.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Ugens dage (* eller 0-7, 0 eller 7 er Søndag) Hours (* or 0-23) Timer (* eller 0-23) Minutes (0-59) Minutter (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Deaktiver</span> for at stoppe automatisk batch indeksering, <span style=" font-style:italic;">Aktiver</span> for at aktivere den, <span style=" font-style:italic;">Annuller</span> for ikke at ændre noget.</p></body></html> Enable Aktiver Disable Deaktiver It seems that manually edited entries exist for recollindex, cannot edit crontab Det ser ud til, at manuelt redigerede indgange findes for recollindeks, kan ikke redigere crontab Error installing cron entry. Bad syntax in fields ? Fejl ved installation af cron-indgange. Forkert syntaks i felter? EditDialog Dialog Vindue EditTrans Source path Kildesti Local path Lokal sti Config error Konfigureringsfejl Original path Original sti EditTransBase Path Translations Oversættelse af stier Setting path translations for Indstilling af oversættelser af stier for Select one or several file types, then use the controls in the frame below to change how they are processed Vælg en eller flere filtyper, brug derefter knapperne i rammen nedenfor for at ændre, hvordan de skal behandles Add Tilføj Delete Slet Cancel Annuller Save Gem FirstIdxDialog First indexing setup Opsætning af første indeksering <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Det fremgår, at indekset for denne konfiguration ikke eksisterer.</span><br /><br />Hvis du blot ønsker at indeksere din hjemmemappe med et sæt fornuftige standardindstillinger, skal du trykke på <span style=" font-style:italic;">Start indeksering nu</span> knappen. Du vil være i stand til at justere detaljerne senere. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hvis du ønsker mere kontrol, kan du bruge følgende link til at justere indekseringskonfiguration og tidsplan.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Disse værktøjer kan tilgås senere fra <span style=" font-style:italic;">Præference</span> menuen.</p></body></html> Indexing configuration Konfiguration af indeksering This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Dette vil lade dig justere de mapper, du vil indeksere, og andre parametre som udelukkede filstier eller navne, standard tegnsæt etc. Indexing schedule Tidsplan for indeksering This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Dette vil lade dig vælge mellem batch og realtime indeksering, og oprette en automatisk tidsplan for batch indeksering (ved hjælp af cron). Start indexing now Start indeksering nu FragButs %1 not found. %1 ikke fundet. %1: %2 %1: %2 Fragment Buttons Fragment Knapper Query Fragments Forespørgsel efter fragmenter IdxSchedW Index scheduling setup Opsætning af indeks skedulering <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indeksering kan køre permanent, indeksere filer når de ændrer sig, eller køre med adskilte intervaller. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Læsning af manualen kan hjælpe dig med at vælge mellem disse tilgange (tryk F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Dette værktøj kan hjælpe dig med at oprette en tidsplan for at automatisere kørsler af batch indeksering, eller starte realtid indeksering når du logger ind (eller begge dele, hvilket sjældent giver mening). </p></body></html> Cron scheduling Cron skedulering The tool will let you decide at what time indexing should run and will install a crontab entry. Værktøjet vil lade dig afgøre, på hvilket tidspunkt indeksering skal køre og det vil installere en crontab indgang. Real time indexing start up Opstart af realtid indeksering Decide if real time indexing will be started when you log in (only for the default index). Beslut, om realtid indeksering skal startes når du logger ind (kun for standard-indekset). ListDialog Dialog Vindue GroupBox Gruppeboks Main No db directory in configuration Ingen dbmappe i konfigurationen Could not open database in Kunne ikke åbne database i . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Klik på Annullér, hvis du ønsker at redigere konfigurationsfilen før indeksering starter, eller Ok for at lade den fortsætte. Configuration problem (dynconf Konfigurationsproblem (dynconf "history" file is damaged or un(read)writeable, please check or remove it: Filen med "historik" er beskadiget eller den kan ikke læses eller skrives til, undersøg det venligst, eller fjern den: "history" file is damaged, please check or remove it: "historie" fil er beskadiget, tjek eller fjern den: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Søger efter: &Next &Næste &Previous &Forrige Match &Case Store/små &Bogstaver Clear Ryd Creating preview text Laver forhåndsvisningstekst Loading preview text into editor Henter forhåndsvisningstekst for redigering Cannot create temporary directory Kan ikke oprette midlertidig mappe Cancel Annuller Close Tab Luk faneblad Missing helper program: Manglende hjælpeprogram: Can't turn doc into internal representation for Kan ikke lave dok til intern repræsentation for Cannot create temporary directory: Kan ikke oprette midlertidig mappe: Error while loading file Fejl ved indlæsning af filen Form Formular Tab 1 Tab 1 Open Åbn Canceled Annulleret Error loading the document: file missing. Fejl ved indlæsning af dokumentet: fil mangler. Error loading the document: no permission. Fejl ved indlæsning af dokumentet: ingen tilladelse. Error loading: backend not configured. Fejl ved indlæsning: backend ikke konfigureret. Error loading the document: other handler error<br>Maybe the application is locking the file ? Fejl ved indlæsning af dokumentet: anden håndteringsfejl<br>Måske er programmet låser filen? Error loading the document: other handler error. Fejl ved indlæsning af dokumentet: anden håndteringsfejl. <br>Attempting to display from stored text. <br>Forsøger at vise fra lagret tekst. Could not fetch stored text Kunne ikke hente gemt tekst Previous result document Forrige resultat dokument Next result document Næste resultat dokument Preview Window Forhåndsvis Vindue Close Window Luk Vindue Next doc in tab Næste dokument i fanebladet Previous doc in tab Forrige dokument i fanen Close tab Luk fane Print tab Print tab Close preview window Luk forhåndsvisningsvindue Show next result Vis næste resultat Show previous result Vis tidligere resultat Print Udskriv PreviewTextEdit Show fields Vis felter Show main text Vis hovedtekst Print Udskriv Print Current Preview Udskriv denne Visning Show image Vis billede Select All Vælg alle Copy Kopier Save document to file Gem dokument til fil Fold lines Ombryd linjer Preserve indentation Bevar indrykning Open document Åbn dokument Reload as Plain Text Reload as HTML QObject Global parameters Globale parametre Local parameters Lokale parametre <b>Customised subtrees <b>Tilpassede undermapper The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Listen over undermapper i det indekserede hierarki <br>hvor nogle parametre behøver at blive omdefineret. Standard: tom. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>De parametre, der følger er angivet enten på øverste niveau, hvis intet<br>eller en tom linje er valgt i listefeltet ovenfor, eller for den valgte undermappe. <br> Du kan tilføje eller fjerne mapper ved at klikke på +/- knapperne. Skipped names Udeladte navne These are patterns for file or directory names which should not be indexed. Dette er mønstre for fil- eller mappenavne, der ikke skal indekseres. Default character set Standard tegnsæt This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Dette er det tegnsæt, der bruges til at læse filer, som ikke identificerer tegnsættet internt, for eksempel rene tekstfiler.<br>Standardværdien er tom, og værdien fra NLS miljøet anvendes. Follow symbolic links Følg symbolske links Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Følg symbolske link under indeksering. Standarden er nej, for at undgå dobbelt indeksering Index all file names Indekser alle filnavne Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indekser navnene på filer, hvor indholdet ikke kan identificeres eller behandles (ingen eller ikke-understøttet mime-type). Standard er true Beagle web history Beagle webhistorik Search parameters Søgeparametre Web history Webhistorik Default<br>character set Standard<br>tegnsæt Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Tegnsæt, der bruges til at læse filer, hvor tegnsættet ikke kan identificeres ud fra indholdet, f.eks. rene tekstfiler.<br>Standardværdien er tom, og værdien fra NLS-omgivelserne anvendes. Ignored endings ignorerede endelser These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Dette er filnavneafslutninger for filer, som kun vil blive indekseret af indhold (intet MIME-typeidentifikationsforsøg, ingen dekomprimering, ingen indholdsindeksering. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Dette er endelser på filnavne for filer, hvor kun navnet vil blive indekseret (ingen forsøg på identifikation af MIME-type, ingen dekomprimering, ingen indeksering af indhold). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>De parametre, der følger, er indstillet enten på øverste niveau, hvis intet eller en tom linje er valgt i listefeltet ovenfor, eller for den valgte undermappe. Du kan tilføje eller fjerne mapper ved at klikke på +/- knapperne. QWidget Create or choose save directory Opret eller vælg mappe til at gemme i Choose exactly one directory Vælg præcis en mappe Could not read directory: Kunne ikke læse mappe: Unexpected file name collision, cancelling. Uventet kollision af filnavn, annullerer. Cannot extract document: Kan ikke udtrække dokument: &Preview &Forhåndsvisning &Open &Åbn Open With Åbn med Run Script Kør skript Copy &File Name Kopier &Filnavn Copy &URL Kopier &URL &Write to File &Skriv til fil Save selection to files Gem det valgte til filer Preview P&arent document/folder Forhåndsvis &Forælderdokument/mappe &Open Parent document/folder &Åbn Forælderdokument/mappe Find &similar documents Find &lignende dokumenter Open &Snippets window Åbn vindue til &tekststumper Show subdocuments / attachments Vis underdokumenter / vedhæftede filer &Open Parent document &Åbn overordnet dokument &Open Parent Folder &Åbn Overordnet Mappe Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Vis ikke igen. RTIToolW Real time indexing automatic start Automatisk start af realtid indeksering <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> Indeksering kan sættes til at køre som en dæmon, der opdatere indekset når filer ændres, i realtid. Du får et indeks, som altid er opdateret, men systemressourcer anvendes permanent..</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Start indekseringsdæmonen med min skrivebordssession. Also start indexing daemon right now. Start også indekseringsdæmon lige nu. Replacing: Erstatter: Replacing file Erstatter fil Can't create: Kan ikke oprette: Warning Advarsel Could not execute recollindex Kunne ikke køre recollindex Deleting: Sletter: Deleting file Sletter fil Removing autostart Fjerner autostart Autostart file deleted. Kill current process too ? Autostartfil er slettet. Stop også nuværende proces? RclCompleterModel Hits RclMain About Recoll Om Recoll Executing: [ Udfører: [ Cannot retrieve document info from database Kan ikke hente dokumentinfo fra databasen Warning Advarsel Can't create preview window Kan ikke oprette forhåndsvisningsvindue Query results Resultater af forespørgsel Document history Dokumenthistorik History data Historik-data Indexing in progress: Indeksering i gang: Files Filer Purge Rydder op Stemdb stammedb Closing Afslutter Unknown Ukendt This search is not active any more Denne søgning er ikke længere aktiv Can't start query: Kan't starte forespørgsel: Bad viewer command line for %1: [%2] Please check the mimeconf file Ugyldig søgerkommandolinje for %1: [%2] Tjek venligst filen mimeconf Cannot extract document or create temporary file Kan ikke udtrække dokument eller oprette midlertidig fil (no stemming) (Ingen ordstammer) (all languages) (alle sprog) error retrieving stemming languages fejl under hentning af ordstammer for sprogene Update &Index Opdater &Indeks Indexing interrupted indeksering afbrudt Stop &Indexing Stop &Indeksering All Alle media medier message besked other andet presentation præsentation spreadsheet regneark text tekst sorted sorteret filtered filtreret External applications/commands needed and not found for indexing your file types: Eksterne programmer/kommandoer er nødvendige og ikke fundet til indeksering af dine filtyper: No helpers found missing Ingen hjælpere mangler Missing helper programs Manglende hjælpeprogrammer Save file dialog Gem fildialog Choose a file name to save under Vælg et filnavn der skal gemmes under Document category filter Dokument kategori filter No external viewer configured for mime type [ Ingen ekstern fremviser konfigureret for mime-type [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Fremviseren angivet i mimeview for %1: %2 er ikke fundet. Ønsker du at åbne indstillingsvinduet? Can't access file: Kan ikke tilgå fil: Can't uncompress file: Kan ikke dekomprimere fil: Save file Gem fil Result count (est.) Optælling af resultat (est.) Query details Detaljer i Forespørgsel Could not open external index. Db not open. Check external indexes list. Kunne ikke åbne ekstern indeks. DB er ikke åben. Tjek liste over eksterne indekser. No results found Ingen resultater fundet None Ingen Updating Opdaterer Done Færdig Monitor Overvåg Indexing failed Indeksering mislykkedes The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Den nuværende indekseringsproces blev ikke startet fra denne grænseflade. Klik på OK for at stoppe den alligevel, eller Annuller for at lade den køre Erasing index Sletter indeks Reset the index and start from scratch ? Nulstil indekset og start forfra? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Forespørgsel er i gang<br>På grund af begrænsninger i indekseringsbiblioteket,<br>vil en annullering afslutte programmet Error Fejl Index not open Indeks ikke åben Index query error Indeks forespørgselsfejl Indexed Mime Types Indekserede Mime-Typer Content has been indexed for these MIME types: Indhold er blevet indekseret for disse MIME-typer: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Indeks er ikke opdateret for denne fil. Afviger for at risikere at vise den forkerte post. Klik på OK for at opdatere indekset for denne fil, og derefter genkør forespørgslen når indeksering er færdig. Else, Annuller. Can't update index: indexer running Kan ikke opdatere indeks: indeksering kører Indexed MIME Types Indekserede MIME-typer Bad viewer command line for %1: [%2] Please check the mimeview file Forkert kommandolinje for fremviser for %1: [%2] Kontroller venligst mimeview-filen Viewer command line for %1 specifies both file and parent file value: unsupported Fremviser kommandolinje for %1 angiver både fil og forælderfil værdier: er ikke understøttet Cannot find parent document Kan ikke finde forælderdokument Indexing did not run yet Indeksering har ikke kørt endnu External applications/commands needed for your file types and not found, as stored by the last indexing pass in Eksterne programmer/kommandoer nødvendige for dine filtyper blev ikke fundet, som gemt af den sidste indeksering Index not up to date for this file. Refusing to risk showing the wrong entry. Indeks er ikke opdateret for denne fil. Afviger for at risikere at vise den forkerte post. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Klik på OK for at opdatere indekset for denne fil, og derefter genkør forespørgslen når indeksering er færdig. Else, Annuller. Indexer running so things should improve when it's done Indeksering kører, så tingene bør forbedres, når det's gjort Sub-documents and attachments Underdokumenter og vedhæftede filer Document filter Dokumentfilter Index not up to date for this file. Refusing to risk showing the wrong entry. Indeks er ikke opdateret for denne fil. Nægter at risikere at vise den forkerte indgang. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Klik OK for at opdatere indekset for denne fil, du bliver så nødt til at gentage forespørgslen når indeksering er færdig. The indexer is running so things should improve when it's done. Indeksering kører, så ting burde være bedre, når den er færdig. The document belongs to an external indexwhich I can't update. Dokumentet tilhører et ekstern indeks, som jeg ikke kan opdatere. Click Cancel to return to the list. Click Ignore to show the preview anyway. Klik på Annuller for at vende tilbage til listen. Klik på Ignorer for at vise forhåndsvisningen alligevel. Duplicate documents Identiske dokumenter These Urls ( | ipath) share the same content: Disse webadresser ( | ipath) deler samme indhold: Bad desktop app spec for %1: [%2] Please check the desktop file Forkert desktop app spec for %1: [%2] Tjek venligst desktopfilen Bad paths Ugyldige stier Bad paths in configuration file: Ugyldige stier i konfigurationsfil: Selection patterns need topdir Mønstre for udvælgelse skal have en øverste mappe Selection patterns can only be used with a start directory Mønstre for udvælgelse kan kun bruges med en startmappe No search Ingen søgning No preserved previous search Ingen tidligere søgning er bevaret Choose file to save Vælg fil, der skal gemmes Saved Queries (*.rclq) Gemte forespørgsler (*.rclq) Write failed Skrivning mislykkedes Could not write to file Kunne ikke skrive til fil Read failed Læsning mislykkedes Could not open file: Kunne ikke åbne fil: Load error Indlæsningsfejl Could not load saved query Kunne ikke indlæse gemte forespørgsel Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Åbner en midlertidig kopi. Ændringer vil gå tabt, hvis du ikke gemmer<br/>dem til et permanent sted. Do not show this warning next time (use GUI preferences to restore). Vis ikke denne advarsel næste gang (brug GUI præferencer for at gendanne). Disabled because the real time indexer was not compiled in. Deaktiveret fordi realtid indeksering ikke blev kompileret ind. This configuration tool only works for the main index. Dette konfigurationsværktøj virker kun for hovedindekset. The current indexing process was not started from this interface, can't kill it Den nuværende indekseringsproces blev ikke startet fra denne grænseflade, kan ikke stoppe den The document belongs to an external index which I can't update. Dokumentet tilhører et eksternt indeks, som jeg ikke kan opdatere. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Klik på Annuller for at vende tilbage til listen. <br>Klik på Ignorer for at vise forhåndsvisningen alligevel. (og husk for denne session). Index scheduling Indeks skedulering Sorry, not available under Windows for now, use the File menu entries to update the index Beklager, er endnu ikke tilgængelig for Windows, bruge Fil menuindgange for at opdatere indekset Can't set synonyms file (parse error?) Kan ikke aktivere synonymer-fil (analysefejl?) Index locked Indeks låst Unknown indexer state. Can't access webcache file. Indeksering i ukendt tilstand. Kan ikke tilgå webcachefil. Indexer is running. Can't access webcache file. Indeksering kører. Kan ikke tilgå webcachefil. with additional message: med yderligere meddelelse: Non-fatal indexing message: Ikke-fatal indeksering besked: Types list empty: maybe wait for indexing to progress? Typer liste tom: måske vente på indeksering til fremskridt? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Viewer kommandolinje for %1 angiver overordnet fil, men URL er http[s]: ikke understøttet Tools Værktøjer Results Resultater (%d documents/%d files/%d errors/%d total files) (%d dokumenter/%d filer/%d fejl/%d samlede filer) (%d documents/%d files/%d errors) (%d dokumenter/%d filer/%d fejl) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Tomme eller ikke-eksisterende stier i konfigurationsfilen. Klik på Ok for at starte indeksering alligevel (manglende data vil ikke blive renset fra indekset): Indexing done Indeksering udført Can't update index: internal error Kan't opdateringsindeks: intern fejl Index not up to date for this file.<br> Indeks ikke opdateret for denne fil.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Det ser også ud til, at den sidste indeksopdatering for filen mislykkedes.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Klik på OK for at forsøge at opdatere indekset for denne fil. Du bliver nødt til at køre forespørgslen igen, når indeksering er afsluttet.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Klik på Annuller for at vende tilbage til listen.<br>Klik Ignorér for at vise forhåndsvisningen alligevel (og husk for denne session). Der er en risiko for at vise den forkerte post.<br/> documents dokumenter document dokument files filer file fil errors fejl error fejl total files) samlede filer) No information: initial indexing not yet performed. Ingen oplysninger: oprindelig indeksering endnu ikke udført. Batch scheduling Batch planlægning The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Værktøjet vil lade dig beslutte på hvilket tidspunkt indeksering skal køre. Det bruger Windows opgave scheduler. Confirm Bekræft Erasing simple and advanced search history lists, please click Ok to confirm Sletning af enkle og avancerede søgehistorik lister, klik på OK for at bekræfte Could not open/create file Kunne ikke åbne/oprette fil F&ilter F&ilter Could not start recollindex (temp file error) Kunne ikke starte recollindex (temp fil fejl) Could not read: Kunne ikke læse: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Dette vil erstatte det nuværende indhold af resultatlistens hovedstreng og GUI qss filnavn. Fortsæt? You will need to run a query to complete the display change. Du bliver nødt til at køre en forespørgsel for at fuldføre visningsændringen. Simple search type Simpel søgetype Any term Vilkårlig ord All terms Alle ord File name Filnavn Query language Forespørgselssprog Stemming language Ordstammer for sprog Main Window Hoved Vindue Focus to Search Fokus på søgning Focus to Search, alt. Fokus på søgning, alt. Clear Search Ryd Søgning Focus to Result Table Fokus på resultattabel Clear search Ryd søgning Move keyboard focus to search entry Flyt tastaturfokus for at søge ind Move keyboard focus to search, alt. Flyt tastaturets fokus for at søge, alt. Toggle tabular display Slå tabelvisning til/fra Move keyboard focus to table Flyt tastaturets fokus til tabellen Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Forrige side Next page Næste side &File &Fil E&xit A&fslut &Tools &Værktøjer &Help &Hjælp &Preferences &Præferencer Search tools Søg efter værktøjer Result list Resultatliste &About Recoll &Om Recoll Document &History Dokument&historik Document History Dokumenthistorik &Advanced Search &Avanceret søgning Advanced/complex Search Avanceret/kompleks søgning &Sort parameters &Sorterings-parametre Sort parameters Sorterings-parametre Next page of results Næste side med resultater Previous page of results Forrige side med resultater &Query configuration &Forespørgselskonfiguration &User manual &Brugermanual Recoll Rekoll Ctrl+Q Ctrl+Q Update &index Opdater &Indeks Term &explorer &Søg efter ord Term explorer tool Værktøj for søgning efter ord External index dialog Eksterne indekser &Erase document history &Slet dokumenthistorik First page Første side Go to first page of results Gå til første side med resultater &Indexing configuration &Konfiguration af indeksering All Alle &Show missing helpers &Vis manglende hjælpere PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen &Fuld skærm F11 F11 Full Screen Fuld skærm &Erase search history &Slet søgehistorik sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Sorter efter dato fra ældste til nyeste sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Sorter efter dato fra nyeste til ældste Show Query Details Vis Detaljer i forespørgsel Show results as table Vis resultater som tabel &Rebuild index &Genopbyg indeks &Show indexed types &Vis indekserede typer Shift+PgUp Shift+PgUp &Indexing schedule &Tidsplan for Indeksering E&xternal index dialog E&ksterne indekser &Index configuration &Konfiguration for Indeks &GUI configuration &Konfiguration for GUI &Results &Resultater Sort by date, oldest first Sorter efter dato, ældste først Sort by date, newest first Sorter efter dato, nyeste først Show as table Vis som tabel Show results in a spreadsheet-like table Vis resultater i en regneark-lignende tabel Save as CSV (spreadsheet) file Gem som CSV (regneark) fil Saves the result into a file which you can load in a spreadsheet Gemmer resultatet i en fil, som du kan indlæse i et regneark Next Page Næste side Previous Page Forrige side First Page Første side Query Fragments Forespørgsel efter fragmenter With failed files retrying Forsøg igen med filer der mislykkedes Next update will retry previously failed files Næste opdatering vil igen forsøge med filer, der tidligere mislykkedes Save last query Gem sidste forespørgsel Load saved query Indlæs gemte forespørgsel Special Indexing Særlig indeksering Indexing with special options Indeksering med særlige indstillinger Indexing &schedule Tid&splan for Indeksering Enable synonyms Aktiver synonymer &View &Vis Missing &helpers Manglende &hjælpere Indexed &MIME types Indekserede &MIME-typer Index &statistics Indeks&statistik Webcache Editor Rediger webcache Trigger incremental pass Udløser trinvis passering E&xport simple search history E&ksporter simpel søgehistorik Use default dark mode Brug standard mørk tilstand Dark mode Mørk tilstand &Query &Forespørgsel Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Filtrer datoer Assisted complex search Filter birth dates RclTrayIcon Restore Gendan Quit Afslut RecollModel Abstract Sammendrag Author Forfatter Document size Dokumentets størrelse Document date Dokumentets dato File size Filstørrelse File name Filnavn File date Fildato Ipath Ipath Keywords Nøgleord Mime type Mime- type Original character set Originale tegnsæt Relevancy rating Relevans bedømmelse Title Titel URL URL Mtime Mtid Date Dato Date and time Dato og tid Ipath Ipath MIME type MIME-type Can't sort by inverse relevance Kan't sortere efter omvendt relevans ResList Result list Resultatliste Unavailable document Dokument ikke tilgængelig Previous Forrige Next Næste <p><b>No results found</b><br> <p><b>Ingen resultater fundet</b><br> &Preview &Forhåndsvisning Copy &URL Kopier &URL Find &similar documents Find &lignende dokumenter Query details Detaljer i Forespørgsel (show query) (vis forespørgsel) Copy &File Name Kopier &Filnavn filtered filtreret sorted sorteret Document history Dokumenthistorik Preview Forhåndsvisning Open Åbn <p><i>Alternate spellings (accents suppressed): </i> <p><i>Alternative stavemåder (accenter undertrykt): </i> &Write to File &Skriv til fil Preview P&arent document/folder Forhåndsvis &Forælderdokument/mappe &Open Parent document/folder &Åbn Forælderdokument/mappe &Open &Åbn Documents Dokumenter out of at least ud af mindst for til <p><i>Alternate spellings: </i> <p><i>Alternative stavemåder: </i> Open &Snippets window Åbn vindue til &tekststumper Duplicate documents Identiske dokumenter These Urls ( | ipath) share the same content: Disse webadresser ( | ipath) deler samme indhold: Result count (est.) Optælling af resultat (est.) Snippets Tekststumper This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Nulstil sortering &Delete column &Slet kolonne Add " Tilføj " " column " kolonne Save table to CSV file Gem tabel til CSV-fil Can't open/create file: Kan ikke åbne/oprette fil: &Preview &Forhåndsvisning &Open &Åbn Copy &File Name Kopier &Filnavn Copy &URL Kopier &URL &Write to File &Skriv til fil Find &similar documents Find &lignende dokumenter Preview P&arent document/folder Forhåndsvis &Forælderdokument/mappe &Open Parent document/folder &Åbn Forælderdokument/mappe &Save as CSV &Gem som CSV Add "%1" column Tilføj "%1" kolonne Result Table Resultattabel Open Åbn Open and Quit Åbn og afslut Preview Forhåndsvisning Show Snippets Vis Stumper Open current result document Åbn det aktuelle resultatdokument Open current result and quit Åbn aktuelle resultat og afslut Show snippets Vis snippets Show header Vis overskrift Show vertical header Vis lodret header Copy current result text to clipboard Kopiér nuværende resultattekst til udklipsholder Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Forhåndsvisning &Open &Åbn Copy &File Name Kopier &Filnavn Copy &URL Kopier &URL &Write to File &Skriv til fil Find &similar documents Find &lignende dokumenter Preview P&arent document/folder Forhåndsvis &Forælderdokument/mappe &Open Parent document/folder &Åbn Forælderdokument/mappe ResultPopup &Preview &Forhåndsvisning &Open &Åbn Copy &File Name Kopier &Filnavn Copy &URL Kopier &URL &Write to File &Skriv til fil Save selection to files Gem det valgte til filer Preview P&arent document/folder Forhåndsvis &Forældre-dokument/mappe &Open Parent document/folder &Åbn Forældre-dokument/mappe Find &similar documents Find &lignende dokumenter Open &Snippets window Åbn vindue til &tekststumper Show subdocuments / attachments Vis underdokumenter / vedhæftede filer Open With Åbn med Run Script Kør skript SSearch Any term Vilkårlig ord All terms Alle ord File name Filnavn Completions Afslutninger Select an item: Vælg et element: Too many completions For mange færdiggørelser Query language Forespørgselssprog Bad query string Forkert forespørgselsstreng Out of memory Ikke mere hukommelse Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Indtast forespørgselssprogudtryk. Snydeark:<br> <i>ord1 ord2</i> : 'ord1' og 'ord2' i et hvilken som helst felt.<br> <i>felt:ord1</i> : 'ord1' i feltet 'felt'.<br> Standard feltnavne/synonymer:<br> titel/emne/billedtekst, forfatter/fra, modtager/til, filnavn, ekst.<br> Pseudofelter: dir, mime/format, type/rclcat, dato.<br> To datointerval-eksempler: 2009-03-01/2009-05-20 2009-03-01/P2M:<br>. <i>ord1 ord2 ELLER ord3</i>: ord1 OG (ord2 ELLER ord3).<br> Ingen egentlige parenteser er tilladt.<br> <i>"ord1 ord2"</i> : frase (skal forekomme nøjagtigt). Mulige modifikatorer:<br> <i>"ord1 ord2"p </i> : uordnet nærheds-søgning med standard afstand.<br> Brug <b>Vis Forespørgsel</b> link når i tvivl om resultatet og se manual (&lt;F1>) for flere detaljer. Enter file name wildcard expression. Indtast filnavn jokertegn udtryk. Enter search terms here. Type ESC SPC for completions of current term. Indtast søgeord her. Tast ESC SPC for færdiggørelse af nuværende ord. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Indtast forespørgselssprogets udtryk. Snydeark:<br> <i>ord1 ord2</i> : 'ord1' og 'ord2' i et hvilken som helst felt.<br> <i>felt:ord1</i> : 'ord1' i feltet 'felt'.<br> Standard feltnavne/synonymer:<br> titel/emne/billedtekst, forfatter/fra, modtager/til, filnavn, ekst.<br> Pseudofelter: dir, mime/format, type/rclcat, dato, størrelse.<br> To datointerval-eksempler: 2009-03-01/2009-05-20 2009-03-01/P2M:<br>. <i>ord1 ord2 OR ord3</i>: ord1 AND (ord2 OR ord3).<br> Du kan bruge parenteser for at gøre tingene klarere.<br> <i>"ord1 ord2"</i> : frase (skal forekomme nøjagtigt). Mulige modifikatorer:<br> <i>"ord1 ord2"p </i> : uordnet nærheds-søgning med standard afstand.<br> Brug <b>Vis Forespørgsel</b> link når i tvivl om resultatet og se manual (&lt;F1>) for flere detaljer. Stemming languages for stored query: Ordstammer til sprogene for gemte forespørgsel: differ from current preferences (kept) adskiller sig fra de nuværende præferencer (beholdt) Auto suffixes for stored query: Automatiske suffikser for gemte forespørgsel: External indexes for stored query: Eksterne Indekser for gemte forespørgsel: Autophrase is set but it was unset for stored query Autofrase er aktiveret, men var deaktiveret for gemte forespørgsel Autophrase is unset but it was set for stored query Autofrase er deaktiveret, men var aktiveret for gemte forespørgsel Enter search terms here. Indtast søgeord her. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid sort border-collapse: collapse; kollaps: kollaps } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Forespørgsel sprog snydeplade. Klik på <b>Vis forespørgsel</b>.&nbsp; You should really look at the manual (F1)</p> Du bør virkelig se på manualen (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Hvad</th><th>Eksempler</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Og</td><td>en to&nbsp;&nbsp;&nbsp;en OG to&nbsp;&nbsp;&nbsp;en && to</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Eller</td><td>en ELLER to&nbsp;&nbsp;&nbsp;en og to</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Kompleks boolean. ELLER har prioritet, brug parenteser&nbsp; where needed</td><td>(one AND two) OR three</td></tr> om nødvendigt</td><td>(et OG to) ELLER tre</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Ikke</td><td>term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Sætning</td><td>"stolthed og prejudice"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Ubestilt prox. (default slack=10)</td><td>"fordomme&nbsp;stolthed"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Ingen stamudvidelse: kapitaliserer</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Feltspecifik</td><td>forfatter: austen&nbsp;&nbsp;titel:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>ELLER inde i feltet</td><td>forfatter:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Feltnavne</td><td>title/subject/caption&nbsp;&nbsp;forfatter/fra<br>modtager/til&nbsp;&nbsp;filnavn&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Mappe sti filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Dato intervaller</td><td>dato:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> dato:2018&nbsp;&nbsp;dato:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Kan't åbne indeks Could not restore external indexes for stored query:<br> Kunne ikke gendanne eksterne indekser for lagret forespørgsel:<br> ??? ??? Using current preferences. Brug af aktuelle indstillinger. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSøgeBase Clear Ryd Ctrl+S Ctrl+S Erase search entry Slet søgeindgang Search Søg Start query Start forespørgsel Enter search terms here. Type ESC SPC for completions of current term. Indtast søgeord her. Type ESC SPC for færdiggørelse af nuværende ord. Choose search type. Vælg søgetype. Show query history Vis forespørgsel historik Enter search terms here. Indtast søgeord her. Main menu Hovedmenu SearchClauseW SearchClauseW SearchClauseW Any of these Enhver af disse All of these Alle disse None of these Ingen af disse This phrase Denne sætning Terms in proximity Vilkår i nærheden File name matching Fil navn matcher Select the type of query that will be performed with the words Vælg den type forespørgsel, der vil blive udført med ordene Number of additional words that may be interspersed with the chosen ones Antal yderligere ord, der kan være blandet med de udvalgte In field I felt No field Intet felt Any Vilkårlig All Alle None Ingen Phrase Frase Proximity Nærhed File name Filnavn Snippets Snippets Tekststumper X X Find: Find: Next Næste Prev Forrige SnippetsW Search Søg <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Desværre blev der ikke, inden for rimelige grænser, fundet en nøjagtig match. Sandsynligvis fordi dokumentet er meget stort, så tekststump-generatoren for vild i mængden...</ p> Sort By Relevance Sorter Efter Relevans Sort By Page Sortér Efter Side Snippets Window Tekstumper Vindue Find Find Find (alt) Find (alt) Find Next Find Næste Find Previous Find Forrige Hide Skjul Find next Find næste Find previous Find forrige Close window Luk vindue SortForm Date Dato Mime type Mime- type SortFormBase Sort Criteria Sorteringskriterier Sort the Sortér most relevant results by: mest relevante resultater ved: Descending Faldende Close Luk Apply Anvend SpecIdxW Special Indexing Særlig indeksering Do not retry previously failed files. Forsøg ikke igen med filer, der tidligere mislykkedes. Else only modified or failed files will be processed. Ellers vil kun ændrede eller mislykkede filer blive behandlet. Erase selected files data before indexing. Slet udvalgte filers data, før indeksering. Directory to recursively index Mappe til rekursivt indeks Browse Gennemse Start directory (else use regular topdirs): Startmappe (ellers brug de regulære øverste mapper): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Lad stå tomt for at vælge alle filer. Du kan bruge adskillige mellemrums-adskilte shell-type mønstre.<br>Mønstre med indlejrede mellemrum skal citeres med dobbelte anførselstegn.<br>Kan kun bruges, hvis startmålet er angivet. Selection patterns: Mønstre for udvælgelse: Top indexed entity Top indekserede enhed Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Mappe for rekursiv indeksering. Dette skal være indenfor det regulære indekserede område<br> som defineret i konfigurationsfilen (øverste mapper). Retry previously failed files. Forsøg tidligere mislykkede filer. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start mappe. Skal være en del af det indekserede træ. Vi bruger topdirs hvis tom. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Start mappe. Skal være en del af det indekserede træ. Brug hele indekseret område hvis tomt. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Søg efter ord &Expand &Udvid Alt+E Alt+E &Close &Luk Alt+C Alt+C Term Ord No db info. Ingen dbinfo. Doc. / Tot. Dok. / Tot. Match Sammenlign Case Stor/Små bogstaver Accents Accenter SpellW Wildcards Jokertegn Regexp Regex Spelling/Phonetic Stavning/Fonetisk Aspell init failed. Aspell not installed? Aspell init mislykkedes. Aspell ikke installeret? Aspell expansion error. Aspell udvidelsesfejl. Stem expansion Udvidelse af stamme error retrieving stemming languages fejl under hentning af ordstammer for sprogene No expansion found Ingen udvidelse fundet Term Ord Doc. / Tot. Dok. / Tot. Index: %1 documents, average length %2 terms Indeks: %1 dokumenter, gennemsnitlig længde %2 led Index: %1 documents, average length %2 terms.%3 results Index: %1 dokumenter, gennemsnitslængde %2 ord %3 resultater %1 results %1 resultater List was truncated alphabetically, some frequent Liste blev afkortet alfabetisk, nogle ofte terms may be missing. Try using a longer root. Der kan mangle ord. Prøv at bruge en længere rod. Show index statistics Vis statistik for indeks Number of documents Antal dokumenter Average terms per document Gennemsnitlige ord pr dokument Smallest document length Mindste dokumentlængde Longest document length Længste dokumentlængde Database directory size Mappestørrelse for database MIME types: MIME-typer: Item Element Value Værdi Smallest document length (terms) Mindste dokumentlængde (ord) Longest document length (terms) Længste dokumentlængde (ord) Results from last indexing: Resultater fra sidste indeksering: Documents created/updated Dokumenter oprettet/opdateret Files tested Filer testet Unindexed files ikke-indekserede filer List files which could not be indexed (slow) List filer som ikke kunne indekseres (langsom) Spell expansion error. Spell ekspansionsfejl. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Den valgte mappe synes ikke at være et Xapianindeks This is the main/local index! Dette er hoved/lokal indekset! The selected directory is already in the index list Den valgte mappe er allerede i indekslisten Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Vælg xapian index directory (ie: /home/buddy/.recoll/xapiandb) error retrieving stemming languages fejl under hentning af ordstammer for sprogene Choose Vælg Result list paragraph format (erase all to reset to default) Afsnitformat for resultatliste (slet alt for at nulstille til standard) Result list header (default is empty) Overskrift for resultatliste (standard er tom) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Vælg recoll konfigmappe eller xapian indeksmappe (f.eks: /home/me/.recoll eller /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Den valgte mappe ligner en Recoll konfigurationmappe, men konfigurationen kunne ikke læses At most one index should be selected Der burde vælges højst et indeks Cant add index with different case/diacritics stripping option Kan ikke tilføje indeks med en anden indstilling for fjernelse af store-bogstaver/diakritiske tegn Default QtWebkit font Standard skrifttype for QtWebkit Any term Vilkårlig ord All terms Alle ord File name Filnavn Query language Forespørgselssprog Value from previous program exit Værdi fra tidligere programafslutning Context Kontekst Description Varebeskrivelse Shortcut Genvej Default Standard Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface brugergrænseflade Number of entries in a result page Antal indgange i en resultatside Result list font Skrifttype for resultatliste Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Åbner et vindue til at vælge resultatlistens skrifttype Reset Nulstil Resets the result list font to the system default Nulstiller resultatlistens skrifttype til systemets standard Auto-start simple search on whitespace entry. Autostart simpel søgning ved blanktegn. Start with advanced search dialog open. Start med åbent avanceret søgevindue. Start with sort dialog open. Start med sorteringsdialog åben. Search parameters Søgeparametre Stemming language Ordstammer for sprog Dynamically build abstracts Lav dynamisk sammendrag Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Skal vi forsøge at lave sammendrag af indgange til resultatliste ved at bruge sammenhænget med forespørgselsordene? Kan være langsomt for store dokumenter. Replace abstracts from documents Erstat sammendrag fra dokumenter Do we synthetize an abstract even if the document seemed to have one? Skal vi sammenfatte et sammendrag, selvom dokumentet synes at have et? Synthetic abstract size (characters) Størrelse på det genererede sammendrag (tegn) Synthetic abstract context words Sammenhængende ord for det genererede sammendrag External Indexes Eksterne Indekser Add index Tilføj index Select the xapiandb directory for the index you want to add, then click Add Index Vælg xapiandb-mappen for det indeks, du vil tilføje, og klik derefter på Tilføj indeks Browse Gennemse &OK &Ok Apply changes Anvend ændringer &Cancel &Annuller Discard changes Kassere ændringer Result paragraph<br>format string Resultat afsnit<br>format streng Automatically add phrase to simple searches Tilføj automatisk frase til simple søgninger A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. En søgning efter [Rullende Sten] (2 ord) vil blive ændret til [rullende eller sten eller (rullende frase 2 sten)]. Dette skulle give højere forrang til resultaterne, hvor søgeordene vises nøjagtigt som angivet. User preferences Brugerindstillinger Use desktop preferences to choose document editor. Brug desktop-præferencer til at vælge dokument-editor. External indexes Eksterne indekser Toggle selected Skift det valgte Activate All Aktiver alle Deactivate All Deaktiver alle Remove selected Fjern valgte Remove from list. This has no effect on the disk index. Fjern fra listen. Dette har ingen virkning på indeks på disken. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definerer formatet for hvert resultat liste afsnit. Brug qt html format og printf-lignende erstatninger:<br>%A Resumé<br> %D Dato<br> %I Ikon billede navn<br> %K Søgeord (hvis nogen)<br> %L Forhåndsvisning og Rediger links<br> %M Mime type<br> %N Resultat nummer<br> %R Relevans procentdel<br> %S Størrelsesinformation<br> %T Afsnit<br> %U Url<br> Remember sort activation state. Husk sorteringens aktiveringstilstand. Maximum text size highlighted for preview (megabytes) Maksimal tekststørrelse der fremhæves for forhåndsvisning (megabyte) Texts over this size will not be highlighted in preview (too slow). Tekster over denne størrelse vil ikke blive fremhævet i forhåndsvisning (for langsom). Highlight color for query terms Farve for fremhævning af søgeord Prefer Html to plain text for preview. Foretræk Html til almindelig tekst for forhåndsvisning. If checked, results with the same content under different names will only be shown once. Afkryds forårsager, at resultater med samme indhold under forskellige navne kun bliver rapporteret en gang. Hide duplicate results. Skjul identiske resultater. Choose editor applications Vælg redigeringsprogrammer Display category filter as toolbar instead of button panel (needs restart). Vis kategorifilter som værktøjslinje i stedet for knappanelet (kræver genstart). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Ordene på listen bliver automatisk vendt til ext: xxx sætninger i forespørgselssprogets indgang. Query language magic file name suffixes. Forespørgselssprogets magiske filnavnendelser. Enable Aktiver ViewAction Changing actions with different current values Ændring af handlinger med forskellige aktuelle værdier Mime type Mime- type Command Kommando MIME type MIME-type Desktop Default Desktop standard Changing entries with different current values Ændrer indgange med forskellige aktuelle værdier ViewActionBase File type Filtype Action Handling Select one or several file types, then click Change Action to modify the program used to open them Vælg en eller flere filtyper, og klik derefter på Skift handling for at ændre det program, der bruges til at åbne dem Change Action Skift Handling Close Luk Native Viewers Oprindelige fremvisere Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Vælg en eller flere mime typer og klik derefter på "Skift handling"<br>Du kan også lukke denne dialog og tjekke "Brug desktop præferencer"<br>i hovedpanelet for at ignorere denne liste og bruge din desktop standardindstillinger. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Vælg en eller flere Mime-typer og brug derefter knapperne i bundrammen til at ændre, hvordan de behandles. Use Desktop preferences by default Brug indstillinger for Desktop som standard Select one or several file types, then use the controls in the frame below to change how they are processed Vælg en eller flere filtyper, og brug derefter knapperne i rammen nedenfor for at ændre, hvordan de behandles Exception to Desktop preferences Undtagelse til indstillinger for Desktop Action (empty -> recoll default) Handling (tom -> recoll standard) Apply to current selection Anvend på aktuelle udvalg Recoll action: Recoll handling: current value aktuelle værdi Select same Vælg det samme <b>New Values:</b> <b>Nye værdier:</b> Webcache Webcache editor Rediger webcache Search regexp Regex søgning TextLabel WebcacheEdit Copy URL Kopier URL Unknown indexer state. Can't edit webcache file. Indeksering i ukendt tilstand. Kan ikke redigere webcachefil. Indexer is running. Can't edit webcache file. Indeksering kører. Kan ikke redigere webcachefil. Delete selection Slet det valgte Webcache was modified, you will need to run the indexer after closing this window. WebCache blev ændret, du er nød til at køre indeksering efter lukning af dette vindue. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url URL Date Dato Size URL URL WinSchedToolW Error Fejl Configuration not initialized Konfiguration ikke initialiseret <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Rekoll indeksering batch scheduling</h3><p>Vi bruger standard Windows task scheduler til dette. Programmet vil blive startet, når du klikker på knappen nedenfor.</p><p>Du kan bruge enten den fulde grænseflade (<i>Opret opgave</i> i menuen til højre), eller den forenklede <i>Opret grundlæggende opgave</i> guide. I begge tilfælde Kopier/Indsæt batchfilstien nedenfor som <i>Handling</i> der skal udføres.</p> Command already started Kommando allerede startet Recoll Batch indexing Rekoll Batch indeksering Start Windows Task Scheduler tool Start Windows Opgavestyring værktøj Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Stjæl Beagle indeksering kø Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle SKAL IKKE køre. Aktiverer behandling af beagle køen til at indeksere Firefox webhistorik.<br>(du bør også installere Firefox Beagle plugin) Web cache directory name Web-cache mappenavn The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Navnet på en mappe hvor cachen for besøgte websider skal gemmes.<br>En ikke-absolut sti er taget i forhold til konfigurationsmappen. Max. size for the web cache (MB) Maks. størrelse for web cache (MB) Entries will be recycled once the size is reached Indgangene vil blive genbrugt, når størrelsen er nået Web page store directory name Mappenavn for lageret til Websider The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Navnet på en mappe hvor du vil gemme kopier af besøgte websider.<br>En relativ sti er taget i forhold til konfigurationsmappen. Max. size for the web store (MB) Max. størrelse til web-lager (MB) Process the WEB history queue Behandl køen for WEB-historik Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Aktiverer indeksering af sider besøgt af Firefox.<br>(Du skal også installere Firefox Recoll plugin) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Indgangene vil blive genbrugt, når størrelsen er nået.<br>Kun en øgning af størrelsen giver god mening, da en reducering af værdien ikke vil afkorte en eksisterende fil (kun spildplads i slutningen). confgui::ConfIndexW Can't write configuration file Kan ikke skrive konfigurationsfil Recoll - Index Settings: Rekoll - Indeks Indstillinger: confgui::ConfParamFNW Browse Gennemse Choose Vælg confgui::ConfParamSLW + + - - Add entry Tilføj post Delete selected entries Slet valgte poster ~ ~ Edit selected entries Rediger valgte poster confgui::ConfSearchPanelW Automatic diacritics sensitivity Automatisk følsomhed over for diakritiske tegn <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Udløser automatisk følsomhed over for diakritiske tegn, hvis søgeordet har accent tegn (ikke i unac_except_trans). Ellers er du nød til bruge forespørgselssproget og <i>D</i> modifikatoren, for at angive følsomhed over for diakritiske tegn. Automatic character case sensitivity Automatisk følsomhed over for store/små bogstaver <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Udløser automatisk følsomhed over for store/små bogstaver, hvis indgangen har store bogstaver i andet end den første position. Ellers er du nød til bruge forespørgselssproget og <i>C</i> modifikatoren, for at angive følsomhed over for store/små bogstaver. Maximum term expansion count Maksimale antal ordudvidelser <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maksimal antal udvidelser-for et enkelt ord (fx: når der bruges jokertegn). Standarden på 10 000 er rimeligt og vil undgå forespørgsler, der synes at fryse mens motoren arbejder sig igennem ordlisten. Maximum Xapian clauses count Maksimale antal Xapiansætninger <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maksimalt antal grundlæggende sætninger vi føjer til en enkel Xapian forespørgsel. I nogle tilfælde kan resultatet af ordudvidelse være multiplikativ, og vi ønsker at undgå at bruge overdreven hukommelse. Standarden på 100 000 bør være både høj nok i de fleste tilfælde og kompatibel med de nuværende typiske hardware konfigurationer. confgui::ConfSubPanelW Global Globalt Max. compressed file size (KB) Maks. komprimeret filstørrelse (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Denne værdi angiver en grænse for, hvornår komprimerede filer ikke vil blive behandlet. Indstil til -1 for ingen grænse, til 0 for ingen dekomprimering nogensinde. Max. text file size (MB) Maks. størrelse på tekstfil (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Denne værdi angiver en grænse for, hvornår tekstfiler ikke vil blive behandlet. Indstil til -1 for ingen grænse. Dette er for at udelukke monster logfiler fra indekset. Text file page size (KB) Sidestørrelse på tekstfil (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Hvis denne værdi er angivet (ikke lig med -1), vil tekstfiler opdeles i bidder af denne størrelse for indeksering. Dette vil hjælpe søgning i meget store tekstfiler (dvs.: log-filer). Max. filter exec. time (S) Maks. udførelsestid for filtre (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Eksterne filtre, der virker længere end dette, vil blive afbrudt. Dette er for det sjældne tilfælde (ie: postscript) hvor et dokument kan få et filter til at loopSet til -1 for ingen grænse. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Eksterne filtre der arbejder længere end dette vil blive afbrudt. Dette er for det sjældne tilfælde (dvs.: postscript) hvor et dokument kan forårsage, at et filter laver et loop. Indstil til -1 for ingen grænse. Only mime types Kun mime-typer An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive En eksklusiv liste over indekserede MIME-typer.<br>Intet andet vil blive indekseret. Normalt tom og inaktiv Exclude mime types Udeluk mime-typer Mime types not to be indexed Mime-typer der ikke skal indekseres Max. filter exec. time (s) Maks. filter eksekveringstid (s) confgui::ConfTopPanelW Top directories Øverste mapper The list of directories where recursive indexing starts. Default: your home. Listen over mapper hvor rekursiv indeksering starter. Standard: din hjemme-mappe (home). Skipped paths Udeladte stier These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Dette er navne på mapper, som indeksering ikke går ind i.<br>Kan indeholde jokertegn. Skal stemme overens med stierne, som de ses af indekseringsprogrammet (dvs. hvis de øverste mapper omfatter '/home/mig' og '/home' er et link til '/usr/home', en korrekt udeladtSti indgang ville være '/home/mig/tmp * ', ikke '/usr/home/mig/tmp * ') Stemming languages Ordstammer for sprogene The languages for which stemming expansion<br>dictionaries will be built. De sprog, hvor ordstamme-udvidelses<br>ordbøger vil blive bygget. Log file name Navn på logfil The file where the messages will be written.<br>Use 'stderr' for terminal output Filen hvor meddelelser vil blive skrevet.<br>Brug 'stderr' for terminal output Log verbosity level Log informationsniveau This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Denne værdi justerer mængden af meddelelser,<br>fra kun fejl til en masse fejlretningsdata. Index flush megabytes interval Megabyte interval for skrivning af Index This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Denne værdi justere mængden af data, der er indekseret mellem skrivning til disken.<br>Dette hjælper med at kontrollere indekseringsprogrammets brug af hukommelse. Standard 10MB Max disk occupation (%) Maks brug af disk (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Dette er den procentdel af diskforbrug hvor indeksering vil mislykkes, og stoppe (for at undgå at fylde dit disk).<br>0 betyder ingen grænse (dette er standard). No aspell usage Brug ikke aspell Aspell language Aspell sprog The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Sproget for aspell ordbog. Dette skal ligne 'da' eller 'fr' . .<br>Hvis denne værdi ikke er angivet, vil NLS- miljøet blive brugt til at beregne den, som normalt virker. o få en idé om, hvad der er installeret på dit system, type 'aspell config' og lede efter . på filer inde i 'data-dir' mappe. Database directory name Databasens mappenavn The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Navnet på en mappe hvor indekset<br>En ikke-absolut sti skal gemmes i forhold til konfigurationsmappen. Standard er 'xapiandb'. Use system's 'file' command Brug system's 'fil' kommando Use the system's 'file' command if internal<br>mime type identification fails. Brug systemet's 'fil' kommando hvis intern<br>mime type identifikation mislykkes. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Deaktiver brug af aspell til at generere stavnings-tilnærmelse i værktøj for søgning efter ord. <br> Nyttigt hvis aspell er fraværende eller ikke virker. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Sproget for aspell ordbog. Det skal se ud som "en" eller "fr" ...<br>Hvis denne værdi ikke er angivet, så vil NLS omgivelser blive brugt til at finde det, det fungerer normalt. For at få en idé om, hvad der er installeret på dit system, kan du skrive 'aspell konfig "og se efter .dat filer inde i 'data-dir' mappen. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Navnet på en mappe hvor du vil gemme indekset<br>En relativ sti er taget i forhold til konfigurationsmappen. Standard er "xapiandb. Unac exceptions Unac-undtagelser <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Disse er undtagelser fra unac mekanismen, der, som standard, fjerner alle diakritiske tegn, og udfører kanonisk nedbrydning. Du kan tilsidesætte fjernelse af accent for nogle tegn, afhængigt af dit sprog, og angive yderligere nedbrydninger, f.eks. for ligaturer. I hver indgang adskilt af mellemrum, er det første tegn kildedelen, og resten er oversættelsen. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Disse er stinavne på mapper som indeksering ikke vil komme ind.<br>Sti elementer kan indeholde jokerkort. Indgangene skal matche stierne set af indekseringen (f.eks. hvis topdirs omfatter '/home/me' og '/home' er faktisk et link til '/usr/home', en korrekt sprunget ind i stien ville være '/home/me/tmp*', ikke '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Maks. diskbelægning (%, 0 betyder ingen grænse) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Dette er procentdelen af diskforbrug - samlet diskforbrug, ikke indeksstørrelse - hvor indeksering vil mislykkes og stoppe.<br>Standardværdien på 0 fjerner enhver grænse. uiPrefsDialogBase User preferences Brugerindstillinger User interface brugergrænseflade Number of entries in a result page Antal indgange i en resultatside If checked, results with the same content under different names will only be shown once. Afkryds forårsager, at resultater med samme indhold under forskellige navne kun bliver rapporteret en gang. Hide duplicate results. Skjul identiske resultater. Highlight color for query terms Farve for fremhævning af søgeord Result list font Skrifttype for resultatliste Opens a dialog to select the result list font Åbner et vindue til at vælge resultatlistens skrifttype Helvetica-10 Helvetica-10 Resets the result list font to the system default Nulstiller resultatlistens skrifttype til systemets standard Reset Nulstil Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definerer formatet for hvert resultat liste afsnit. Brug qt html format og printf-lignende erstatninger:<br>%A Resumé<br> %D Dato<br> %I Ikon billede navn<br> %K Søgeord (hvis nogen)<br> %L Forhåndsvisning og Rediger links<br> %M Mime type<br> %N Resultat nummer<br> %R Relevans procentdel<br> %S Størrelsesinformation<br> %T Afsnit<br> %U Url<br> Result paragraph<br>format string Resultat afsnit<br>format streng Texts over this size will not be highlighted in preview (too slow). Tekster over denne størrelse vil ikke blive fremhævet i forhåndsvisning (for langsom). Maximum text size highlighted for preview (megabytes) Maksimal tekststørrelse der fremhæves for forhåndsvisning (megabyte) Use desktop preferences to choose document editor. Brug desktop-præferencer til at vælge dokument-editor. Choose editor applications Vælg redigeringsprogrammer Display category filter as toolbar instead of button panel (needs restart). Vis kategorifilter som værktøjslinje i stedet for knappanelet (kræver genstart). Auto-start simple search on whitespace entry. Autostart simpel søgning ved blanktegn. Start with advanced search dialog open. Start med åbent avanceret søgevindue. Start with sort dialog open. Start med sorteringsdialog åben. Remember sort activation state. Husk sorteringens aktiveringstilstand. Prefer Html to plain text for preview. Foretræk Html til almindelig tekst for forhåndsvisning. Search parameters Søgeparametre Stemming language Ordstammer for sprog A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. En søgning efter [Rullende Sten] (2 ord) vil blive ændret til [rullende eller sten eller (rullende frase 2 sten)]. Dette skulle give højere forrang til resultaterne, hvor søgeordene vises nøjagtigt som angivet. Automatically add phrase to simple searches Tilføj automatisk frase til simple søgninger Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Skal vi forsøge at lave sammendrag af indgange til resultatliste ved at bruge sammenhænget med forespørgselsordene? Kan være langsomt for store dokumenter. Dynamically build abstracts Lav dynamisk sammendrag Do we synthetize an abstract even if the document seemed to have one? Skal vi sammenfatte et sammendrag, selvom dokumentet synes at have et? Replace abstracts from documents Erstat sammendrag fra dokumenter Synthetic abstract size (characters) Størrelse på det genererede sammendrag (tegn) Synthetic abstract context words Sammenhængende ord for det genererede sammendrag The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Ordene på listen bliver automatisk vendt til ext: xxx sætninger i forespørgselssprogets indgang. Query language magic file name suffixes. Forespørgselssprogets magiske filnavnendelser. Enable Aktiver External Indexes Eksterne Indekser Toggle selected Skift det valgte Activate All Aktiver alle Deactivate All Deaktiver alle Remove from list. This has no effect on the disk index. Fjern fra listen. Dette har ingen virkning på indeks på disken. Remove selected Fjern valgte Click to add another index directory to the list Klik for at tilføje en anden indeksmappe til listen Add index Tilføj index Apply changes Anvend ændringer &OK &Ok Discard changes Kassere ændringer &Cancel &Annuller Abstract snippet separator Separator mellem sammendragets tekststumper Use <PRE> tags instead of <BR>to display plain text as html. Brug <PRE> tags i stedet for <BR>til at vise almindelig tekst som html. Lines in PRE text are not folded. Using BR loses indentation. Linjer i PRE tekst er ikke foldet. Brug BR taber indrykning. Style sheet Stilark Opens a dialog to select the style sheet file Åbn et vindue for at vælge stilark-filen Choose Vælg Resets the style sheet to default Nulstil stilark til standard Lines in PRE text are not folded. Using BR loses some indentation. Linjer i PRE tekst er ikke foldet. Brug BR taber noget indrykning. Use <PRE> tags instead of <BR>to display plain text as html in preview. Brug <PRE> tags i stedet for <BR>til at vise almindelig tekst som html i forhåndsvisning. Result List Resultatliste Edit result paragraph format string Rediger formatstreng for resultatafsnit Edit result page html header insert Rediger kode for indsætnig i html-hoved for resultatside Date format (strftime(3)) Datoformat (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Hyppighedens procentvise tærskel, hvorover vi ikke bruger ord inde i autofrase. Hyppige ord er et stort problem for ydeevnen med fraser. Udeladte ord forøger frase stilstand, og reducere effektiviteten af autofrase. Standardværdien er 2 (procent). Autophrase term frequency threshold percentage Tærskelprocentsats for ordhyppighed ved autofrase Plain text to HTML line style Almindelig tekst til HTML linjetype Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Linjer i PRE tekst ombrydes ikke. Brug af BR mister en del indrykning. PRE + Wrap stil kunne være, hvad du ønsker. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + wrap Exceptions Undtagelser Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Mime typer, der ikke bør sendes til xdg-open selv når "Brug desktop præferencer" er indstillet.<br> Nyttigt at videregive sidetal og søgestrengsmuligheder til, f.eks. evince. Disable Qt autocompletion in search entry. Deaktiver Qt autofuldførelse i søgeindgange. Search as you type. Søg mens du skriver. Paths translations Oversættelser af stier Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Klik for at tilføje endnu en indeksmappe til listen. Du kan vælge enten en Recoll konfigurationsmappe eller et Xapianindeks. Snippets window CSS file CSS-fil for vindue til tekststumper Opens a dialog to select the Snippets window CSS style sheet file Åbner et vindue til at vælge CSS stilark-fil for vinduet til tekststumper Resets the Snippets window style Nulstil stilen for vinduet til tekststumper Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Bestemmer om dokumentfiltre er vist som radioknapper, værktøjslinje kombinationsfelt eller menu. Document filter choice style: Valgmetode for dokumentfilter: Buttons Panel Panel med knapper Toolbar Combobox værktøjslinje kombinationsfelt Menu Menu Show system tray icon. Vis statusikon. Close to tray instead of exiting. Luk til systembakke i stedet for at afslutte. Start with simple search mode Start med enkel søgetilstand Show warning when opening temporary file. Vis advarsel, når der åbnes en midlertidig fil. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Brugerstil der skal anvendes på vinduet til tekststumper.<br>Bemærk: Det færdige sidehoved-indstik er også inkluderet i tekststumper-vinduets hoved. Synonyms file Synonymer-fil Highlight CSS style for query terms Fremhæv CSS stil for forespørgeord Recoll - User Preferences Rekoll - Brugerindstillinger Set path translations for the selected index or for the main one if no selection exists. Angiv stioversættelser for det valgte indeks eller for det vigtigste, hvis der ikke findes et valg. Activate links in preview. Aktiver links i forhåndsvisning. Make links inside the preview window clickable, and start an external browser when they are clicked. Gør links inde i preview vinduet klikbare, og starte en ekstern browser, når de klikkes. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Forespørgsel vilkår fremhæve i resultater. <br>Måske prøve noget som "color:red;background:gul" for noget mere livligt end standard blå... Start search on completer popup activation. Start søgning ved fuldføre popup aktivering. Maximum number of snippets displayed in the snippets window Maksimalt antal snippets der vises i snippets vinduet Sort snippets by page number (default: by weight). Sorter snippets efter sidenummer (standard: efter vægt). Suppress all beeps. Undertryk alle bipper. Application Qt style sheet Programmets Qt- stilark Limit the size of the search history. Use 0 to disable, -1 for unlimited. Begræns størrelsen af søgehistorikken. Brug 0 til at deaktivere, - 1 for ubegrænset. Maximum size of search history (0: disable, -1: unlimited): Maksimal størrelse på søgehistorik (0: deaktiveret, -1: ubegrænset): Generate desktop notifications. Generer skrivebordsnotifikationer. Misc Diverse Work around QTBUG-78923 by inserting space before anchor text Arbejd omkring QTBUG-78923 ved at indsætte plads før ankertekst Display a Snippets link even if the document has no pages (needs restart). Vis et stumper link, selvom dokumentet ikke har nogen sider (kræver genstart). Maximum text size highlighted for preview (kilobytes) Maksimal tekststørrelse fremhævet for forhåndsvisning (kilobytes) Start with simple search mode: Start med enkel søgetilstand: Hide toolbars. Skjul værktøjslinjer. Hide status bar. Skjul statusbjælke. Hide Clear and Search buttons. Skjul Ryd og Søg knapper. Hide menu bar (show button instead). Skjul menulinjen (vis knap i stedet). Hide simple search type (show in menu only). Skjul simpel søgetype (kun i menuen). Shortcuts Genveje Hide result table header. Skjul resultattabel header. Show result table row headers. Vis rækkeoverskrifter i resultattabellen. Reset shortcuts defaults Nulstil genveje standarder Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Deaktivér genvejene Ctrl+[0-9]/[a-z] for at hoppe til tabelrækker. Use F1 to access the manual Brug F1 til at få adgang til manualen Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Simpel søgetype Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Mørk tilstand Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Resultattabel Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_ja.ts0000644000175000017500000050356714444307651014245 00000000000000 ActSearchDLG Menu search メニュー検索 AdvSearch All clauses 全ての条件 Any clause 任意の条件 media マルチメディア other その他 Bad multiplier suffix in size filter ファイルサイズフィルターのサフィックスが正しくありません text テキスト spreadsheet 表計算シート (spreadsheets) presentation プレゼンテーション message メール(メッセージ) texts テキスト spreadsheets 表計算シート (spreadsheets) Advanced Search 高度な検索 Load next stored search 次の保存済み検索の呼出し Load previous stored search 前の保存済み検索の呼出し AdvSearchBase Advanced search 高度な検索 Search for <br>documents<br>satisfying: Search for <br>documents<br>satisfying: Delete clause 条項を削除 Add clause 条項を追加 Restrict file types 制限付きファイルタイプ Check this to enable filtering on file types ファイル形式で検索するときはここにチェック By categories カテゴリー毎 Check this to use file categories instead of raw mime types チェックして、MIMEタイプの代わりにファイルカテゴリを使用する Save as default デフォルトとして保存 Searched file types 検索されたファイルタイプ All ----> 全て移動→ Sel -----> 選択移動→ <----- Sel ←選択移動 <----- All ←全て移動 Ignored file types 無視されたファイルタイプ Enter top directory for search 検索するTOPディレクトリを入力 Browse ブラウズ Restrict results to files in subtree: 結果のファイルをこのサブディレクトリツリーに制限します: Start Search 検索開始 Close 閉じる All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. 右側の空白でないフィールドはすべて、論理AND("全条件"オプション)または論理OR("任意条件"オプション)に従って結合されます。<br>“任意”“全部”和“无”三种字段类型都接受输入简单词语和双引号引用的词组的组合。<br>空的输入框会被忽略。 Invert 条件を反転 Minimum size. You can use k/K,m/M,g/G as multipliers 最小サイズ。サブツリー内のファイルへの乗数結果として k/K,m/M,g/G を使用できます Min. Size 最小サイズ Maximum size. You can use k/K,m/M,g/G as multipliers 最大サイズ。サブツリー内のファイルへの乗数結果として k/K,m/M,g/G を使用できます Max. Size 最大サイズ Filter フィルター From From To To Check this to enable filtering on dates チェックして、日付によるフィルタリングを有効にする Filter dates 日付でフィルター Find 検索 Check this to enable filtering on sizes チェックして、サイズによるフィルタリングを有効にする Filter sizes サイズでフィルター Filter birth dates ConfIndexW Can't write configuration file configuration を書き込めません Global parameters グローバル・パラメータ Local parameters ローカル・パラメータ Search parameters 検索パラメータ Top directories トップ・ディレクトリ The list of directories where recursive indexing starts. Default: your home. 再帰的なインデックス作成を開始するディレクトリ Default:your home. Skipped paths Skipped PATH These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Stemming languages Stemming languages Log file name Logファイル名 The file where the messages will be written.<br>Use 'stderr' for terminal output プログラムより出力されたメッセージは、このファイルに保存されます。<br>ターミナル出力は 'stderr' を使用 Log verbosity level Log冗長レベル This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. この値は、出力メッセージ量を<br>エラーのみ 〜 大量のデバッグデータまで調整します。 Index flush megabytes interval インデックス更新(MB 間隔) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB この値は、インデックスデータが少し蓄積された場合にのみデータをハードディスクに更新することです。 <br>インデックス作成プロセスのメモリ使用量を制御するために使用されます。 Default: 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. この値は、インデックス作成が失敗したときに停止するディスク使用量のパーセンテージです(これは合計ディスク使用量であり、インデックスサイズではありません)。 <br>デフォルト値の0は、すべての制限を削除します。 No aspell usage aspell を使用しない Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. エクスプローラーでaspellを使用して、同様のスペルの単語を生成することは禁止されています。 <br>このオプションは、aspellがインストールされていない場合、または正しく機能していない場合に使用します。 Aspell language Aspell语言 Database directory name Database ディレクトリ名 The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Unac exceptions Unac exceptions <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Web page store directory name Web page store directory name The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. 訪れたWebページのコピーを保存するディレクトリ名 <br>非絶対パスは、構成ディレクトリに対して相対的に取得されます。 Max. size for the web store (MB) Max. size for the web store (MB) Max. size for the web store (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Automatic diacritics sensitivity 大文字・小文字を自動判別 <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Automatic character case sensitivity 文字の大文字・小文字の区別を自動的に調整 <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. Maximum term expansion count Maximum term expansion count <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Maximum Xapian clauses count Xapian条項の最大数 <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Indexer log file name インデクサーログファイル名 If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Web history WEB履歴 Process the Web history queue Web履歴キューを処理 Note: old pages will be erased to make space for new ones when the maximum size is reached Note: 最大サイズに達すると、古いページが消去され、新しいページ用のスペースが確保されます (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types MIME形式のみ An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive インデックス付けされたmimeタイプの排他的なリスト。<br>他にインデックス付けされるものはありません。 通常は空白で非アクティブ Exclude mime types MEME形式を除外 Mime types not to be indexed MIMEタイプはインデックス登録されません Max. compressed file size (KB) 圧縮ファイルの最大サイズ(KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. この値は、それを超えると圧縮ファイルが処理されないしきい値を設定します。 制限がない場合は -1 に設定し、解凍がない場合は 0 に設定します。 Max. text file size (MB) 最大テキストファイルサイズ(MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. この値は、それを超えるとテキストファイルが処理されないしきい値を設定します。 制限なしの場合は -1 に設定します。 モンスターログファイルをインデックスから除外するためのものです。 Text file page size (KB) テキストファイル・頁サイズ(KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). この値が設定されている場合( -1 に等しくない場合)、テキストファイルはインデックス作成のためにこのサイズのチャンクに分割されます。 これは、非常に大きなテキストファイル(ログファイルなど)の検索に役立ちます。 Max. filter exec. time (s) 最大フィルター実行時間(秒) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. これより長く動作する外部フィルターは中止されます。 これは、ドキュメントによってフィルターがループする可能性があるまれなケース(つまり、ポストスクリプト)の場合です。 制限なしの場合は-1に設定します。 Global Global CronToolW Cron Dialog Cronダイアログ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Days of week (* or 0-7, 0 or 7 is Sunday) Hours (* or 0-23) 時間 (* or 0-23) Minutes (0-59) 分 (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> Enable 有効 Disable 無効 It seems that manually edited entries exist for recollindex, cannot edit crontab recoll-index用に手動で編集されたエントリが存在するようです。crontabを編集できません Error installing cron entry. Bad syntax in fields ? cronエントリのインストール中にエラーが発生しました。 フィールドの構文が間違っていませんか? EditDialog Dialog ダイアログ EditTrans Source path Source path Local path Local path Config error Config error Original path Original path EditTransBase Path Translations Path Translations Setting path translations for Setting path translations for Select one or several file types, then use the controls in the frame below to change how they are processed 1つまたは複数のファイルタイプを選択し、下のフレームのコントロールを使用して、それらの処理方法を変更します Add 追加 Delete 削除 Cancel キャンセル Save 保存 FirstIdxDialog First indexing setup 最初のインデックス設定 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> Indexing configuration インデックス設定 This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. これにより、インデックスを作成するディレクトリや、除外されたファイルのパスや名前、デフォルトの文字セットなどの他のパラメータを調整できます。 Indexing schedule インデックス・スケジュール This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). これにより、バッチインデックスとリアルタイムインデックスのどちらかを選択し、バッチインデックスの自動スケジュールを設定できます(cronを使用). Start indexing now 今すぐインデックス作成を開始 FragButs %1 not found. %1 が見つかりません。 %1: %2 %1: %2 Query Fragments Query 断片 IdxSchedW Index scheduling setup インデックス・スケジュール設定 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> Cron scheduling Cronスケジューリング The tool will let you decide at what time indexing should run and will install a crontab entry. このツールを使用すると、インデックス作成を実行するタイミングを決定し、crontabエントリをインストールできます。 Real time indexing start up リアルタイム・インデックス開始 Decide if real time indexing will be started when you log in (only for the default index). ログイン時にリアルタイムのインデックス作成を開始するかどうかを決定します(デフォルトのインデックスに対してのみ有効)。 ListDialog Dialog ダイアログ GroupBox グループボックス Main No db directory in configuration 環境設定にDatabaseディレクトリがありません "history" file is damaged, please check or remove it: "history"ファイルが損傷しています。確認もしくは削除してください。: Needs "Show system tray icon" to be set in preferences! Preview Cancel キャンセル Missing helper program: 補助プログラム(helper)が見つかりません: Can't turn doc into internal representation for ドキュメントを内部表現に変換できません Creating preview text プレビューテキストを作成 Loading preview text into editor プレビューテキストをエディターにロード中 &Search for: 検索(&S): &Next 次(&N) &Previous 前(&P) Clear 消去(Clear) Match &Case Match &Case Form Form Tab 1 Tab 1 Open 開く Canceled キャンセル Error loading the document: file missing. ドキュメント・ロードエラー:ファイルがありません。 Error loading the document: no permission. ドキュメント・ロードエラー:許可がありません。 Error loading: backend not configured. ロードエラー:backendが構成されていません。 Error loading the document: other handler error<br>Maybe the application is locking the file ? ドキュメント読み込みエラー:他ハンドラーのエラー<br>アプリケーションがファイルをロックしている可能性? Error loading the document: other handler error. ドキュメントの読み込みエラー:その他のハンドラーエラー. <br>Attempting to display from stored text. <br>保存されたテキストから表示中。 Could not fetch stored text 保存されたテキストを取得できません Previous result document 前の結果ドキュメント Next result document 次の結果ドキュメント Preview Window プレビュー・ウィンドウ Close tab タブを閉じる Close preview window プレビューウィンドウを閉じる Show next result 次の結果を表示 Show previous result 前の結果を表示 Print 印刷 PreviewTextEdit Show fields フィールド表示 Show main text 主テキストを表示 Print 印刷 Print Current Preview 現在のプレビューを印刷 Show image イメージを表示 Select All 全て選択 Copy コピー Save document to file ドキュメントをファイルに保存 Fold lines ワードラップ Preserve indentation インデントを維持 Open document ドキュメントを開く Reload as Plain Text Reload as HTML QObject <b>Customised subtrees <b>Customised subtrees The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Skipped names Skipped names These are patterns for file or directory names which should not be indexed. These are patterns for file or directory names which should not be indexed. Follow symbolic links Follow symbolic links Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Index all file names Index all file names Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Default<br>character set Default<br>character set Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Ignored endings Ignored endings These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. QWidget Create or choose save directory 保存ディレクトリを作成または選択 Choose exactly one directory 特定のディレクトリを選択 Could not read directory: ディレクトリを読み込めません: Unexpected file name collision, cancelling. 予期しないファイル名の衝突のためキャンセル。 Cannot extract document: ドキュメントを開けません: &Preview プレビュー(&P) &Open 開く(&O) Open With として開く Run Script Script実行 Copy &File Name ファイル名をコピー(&F) Copy &URL URLコピー(&U) &Write to File ファイルに書込み(&W) Save selection to files 選択したコンテンツをファイルに保存 Preview P&arent document/folder プレビュー 主ドキュメント/フォルダー(&a) Find &similar documents 類似ドキュメントを検出(&s) Open &Snippets window 断片ウィンドウを表示(&S) Show subdocuments / attachments サブドキュメント/添付ファイルを表示 &Open Parent document 親ドキュメントを開く(&O) &Open Parent Folder 親フォルダーを開く(&O) Copy Text コピー・テキスト Copy &File Path ファイル Pathをコピー( &F) Copy File Name コピー ファイル名 QxtConfirmationMessage Do not show again. Do not show again. RTIToolW Real time indexing automatic start リアルタイム・インデックス作成(autostart) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. デスクトップセッション開始時にインデックス・デーモンをStart. Also start indexing daemon right now. 今すぐインデックスデーモンを開始. Replacing: 置き換え: Replacing file ファイル置き換え Can't create: 作成不可: Warning 警告 Could not execute recollindex recollインデックスを実行できませんでした Deleting: 削除中: Deleting file ファイル削除 Removing autostart Autostart削除 Autostart file deleted. Kill current process too ? Autostartファイルが削除されました。現在のプロセスも停止しますか? RclCompleterModel Hits RclMain (no stemming) (no stemming) (all languages) (all languages) error retrieving stemming languages error retrieving stemming languages Indexing in progress: インデックス作成(進行中): Purge Purge Stemdb Stemdb Closing Closing Unknown 未知 Query results クエリ結果 Cannot retrieve document info from database データベースからドキュメント情報を取得できません Warning 警告 Can't create preview window プレビューウィンドウを作成できません This search is not active any more This search is not active any more Cannot extract document or create temporary file ドキュメントを開けません(または、一時ファイルを作成できません) Executing: [ 実行中: [ About Recoll Recoll について History data 履歴データ Document history ドキュメント履歴 Update &Index アップデート インデックス(&I) Stop &Indexing インデックス作成中止(&I) All 全て media マルチメディア message メール(メッセージ) other その他 presentation プレゼンテーション spreadsheet スプレッドシート text テキスト sorted 並替え filtered フォルター化 No helpers found missing 不足している補助プログラム(helper)なし Missing helper programs 不足している補助プログラム(helper) No external viewer configured for mime type [ 外部ビューワーが設定されていません mime type [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Can't access file: ファイルへのアクセス不能: Can't uncompress file: ファイル展開不可: Save file ファイル保存 Result count (est.) カウント結果(推測) Could not open external index. Db not open. Check external indexes list. Could not open external index. Db not open. Check external indexes list. No results found 結果が見つかりません None なし Updating 更新中 Done 完了 Monitor モニター Indexing failed インデックス作成失敗 The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Erasing index インデックス消去中 Reset the index and start from scratch ? インデックスをリセットして最初から始めますか? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program クエリ進行中.<br>インデックス作成ライブラリの制限により、<br>キャンセルするとプログラムが終了します Error エラー Index query error インデックス・クエリ・エラー Can't update index: indexer running インデックス更新不可: indexer running Indexed MIME Types Indexed MIME Types Bad viewer command line for %1: [%2] Please check the mimeview file Bad viewer command line for %1: [%2] Please check the mimeview file Viewer command line for %1 specifies both file and parent file value: unsupported Viewer command line for %1 specifies both file and parent file value: unsupported Cannot find parent document 親ドキュメントが見つかりません External applications/commands needed for your file types and not found, as stored by the last indexing pass in External applications/commands needed for your file types and not found, as stored by the last indexing pass in Sub-documents and attachments サブドキュメント及び添付ファイル Document filter ドキュメントフィルター The indexer is running so things should improve when it's done. The indexer is running so things should improve when it's done. Duplicate documents ドキュメント重複 These Urls ( | ipath) share the same content: These Urls ( | ipath) share the same content: Bad desktop app spec for %1: [%2] Please check the desktop file Bad desktop app spec for %1: [%2] Please check the desktop file Indexing interrupted インデックス作成が中断されました Bad paths Bad paths Selection patterns need topdir Selection patterns need topdir Selection patterns can only be used with a start directory Selection patterns can only be used with a start directory No search No search No preserved previous search 保存済みの過去の検索はありません Choose file to save 保存するファイルを選択 Saved Queries (*.rclq) 保存されたクエリ (*.rclq) Write failed 書込み失敗 Could not write to file ファイルへの書込みができませんでした Read failed 読み込み失敗 Could not open file: ファイルを開けませんでした: Load error ロード・エラー Could not load saved query 保存済みクエリをロードできませんでした Disabled because the real time indexer was not compiled in. リアルタイムインデクサーが内部コンパイルされていなため無効になっています. This configuration tool only works for the main index. この設定ツールはマイン・インデックスに対してのみ機能します。 Can't set synonyms file (parse error?) 同義語ファイルを設定できません(解析エラー?) The document belongs to an external index which I can't update. ドキュメントは、更新できない外部インデックスに属しています。 Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. 一時ファイルとしてのコピーを開く。編集を永続的な場所に保存しないと、<br/>編集内容が失われます。 Do not show this warning next time (use GUI preferences to restore). 次回からこの警告を表示しない (復元のためGUI設定を使用する)。 Index locked インデックスがロックされています Unknown indexer state. Can't access webcache file. インデクサー状態が不明。Webキャッシュファイルにアクセスできません。 Indexer is running. Can't access webcache file. インデクサー実行中。Webキャッシュファイルにアクセスできません。 with additional message: 追加メッセージ付き: Non-fatal indexing message: 非致命的な索引付けメッセージ: Types list empty: maybe wait for indexing to progress? タイプリストが空です:インデックス作成が進行するのを待つ可能性がありますか? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported ビューアコマンドライン %1 は親ファイルを指定していますが、URL http [s]: はサポートされていません Tools ツール Results 結果 Content has been indexed for these MIME types: コンテンツは、次のMIME形式のインデックスに登録されています: Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): 構成ファイルに空白パスまたは存在しないパスが存在。 OKをクリックし、とにかくインデックス作成を開始します(存在しないデータはインデックスから削除されません): Indexing done インデックス作成完了 Can't update index: internal error インデックス更新できません:内部エラー Index not up to date for this file.<br> このファイルのインデックスは最新ではありません。<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>また、ファイルの最後のインデックス更新が失敗したようです。</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> OK をクリックして、このファイルのインデックスを更新してみてください。インデックス作成が完了したら、クエリを再度実行する必要があります。<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> [キャンセル]をクリックしてリストに戻ります。<br> [無視]をクリックしてプレビューを表示します(このセッションでは覚えておいてください)。間違ったエントリが表示されるリスクがあります。<br/> documents ドキュメント document ドキュメント files ファイル file ファイル errors エラー error エラー total files) ファイル合計) No information: initial indexing not yet performed. 情報なし:初期インデックス作成はまだ実行されていません。 Batch scheduling バッチ・スケジューリング The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. このツールを使用すると、インデックス作成を実行するタイミングを決定できます。このツールは、Windowsタスクスケジューラを使用します。 Confirm 確認 Erasing simple and advanced search history lists, please click Ok to confirm シンプルで高度な検索履歴リストを消去するには、[OK]をクリックして確認してください Could not open/create file ファイル・オープン/作成不可 F&ilter フィルター(&i) Could not read: 読込み不可: Simple search type 単純な検索形式 Any term 任意の用語 All terms 全ての用語 File name ファイル名 Query language Query language Stemming language Stemming language Main Window メイン・ウィンドウ Clear search 検索消去 Move keyboard focus to search entry キーボードフォーカスを検索エントリに移動します Move keyboard focus to search, alt. キーボードのフォーカスを検索に移動します。 Toggle tabular display 表形式の表示を切り替えます Move keyboard focus to table キーボードのフォーカスをテーブルに移動します Flushing フラッシング Show menu search dialog メニュー検索ダイアログを表示 Duplicates 繰り返し Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Recoll Recoll &File ファイル(&F) &Tools ツール(&T) &Preferences 環境設定(&P) &Help ヘルプ(&H) E&xit 終了(&x) Ctrl+Q Ctrl+Q Update &index インデックス更新(&i) &Erase document history ドキュメント履歴を消去(&E) &About Recoll Recollについて(&A) &User manual ユーザーマニュアル(&U) Document &History ドキュメント履歴(&H) Document History ドキュメント履歴 &Advanced Search 高度な検索(&A) Advanced/complex Search 高度/複雑な検索 &Sort parameters パラメータ並替え(&S) Sort parameters パラメータ並替え Term &explorer Term &explorer Term explorer tool Term explorer tool Next page 次のページ Next page of results 結果頁 First page 最初の頁 Go to first page of results 結果の最初頁へ移動 Previous page 前の頁 Previous page of results 前の結果頁 External index dialog 外部インデックスダイアログ PgDown PdDown PgUp PgUp &Full Screen 全画面(&F) F11 F11 Full Screen 全画面 &Erase search history 検索履歴を消去(&E) Sort by dates from oldest to newest 日付で並替え(古い順) Sort by dates from newest to oldest 日付で並替え(新しい順) Show Query Details クエリ(照会)詳細を表示 &Rebuild index インデックスを再構築(&R) Shift+PgUp Shift+PgUp E&xternal index dialog 外部インデックスダイアログ(&x) &Index configuration インデックス環境設定(&I) &GUI configuration GUI環境設定(&G) &Results 結果(&R) Sort by date, oldest first 日付で並び替え(古い順) Sort by date, newest first 日付で並び替え(新しい順) Show as table テーブル表示 Show results in a spreadsheet-like table 結果をスプレッドシート状テーブルで表示 Save as CSV (spreadsheet) file CSV (spreadsheet)ファイルに保存 Saves the result into a file which you can load in a spreadsheet 結果をスプレッドシートとしてロード可能なファイルに保存 Next Page 次の頁 Previous Page 前の頁 First Page 最初の頁 Query Fragments クエリ(照会)フラグメント With failed files retrying With failed files retrying Next update will retry previously failed files Next update will retry previously failed files Indexing &schedule インデックス・スケジュール(&s) Enable synonyms 同義語を有効にする Save last query 最後のクエリ(照会)を保存 Load saved query 保存されたクエリ(照会)をロード Special Indexing スペシャル・インデックス Indexing with special options スペシャルオプション付きインデックス &View 表示(&V) Missing &helpers 補助プログラム(helper)なし(&h) Indexed &MIME types インデックスMIMEタイプ (&M) Index &statistics インデックス統計 (&s) Webcache Editor Webcache エディタ Trigger incremental pass 増分パスをトリガー E&xport simple search history 単純な検索履歴をエクスポート(&x) &Query クエリ &Query Could not extract or copy text テキストを抽出またはコピーできませんでした %1 bytes copied to clipboard %1 bytes をクリップボードにコピーしました Increase results text font size 結果のテキストフォントサイズを大きくする Increase Font Size フォントサイズを大きくする Decrease results text font size 結果のテキストフォントサイズを小さくする Decrease Font Size フォントサイズを小さくする Start real time indexer リアルタイムインデクサを開始 Query Language Filters 言語フィルター・クエリ Filter dates 日付でフィルター Assisted complex search 複雑な検索支援 Filter birth dates RclTrayIcon Restore リストア Quit 中止 RecollModel Abstract 概要 Author 著者 Document size ドキュメントサイズ Document date ドキュメント日付 File size ファイルサイズ File name ファイル名 File date ファイル日付 Keywords キーワード Original character set オリジナル character set Relevancy rating 関連性の評価 Title タイトル URL URL Date 日付 Date and time 日付と時刻 Ipath Ipath MIME type MIME形式 Can't sort by inverse relevance 逆の関連性による並替えはできません ResList Result list 結果リスト (show query) (クエリ表示) Document history ドキュメント履歴 <p><b>No results found</b><br> <p><b>結果が見つかりません</b><br> Previous Next Unavailable document ドキュメント利用不可 Preview プレビュー Open 開く <p><i>Alternate spellings (accents suppressed): </i> <p><i>代替スペリング(アクセント抑制): </i> Documents ドキュメント out of at least out of at least for for <p><i>Alternate spellings: </i> <p><i>代替スペリング: </i> Result count (est.) カウント結果(推定) Query details クエリ詳細 Snippets Snippets This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort 並替えリセット(&R) &Delete column 列を削除(&D) &Delete column Save table to CSV file テーブルをCSV ファイルに保存 Can't open/create file: ファイルを開くこと/作成ができません: &Save as CSV CSVとして保存(&S) Add "%1" column "%1" 列を追加 Result Table 結果テープル Preview プレビュー Open current result document 現在の結果ドキュメントを開く Open current result and quit 現在の結果を開いて終了する Show snippets snippets表示 Show header ヘッダーを表示 Show vertical header verticalヘッダーを表示 Copy current result text to clipboard 現在の結果テキストをクリップボードへコピー Use Shift+click to display the text instead. Use Shift+click to display the text instead. %1 bytes copied to clipboard %1 bytes をクリップボードにコピーしました Copy result text and quit 結果テキストをコピーして終了 SSearch Any term 任意の表現 All terms 全ての表現 File name ファイル名 Query language クエリ(照会)言語 Bad query string クエリ文字列が不正 Out of memory メモリ不足 Enter file name wildcard expression. ワイルドカード式のファイル名を入力. Stemming languages for stored query: 保存されたクエリのステミング言語: differ from current preferences (kept) differ from current preferences (kept) Auto suffixes for stored query: 保存されたクエリの自動サフィックス: Autophrase is set but it was unset for stored query オートフレーズが設定されていますが、保存されたクエリに対して設定されていません Autophrase is unset but it was set for stored query オートフレーズは設定されていませんが、保存されたクエリ用に設定されています Enter search terms here. ここに検索用語を入力. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border-collapse: collapse; } } th,td { th,td { text-align: center; テキスト-整列:中央; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; You should really look at the manual (F1)</p> マニュアルを見る必要があります (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>What</th><th>Examples</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; where needed</td><td>(one AND two) OR three</td></tr> where needed</td><td>(one AND two) OR three</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index インデックスを開けません Could not restore external indexes for stored query:<br> 保存されたクエリの外部インデックスを復元できませんでした:<br> ??? ??? Using current preferences. 現在の詳細設定を使用. Simple search 単純検索 History 履歴 <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Clear Erase search entry 検索エントリを消去 Search 検索 Start query クエリ開始 Choose search type. 検索タイプを選択. Show query history クエリ履歴を表示 Main menu メイン・メニュー SearchClauseW Select the type of query that will be performed with the words 単語で実行されるクエリ・タイプを選択 Number of additional words that may be interspersed with the chosen ones Number of additional words that may be interspersed with the chosen ones No field フィールドなし Any 任意 All 全て None なし Phrase フレーズ Proximity 近接性 File name ファイル名 Snippets Snippets Snippets Find: 検出: Next Prev SnippetsW Search 検索 <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>申し訳ありません、制限内で完全に一致するものが見つかりませんでした。おそらくドキュメントが非常に大きく、スニペットジェネレータが迷路の中で迷子になりました...</p> Sort By Relevance 関連性で並替え Sort By Page 頁で並替え Snippets Window Snippets Window Find 検出 Find (alt) 検出 (alt) Find next 後方検出 Find previous 前方検出 Close window ウィンドウを閉じる SpecIdxW Special Indexing スペシャル・インっデックス作成 Else only modified or failed files will be processed. それ以外の場合は、変更または失敗したファイルのみが処理されます。 Erase selected files data before indexing. インデックス作成前に、選択したファイルのデータを消去します。 Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). 再帰的にインデックスを作成するディレクトリ。 This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Browse ブラウズ Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. 空白のままで、すべてのファイルを選択します。スペースで区切られた複数のシェルタイプのパターンを使用できます。<br>スペースが埋め込まれているパターンは、二重引用符で囲む必要があります。<br>開始ターゲットが設定されている場合にのみ使用できます。 Selection patterns: Selection patterns: Top indexed entity Top indexed entity Retry previously failed files. Retry previously failed files. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file Diagnostics file SpellBase Term Explorer Term Explorer &Expand &Expand Alt+E Alt+E &Close 閉じる(&C) Alt+C Alt+C No db info. database情報なし. Match 一致 Case Case Accents Accents SpellW Wildcards Wildcards Regexp Regexp Stem expansion Stem expansion Spelling/Phonetic Spelling/Phonetic error retrieving stemming languages error retrieving stemming languages No expansion found No expansion found Term Term Doc. / Tot. Doc. / Tot. Index: %1 documents, average length %2 terms.%3 results Index: %1 documents, average length %2 terms.%3 results %1 results %1 results List was truncated alphabetically, some frequent List was truncated alphabetically, some frequent terms may be missing. Try using a longer root. terms may be missing. Try using a longer root. Show index statistics インデックス統計を表示 Number of documents ドキュメント数 Average terms per document ドキュメントあたりの平均用語 Database directory size データベース・ディレクトリサイズ MIME types: MIME 形式: Item 項目 Value Smallest document length (terms) 最小のドキュメント長さ (terms) Longest document length (terms) 最大のドキュメント長さ (terms) Results from last indexing: Results from last indexing: Documents created/updated Documents created/updated Files tested Files tested Unindexed files Unindexed files List files which could not be indexed (slow) List files which could not be indexed (slow) Spell expansion error. Spell expansion error. Spell expansion error. UIPrefsDialog error retrieving stemming languages error retrieving stemming languages The selected directory does not appear to be a Xapian index 選択されたディレクトリには Xapian インデックスがありません This is the main/local index! これは main/localインデックスです! The selected directory is already in the index list 選択したディレクトリは既にインデックスリストに含まれています Choose 選択 Result list paragraph format (erase all to reset to default) 段落形式による結果リスト(すべて消去してデフォルトにリセット) Result list header (default is empty) Result list header (default is empty) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read 選択されたディレクトリはRecoll設定ディレクトリのようですが、設定を読み込めません At most one index should be selected 最大で1つのインデックスを選択する必要があります Cant add index with different case/diacritics stripping option ケース/発音区別符号のストリッピングオプションが異なるインデックスは追加できません Default QtWebkit font デフォルトの QtWebkitフォント Any term 任意用語 All terms 全ての用語 File name ファイル名 Query language クエリ言語 Value from previous program exit Value from previous program exit Context Context Description 説明 Shortcut ショートカット Default デフォルト Choose QSS File QSS ファイルを選択 Can't add index with different case/diacritics stripping option. ViewAction Command Command MIME type MIME形式 Desktop Default デスクトップ・デフォルト Changing entries with different current values Changing entries with different current values ViewActionBase Native Viewers ネイティブ・ビューワー Close 閉じる Select one or several mime types then use the controls in the bottom frame to change how they are processed. 1つまたは複数のMIME形式を選択し、下部フレームにあるコントロールを使用して、それらの処理方法を変更します。 Use Desktop preferences by default デスクトップ設定をでデフォルトで使用する Select one or several file types, then use the controls in the frame below to change how they are processed 1つまたは複数のファイルタイプを選択し、下のフレームのコントロールを使用して、それらの処理方法を変更します Exception to Desktop preferences デスクトップ設定の例外 Action (empty -> recoll default) Action (empty -> recoll default) Apply to current selection 現在の選択に追加 Recoll action: Recoll action: current value 現在の値 Select same Select same <b>New Values:</b> <b>新しい値:</b> Webcache Webcache editor Webcache エディタ Search regexp 正規表現で検索 TextLabel WebcacheEdit Copy URL URLをコピー Unknown indexer state. Can't edit webcache file. インデクサー状態が不明です。 Webキャッシュファイルを編集できません。 Indexer is running. Can't edit webcache file. インデクサーが実行されています。 Webキャッシュファイルを編集できません。 Delete selection 選択を消去 Webcache was modified, you will need to run the indexer after closing this window. Webcacheが変更されました。このウィンドウを閉じた後、インデクサーを実行する必要があります。 Save to File ファイルに保存 File creation failed: ファイル保存に失敗: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url Url Date 日付 Size サイズ URL URL WinSchedToolW Recoll Batch indexing Start Windows Task Scheduler tool Error エラー Configuration not initialized Could not create batch file <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> Command already started confgui::ConfParamFNW Choose 選択 confgui::ConfParamSLW + + - - Add entry エントリー追加 Delete selected entries 選択したエントリーを削除 ~ ~ Edit selected entries 選択したエントリーを編集 uiPrefsDialogBase User interface ユーザー・インターフェース Number of entries in a result page 結果ページのエントリ数 If checked, results with the same content under different names will only be shown once. オンにすると、同じコンテンツで異なる名前の結果が1回だけ表示されます。 Hide duplicate results. 重複結果を隠す. Result list font 結果リストのフォント Opens a dialog to select the result list font 結果リストのフォントを選択するためのダイアログを開きます Helvetica-10 Helvetica-10 Resets the result list font to the system default 結果リストのフォントをシステムのデフォルトにリセットします Reset リセット Texts over this size will not be highlighted in preview (too slow). このサイズを超えるテキストはプレビューで強調表示されません(遅すぎます)。 Choose editor applications エディタ・アプリケーションを選択 Start with advanced search dialog open. 詳細検索ダイアログを開いて開始. Remember sort activation state. 並べ替えのアクティブ化状態を覚えておいてください。 Prefer Html to plain text for preview. プレビューにはプレーンテキストよりもHTMLを優先します。 Search parameters 検索パラメータ Stemming language Stemming language A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Automatically add phrase to simple searches 単純検索に自動的にフレーズを追加 Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. クエリ用語のコンテキストを使用して、結果リストエントリの要約を作成しようとしていますか? 大きなドキュメントの場合は遅くなる可能性があります。 Dynamically build abstracts アブストラクトを動的に構築する Do we synthetize an abstract even if the document seemed to have one? ドキュメントに要約が含まれているように見えても、要約を合成しますか? Replace abstracts from documents ドキュメントから要約を置き換える Synthetic abstract size (characters) 合成された要約サイズ (characters) Synthetic abstract context words 合成された要約コンテキストワード The words in the list will be automatically turned to ext:xxx clauses in the query language entry. リスト内の単語は、クエリ言語エントリ ext:xxx条項に自動的に変換されます。 Query language magic file name suffixes. クエリ言語のマジックファイル名のサフィックス。 Enable 有効化 External Indexes 外部インデックス Toggle selected Toggle selected Activate All 全てアクティブ化 Deactivate All 全て非アクティブ化 Remove from list. This has no effect on the disk index. リストから削除。これはディスクインデックスには影響しません。 Remove selected 選択を削除 Add index インデックス追加 Apply changes 変更を適用 &OK &OK Discard changes 変更を破棄 &Cancel キャンセル(&C) Abstract snippet separator 要約snippetセパレータ Opens a dialog to select the style sheet file スタイルシートファイルを選択するためのダイアログを開きます Choose 選択 Resets the style sheet to default スタイルシートをデフォルトにリセット Result List 結果リスト Edit result paragraph format string 結果の段落形式の文字列を編集する Edit result page html header insert 結果ページのHTMLヘッダー挿入を編集しま Date format (strftime(3)) 日付フォーマット (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Autophrase term frequency threshold percentage Autophrase term frequency threshold percentage Plain text to HTML line style プレイン・テキストをHTMLへ。ライン形式 Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. PREテキストの行は折りたたまれていません。 BRを使用すると、インデントが失われます。 PRE +ラップスタイルがあなたの望むものかもしれません。 <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + wrap Disable Qt autocompletion in search entry. 検索エントリでQtオートコンプリートを無効にします。 Paths translations Paths translations Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. クリックして、別のインデックスディレクトリをリストに追加します。 Recoll構成ディレクトリまたはXapianインデックスのいずれかを選択できます。 Snippets window CSS file Snippets window CSS file Opens a dialog to select the Snippets window CSS style sheet file Snippets windowのCSSスタイルシートファイルを選択するためのダイアログを開きます Resets the Snippets window style Snippets window形式をリセット Decide if document filters are shown as radio buttons, toolbar combobox, or menu. ドキュメントフィルターをラジオボタン、ツールバーコンボボックス、またはメニューとして表示するかどうかを決定します。 Document filter choice style: ドキュメントフィルターのチェック形式: Buttons Panel ボタンパネル Toolbar Combobox ツールバーコンボボックス Menu メニュー Show system tray icon. システムトレイ・アイコンを表示. Close to tray instead of exiting. Close to tray instead of exiting. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. snippets windowに適用するユーザースタイル。<br>注:結果ページのヘッダー挿入は、snippets windowのヘッダーにも含まれています。 Synonyms file snippets ファイル Show warning when opening temporary file. 一時ファイルを開くときに警告を表示します。 Highlight CSS style for query terms クエリ用語のCSSスタイルを強調表示 Recoll - User Preferences Recoll - ユーザー詳細設定 Set path translations for the selected index or for the main one if no selection exists. 選択したインデックス、または選択が存在しない場合はメインインデックスのパス変換を設定します。 Activate links in preview. プレビューでリンクをアクティブにします。 Make links inside the preview window clickable, and start an external browser when they are clicked. プレビューウィンドウ内のリンクをクリック可能にし、クリックされたときに外部ブラウザを起動します。 Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... 結果で強調表示されているクエリ用語。 <br>デフォルト(青色)より目立つ色にするには、「color:red; background:yellow」のようなものを試してみてください... Start search on completer popup activation. 完全なポップアップアクティベーションで検索を開始します。 Maximum number of snippets displayed in the snippets window snippets windowに表示されるsnippets の最大数 Suppress all beeps. すべてのビープ音を抑制。 Application Qt style sheet アプリケーションQtスタイルシート Limit the size of the search history. Use 0 to disable, -1 for unlimited. 検索履歴のサイズを制限します。無効にするには0を使用し、無制限にするには-1を使用します。 Maximum size of search history (0: disable, -1: unlimited): 検索履歴の最大サイズ(0:無効、-1:無制限): Generate desktop notifications. デスクトップ通知を生成します。 Sort snippets by page number (default: by weight). snippetsをページ番号で並べ替えます(デフォルト:by weight). Misc その他 Work around QTBUG-78923 by inserting space before anchor text アンカーテキストの前にスペースを挿入して、QTBUG-78923を回避します Display a Snippets link even if the document has no pages (needs restart). ドキュメントにページがない場合でも、Snippets linkを表示します(再起動が必要です)。 Maximum text size highlighted for preview (kilobytes) プレビュー用に強調表示された最大テキストサイズ(KB) Start with simple search mode: 単純検索モードで開始: Shortcuts ショートカット Hide result table header. 結果テーブルヘッダーを隠す. Show result table row headers. 結果テーブルの行ヘッダーを表示. Reset shortcuts defaults ショートカットをデフォルトにリセット Use F1 to access the manual マニュアルにアクセスするには F1 を押す Hide some user interface elements. 一部のユーザーインターフェイス要素を非表示. Hide: 隠す: Toolbars ツールバー Status bar ステータス・バー Show button instead. 代わりにボタンを表示. Menu bar メニュー・バー Show choice in menu only. メニューにのみ選択肢を表示. Simple search type 単純検索形式 Clear/Search buttons Clear/Search buttons Show text contents when clicking result table row (else use Shift+click). 結果テーブルの行をクリックするとテキストの内容が表示されます(それ以外の場合はShiftキーを押しながらクリックします)。 Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. 表の行にジャンプするためのCtrl + [0-9] / Shift + [a-z]ショートカットを無効にします。 None (default) None (default) Uses the default dark mode style sheet デフォルトのダークモード・スタイルシートを使用 Dark mode ダークモード Choose QSS File QSS ファイルを選択 Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table 結果テープル To display document text instead of metadata in result table detail area, use: To display document text instead of metadata in result table detail area, use: left mouse click left mouse click Shift+click Shift+click Do not display metadata when hovering over rows. Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text アンカーテキストの前にスペースを挿入して、タミルQTBUG-78923を回避します The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. このバグにより、強調表示されたタミル語の中に奇妙な円の文字が表示されます。 回避策は、問題を修正するように見える追加のスペース文字を挿入します。 Depth of side filter directory tree サイドフィルターディレクトリツリーの深さ Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_es.ts0000644000175000017500000071625514444307651014262 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Todas las cláusulas Any clause Cualquier cláusula texts textos spreadsheets hojas de cálculo presentations presentaciones media medios messages mensajes other otros Bad multiplier suffix in size filter Sufijo multiplicador incorrecto en filtro de tamaño text texto spreadsheet hoja de cálculo presentation presentación message mensaje Advanced Search Búsqueda avanzada History Next Historial siguiente History Prev Historial anterior Load next stored search Cargar la siguiente búsqueda almacenada Load previous stored search Cargar búsqueda almacenada anterior AdvSearchBase Advanced search Búsqueda avanzada Restrict file types Restringir tipos de archivo Save as default Guardar como predeterminado Searched file types Tipos de archivos buscados All ----> Todos ----> Sel -----> Sel -----> <----- Sel <----- Sel <----- All <----- Todos Ignored file types Tipos de archivos ignorados Enter top directory for search Ingrese directorio inicial para la búsqueda Browse Buscar Restrict results to files in subtree: Restringir resultados a archivos en subdirectorio: Start Search Iniciar búsqueda Search for <br>documents<br>satisfying: Buscar documentos<br>que satisfagan: Delete clause Borrar cláusula Add clause Añadir cláusula Check this to enable filtering on file types Marque esto para habilitar filtros en tipos de archivos By categories Por categorías Check this to use file categories instead of raw mime types Marque esto para usar categorías en lugar de tipos MIME Close Cerrar All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Todos los campos no vacíos a la derecha serán combinados con conjunciones AND (opción "Todas las cláusulas") o OR (opción "Cualquier cláusula").<br>Los campos "Cualquiera", "Todas" y "Ninguna" pueden aceptar una mezcla de palabras simples y frases dentro de comillas dobles.<br>Campos sin datos son ignorados. Invert Invertir Minimum size. You can use k/K,m/M,g/G as multipliers Tamaño mínimo. Puede utilizar k/K, m/M o g/G como multiplicadores Min. Size Tamaño Mínimo Maximum size. You can use k/K,m/M,g/G as multipliers Tamaño máximo. Puede utilizar k/K, m/M o g/G como multiplicadores Max. Size Tamaño máximo Select Seleccionar Filter Filtro From Desde To Hasta Check this to enable filtering on dates Marque esto para habilitar filtros en fechas Filter dates Filtrar fechas Find Buscar Check this to enable filtering on sizes Marque esto para habilitar filtros en tamaños Filter sizes Filtro de tamaños Filter birth dates ConfIndexW Can't write configuration file No se puede escribir archivo de configuración Global parameters Parámetros globales Local parameters Parámetros locales Search parameters Parámetros de búsqueda Top directories Directorios primarios The list of directories where recursive indexing starts. Default: your home. La lista de directorios donde la indexación recursiva comienza. Valor por defecto: su directorio personal. Skipped paths Directorios omitidos These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Estos son los nombres de los directorios que no entrarán en la indexación.<br>Los elementos de ruta pueden contener comodines. Las entradas deben coincidir con las rutas vistas por el indexador (p. ej. si topdirs incluye '/home/me' y '/home' es en realidad un enlace a '/usr/home', una entrada correcta de skippedPath sería '/home/me/tmp*', no '/usr/home/me/tmp*') Stemming languages Lenguajes para raíces The languages for which stemming expansion<br>dictionaries will be built. Los lenguajes para los cuales los diccionarios de expansión de raíces serán creados. Log file name Nombre de archivo de registro The file where the messages will be written.<br>Use 'stderr' for terminal output El archivo donde los mensajes serán escritos.<br>Use 'stderr' para salida a la terminal Log verbosity level Nivel de verbosidad del registro This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Este valor ajusta la cantidad de mensajes,<br>desde solamente errores hasta montones de información de depuración. Index flush megabytes interval Intervalo en megabytes de escritura del índice This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Este valor ajusta la cantidad de datos indexados entre escrituras al disco.<br> Esto ayuda a controlar el uso de memoria del indexador. Valor estándar 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Este es el porcentaje de uso del disco - uso total del disco, no tamaño del índice- en el que la indexación fallará y se detendrá.<br>El valor predeterminado de 0 elimina cualquier límite. No aspell usage No utilizar aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Deshabilita el uso de aspell para generar aproximaciones ortográficas en la herramienta explorador de términos.<br>Útil si aspell no se encuentra o no funciona. Aspell language Lenguaje Aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. El lenguaje para el diccionario aspell. Esto debería ser algo como 'en' o 'fr' ...<br>Si no se establece este valor, el ambiente NLS será utilizado para calcularlo, lo cual usualmente funciona. Para tener una idea de lo que está instalado en sus sistema, escriba 'aspell-config' y busque archivos .dat dentro del directorio 'data-dir'. Database directory name Nombre del directorio de base de datos The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. El nombre de un directorio donde almacenar el índice.<br>Una ruta no absoluta se interpreta como relativa al directorio de configuración. El valor por defecto es 'xapiandb'. Unac exceptions Excepciones Unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Estas son excepciones al mecanismo unac, el cual, de forma predeterminada, elimina todos los diacríticos, y realiza una descomposición canónica. Es posible prevenir la eliminación de acentos para algunos caracteres, dependiendo de su lenguaje, y especificar descomposiciones adicionales, por ejemplo, para ligaturas. En cada entrada separada por espacios, el primer caracter es el origen, y el resto es la traducción. Process the WEB history queue Procesar la cola del historial WEB Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Habilita la indexación de páginas visitadas en Firefox.<br>(necesita también el plugin Recoll para Firefox) Web page store directory name Nombre del directorio del almacén para páginas web The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. El nombre del directorio dónde almacenar las copias de páginas web visitadas.<br>Una ruta de directorio no absoluta es utilizada, relativa al directorio de configuración. Max. size for the web store (MB) Tamaño máximo para el almacén web (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Las entradas serán recicladas una vez que se alcance el tamaño.<br>Solo aumentar el tamaño realmente tiene sentido porque reducir el valor no truncará un archivo existente (solo perder espacio al final). Automatic diacritics sensitivity Sensibilidad automática de diacríticos <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Habilitar automáticamente la sensibilidad de diacríticos si el término de búsqueda tiene caracteres acentuados (no presentes en unac_except_trans). De otra forma necesita usar el lenguage de búsqueda y el modificador <i>D</i> para especificar la sensibilidad de los diacríticos. Automatic character case sensitivity Sensibilidad automática a la distinción de mayúsculas/minúsculas de los caracteres <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Habilitar automáticamente la sensibilidad a las mayúsculas/minúsculas si la entrada tiene caracteres en mayúscula en una posición distinta al primer caracter. De otra forma necesita usar el lenguaje de búsqueda y el modificador <i>C</i> para especificar la sensibilidad a las mayúsculas y minúsculas. Maximum term expansion count Máximo conteo de expansión de términos <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Máxima expansión de conteo para un solo término (ej: cuando se usan comodines). El valor por defecto de 10000 es razonable y evitará consultas que parecen congelarse mientras el motor de búsqueda recorre la lista de términos. Maximum Xapian clauses count Máximo conteo de cláusulas de Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Número máximo de cláusulas elementales agregadas a una consulta de Xapian. En algunos casos, el resultado de la expansión de términos puede ser multiplicativo, y deseamos evitar el uso excesivo de memoria. El valor por defecto de 100000 debería ser lo suficientemente alto en la mayoría de los casos, y compatible con las configuraciones de hardware típicas en la actualidad. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... Los idiomas para los que se construirán los diccionarios de expansión de stemming.<br>Consulte la documentación del stemmer de Xapian para ver los valores posibles. Por ejemplo, glish, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. El idioma para el diccionario de aspell. Los valores son códigos de idioma de 2 letras, por ejemplo, 'es', 'fr' . .<br>Si no se establece este valor, se utilizará el entorno NLS para calcularlo, que normalmente funciona. Para obtener una idea de lo que está instalado en su sistema, escriba 'aspell config' y busque . en archivos dentro del directorio 'data-dir'. Indexer log file name Nombre del archivo de registro del indexador If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Si está vacío, se utilizará el valor del archivo de registro anterior. Puede ser útil tener un registro separado para fines de diagnóstico porque el registro común se borrará cuando<br>la interfaz de usuario se inicie. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Porcentaje de umbral completo de disco en el que dejamos de indexar<br>p.e. 90% para parar al 90% lleno, 0 o 100 significa que no hay límite) Web history Historial Web Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Solo tipos mime An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Una lista exclusiva de tipos de mime indexados.<br>Nada más será indexado. Normalmente vacío e inactivo Exclude mime types Excluir tipos mime Mime types not to be indexed Tipos Mime que no deben ser indexados Max. compressed file size (KB) Tamaño máximo de archivo comprimido (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Este valor establece un umbral mas allá del cual los archivos<br>comprimidos no serán procesados. Escriba 1 para no tener límite,<br>o el número 0 para nunca hacer descompresión. Max. text file size (MB) Tamaño máximo para archivo de texto (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Este valor establece un umbral más allá del cual los archivos de texto no serán procesados.<br>Escriba 1 para no tener límites. Este valor es utilizado para excluir archivos de registro gigantescos del índice. Text file page size (KB) Tamaño de página para archivo de texto (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Si se utiliza este valor (diferente de -1), los archivos de texto serán separados en partes de este tamaño para ser indexados. Esto ayuda con las búsquedas de archivos de texto muy grandes (ej: archivos de registro). Max. filter exec. time (s) Máximo filtro exec. tiempo (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Filtros externos que se ejecuten por más tiempo del establecido serán detenidos. Esto es por el caso inusual (ej: postscript) dónde un documento puede causar que un filtro entre en un ciclo infinito. Establezca el número -1 para indicar que no hay límite. Global Global CronToolW Cron Dialog Ventana de Cron <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> horario de indexado por lotes (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Cada campo puede contener un comodín (*), un valor numérico único, listas separadas por comas (1,3,5) y rangos (1-7). Más generalmente, los campos serán usados <span style=" font-style:italic;">tal como son</span> dentro del archivo crontab, y toda la sintaxis crontab puede ser usada, ver crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Por ejemplo, ingresar <span style=" font-family:'Courier New,courier';">*</span> en <span style=" font-style:italic;">Días, </span><span style=" font-family:'Courier New,courier';">12,19</span> en <span style=" font-style:italic;">Horas</span> y <span style=" font-family:'Courier New,courier';">15</span> en <span style=" font-style:italic;">Minutos</span> iniciaría recollindex cada día a las 12:15 AM y 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Un horario con activaciones frecuentes es probablemente menos eficiente que la indexación en tiempo real.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Días de la semana (* o 0-7, 0 o 7 es Domingo) Hours (* or 0-23) Horas (* o 0-23) Minutes (0-59) Minutos (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Presione <span style=" font-style:italic;">Deshabilitar</span> para detener la indexación automática por lotes, <span style=" font-style:italic;">Habilitar</span> para activarla, <span style=" font-style:italic;">Cancelar</span> para no cambiar nada.</p></body></html> Enable Habilitar Disable Deshabilitar It seems that manually edited entries exist for recollindex, cannot edit crontab Parece ser que existen entradas para recollindex editadas manualmente, no se puede editar crontab Error installing cron entry. Bad syntax in fields ? Error al instalar entrada de cron. Sintaxis incorrecta en los campos? EditDialog Dialog Ventana de diálogo EditTrans Source path Ruta de origen Local path Ruta local Config error Error de configuración Original path Ruta original EditTransBase Path Translations Ruta de traducciones Setting path translations for Establecer ruta de traducciones para Select one or several file types, then use the controls in the frame below to change how they are processed Seleccione uno o más tipos de archivos, y use los controles en la caja abajo para cambiar cómo se procesan Add Añadir Delete Borrar Cancel Cancelar Save Guardar FirstIdxDialog First indexing setup Primera configuración de indexación <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Parece ser que el índice para esta configuración no existe.</span><br /><br />Si solamente desea indexar su directorio personal con un conjunto de valores iniciales razonables, presione el botón <span style=" font-style:italic;">Iniciar indexación ahora</span>. Es posible ajustar los detalles más tarde.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si necesita más control, use los enlaces siguientes para ajustar la configuración de indexación y el horario.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Estas herramientas pueden ser accedidas luego desde el menú <span style=" font-style:italic;">Preferencias</span>.</p></body></html> Indexing configuration Configuración de indexación This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Esto le permite ajustar los directorios que quiere indexar y otros parámetros, como rutas de archivos o nombres excluidos, conjuntos de caracteres estándar, etc. Indexing schedule Horario de indexación This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Esto le permite escoger entre indexación en tiempo real y por lotes, y configurar un horario automático para indexar por lotes (utilizando cron). Start indexing now Iniciar indexación ahora FragButs %1 not found. %1 no encontrado. %1: %2 %1: %2 Fragment Buttons Botones de fragmentos Query Fragments Fragmentos de consulta IdxSchedW Index scheduling setup Configuración de horario de indexación <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">La indexación de <span style=" font-weight:600;">Recoll</span> puede ejecutarse permanentemente, indexando archivos cuando cambian, o puede ejecutarse en intervalos discretos. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Leer el manual puede ayudarle a decidir entre estos dos métodos (presione F1).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Esta herramienta puede ayudarle a configurar un horario para automatizar la ejecución de indexación por lotes, o iniciar la indexación en tiempo real cuando inicia la sesión (o ambos, lo cual rara vez tiene sentido).</p></body></html> Cron scheduling Horario de Cron The tool will let you decide at what time indexing should run and will install a crontab entry. Esta herramienta le permite decidir a qué hora la indexación se ejecutará e instalará una entrada en el crontab. Real time indexing start up Inicio de la indexación en tiempo real Decide if real time indexing will be started when you log in (only for the default index). Decida si la indexación en tiempo real será ejecutada cuando inicie la sesión (solo para el índice estándar). ListDialog Dialog Ventana de diálogo GroupBox Cuadro de grupo Main No db directory in configuration Directorio de base de datos no está configurado Could not open database in No se puede abrir base de datos en . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. Presione Cancelar si desea editar la configuración antes de indexar, o Ok para proceder. Configuration problem (dynconf Problema de configuración (dynconf "history" file is damaged or un(read)writeable, please check or remove it: El archivo de historial esta dañado o no se puede leer, por favor revíselo o bórrelo: "history" file is damaged, please check or remove it: "historial" archivo está dañado, por favor compruébalo o quite: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Buscar por: &Next &Siguiente &Previous &Previo Match &Case &Coincidir mayúsculas y minúsculas Clear Limpiar Creating preview text Creando texto de vista previa Loading preview text into editor Cargando texto de vista previa en el editor Cannot create temporary directory No se puede crear directorio temporal Cancel Cancelar Close Tab Cerrar Pestaña Missing helper program: Programa ayudante faltante: Can't turn doc into internal representation for No se puede convertir documento a representación interna para Cannot create temporary directory: No se puede crear directorio temporal: Error while loading file Error al cargar archivo Form Forma Tab 1 Tab 1 Open Abrir Canceled Cancelado Error loading the document: file missing. Error al cargar el documento: falta el archivo. Error loading the document: no permission. Error al cargar el documento: sin permiso. Error loading: backend not configured. Error al cargar: backend no configurado. Error loading the document: other handler error<br>Maybe the application is locking the file ? Error al cargar el documento: otro error de manejador<br>¿Tal vez la aplicación está bloqueando el archivo? Error loading the document: other handler error. Error al cargar el documento: otro error de manejador. <br>Attempting to display from stored text. <br>Intentando mostrar el texto almacenado. Could not fetch stored text No se pudo obtener el texto almacenado Previous result document Documento de resultado anterior Next result document Siguiente documento resultado Preview Window Vista previa Close Window Cerrar ventana Next doc in tab Siguiente documento en pestaña Previous doc in tab Doc anterior en pestaña Close tab Cerrar pestaña Print tab Print tab Close preview window Cerrar ventana de vista previa Show next result Mostrar siguiente resultado Show previous result Mostrar resultado anterior Print Imprimir PreviewTextEdit Show fields Mostrar campos Show main text Mostrar texto principal Print Imprimir Print Current Preview Imprimir vista previa actual Show image Mostrar imagen Select All Seleccionar todo Copy Copiar Save document to file Guardar documento en un archivo Fold lines Doblar líneas Preserve indentation Preservar indentación Open document Abrir documento Reload as Plain Text Reload as HTML QObject Global parameters Parámetros globales Local parameters Parámetros locales <b>Customised subtrees <b>Subdirectorios personalizados The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. La lista de subdirectorios en la jerarquía indexada<br>dónde algunos parámetros necesitan ser definidos. Valor por defecto: vacío. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Los parámetros siguientes se aplican a nivel superior, si una línea vacía<br>o ninguna es seleccionada en el listado arriba, o para cada directorio seleccionado.<br>Puede añadir o remover directorios presionando los botones +/-. Skipped names Nombres omitidos These are patterns for file or directory names which should not be indexed. Estos son patrones de nombres de archivos o directorios que no deben ser indexados. Default character set Conjunto de caracteres por defecto This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Este es el conjunto de caracteres usado para leer archivos que no son identificados internamente, por ejemplo, archivos de texto puro.<br>El valor por defecto está vacío, y el valor del ambiente NLS es usado. Follow symbolic links Seguir enlaces simbólicos Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Seguir enlaces simbólicos al indexar. El valor por defecto es no, para evitar indexar duplicados Index all file names Indexar todos los nombres de archivos Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indexar los nombres de los archivos para los cuales los contenidos no pueden ser<br>identificados o procesados (tipo MIME inválido o inexistente). El valor por defecto es verdadero Beagle web history Historial web Beagle Search parameters Parámetros de búsqueda Web history Historial Web Default<br>character set Conjunto de caracteres<br>por defecto Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Juego de caracteres usado para leer archivos que no identifican el conjunto de caracteres internamente, por ejemplo archivos de texto puros.<br>El valor por defecto está vacío, y se utiliza el valor del entorno NLS. Ignored endings Finales ignorados These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Estos son los nombres de archivo finalizados para archivos que serán indexados solo por contenido (no intento de identificación de tipo MIME, sin descompresión, sin indexación de contenido. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Estos son los nombres de archivo finalizados para archivos que serán indexados solo por nombre (no intento de identificación de tipo MIME, sin descompresión, sin indexación de contenido). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>Los parámetros que siguen se establecen en el nivel superior, si nada o una línea vacía está seleccionada en el cuadro de lista de arriba, o para el subdirectorio seleccionado. Puede añadir o eliminar directorios haciendo clic en los botones +/- . QWidget Create or choose save directory Crear o elegir directorio de guardado Choose exactly one directory Elija exactamente un directorio Could not read directory: No se pudo leer el directorio: Unexpected file name collision, cancelling. Colisión de nombres de archivo inesperada, cancelando. Cannot extract document: No se puede extraer el documento: &Preview &Vista Previa &Open &Abrir Open With Abrir con Run Script Ejecutar Script Copy &File Name Copiar nombre de &fichero Copy &URL Copiar &URL &Write to File &Escribir a fichero Save selection to files Guardar selección a archivos Preview P&arent document/folder &Vista previa de documento/directorio ascendente &Open Parent document/folder &Abrir documento/directorio ascendente Find &similar documents Buscar documentos &similares Open &Snippets window Abrir ventana de &fragmentos Show subdocuments / attachments Mostrar subdocumentos / adjuntos &Open Parent document &Abrir documento padre &Open Parent Folder &Abrir carpeta padre Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. No mostrar de nuevo. RTIToolW Real time indexing automatic start Inicio automático de la indexación en tiempo real <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">La indexación de <span style=" font-weight:600;">Recoll</span> puede configurarse para ejecutar como un demonio, actualizando el índice cuando los archivos cambian, en tiempo real. Obtiene un índice actualizado siempre, pero los recursos del sistema son utilizados permanentemente.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Iniciar el demonio de indexación con mi sesión de escritorio. Also start indexing daemon right now. También iniciar demonio de indexación ahora mismo. Replacing: Reemplazando: Replacing file Reemplazando archivo Can't create: No se puede crear: Warning Advertencia Could not execute recollindex No se puede ejecutar recollindex Deleting: Borrando: Deleting file Borrando archivo Removing autostart Eliminando autoinicio Autostart file deleted. Kill current process too ? Archivo de autoinicio borrado. Detener el proceso actual también? RclCompleterModel Hits RclMain About Recoll Acerca de Recoll Executing: [ Ejecutando: [ Cannot retrieve document info from database No se puede recuperar información del documento de la base de datos Warning Advertencia Can't create preview window No se puede crear ventana de vista previa Query results Resultados de búsqueda Document history Historial de documentos History data Datos de historial Indexing in progress: Indexación en progreso: Files Ficheros Purge Purge Stemdb Raízdb Closing Cerrando Unknown Desconocido This search is not active any more Esta búsqueda no está activa Can't start query: No se puede iniciar la consulta: Bad viewer command line for %1: [%2] Please check the mimeconf file Línea de comando incorrecta de visualizador para %1: [%2] Por favor revise el fichero mimeconf Cannot extract document or create temporary file No se puede extraer el documento o crear archivo temporal (no stemming) (sin raíces) (all languages) (todos los lenguajes) error retrieving stemming languages error al recuperar lenguajes para raíces Update &Index Actualizar &Índice Indexing interrupted Indexación interrumpida Stop &Indexing Detener &Indexación All Todo media medios message mensaje other otro presentation presentación spreadsheet hoja de cálculo text texto sorted ordenado filtered filtrado External applications/commands needed and not found for indexing your file types: Aplicaciones/comandos externos necesarios y no encontrados para indexar sus tipos de fichero: No helpers found missing No se encontraron ayudantes Missing helper programs Programas ayudantes faltantes Save file dialog Guardar diálogo de archivo Choose a file name to save under Elija un nombre de archivo en el que guardar Document category filter Filtro de categorías de documentos No external viewer configured for mime type [ No hay visualizador configurado para tipo MIME [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? El visor especificado en mimevimax para %1: %2 no se encuentra. ¿Desea iniciar el diálogo de preferencias? Can't access file: No se puede accesar el archivo: Can't uncompress file: No se puede descomprimir el archivo: Save file Guardar archivo Result count (est.) Conteo de resultados (est.) Query details Detalles de búsqueda Could not open external index. Db not open. Check external indexes list. No se puede abrir índice externo. Base de datos no abierta. Revise listado de índices externos. No results found No hay resultados None Ninguno Updating Actualizando Done Hecho Monitor Seguidor Indexing failed Indexación falló The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone El proceso de indexación actual no se inicio desde esta interfaz. Presione Ok para detenerlo, o Cancelar para dejarlo ejecutar Erasing index Borrando índice Reset the index and start from scratch ? Restaurar el índice e iniciar desde cero? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Consulta en progreso.<br>Debido a limitaciones en la librería de indexación,<br>cancelar terminará el programa Error Error Index not open Índice no está abierto Index query error Error de consulta del índice Indexed Mime Types Tipos MIME indexados Content has been indexed for these MIME types: El contenido ha sido indexado para estos tipos MIME: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Índice no actualizado para este fichero. No mostrado para evitar utilizar la entrada errónea. Presione Ok para actualizar el índice para este fichero, luego ejecute la consulta de nuevo cuando la indexación termine. En caso contrario, presione Cancelar. Can't update index: indexer running No se puede actualizar el índice: indexador en ejecución Indexed MIME Types Tipos MIME indexados Bad viewer command line for %1: [%2] Please check the mimeview file Línea de comando incorrecta de visualizador para %1: [%2] Por favor revise el archivo mimeconf Viewer command line for %1 specifies both file and parent file value: unsupported Línea de comandos del visualizador para %1 especifica valores para el archivo y el archivo padre: no soportado Cannot find parent document No se encuentra documento padre Indexing did not run yet La indexación no se ha ejecutado aún External applications/commands needed for your file types and not found, as stored by the last indexing pass in Aplicaciones/comandos externos requeridos por sus tipos de archivos y no encontrados, como se almacenaron en el último pase de indexación en Index not up to date for this file. Refusing to risk showing the wrong entry. El índice no está actualizado para este archivo. Rehusando mostrar la entrada equivocada. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Presione Ok para actualizar el índice para este archivo, y ejecute de nuevo la consulta cuando la indexación termine. En caso contrario, cancele. Indexer running so things should improve when it's done El indexador está en ejecución, así que las cosas deberían mejorar cuando termine Sub-documents and attachments Sub-documentos y adjuntos Document filter Filtro de documentos Index not up to date for this file. Refusing to risk showing the wrong entry. El índice no está actualizado para este archivo. Rehusando mostrar la entrada equivocada. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Haga clic en Aceptar para actualizar el índice de este archivo, entonces tendrá que volver a ejecutar la consulta cuando se termine la indexación. The indexer is running so things should improve when it's done. El indexador se está ejecutando así que las cosas deberían mejorar cuando's termine. The document belongs to an external indexwhich I can't update. El documento pertenece a un índice externo que puedo't actualizar. Click Cancel to return to the list. Click Ignore to show the preview anyway. Haga clic en Cancelar para volver a la lista. Haga clic en Ignorar para mostrar la vista previa de todos modos. Duplicate documents Documentos duplicados These Urls ( | ipath) share the same content: Estos URLs ( | ipath) comparten el mismo contenido: Bad desktop app spec for %1: [%2] Please check the desktop file Especificación de aplicación de escritorio incorrecta para %1: [%2] Por favor, compruebe el archivo de escritorio Bad paths Rutas incorrectas Bad paths in configuration file: Rutas incorrectas en el archivo de configuración: Selection patterns need topdir Los patrones de selección necesitan topdir Selection patterns can only be used with a start directory Los patrones de selección sólo pueden utilizarse con un directorio de inicio No search Sin búsqueda No preserved previous search Ninguna búsqueda anterior preservada Choose file to save Elegir archivo para guardar Saved Queries (*.rclq) Consultas guardadas (*.rclq) Write failed Error al escribir Could not write to file No se pudo escribir en el archivo Read failed Lectura fallida Could not open file: No se pudo abrir el archivo: Load error Error de carga Could not load saved query No se pudo cargar la consulta guardada Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Abriendo una copia temporal. Las ediciones se perderán si lo haces't guardarlas<br/>en una ubicación permanente. Do not show this warning next time (use GUI preferences to restore). No mostrar esta advertencia la próxima vez (utilice preferencias GUI para restaurar). Disabled because the real time indexer was not compiled in. Deshabilitado porque el indexador en tiempo real no fue compilado. This configuration tool only works for the main index. Esta herramienta de configuración sólo funciona para el índice principal. The current indexing process was not started from this interface, can't kill it El proceso de indexación actual no se inició desde esta interfaz, puede'no eliminarlo The document belongs to an external index which I can't update. El documento pertenece a un índice externo que puedo't actualizar. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Haga clic en Cancelar para volver a la lista. <br>Haga clic en Ignorar para mostrar la vista previa de todos modos (y recuerde para esta sesión). Index scheduling Programación de índices Sorry, not available under Windows for now, use the File menu entries to update the index Lo sentimos, no está disponible en Windows por ahora, utilice las entradas del menú Archivo para actualizar el índice Can't set synonyms file (parse error?) Puede't establecer archivo de sinónimos (¿analizar error?) Index locked Índice bloqueado Unknown indexer state. Can't access webcache file. Estado indexador desconocido. Puede'acceder al archivo de caché web. Indexer is running. Can't access webcache file. El indexador se está ejecutando. Puede'acceder al archivo de caché web. with additional message: con mensaje adicional: Non-fatal indexing message: Mensaje de indexación no fatal: Types list empty: maybe wait for indexing to progress? Lista de tipos vacíos: ¿tal vez esperar a indexar para progresar? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported La línea de comandos del visor para %1 especifica el archivo padre, pero la URL es http[s]: no soportada Tools Herramientas Results Resultados (%d documents/%d files/%d errors/%d total files) (%d documentos/%d archivos/%d errores/%d en total archivos) (%d documents/%d files/%d errors) (%d documentos/%d archivos /%d errores) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Rutas vacías o inexistentes en el archivo de configuración. Haga clic en Aceptar para comenzar a indexar de todos modos (los datos ausentes no serán borrados del índice): Indexing done Indexación hecha Can't update index: internal error Puede't actualizar el índice: error interno Index not up to date for this file.<br> El índice no está actualizado para este archivo.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Además, parece que la última actualización del índice falló.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Haga clic en Ok para intentar actualizar el índice de este archivo. Necesitará ejecutar la consulta de nuevo cuando finalice la indexación.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Haga clic en Cancelar para volver a la lista.<br>Haga clic en Ignorar para mostrar la vista previa de todos modos (y recuerde para esta sesión). Existe el riesgo de mostrar una entrada incorrecta.<br/> documents documentos document documento files ficheros file archivo errors errores error error total files) total de archivos) No information: initial indexing not yet performed. Sin información: índice inicial aún no realizado. Batch scheduling Programación por lotes The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. La herramienta le permitirá decidir en qué momento debe ejecutarse el indexado. Utiliza el planificador de tareas de Windows. Confirm Confirmar Erasing simple and advanced search history lists, please click Ok to confirm Borrando listas de historial de búsqueda simples y avanzadas, por favor haga clic en Aceptar para confirmar Could not open/create file No se pudo abrir/crear el archivo F&ilter &Filtro Could not start recollindex (temp file error) No se pudo iniciar recollindex (error de archivo temporal) Could not read: No se pudo leer: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Esto reemplazará el contenido actual de la cadena de cabecera de la lista de resultados y el nombre del archivo qsd. GUI ¿Continuar? You will need to run a query to complete the display change. Necesitará ejecutar una consulta para completar el cambio de pantalla. Simple search type Tipo de búsqueda simple Any term Cualquier término All terms Todos los términos File name Nombre de archivo Query language Lenguaje de consulta Stemming language Lenguaje de raíces Main Window Ventana principal Focus to Search Concéntrico en buscar Focus to Search, alt. Concéntrate en buscar, alto. Clear Search Limpiar búsqueda Focus to Result Table Concéntrate en la tabla de resultados Clear search Limpiar búsqueda Move keyboard focus to search entry Mover foco del teclado a la entrada de búsqueda Move keyboard focus to search, alt. Mover enfoque del teclado a la búsqueda, alto. Toggle tabular display Alternar pantalla tabular Move keyboard focus to table Mover foco del teclado a la tabla Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Página anterior Next page Siguiente página &File &Archivo E&xit &Salir &Tools &Herramientas &Help &Ayuda &Preferences &Preferencias Search tools Herramientas de búsqueda Result list Lista de resultados &About Recoll &Acerca de Recoll Document &History Historial de &Documentos Document History Historial de Documentos &Advanced Search Búsqueda &Avanzada Advanced/complex Search Búsqueda avanzada/compleja &Sort parameters Parámetros de &ordenamiento Sort parameters Parámetros de ordenamiento Next page of results Página de resultados siguiente Previous page of results Página de resultados anterior &Query configuration Configuración de &consulta &User manual Manual de &Usuario Recoll Recoll Ctrl+Q Ctrl+Q Update &index Actualizar &índice Term &explorer &Explorador de términos Term explorer tool Herramienta de exploración de términos External index dialog Configuración de índices externos &Erase document history Borrar historial de &documentos First page Primera página Go to first page of results Ir a la primera página de resultados &Indexing configuration Configuración de &indexación All Todo &Show missing helpers &Mostrar ayudantes faltantes PgDown AvPág Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Mayús+Inicio, Ctrl+S, Ctrl+Q, Ctrl+S PgUp RePág &Full Screen Pantalla &Completa F11 F11 Full Screen Pantalla Completa &Erase search history Borrar historial de &búsqueda sortByDateAsc ordenarPorFechaAsc Sort by dates from oldest to newest Ordenar por fechas de la más antigua a la más reciente sortByDateDesc ordenarPorFechaDesc Sort by dates from newest to oldest Ordenar por fechas de la más reciente a la más antigua Show Query Details Mostrar resultados de la consulta Show results as table Mostrar resultados tabulados &Rebuild index &Reconstruir índice &Show indexed types &Mostrar tipos indexados Shift+PgUp Mayúsculas+RePág &Indexing schedule &Horario de indexación E&xternal index dialog &Configuración de índices externos &Index configuration &Configuración del Índice &GUI configuration Configuración de &GUI &Results &Resultados Sort by date, oldest first Ordenar por fecha, antiguos primero Sort by date, newest first Ordenar por fecha, recientes primero Show as table Mostrar como tabla Show results in a spreadsheet-like table Mostrar resultados en una tabla similar a una hoja de cálculo Save as CSV (spreadsheet) file Guardar como un archivo CSV (hoja de cálculo) Saves the result into a file which you can load in a spreadsheet Guardar el resultado en un archivo que se puede cargar en una hoja de cálculo Next Page Página Siguiente Previous Page Página Anterior First Page Primera Página Query Fragments Fragmentos de consulta With failed files retrying Con archivos fallidos reintentando Next update will retry previously failed files La próxima actualización reintentará archivos fallidos anteriormente Save last query Guardar última consulta Load saved query Cargar consulta guardada Special Indexing Indexación especial Indexing with special options Indexando con opciones especiales Indexing &schedule &Programa de indexación Enable synonyms Habilitar sinónimos &View &Ver Missing &helpers &Falta ayudantes Indexed &MIME types Tipos &MIME indexados Index &statistics &Estadísticas del índice Webcache Editor Editor de caché web Trigger incremental pass Activar paso incremental E&xport simple search history E&xportar historial de búsqueda simple Use default dark mode Usar modo oscuro por defecto Dark mode Modo oscuro &Query &Consulta Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Filtrar fechas Assisted complex search Filter birth dates RclTrayIcon Restore Restaurar Quit Salir RecollModel Abstract Resumen Author Autor Document size Tamaño del documento Document date Fecha del documento File size Tamaño del archivo File name Nombre del archivo File date Fecha del archivo Ipath Iván Keywords Palabras clave Mime type Tipo MIME Original character set Conjunto de caracteres original Relevancy rating Calificación de relevancia Title Título URL URL Mtime Fecha Mod Date Fecha Date and time Fecha y hora Ipath Iván MIME type Tipo MIME Can't sort by inverse relevance Puede'ordenar por relevancia inversa ResList Result list Lista de resultados Unavailable document Documento no disponible Previous Anterior Next Siguiente <p><b>No results found</b><br> <p><b>No hay resultados</b></br> &Preview &Vista Previa Copy &URL Copiar &URL Find &similar documents Buscar documentos &similares Query details Detalles de búsqueda (show query) (mostrar consulta) Copy &File Name Copiar nombre de &fichero filtered filtrado sorted ordenado Document history Historial de documentos Preview Vista previa Open Abrir <p><i>Alternate spellings (accents suppressed): </i> <p><i>Ortografía alterna (acentos suprimidos): </i> &Write to File &Escribir a fichero Preview P&arent document/folder &Vista previa de documento/directorio ascendente &Open Parent document/folder &Abrir documento/directorio ascendente &Open &Abrir Documents Documentos out of at least de por lo menos for para <p><i>Alternate spellings: </i> <p><i>Escrituras Alternas: </i> Open &Snippets window Abrir ventana de &fragmentos Duplicate documents Documentos duplicados These Urls ( | ipath) share the same content: Estos URLs ( | ipath) comparten el mismo contenido: Result count (est.) Conteo de resultados (est.) Snippets Fragmentos This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Restaurar ordenamiento &Delete column &Borrar columna Add " Añadir " " column " columna Save table to CSV file Guardar tabla a archivo CSV Can't open/create file: No se puede abrir/crear archivo: &Preview &Vista previa &Open &Abrir Copy &File Name Copiar nombre de &fichero Copy &URL Copiar &URL &Write to File &Escribir a fichero Find &similar documents Buscar documentos &similares Preview P&arent document/folder &Vista previa de documento/directorio ascendente &Open Parent document/folder &Abrir documento/directorio ascendente &Save as CSV &Guardar como CSV Add "%1" column Agregar columna "%1" Result Table Tabla de Resultados Open Abrir Open and Quit Abrir y salir Preview Vista previa Show Snippets Mostrar Fragmentos Open current result document Abrir documento de resultado actual Open current result and quit Abrir el resultado actual y salir Show snippets Mostrar fragmentos Show header Mostrar cabecera Show vertical header Mostrar cabecera vertical Copy current result text to clipboard Copiar texto actual al portapapeles Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Vista previa &Open &Abrir Copy &File Name Copiar nombre de &fichero Copy &URL Copiar &URL &Write to File &Escribir a fichero Find &similar documents Buscar documentos &similares Preview P&arent document/folder &Vista previa de documento/directorio ascendente &Open Parent document/folder &Abrir documento/directorio ascendente ResultPopup &Preview &Previsualización &Open &Abrir Copy &File Name Copiar nombre de &archivo Copy &URL Copiar &URL &Write to File &Escribir a archivo Save selection to files Guardar selección a archivos Preview P&arent document/folder &Vista previa de documento o directorio ascendente &Open Parent document/folder &Abrir documento/directorio ascendente Find &similar documents Buscar documentos &similares Open &Snippets window Abrir ventana de &fragmentos Show subdocuments / attachments Mostrar subdocumentos / adjuntos Open With Abrir con Run Script Ejecutar Script SSearch Any term Cualquier término All terms Todos los términos File name Nombre de archivo Completions Finalizaciones Select an item: Seleccione un ítem: Too many completions Demasiadas finalizaciones Query language Lenguaje de consulta Bad query string Consulta inválida Out of memory No hay memoria Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Ingrese expresión de lenguaje de consulta. Hoja de trucos:<br> <i>term1 term2</i> : 'term1' y 'term2' en cualquier campo.<br> <i>campo:term1</i> : 'term1' en campo 'campo'. <br> Nombres de campos estándar/sinónimos:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-campos: dir, mime/format, type/rclcat, date.<br> Dos ejemplos de intervalo de fechas: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> Los paréntesis no están permitidos en realidad.<br> <i>"term1 term2"</i> : frase (debe aparecer exactamente). Modificadores posibles:<br> <i>"term1 term2"p</i> : busca de proximidad sin orden con distancia estándar.<br> Use el enlace <b>Mostrar Consulta</b> en caso de duda sobre el resultado y vea el manual (&lt;F1>) para más detalles. Enter file name wildcard expression. Ingrese expresión de comodín para nombre de archivo. Enter search terms here. Type ESC SPC for completions of current term. Ingrese términos de búsqueda aquí. Presione ESC ESPACIO para completar el término actual. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Introduzca la expresión del idioma de la consulta. Hoja de trucos:<br> <i>term1 term2</i> : 'term1' y 'term2' en cualquier campo.<br> <i>campo:term1</i> : 'término1' en el campo 'campo'.<br> Nombre/sinónimos de campos estándar:<br> título/sujeto/caption, autor/de, destinatario/to, nombre de archivo, ext.<br> Pseudo-campos: dir, mime/format, type/rclcat, fecha, tamaño.<br> Ejemplos de dos intervalos de fecha: 2009-03-01/2009-05.U2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> Puedes usar paréntesis para hacer las cosas más claras.<br> <i>"término 1 término 2"</i> : frase (debe ocurrir exactamente). Posibles modificadores:<br> <i>"term1 term2"p</i> : búsqueda de proximidad desordenada con distancia predeterminada.<br> Usar <b>Mostrar consulta</b> enlace cuando haya dudas sobre el resultado y ver manual (&lt; 1>) para más detalles. Stemming languages for stored query: Idiomas para la consulta almacenada: differ from current preferences (kept) difiere de las preferencias actuales (Izquierda) Auto suffixes for stored query: Sufijos automáticos para la consulta almacenada: External indexes for stored query: Índices externos para la consulta almacenada: Autophrase is set but it was unset for stored query La frase automática está definida pero no está establecida para la consulta almacenada Autophrase is unset but it was set for stored query La frase automática no está definida pero ha sido establecida para la consulta almacenada Enter search terms here. Introduzca los términos de búsqueda aquí. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; borde: 1px negro sólido; border-collapse: collapse; colapsar la frontera: colapsar; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Consulta la hoja de trampas. En duda: haga clic en <b>Mostrar consulta</b>.&nbsp; You should really look at the manual (F1)</p> Realmente deberías mirar el manual (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Qué</th><th>Ejemplos</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Y</td><td>uno de dos&nbsp;&nbsp;&nbsp;uno AND dos&nbsp;&nbsp;&nbsp;uno y dos</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>O</td><td>uno o dos&nbsp;&nbsp;&nbsp;uno || dos</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Booleano complejo. O tiene prioridad, use paréntesis&nbsp; where needed</td><td>(one AND two) OR three</td></tr> donde sea necesario</td><td>(uno Y dos) O tres</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>No</td><td></td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Frase</td><td>"orgullo y prejuzga"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Prox sin ordenar. (por defecto slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Sin expansión de tallo: capitalizar</td><td>Planta</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>Y dentro del campo (sin orden)</td><td>autor:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>O dentro del campo</td><td>autor:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Nombre de campo</td><td>título/tema/título&nbsp;&nbsp;autor/de<br>destinatario/a&nbsp;&nbsp;nombre de archivo&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Filtro de ruta de directorio</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME filtro de tipo</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Intervalos de fecha</td><td>fecha:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> fecha:2018&nbsp;&nbsp;fecha:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Puede't abrir el índice Could not restore external indexes for stored query:<br> No se pudieron restaurar índices externos para la consulta almacenada:<br> ??? ??? Using current preferences. Utilizando las preferencias actuales. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Limpiar Ctrl+S Ctrl+S Erase search entry Borrar entrada de búsqueda Search Búsqueda Start query Iniciar consulta Enter search terms here. Type ESC SPC for completions of current term. Ingrese términos de búsqueda aquí. Presione ESC ESP para completar el término actual. Choose search type. Elija tipo de búsqueda. Show query history Mostrar historial de consultas Enter search terms here. Introduzca los términos de búsqueda aquí. Main menu Menú principal SearchClauseW SearchClauseW Buscar clavija Any of these Cualquiera All of these Todas None of these Ninguna This phrase Frase Terms in proximity Términos en proximidad File name matching Nombre de fichero Select the type of query that will be performed with the words Elija el tipo de consulta que será realizada con las palabras Number of additional words that may be interspersed with the chosen ones Número de palabras adicionales que pueden ser intercaladas con las escogidas In field En campo No field Ningún campo Any Cualquiera All Todo None Ninguno Phrase Frase Proximity Proximidad File name Nombre de archivo Snippets Snippets Fragmentos X X Find: Buscar: Next Siguiente Prev Anterior SnippetsW Search Buscar <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Lo sentimos, no se encontró una coincidencia exacta dentro de los límites. Probablemente el documento es muy grande y el generador de fragmentos se perdió en un laberinto...</p> Sort By Relevance Ordenar por relevancia Sort By Page Ordenar por página Snippets Window Ventana de fragmentos Find Buscar Find (alt) Buscar (alto) Find Next Buscar Siguiente Find Previous Buscar Anterior Hide Ocultar Find next Buscar siguiente Find previous Buscar anterior Close window Cerrar ventana SortForm Date Fecha Mime type Tipo MIME SortFormBase Sort Criteria Ordenar criterios Sort the Ordenar most relevant results by: resultados más relevantes por: Descending Descendente Close Cerrar Apply Aplicar SpecIdxW Special Indexing Indexación especial Do not retry previously failed files. No vuelva a intentar archivos fallidos previamente. Else only modified or failed files will be processed. De lo contrario, sólo se procesarán los archivos modificados o fallidos. Erase selected files data before indexing. Borrar los datos de los archivos seleccionados antes de indexar. Directory to recursively index Directorio a índice recursivo Browse Buscar Start directory (else use regular topdirs): Directorio de inicio (si no usa topdirs normales): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Dejar en blanco para seleccionar todos los archivos. Puede utilizar varios patrones de tipo shell separados por espacios.<br>Los patrones con espacios incrustados deben ser comillados con comillas dobles.<br>Sólo se puede usar si el objetivo inicial está establecido. Selection patterns: Patrones de selección: Top indexed entity Entidad índice superior Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Directorio para el índice recursivo. Debe estar dentro del área indexada regular<br> como se define en el archivo de configuración (topdirs). Retry previously failed files. Reintentar archivos fallidos previamente. Start directory. Must be part of the indexed tree. We use topdirs if empty. Directorio de inicio. Debe ser parte del árbol indexado. Utilizamos topdirs si está vacío. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Directorio de inicio. Debe ser parte del árbol índice. Utilice el área índice completa si está vacía. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Explorador de términos &Expand &Expandir Alt+E Alt + E &Close &Cerrar Alt+C Alt+C Term Término No db info. No hay información de bd. Doc. / Tot. Doc./Tot. Match Lenguaje Case Distinción de mayúsculas Accents Acentos SpellW Wildcards Comodines Regexp Expresión regular Spelling/Phonetic Ortografía/fonética Aspell init failed. Aspell not installed? Inicialización de Aspell falló. Está instalado Aspell? Aspell expansion error. Error de expansión de Aspell. Stem expansion Expansión de raíces error retrieving stemming languages error al recuperar lenguajes para raíces No expansion found Expansión no encontrada Term Término Doc. / Tot. Doc./Tot. Index: %1 documents, average length %2 terms Índice: %1 documentos, largo promedio %2 términos Index: %1 documents, average length %2 terms.%3 results Índice: %1 documentos, largo promedio %2 términos. %3 resultados %1 results %1 resultados List was truncated alphabetically, some frequent La lista fue separada alfabéticamente, algunos términos terms may be missing. Try using a longer root. frecuentes pueden no aparecer. Intente usar una raíz más larga. Show index statistics Mostrar estadísticas del índice Number of documents Número de documentos Average terms per document Términos promedio por documento Smallest document length Tamaño del documento más pequeño Longest document length Tamaño del documento más grande Database directory size Tamaño del directorio de la base de datos MIME types: Tipos MIME: Item Elemento Value Valor Smallest document length (terms) Longitud más pequeña del documento (términos) Longest document length (terms) Longitud más larga del documento (términos) Results from last indexing: Resultados de la última indexación: Documents created/updated Documentos creados/actualizados Files tested Archivos probados Unindexed files Archivos indexados List files which could not be indexed (slow) Listar archivos que no pudieron ser indexados (lentos) Spell expansion error. Error de expansión ortografía. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index El directorio seleccionado no parece ser un índice Xapian This is the main/local index! Este es el índice local o principal! The selected directory is already in the index list El directorio seleccionado ya está en la lista de índices Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Seleccione el directorio para el índice Xapian (ej: /home/buddy/.recoll/xapiandb) error retrieving stemming languages error al recuperar lenguajes para raíces Choose Elegir Result list paragraph format (erase all to reset to default) Formato de párrafo para la lista de resultados (borre todo para volver al valor por defecto) Result list header (default is empty) Encabezado de la lista de resultados (valor por defecto es vacío) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Seleccionar el directorio de configuración de recoll o el directorio para el índice xapian (ej: /home/me/.recoll o /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read El directorio seleccionado parecer ser un directorio de configuración de Recoll pero la configuración no puede ser leída At most one index should be selected Al menos un índice debe ser seleccionado Cant add index with different case/diacritics stripping option No se puede agregar un índice con diferente opción para remover mayúsculas/minúsculas/diacríticos Default QtWebkit font Default QtWebkit font Any term Cualquier término All terms Todos los términos File name Nombre de archivo Query language Lenguaje de consulta Value from previous program exit Valor de salida del programa anterior Context Contexto Description Descripción Shortcut Acceso directo Default Por defecto Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Interfaz de usuario Number of entries in a result page Número de elementos en la página de resultados Result list font Tipo de letra para lista de resultados Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Abre una ventana para seleccionar el tipo de letra para la lista de resultados Reset Restaurar Resets the result list font to the system default Restaurar el tipo de letra de la lista de resultados al valor por defecto del sistema Auto-start simple search on whitespace entry. Auto iniciar búsqueda simple al entrar espacios en blanco. Start with advanced search dialog open. Iniciar con la ventana de búsqueda avanzada abierta. Start with sort dialog open. Comenzar con el diálogo de ordenar abierto. Search parameters Parámetros de búsqueda Stemming language Lenguaje de raíces Dynamically build abstracts Construir resúmenes dinámicamente Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. ¿Intentar construir resúmenes para elementos en la lista de resultados utilizando el contexto de los términos de búsqueda? Puede ser lento para documentos grandes. Replace abstracts from documents Reemplazar resúmenes de los documentos Do we synthetize an abstract even if the document seemed to have one? ¿Sintetizar un resumen aunque el documento parece tener uno? Synthetic abstract size (characters) Tamaño del resumen sintetizado (caracteres) Synthetic abstract context words Palabras de contexto del resumen sintetizado External Indexes Índices Externos Add index Añadir índice Select the xapiandb directory for the index you want to add, then click Add Index Seleccione el directorio xapiandb para el índice que desea añadir, luego haga clic en Agregar índice Browse Buscar &OK &Aceptar Apply changes Aplicar cambios &Cancel &Cancelar Discard changes Descartar cambios Result paragraph<br>format string Texto de formato para<br>párrafo de resultados Automatically add phrase to simple searches Automáticamente añadir frases a búsquedas simples A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Una búsqueda por [rolling stones] (2 términos) será cambiada por [rolling or stones or (rolling phrase 2 stones)]. Esto dará mayor precedencia a los resultados en los cuales los términos de búsqueda aparecen exactamente como fueron escritos. User preferences Preferencias de usuario Use desktop preferences to choose document editor. Usar preferencias del escritorio para seleccionar editor de documentos. External indexes Índice externo Toggle selected Cambiar selección Activate All Activar Todos Deactivate All Desactivar Todos Remove selected Eliminar selección Remove from list. This has no effect on the disk index. Eliminar de la lista. Esto no tiene efecto en el índice en disco. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Define el formato para cada párrafo de resultados. Utilice formato qt-html y reemplazos estilo printf:<br>%A Resumen<br> %D Fecha<br> %I Nombre del ícono<br> %K Palabras clave (si existen)<br> %L Enlaces de vista previa y edición<br> %M Tipo MIME<br> %Número de resultado<br> %R Porcentaje de relevancia<br> %S Información de tamaño<br> %T Título<br> %U Url<br> Remember sort activation state. Recordar estado de activación de ordenamiento. Maximum text size highlighted for preview (megabytes) Tamaño máximo de texto resaltado para vista previa (megabytes) Texts over this size will not be highlighted in preview (too slow). Textos más allá de este tamaño no serán resaltados (muy lento). Highlight color for query terms Color de resaltado para términos de búsqueda Prefer Html to plain text for preview. Preferir HTML a texto simple para vista previa. If checked, results with the same content under different names will only be shown once. Si está marcado, los resultados con el mismo contenido bajo nombres diferentes serán mostrados solo una vez. Hide duplicate results. Esconder resultados duplicados. Choose editor applications Escoger aplicaciones para edición Display category filter as toolbar instead of button panel (needs restart). Mostrar filtros de categorías como barra de herramientas en lugar de panel de botones (necesita reinicio). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Las palabras en la lista serán convertidas automáticamente a cláusulas ext:xxx en el ingreso de lenguaje de consulta. Query language magic file name suffixes. Sufijos para nombres mágicos de archivos en el lenguaje de consulta. Enable Habilitar ViewAction Changing actions with different current values Cambiando acciones con valores actuales diferentes Mime type Tipo MIME Command Comando MIME type Tipo MIME Desktop Default Valor predeterminado del ambiente de escritorio Changing entries with different current values Cambiando entradas con diferentes valores actuales ViewActionBase File type Tipo de archivo Action Accin Select one or several file types, then click Change Action to modify the program used to open them Seleccione uno o varios tipos de fichero, luego presione Cambiar Acción para modificar el programa usado para abrirlos Change Action Cambiar Acción Close Cerrar Native Viewers Visualizadores Nativos Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Seleccione uno o varios tipos MIME y presione "Cambiar Acción"<br>Puede también cerrar esta ventana y marcar "Usar preferencias del escritorio"<br>en el panel principal para ignorar esta lista y usar los valores estándar de su escritorio. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Seleccione uno o más tipos mime, y use los controles en la caja abajo para cambiar cómo se procesan. Use Desktop preferences by default Usar preferencias del escritorio como estándar Select one or several file types, then use the controls in the frame below to change how they are processed Seleccione uno o más tipos de archivos, y use los controles en la caja abajo para cambiar cómo se procesan Exception to Desktop preferences Excepción de las preferencias del escritorio Action (empty -> recoll default) Acción (vacío -> valor por defecto de recoll) Apply to current selection Aplicar a la selección actual Recoll action: Acción current value valor Select same Seleccionar misma <b>New Values:</b> <b>Nuevos valores</b> Webcache Webcache editor Editor de caché web Search regexp Buscar regexp TextLabel WebcacheEdit Copy URL Copiar URL Unknown indexer state. Can't edit webcache file. Estado indexador desconocido. Puede'editar archivo de caché web. Indexer is running. Can't edit webcache file. El indexador se está ejecutando. Puede't editar archivo de caché web. Delete selection Eliminar selección Webcache was modified, you will need to run the indexer after closing this window. La caché web fue modificada, necesitarás ejecutar el indexador después de cerrar esta ventana. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url Url Date Fecha Size URL URL WinSchedToolW Error Error Configuration not initialized Configuración no inicializada <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexing batch scheduling</h3><p>Utilizamos el planificador de tareas estándar de Windows para esto. El programa se iniciará cuando haga clic en el botón de abajo.</p><p>Puede utilizar la interfaz completa (<i>Crear tarea</i> en el menú de la derecha), o el asistente de <i>Crear tarea básica</i> . En ambos casos Copiar/Pegar la ruta del archivo por lotes que aparece a continuación como la <i>Acción</i> a realizar.</p> Command already started Comando ya iniciado Recoll Batch indexing Indexación de Lote Recoll Start Windows Task Scheduler tool Iniciar la herramienta Planificador de tareas de Windows Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Robar cola de indexado de Beagle Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle NO DEBE estar ejecutándose. Habilita procesar la cola para indexar el historial web de Firefox de Beagle.<br>(debe también instalar el plugin Beagle para Firefox) Web cache directory name Nombre del directorio de caché web The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. El nombre de un directorio donde almacenar la caché para las páginas web visitadas.<br>Se toma una ruta no absoluta relativa al directorio de configuración. Max. size for the web cache (MB) Tamaño máximo para la caché web (MB) Entries will be recycled once the size is reached Las entradas serán recicladas una vez que el tamaño es alcanzado Web page store directory name Nombre del directorio del almacén para páginas web The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. El nombre del directorio dónde almacenar las copias de páginas web visitadas.<br>Una ruta de directorio no absoluta es utilizada, relativa al directorio de configuración. Max. size for the web store (MB) Tamaño máximo para el almacén web (MB) Process the WEB history queue Procesar la cola del historial WEB Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Habilita la indexación de páginas visitadas en Firefox.<br>(necesita también el plugin Recoll para Firefox) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Las entradas serán recicladas una vez que se alcance el tamaño.<br>Solo aumentar el tamaño realmente tiene sentido porque reducir el valor no truncará un archivo existente (solo perder espacio al final). confgui::ConfIndexW Can't write configuration file No se puede escribir archivo de configuración Recoll - Index Settings: Recoll - Configuración de índice: confgui::ConfParamFNW Browse Buscar Choose Elegir confgui::ConfParamSLW + + - - Add entry Añadir entrada Delete selected entries Eliminar entradas seleccionadas ~ ~ Edit selected entries Editar entradas seleccionadas confgui::ConfSearchPanelW Automatic diacritics sensitivity Sensibilidad automática de diacríticos <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Habilitar automáticamente la sensibilidad de diacríticos si el término de búsqueda tiene caracteres acentuados (no presentes en unac_except_trans). De otra forma necesita usar el lenguage de búsqueda y el modificador <i>D</i> para especificar la sensibilidad de los diacríticos. Automatic character case sensitivity Sensibilidad automática a la distinción de mayúsculas/minúsculas de los caracteres <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Habilitar automáticamente la sensibilidad a las mayúsculas/minúsculas si la entrada tiene caracteres en mayúscula en una posición distinta al primer caracter. De otra forma necesita usar el lenguaje de búsqueda y el modificador <i>C</i> para especificar la sensibilidad a las mayúsculas y minúsculas. Maximum term expansion count Máximo conteo de expansión de términos <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Máxima expansión de conteo para un solo término (ej: cuando se usan comodines). El valor por defecto de 10000 es razonable y evitará consultas que parecen congelarse mientras el motor de búsqueda recorre la lista de términos. Maximum Xapian clauses count Máximo conteo de cláusulas de Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Número máximo de cláusulas elementales agregadas a una consulta de Xapian. En algunos casos, el resultado de la expansión de términos puede ser multiplicativo, y deseamos evitar el uso excesivo de memoria. El valor por defecto de 100000 debería ser lo suficientemente alto en la mayoría de los casos, y compatible con las configuraciones de hardware típicas en la actualidad. confgui::ConfSubPanelW Global Global Max. compressed file size (KB) Tamaño máximo de archivo comprimido (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Este valor establece un umbral mas allá del cual los archivos<br>comprimidos no serán procesados. Escriba 1 para no tener límite,<br>o el número 0 para nunca hacer descompresión. Max. text file size (MB) Tamaño máximo para archivo de texto (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Este valor establece un umbral más allá del cual los archivos de texto no serán procesados.<br>Escriba 1 para no tener límites. Este valor es utilizado para excluir archivos de registro gigantescos del índice. Text file page size (KB) Tamaño de página para archivo de texto (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Si se utiliza este valor (diferente de -1), los archivos de texto serán separados en partes de este tamaño para ser indexados. Esto ayuda con las búsquedas de archivos de texto muy grandes (ej: archivos de registro). Max. filter exec. time (S) Tiempo máximo de ejecución de filtros (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Filtros externos que se ejecuten por más tiempo del establecido serán abortados.<br>Esto ocurre en los raros casos (ej: postscript) cuando un documento hace que un filtro entre en un ciclo.<br>Establezca un valor de -1 para no tener límite. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Filtros externos que se ejecuten por más tiempo del establecido serán detenidos. Esto es por el caso inusual (ej: postscript) dónde un documento puede causar que un filtro entre en un ciclo infinito. Establezca el número -1 para indicar que no hay límite. Only mime types Solo tipos mime An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Una lista exclusiva de tipos de mime indexados.<br>Nada más será indexado. Normalmente vacío e inactivo Exclude mime types Excluir tipos mime Mime types not to be indexed Tipos Mime que no deben ser indexados Max. filter exec. time (s) Máximo filtro exec. tiempo (s) confgui::ConfTopPanelW Top directories Directorios primarios The list of directories where recursive indexing starts. Default: your home. La lista de directorios donde la indexación recursiva comienza. Valor por defecto: su directorio personal. Skipped paths Directorios omitidos These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Estos son los nombres de directorios los cuales no se indexan.<br>Puede contener comodines. Debe corresponder a las rutas vistas por el indexador (ej: si los directorios primarios incluyen '/home/me' y '/home' es en realidad un enlace a '/usr/home', la entrada correcta para directorios omitidos sería '/home/me/tmp*', no '/usr/home/me/tmp*') Stemming languages Lenguajes para raíces The languages for which stemming expansion<br>dictionaries will be built. Los lenguajes para los cuales los diccionarios de expansión de raíces serán creados. Log file name Nombre de archivo de registro The file where the messages will be written.<br>Use 'stderr' for terminal output El archivo donde los mensajes serán escritos.<br>Use 'stderr' para salida a la terminal Log verbosity level Nivel de verbosidad del registro This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Este valor ajusta la cantidad de mensajes,<br>desde solamente errores hasta montones de información de depuración. Index flush megabytes interval Intervalo en megabytes de escritura del índice This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Este valor ajusta la cantidad de datos indexados entre escrituras al disco.<br> Esto ayuda a controlar el uso de memoria del indexador. Valor estándar 10MB Max disk occupation (%) Utilización máxima de disco (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Este es el porcentaje de utilización de disco donde la indexación fallará y se detendrá (para evitar llenarle el disco).<br>0 significa sin límites (valor por defecto). No aspell usage No utilizar aspell Aspell language Lenguaje Aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. El lenguaje para el diccionario aspell. Esto debe ser algo como 'en' o 'fr'...<br>Si este valor no se especifica, el ambiente NLS será usado para averiguarlo, lo cual usualmente funciona. Para tener una idea de qué esta instalado en su sistema escriba 'aspell-config' y busque por ficheros .dat dentro del directorio 'data-dir'. Database directory name Nombre del directorio de base de datos The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Nombre del directorio donde almacenar el índice.<br>Un valor no absoluto para la ruta de directorio es usado, relativo al directorio de configuración. El valor estándar es 'xapiandb'. Use system's 'file' command Utilizar el comando 'file' del sistema Use the system's 'file' command if internal<br>mime type identification fails. Utilizar el comando 'file' del sistema si la identificación interna de tipos MIME falla. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Deshabilita el uso de aspell para generar aproximaciones ortográficas en la herramienta explorador de términos.<br>Útil si aspell no se encuentra o no funciona. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. El lenguaje para el diccionario aspell. Esto debería ser algo como 'en' o 'fr' ...<br>Si no se establece este valor, el ambiente NLS será utilizado para calcularlo, lo cual usualmente funciona. Para tener una idea de lo que está instalado en sus sistema, escriba 'aspell-config' y busque archivos .dat dentro del directorio 'data-dir'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. El nombre de un directorio donde almacenar el índice.<br>Una ruta no absoluta se interpreta como relativa al directorio de configuración. El valor por defecto es 'xapiandb'. Unac exceptions Excepciones Unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Estas son excepciones al mecanismo unac, el cual, de forma predeterminada, elimina todos los diacríticos, y realiza una descomposición canónica. Es posible prevenir la eliminación de acentos para algunos caracteres, dependiendo de su lenguaje, y especificar descomposiciones adicionales, por ejemplo, para ligaturas. En cada entrada separada por espacios, el primer caracter es el origen, y el resto es la traducción. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Estos son los nombres de los directorios que no entrarán en la indexación.<br>Los elementos de ruta pueden contener comodines. Las entradas deben coincidir con las rutas vistas por el indexador (p. ej. si topdirs incluye '/home/me' y '/home' es en realidad un enlace a '/usr/home', una entrada correcta de skippedPath sería '/home/me/tmp*', no '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Máxima ocupación de disco (%, 0 significa sin límite) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Este es el porcentaje de uso del disco - uso total del disco, no tamaño del índice- en el que la indexación fallará y se detendrá.<br>El valor predeterminado de 0 elimina cualquier límite. uiPrefsDialogBase User preferences Preferencias de usuario User interface Interfaz de usuario Number of entries in a result page Número de elementos en la página de resultados If checked, results with the same content under different names will only be shown once. Si está marcado, los resultados con el mismo contenido bajo nombres diferentes serán mostrados solo una vez. Hide duplicate results. Esconder resultados duplicados. Highlight color for query terms Color de resaltado para términos de búsqueda Result list font Tipo de letra para lista de resultados Opens a dialog to select the result list font Abre una ventana para seleccionar el tipo de letra para la lista de resultados Helvetica-10 Helvetica-10 Resets the result list font to the system default Restaurar el tipo de letra de la lista de resultados al valor por defecto del sistema Reset Restaurar Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Define el formato para cada párrafo de resultados. Utilice formato qt-html y reemplazos estilo printf:<br>%A Resumen<br> %D Fecha<br> %I Nombre del ícono<br> %K Palabras clave (si existen)<br> %L Enlaces de vista previa y edición<br> %M Tipo MIME<br> %Número de resultado<br> %R Porcentaje de relevancia<br> %S Información de tamaño<br> %T Título<br> %U Url<br> Result paragraph<br>format string Texto de formato para<br>párrafo de resultados Texts over this size will not be highlighted in preview (too slow). Textos más allá de este tamaño no serán resaltados (muy lento). Maximum text size highlighted for preview (megabytes) Tamaño máximo de texto resaltado para vista previa (megabytes) Use desktop preferences to choose document editor. Usar preferencias del escritorio para seleccionar editor de documentos. Choose editor applications Escoger aplicaciones para edición Display category filter as toolbar instead of button panel (needs restart). Mostrar filtros de categorías como barra de herramientas en lugar de panel de botones (necesita reinicio). Auto-start simple search on whitespace entry. Auto iniciar búsqueda simple al entrar espacios en blanco. Start with advanced search dialog open. Iniciar con la ventana de búsqueda avanzada abierta. Start with sort dialog open. Comenzar con el diálogo de ordenar abierto. Remember sort activation state. Recordar estado de activación de ordenamiento. Prefer Html to plain text for preview. Preferir HTML a texto simple para vista previa. Search parameters Parámetros de búsqueda Stemming language Lenguaje de raíces A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Una búsqueda por [rolling stones] (2 términos) será cambiada por [rolling or stones or (rolling phrase 2 stones)]. Esto dará mayor precedencia a los resultados en los cuales los términos de búsqueda aparecen exactamente como fueron escritos. Automatically add phrase to simple searches Automáticamente añadir frases a búsquedas simples Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. ¿Intentar construir resúmenes para elementos en la lista de resultados utilizando el contexto de los términos de búsqueda? Puede ser lento para documentos grandes. Dynamically build abstracts Construir resúmenes dinámicamente Do we synthetize an abstract even if the document seemed to have one? ¿Sintetizar un resumen aunque el documento parece tener uno? Replace abstracts from documents Reemplazar resúmenes de los documentos Synthetic abstract size (characters) Tamaño del resumen sintetizado (caracteres) Synthetic abstract context words Palabras de contexto del resumen sintetizado The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Las palabras en la lista serán convertidas automáticamente a cláusulas ext:xxx en el ingreso de lenguaje de consulta. Query language magic file name suffixes. Sufijos para nombres mágicos de archivos en el lenguaje de consulta. Enable Habilitar External Indexes Índices Externos Toggle selected Cambiar selección Activate All Activar Todos Deactivate All Desactivar Todos Remove from list. This has no effect on the disk index. Eliminar de la lista. Esto no tiene efecto en el índice en disco. Remove selected Eliminar selección Click to add another index directory to the list Presione para añadir otro directorio de índice a la lista Add index Añadir índice Apply changes Aplicar cambios &OK &Aceptar Discard changes Descartar cambios &Cancel &Cancelar Abstract snippet separator Separador de fragmentos de resumen Use <PRE> tags instead of <BR>to display plain text as html. Utilizar etiquetas <PRE> en lugar de <BR> para mostrar texto simple como html. Lines in PRE text are not folded. Using BR loses indentation. Líneas en texto PRE no se parten. Al usar BR se pierde indentación. Style sheet Hoja de estilo Opens a dialog to select the style sheet file Abre una ventana de diálogo para seleccionar la hoja de estilos Choose Elegir Resets the style sheet to default Restablecer la hoja de estilo al valor por defecto Lines in PRE text are not folded. Using BR loses some indentation. Líneas en texto PRE no se parten. Al usar BR se pierde indentación. Use <PRE> tags instead of <BR>to display plain text as html in preview. Use etiquetas <PRE> en lugar de <BR> para desplegar texto corriente como html en la vista previa. Result List Lista de resultados Edit result paragraph format string Editar texto de formato para el párrafo de resultados Edit result page html header insert Editar encabezado html insertado en página de resultados Date format (strftime(3)) Formato de fecha (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Umbral de porcentaje de frecuencia sobre el cuál no utilizamos términos dentro de la autofrase. Los términos frequentes son un problema importante de desempeño con las frases. Términos omitidos aumenta la holgura de la frase, y reducen la eficiencia de la autofrase. El valor por defecto es 2 (por ciento). Autophrase term frequency threshold percentage Porcentaje del umbral de frecuencia de términos de autofrase Plain text to HTML line style Texto común a estilo de línea HTML Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Las líneas en texto PRE no son dobladas. Al usar BR se pierde indentación. El estilo PRE + Wrap probablemente es lo que está buscando. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + envoltura Exceptions Excepciones Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Tipos Mime que no deben pasarse a xdg-open incluso cuando "Usar preferencias de escritorio" está establecido.<br> Útil para pasar número de página y opciones de cadena de búsqueda para, por ejemplo, evince. Disable Qt autocompletion in search entry. Deshabilitar autocompletar de Qt en la entrada de búsqueda. Search as you type. Buscar al escribir. Paths translations Rutas de traducciones Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Haga clic para agregar otro directorio de índice a la lista. Puede seleccionar un directorio de configuración de Recoll o un índice Xapian. Snippets window CSS file Archivo CSS para la ventana de fragmentos Opens a dialog to select the Snippets window CSS style sheet file Abre una ventana de diálogo para el archivo de estilos CSS de la ventana de fragmentos Resets the Snippets window style Establece el valor por defecto para el estilo de la ventana de Fragmentos Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Decide si los filtros de documentos se muestran como botones de radio, combobox, barra de herramientas o menú. Document filter choice style: Estilo de selección de filtro de documentos: Buttons Panel Panel de botones Toolbar Combobox Combobox barra de herramientas Menu Menú Show system tray icon. Mostrar icono de la bandeja del sistema. Close to tray instead of exiting. Cerrar la bandeja en lugar de salir. Start with simple search mode Empezar con el modo de búsqueda simple Show warning when opening temporary file. Mostrar advertencia al abrir el archivo temporal. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Estilo de usuario para aplicar a la ventana de fragmentos.<br> Nota: la inserción del encabezado de página de resultado también está incluida en el encabezado de la ventana de fragmentos. Synonyms file Archivo de sinónimos Highlight CSS style for query terms Resaltar el estilo CSS para los términos de consulta Recoll - User Preferences Recoll - Preferencias de usuario Set path translations for the selected index or for the main one if no selection exists. Establece traducciones de rutas para el índice seleccionado o para el principal si no existe selección. Activate links in preview. Activar enlaces en vista previa. Make links inside the preview window clickable, and start an external browser when they are clicked. Haga clic en los enlaces dentro de la ventana de vista previa e inicie un navegador externo cuando se haga clic en ellos. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Resaltado de términos de consulta en resultados. <br>Tal vez intente algo como "color:red;fondo:amarillo" para algo más animado que el azul predeterminado... Start search on completer popup activation. Empezar la búsqueda al activar la ventana emergente. Maximum number of snippets displayed in the snippets window Número máximo de fragmentos mostrados en la ventana de fragmentos Sort snippets by page number (default: by weight). Ordenar fragmentos por número de página (por defecto: por peso). Suppress all beeps. Suprimir todos los pitidos. Application Qt style sheet Hoja de estilo Qt de aplicación Limit the size of the search history. Use 0 to disable, -1 for unlimited. Limita el tamaño del historial de búsqueda. Usa 0 para desactivar, -1 para ilimitado. Maximum size of search history (0: disable, -1: unlimited): Tamaño máximo del historial de búsqueda (0: deshabilitable, -1: ilimitado): Generate desktop notifications. Generar notificaciones de escritorio. Misc Misc Work around QTBUG-78923 by inserting space before anchor text Trabaje alrededor de QTBUG-78923 insertando espacio antes del texto del anclaje Display a Snippets link even if the document has no pages (needs restart). Mostrar un enlace de Snippets incluso si el documento no tiene páginas (necesita reiniciar). Maximum text size highlighted for preview (kilobytes) Tamaño máximo de texto resaltado para la previsualización (kilobytes) Start with simple search mode: Empezar con el modo de búsqueda simple: Hide toolbars. Ocultar barras de herramientas. Hide status bar. Ocultar barra de estado. Hide Clear and Search buttons. Ocultar botones de Borrar y Buscar. Hide menu bar (show button instead). Ocultar barra de menú (mostrar botón en su lugar). Hide simple search type (show in menu only). Ocultar tipo de búsqueda simple (mostrar sólo en el menú). Shortcuts Atajos Hide result table header. Ocultar la cabecera de tabla de resultados. Show result table row headers. Mostrar las cabeceras de fila de la tabla de resultados. Reset shortcuts defaults Restablecer accesos directos por defecto Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Deshabilita los accesos directos Ctrl+[0-9]/[a-z] para saltar a filas de la tabla. Use F1 to access the manual Usar F1 para acceder al manual Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Tipo de búsqueda simple Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Modo oscuro Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Tabla de Resultados Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_el.ts0000644000175000017500000106467014444307651014251 00000000000000 ActSearchDLG Menu search Μενού αναζήτησης AdvSearch All clauses Όλες οι ρήτρες Any clause Οποιαδήποτε ρήτρα texts κείμενα spreadsheets φύλλα εργασίας presentations παρουσιάσεις media πολυμέσα messages Μηνύματα other άλλα Bad multiplier suffix in size filter Κακό επίθεμα πολλαπλασιαστή στο φίλτρο μεγέθους text κείμενο spreadsheet λογιστικό φύλλο presentation παρουσίαση message μήνυμα Advanced Search Προχωρημένη αναζήτηση History Next Ιστορικό Επόμενο History Prev Προηγούμενο Ιστορικό Load next stored search Φόρτωση της επόμενης αποθηκευμένης αναζήτησης Load previous stored search Φόρτωση προηγούμενης αποθηκευμένης αναζήτησης AdvSearchBase Advanced search Προχωρημένη αναζήτηση Restrict file types Περιορισμός του τύπου αρχείων Save as default Αποθήκευση ως προεπιλογή Searched file types Αναζητούμενοι τύποι αρχείων All ----> Όλα ----> Sel -----> Επιλ ----> <----- Sel <----- Επιλ <----- All <----- Όλα Ignored file types Τύποι αρχείων που θα αγνοηθούν Enter top directory for search Εισαγάγετε τον κατάλογο εκκίνησης της αναζήτησης Browse Περιήγηση Restrict results to files in subtree: Περιορισμός των αποτελεσμάτων στα αρχεία του δέντρου: Start Search Εκκίνηση αναζήτησης Search for <br>documents<br>satisfying: Αναζήτηση <br>εγγράφων<br>που ικανοποιούν: Delete clause Διαγραφή ρήτρας Add clause Προσθήκη ρήτρας Check this to enable filtering on file types Ενεργοποιήστε αυτή την επιλογή για να χρησιμοποιηθεί το φιλτράρισμα στους τύπους αρχείων By categories Ανά κατηγορία Check this to use file categories instead of raw mime types Επιλέξτε το για να χρησιμοποιήσετε τις κατηγορίες αρχείων αντί των τύπων mime Close Κλείσιμο All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Όλα τα μη κενά πεδία στα δεξιά θα συνδυαστούν με ένα συνδυασμό ΚΑΙ (επιλογή «Όλες οι ρήτρες») ή Ή (επιλογή «Μια από τις ρήτρες»). <br> Τα πεδία του τύπου «Μια από αυτές τις λέξεις», «Όλες οι λέξεις» και «Καμιά από αυτές τις λέξεις» δέχονται ένα ανακάτεμα λέξεων και φράσεων σε εισαγωγικά. <br>Τα κενά πεδία αγνοούνται. Invert Αντιστροφή Minimum size. You can use k/K,m/M,g/G as multipliers Ελάχιστο μέγεθος: Μπορείτε να χρησιμοποιήσετε τα k/K,m/M,g/G ως πολλαπλασιαστές Min. Size Ελαχ. μέγεθος Maximum size. You can use k/K,m/M,g/G as multipliers Μέγιστο μέγεθος: Μπορείτε να χρησιμοποιήσετε τα k/K,m/M,g/G ως πολλαπλασιαστές Max. Size Μέγ. μέγεθος Select Επιλογή Filter Φίλτρο From Από To Έως Check this to enable filtering on dates Επιλέξτε αυτό για να ενεργοποιήσετε το φίλτρο στις ημερομηνίες Filter dates Φίλτρο ημερομηνίας Find Αναζήτηση Check this to enable filtering on sizes Επιλέξτε αυτό για να ενεργοποιήσετε το φιλτράρισμα στο μέγεθος αρχείων Filter sizes Φίλτρο μεγέθους Filter birth dates ConfIndexW Can't write configuration file Αδύνατη η εγγραφή του αρχείου διαμόρφωσης Global parameters Καθολικές ρυθμίσεις Local parameters Τοπικές ρυθμίσεις Search parameters Ρυθμίσεις αναζήτησης Top directories Κατάλογοι εκκίνησης The list of directories where recursive indexing starts. Default: your home. Η λίστα των καταλόγων για την έναρξη της αναδρομικής ευρετηρίασης. Προεπιλογή: ο προσωπικός σας κατάλογος. Skipped paths Παραλειπόμενες διαδρομές These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Αυτά είναι ονόματα διαδρομών καταλόγων που δεν θα εισέλθουν στο ευρετήριο.<br>Τα στοιχεία της διαδρομής μπορεί να περιέχουν μπαλαντέρ (wildcards). Οι καταχωρήσεις πρέπει να ταιριάζουν με τις διαδρομές που βλέπει ο ευρετής (π.χ. εάν οι topdirs περιλαμβάνουν '/home/me' και '/home' είναι στην πραγματικότητα ένας σύνδεσμος με '/usr/home', μια σωστή καταχώρηση skippedPath θα είναι '/home/me/tmp*', όχι '/usr/home/me/tmp*') Stemming languages Γλώσσα για την επέκταση των όρων The languages for which stemming expansion<br>dictionaries will be built. Οι γλώσσες για τις οποίες θα δημιουργηθούν τα λεξικά επεκτάσεων<br>των όρων. Log file name Όνομα του αρχείου καταγραφών The file where the messages will be written.<br>Use 'stderr' for terminal output Το αρχείο που θα εγγραφούν τα μηνύματα.<br>Χρησιμοποιήστε 'stderr' για την έξοδο τερματικού Log verbosity level Επίπεδο ανάλυσης των καταγραφών This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Αυτή η τιμή ρυθμίζει την ποσότητα των απεσταλμένων μηνυμάτων,<br>από μόνο τα σφάλματα μέχρι πολλά δεδομένα αποσφαλμάτωσης. Index flush megabytes interval Καθυστέρηση εγγραφής του ευρετηρίου σε megabyte This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Αυτή η τιμή ρυθμίζει την ποσότητα των δεδομένων που δεικτοδοτούνται μεταξύ των εγγραφών στο δίσκο.<br>Βοηθά στον έλεγχο χρήσης της μνήμης. Προεπιλογή: 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Αυτό είναι το ποσοστό χρήσης δίσκου - συνολική χρήση δίσκων, όχι μέγεθος δείκτη - στο οποίο ευρετηρίαση θα αποτύχει και να σταματήσει.<br>Η προεπιλεγμένη τιμή του 0 αφαιρεί οποιοδήποτε όριο. No aspell usage Χωρίς χρήση του aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Απενεργοποιεί τη χρήση του aspell για τη δημιουργία των ορθογραφικών προσεγγίσεων.<br>Χρήσιμο αν το aspell δεν είναι εγκατεστημένο ή δεν λειτουργεί. Aspell language Γλώσσα του aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Η γλώσσα για το λεξικό aspell. Αυτό θα πρέπει να είναι του τύπου «en» ή «el» ...<br> Αν αυτή η τιμή δεν οριστεί, χρησιμοποιείται το εθνικό περιβάλλον NLS για να την υπολογίσει, που συνήθως δουλεύει. Για να πάρετε μια ιδέα του τι είναι εγκατεστημένο στο σύστημά σας, πληκτρολογήστε «aspell config» και παρατηρήστε τα αρχεία .dat στον κατάλογο «data-dir». Database directory name Κατάλογος αποθήκευσης του ευρετηρίου The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Το όνομα του καταλόγου αποθήκευσης του ευρετηρίου<br>Μια σχετική διαδρομή αναφερόμενη στη διαδρομή διαμόρφωσης. Η εξ' ορισμού είναι «xapiandb». Unac exceptions Εξαιρέσεις unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Αυτές είναι εξαιρέσεις για τον μηχανισμό unac, ο οποίος εξ' ορισμού, αφαιρεί όλους τους τονισμούς, και πραγματοποιεί κανονική αποσύνθεση. Μπορείτε να αναιρέσετε την αφαίρεση των τονισμών για ορισμένους χαρακτήρες, ανάλογα με τη γλώσσα σας, και διευκρινίστε άλλους αποσυνθέσεις, για παράδειγμα συμπλεγμένους χαρακτήρες. Στη λίστα διαχωρισμένη με κενά, ο πρώτος χαρακτήρας ενός αντικειμένου είναι η πηγή, το υπόλοιπο είναι η μετάφραση. Process the WEB history queue Επεξεργασία της ουράς ιστορικού του Ιστού Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Ενεργοποιεί τη δεικτοδότηση των επισκεπτόμενων σελίδων στον Firefox.<br>(θα πρέπει να εγκαταστήσετε και το πρόσθετο Firefox Recoll) Web page store directory name Όνομα καταλόγου αποθήκευσης ιστοσελίδων The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Το όνομα του καταλόγου αποθήκευσης αντιγράφων των επισκεφθέντων ιστοσελίδων.<br>Μια σχετική διαδρομή αναφερόμενη στη διαδρομή διαμόρφωσης. Max. size for the web store (MB) Μέγ. μέγεθος της λανθάνουσας μνήμης ιστού (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Οι εγγραφές θα ανακυκλωθούν μόλις επιτευχθεί το μέγεθος.<br>Μόνο η αύξηση του μεγέθους έχει πραγματικά νόημα, επειδή η μείωση της τιμής δεν θα περικόψει ένα υπάρχον αρχείο (μόνο χώρο αποβλήτων στο τέλος). Automatic diacritics sensitivity Αυτόματη ευαισθησία στους τόνους <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Αυτόματη εναλλαγή ευαισθησίας τονισμού αν ο όρος αναζήτησης διαθέτει τονισμένους χαρακτήρες (εκτός αυτών του unac_except_trans). Διαφορετικά θα πρέπει να χρησιμοποιήσετε τη γλώσσα της αναζήτησης και τον τροποποιητή <i>D</i> για τον καθορισμό της ευαισθησίας τονισμών. Automatic character case sensitivity Αυτόματη ευαισθησία πεζών/κεφαλαίων <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Αυτόματη εναλλαγή ευαισθησίας διάκρισης πεζών/κεφαλαίων αν η ο όρος αναζήτησης διαθέτει κεφαλαία γράμματα (εκτός του πρώτου γράμματος). Διαφορετικά θα πρέπει να χρησιμοποιήσετε τη γλώσσα της αναζήτησης και τον τροποποιητή <i>C</i> για τον καθορισμό της ευαισθησίας διάκρισης πεζών / κεφαλαίων. Maximum term expansion count Μέγιστο μέγεθος επέκτασης ενός όρου <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Μέγιστος αριθμός επέκτασης για έναν όρο (π.χ.: κατά τη χρήση χαρακτήρων υποκατάστασης). Η προκαθορισμένη τιμή 10000 είναι λογική και θα αποφύγει ερωτήματα που εμφανίζονται σαν παγωμένα την ίδια στιγμή που η μηχανή διαπερνά τη λίστα όρων. Maximum Xapian clauses count Μέγιστος αριθμός ρητρών Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Μέγιστος αριθμός στοιχειωδών ρητρών που προσθέτουμε σε ένα απλό ερώτημα Xapian. Σε μερικές περιπτώσεις, το αποτέλεσμα της επέκτασης των όρων μπορεί να είναι πολλαπλασιαστικό, και θα χρησιμοποιούσε υπερβολική μνήμη. Η προκαθορισμένη τιμή 100000 θα πρέπει να είναι επαρκής και συμβατή με μια τυπική διαμόρφωση υλικού. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... Οι γλώσσες για τις οποίες θα κατασκευαστούν οριοθετημένα λεξικά επέκτασης.<br>Δείτε την τεκμηρίωση Xapian stemmer για πιθανές αξίες. Π.χ. αγγλικά, γαλλικά, γερμανικά... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Η γλώσσα για το λεξικό aspell. Οι τιμές είναι κωδικοί γλώσσας 2 γραμμάτων, π.χ. 'en', 'fr' . .<br>Εάν αυτή η τιμή δεν οριστεί, το περιβάλλον NLS θα χρησιμοποιηθεί για τον υπολογισμό, το οποίο συνήθως λειτουργεί. Για να πάρετε μια ιδέα για το τι είναι εγκατεστημένο στο σύστημά σας, πληκτρολογήστε 'aspell config' και αναζητήστε . στα αρχεία μέσα στον κατάλογο 'data-dir'. Indexer log file name Όνομα αρχείου καταγραφής ευρετηρίου If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Αν είναι κενό, θα χρησιμοποιηθεί το παραπάνω όνομα αρχείου καταγραφής. Μπορεί να είναι χρήσιμο να έχετε ένα ξεχωριστό αρχείο καταγραφής για διαγνωστικούς σκοπούς, επειδή το κοινό αρχείο καταγραφής θα διαγραφεί όταν ξεκινήσει<br>το γραφικό περιβάλλον. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Δίσκος πλήρες όριο ποσοστό στο οποίο σταματάμε ευρετηρίαση<br>Π.χ. 90% για να σταματήσει σε 90% πλήρη, 0 ή 100 σημαίνει χωρίς όριο) Web history Ιστορικό ιστού Process the Web history queue Επεξεργασία της ουράς αναμονής ιστορικού ιστού (by default, aspell suggests mispellings when a query has no results). (εξ ορισμού, το aspell προτείνει ορθογραφικά λάθη όταν ένα ερώτημα δεν φέρει αποτελέσματα). Page recycle interval Χρονικό διάστημα ανακύκλωσης της σελίδας <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. <p>Εξ ορισμού, μόνο μια υπόσταση ενός URL διατηρείται στην κρυφή μνήμη. Αυτό μπορείτε να το αλλάξετε θέτοντας μια τιμή που καθορίζει την συχνότητα διατήρησης πολλαπλών υποστάσεων ('ημέρα', 'εβδομάδα', 'μήνας', 'έτος'). Σημειώστε ότι ξ αύξηση του χρονικού διαστήματος δεν διαγράφει τις υπάρχουσες καταχωρήσεις. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Σημείωση: οι παλιές σελίδες θα διαγραφούν ούτως ώστε να δημιουργηθεί χώρος για νέες σελίδες όταν θα επιτευχθεί το μέγιστο μέγεθος. Τρέχον μέγεθος: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Μόνο οι τύποι MIME An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Μια αποκλειστική λίστα δεικτοδοτημένων τύπων mime.<br>Δεν θα δεικτοδοτηθεί τίποτα άλλο. Φυσιολογικά κενό και αδρανές Exclude mime types Αποκλεισμός τύπων αρχείων Mime types not to be indexed Οι τύποι Mime που δεν θα δεικτοδοτηθούν Max. compressed file size (KB) Μεγ.μέγεθος για τα συμπιεσμένα αρχεία (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Αυτή η τιμή καθορίζει ένα όριο πέραν του οποίου τα συμπιεσμένα αρχεία δεν θα επεξεργάζονται. Χρησιμοποιήστε -1 για κανένα όριο, 0 για να μην επεξεργάζονται τα συμπιεσμένα αρχεία. Max. text file size (MB) Μεγ. μέγεθος αρχείων κειμένου (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Αυτή η τιμή ορίζει ένα όριο πέραν του οποίου δεν θα γίνεται ευρετηρίαση για τα αρχεία κειμένου. Ορίστε -1 για κανένα όριο. Αυτό χρησιμεύει για τον αποκλεισμό από την ευρετηρίαση τεράστιων αρχείων καταγραφών. Text file page size (KB) Μέγεθος κοπής για τα αρχεία κειμένου (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Αν αυτή η τιμή έχει οριστεί και είναι θετική, τα αρχεία κειμένου θα κοπούν σε κομμάτια αυτού του μεγέθους για την ευρετηρίαση. Αυτό βοηθά στη μείωση των καταναλωμένων πόρων από την ευρετηρίαση και βοηθά τη φόρτωση για την προεπισκόπηση. Max. filter exec. time (s) Μέγιστο φίλτρο exec. χρόνος (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Τα εξωτερικά φίλτρα σε λειτουργία μεγαλύτερη από αυτό θα διακόπτονται. Χρήσιμο για τη σπάνια περίπτωση (π.χ. postscript) όπου ένα έγγραφο μπορεί να προκαλέσει ένα βρόγχο στο φίλτρο. Ορίστε το σε -1 για να αφαιρέσετε το όριο. Global Γενικά CronToolW Cron Dialog Διάλογος Cron <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> προγραμματισμός της περιοδικής ευρετηρίασης (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Κάθε πεδίο μπορεί να περιέχει ένα χαρακτήρα υποκατάστασης (*), μια απλή αριθμητική τιμή, λίστες διαχωρισμένες με κόμμα (1,3,5) και εύρη (1-7). Γενικότερα, τα πεδία θα χρησιμοποιηθούν <span style=" font-style:italic;">ως έχουν</span> στο αρχείο crontab, και η γενική σύνταξη crontab μπορεί να χρησιμοποιηθεί, δείτε στη σελίδα του εγχειριδίου crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Για παράδειγμα, εισάγοντας <span style=" font-family:'Courier New,courier';">*</span> στις <span style=" font-style:italic;">Ημέρες, </span><span style=" font-family:'Courier New,courier';">12,19</span> στις <span style=" font-style:italic;">Ώρες</span> και <span style=" font-family:'Courier New,courier';">15</span> στα <span style=" font-style:italic;">Λεπτά</span>, το recollindex θα ξεκινά κάθε μέρα στις 12:15 AM και 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ο προγραμματισμός με πολύ συχνές ενεργοποιήσεις είναι πιθανώς λιγότερο αποτελεσματικός από την ευρετηρίαση σε πραγματικό χρόνο.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Ημέρες της εβδομάδας (* ή 0-7, 0 ή 7 σημαίνει Κυριακή) Hours (* or 0-23) Ώρες (* ή 0-23) Minutes (0-59) Λεπτά (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Κάντε κλικ στο <span style=" font-style:italic;">Απενεργοποίηση</span> για να διακόψετε την περιοδική αυτόματη ευρετηρίαση, στο <span style=" font-style:italic;">Ενεργοποίηση</span> για να την ενεργοποιήσετε, και <span style=" font-style:italic;">Ακύρωση</span> για να μην αλλάξει τίποτα.</p></body></html> Enable Ενεργοποίηση Disable Απενεργοποίηση It seems that manually edited entries exist for recollindex, cannot edit crontab Φαίνεται ότι υπάρχουν καταχωρήσεις δημιουργημένες χειροκίνητα για το recollindex. Η επεξεργασία του αρχείου Cron δεν είναι δυνατή Error installing cron entry. Bad syntax in fields ? Σφάλμα κατά την εγκατάσταση της καταχώρησης cron. Κακή σύνταξη των πεδίων; EditDialog Dialog Διάλογος EditTrans Source path Διαδρομή πηγής Local path Τοπική διαδρομή Config error Σφάλμα διαμόρφωσης Original path Αρχική διαδρομή EditTransBase Path Translations Διαδρομή μεταφράσεων Setting path translations for Ορισμός διαδρομής μεταφράσεων για Select one or several file types, then use the controls in the frame below to change how they are processed Επιλέξτε έναν οι περισσότερους τύπους αρχείων, και στη συνέχεια χρησιμοποιήστε τα κουμπιά ελέγχου στο παρακάτω πλαίσιο για να αλλάξετε τον τρόπο επεξεργασίας Add Προσθήκη Delete Διαγραφή Cancel Ακύρωση Save Αποθήκευση FirstIdxDialog First indexing setup Διαμόρφωση της πρώτης δεικτοδότησης <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Φαίνεται ότι το ευρετήριο για αυτήν τη διαμόρφωση δεν υπάρχει ακόμα..</span><br /><br />Αν θέλετε απλά να δεικτοδοτήσετε τον προσωπικό σας κατάλογο με ένα ικανοποιητικό σύνολο προεπιλογών, πατήστε το κουμπί <span style=" font-style:italic;">«Έναρξη της ευρετηρίασης τώρα»</span>. Μπορείτε να ρυθμίσετε τις λεπτομέρειες αργότερα. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Αν επιθυμείτε περισσότερο έλεγχο, χρησιμοποιήστε τους παρακάτω συνδέσμους για να ρυθμίσετε τη διαμόρφωση της ευρετηρίασης και του προγραμματισμού.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Μπορείτε να έχετε πρόσβαση στα εργαλεία αυτά αργότερα από το μενού <span style=" font-style:italic;">Προτιμήσεις</span>.</p></body></html> Indexing configuration Διαμόρφωση ευρετηρίασης This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Σας επιτρέπει τη ρύθμιση των καταλόγων που επιθυμείτε να δεικτοδοτήσετε, και άλλων παραμέτρων όπως οι εξαιρούμενες διαδρομές αρχείων ή ονομάτων, των προκαθορισμένων συνόλων χαρακτήρων, κλπ. Indexing schedule Προγραμματισμός ευρετηρίασης This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Σας επιτρέπει την επιλογή μεταξύ της προγραμματισμένης ευρετηρίασης και αυτής σε πραγματικό χρόνο, και τον καθορισμό του προγραμματισμού για την πρώτη (βασισμένη στο εργαλείο cron). Start indexing now Έναρξη της ευρετηρίασης τώρα FragButs %1 not found. Δεν βρέθηκε το %1. %1: %2 %1: %2 Fragment Buttons Πλήκτρα θραυσμάτων Query Fragments Θραύσματα ερωτήματος IdxSchedW Index scheduling setup Διαμόρφωση του προγραμματισμού ευρετηρίασης <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Η ευρετηρίαση του <span style=" font-weight:600;">Recoll</span> μπορεί να βρίσκεται μόνιμα σε λειτουργία, επεξεργάζοντας τα αρχεία αμέσως μετά αφού τροποποιηθούν, ή να εκτελείται σε προκαθορισμένες στιγμές. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Μια ανάγνωση του εγχειριδίου μπορεί να σας βοηθήσει να επιλέξετε μεταξύ αυτών των προσεγγίσεων (πατήστε F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Αυτό το εργαλείο μπορεί να σας βοηθήσει να διαμορφώσετε την προγραμματισμένη ευρετηρίαση ή να ορίσετε μια αυτόματη έναρξη της ευρετηρίασης σε πραγματικό χρόνο κατά τη σύνδεσή σας (ή και τα δύο, κάτι που σπάνια χρειάζεται). </p></body></html> Cron scheduling Προγραμματισμός Cron The tool will let you decide at what time indexing should run and will install a crontab entry. Ο διάλογος σας επιτρέπει να προσδιορίσετε την ώρα έναρξης της ευρετηρίασης και θα εισάγει μια καταχώρηση crontab. Real time indexing start up Έναρξη της ευρετηρίασης σε πραγματικό χρόνο Decide if real time indexing will be started when you log in (only for the default index). Προσδιορίστε αν η ευρετηρίαση σε πραγματικό χρόνο θα ξεκινά με τη σύνδεσή σας (μόνο για το προκαθορισμένο ευρετήριο). ListDialog Dialog Διάλογος GroupBox Πλαίσιο Ομάδας Main No db directory in configuration Δεν έχει προσδιοριστεί ο κατάλογος της βάσης δεδομένων στη διαμόρφωση Could not open database in Αδυναμία ανοίγματος βάσης δεδομένων στο . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Κάντε κλικ στο κουμπί Ακύρωση αν θέλετε να επεξεργαστείτε το αρχείο ρυθμίσεων πριν ξεκινήσετε την ευρετηρίαση ή Εντάξει για να το αφήσετε να προχωρήσει. Configuration problem (dynconf Πρόβλημα ρύθμισης (dynconf "history" file is damaged or un(read)writeable, please check or remove it: Το αρχείο ιστορικού είτε είναι κατεστραμμένο είτε δεν είναι αναγνώσιμο/εγγράψιμο, παρακαλώ ελέγξτε το ή διαγράψτε το: "history" file is damaged, please check or remove it: "το ιστορικό" το αρχείο είναι κατεστραμμένο, παρακαλώ ελέγξτε ή αφαιρέστε το: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Αναζήτηση για: &Next &Επόμενο &Previous &Προηγούμενο Match &Case Διάκριση &πεζών/κεφαλαίων Clear Καθαρισμός Creating preview text Δημιουργία του κειμένου προεπισκόπησης Loading preview text into editor Φόρτωση του κειμένου προεπισκόπησης στον επεξεργαστή Cannot create temporary directory Αδυναμία δημιουργίας προσωρινού καταλόγου Cancel Ακύρωση Close Tab Κλείσιμο της καρτέλας Missing helper program: Ελλείποντα εξωτερικά προγράμματα φίλτρου: Can't turn doc into internal representation for Αδύνατη η μεταγλώττιση του εγγράφου σε εσωτερική αναπαράσταση για Cannot create temporary directory: Αδυναμία δημιουργίας του προσωρινού καταλόγου: Error while loading file Σφάλμα κατά τη φόρτωση του αρχείου Form Φόρμα Tab 1 Καρτέλα 1 Open Άνοιγμα Canceled Ακυρώθηκε Error loading the document: file missing. Σφάλμα κατά τη φόρτωση του εγγράφου: το αρχείο λείπει. Error loading the document: no permission. Σφάλμα κατά τη φόρτωση του εγγράφου: δεν υπάρχει άδεια. Error loading: backend not configured. Σφάλμα φόρτωσης: το σύστημα υποστήριξης δεν έχει ρυθμιστεί. Error loading the document: other handler error<br>Maybe the application is locking the file ? Σφάλμα κατά τη φόρτωση του εγγράφου: άλλο σφάλμα χειριστή<br>Ίσως η εφαρμογή να κλειδώνει το αρχείο ? Error loading the document: other handler error. Σφάλμα κατά τη φόρτωση του εγγράφου: άλλο σφάλμα χειρισμού. <br>Attempting to display from stored text. <br>Προσπάθεια εμφάνισης από το αποθηκευμένο κείμενο. Could not fetch stored text Αδύνατη η ανάκτηση του αποθηκευμένου κειμένου Previous result document Έγγραφο προηγούμενου αποτελέσματος Next result document Έγγραφο επόμενου αποτελέσματος Preview Window Προεπισκόπηση Παραθύρου Close Window Κλείσιμο Παραθύρου Next doc in tab Επόμενο doc στην καρτέλα Previous doc in tab Προηγούμενο doc στην καρτέλα Close tab Κλείσιμο καρτέλας Print tab Εκτύπωση καρτέλας Close preview window Κλείσιμο παραθύρου προεπισκόπησης Show next result Εμφάνιση επόμενου αποτελέσματος Show previous result Εμφάνιση προηγούμενου αποτελέσματος Print Εκτύπωση PreviewTextEdit Show fields Εμφάνιση των πεδίων Show main text Εμφάνιση του σώματος του κειμένου Print Εκτύπωση Print Current Preview Εκτύπωση του παραθύρου προεπισκόπησης Show image Εμφάνιση της εικόνας Select All Επιλογή όλων Copy Αντιγραφή Save document to file Αποθήκευση του εγγράφου Fold lines Αναδίπλωση των γραμμών Preserve indentation Διατήρηση της εσοχής Open document Άνοιγμα εγγράφου Reload as Plain Text Reload as HTML QObject Global parameters Καθολικές ρυθμίσεις Local parameters Τοπικές ρυθμίσεις <b>Customised subtrees <b>Κατάλογοι με προσαρμοσμένες ρυθμίσεις The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Η λίστα των υποκαταλόγων της ζώνης με ευρετήριο<br>όπου έχουν προκαθοριστεί ορισμένες παράμετροι. Προεπιλογή: κενό. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Οι παράμετροι που ακολουθούν έχουν καθοριστεί είτε καθολικά, αν η επιλογή στην παραπάνω λίστα<br>είναι κενή ή μια κενή γραμμή, είτε για τον επιλεγμένο κατάλογο.<br>Μπορείτε να προσθέσετε και να αφαιρέσετε καταλόγους κάνοντας κλικ στα κουμπιά +/-. Skipped names Αγνοημένα ονόματα These are patterns for file or directory names which should not be indexed. Μοτίβα που καθορίζουν τα αρχεία ή καταλόγους που δεν θα πρέπει να έχουν ευρετήριο. Default character set Σύνολο χαρακτήρων<br>εξ ορισμού This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Το σύνολο των χαρακτήρων που χρησιμοποιείται για την ανάγνωση των αρχείων στα οποία δεν μπορεί να αναγνωριστεί το σύνολο χαρακτήρων με εσωτερικό τρόπο, για παράδειγμα τα αρχεία απλού κειμένου.<br>Η προκαθορισμένη τιμή είναι κενή, και το πρόγραμμα χρησιμοποιεί αυτή του περιβάλλοντος. Follow symbolic links Ακολούθηση των συμβολικών δεσμών Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Να δημιουργηθεί ευρετήριο για αρχεία και καταλόγους που υποδεικνύονται από συμβολικούς δεσμούς. Η προκαθορισμένη τιμή είναι όχι, για την αποφυγή διπλότυπης ευρετηρίασης Index all file names Ευρετήριο για όλα τα ονόματα αρχείων Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Ευρετήριο για τα ονόματα των αρχείων των οποίων το περιεχόμενο δεν έχει αναγνωριστεί ή επεξεργαστεί (χωρίς τύπο mime, ή μη υποστηριζόμενος τύπος). Η προκαθορισμένη τιμή είναι αληθές Beagle web history Ιστορικό ιστού Beagle Search parameters Ρυθμίσεις αναζήτησης Web history Ιστορικό ιστού Default<br>character set Σύνολο χαρακτήρων<br>εξ ορισμού Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Το σύνολο των χαρακτήρων που χρησιμοποιείται για την ανάγνωση των αρχείων που δεν έχουν εσωτερικό αναγνωριστικό των χαρακτήρων, για παράδειγμα αρχεία απλού κειμένου: <br>Η τιμή εξ ορισμού είναι κενή, και χρησιμοποιείται η τιμή του περιβάλλοντος NLS. Ignored endings Αγνοημένες καταλήξεις These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Αυτές είναι καταλήξεις ονομάτων αρχείων για αρχεία τα οποία θα αναπροσαρμόζονται μόνο από περιεχόμενο (καμία προσπάθεια ταυτοποίησης τύπου MIME, καμία αποσυμπίεση, δεν ευρετηρίαση περιεχομένου. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Αυτές είναι καταλήξεις αρχείων στα οποία η ευρετηρίαση θα γίνει μόνο βάσει του ονόματος (χωρίς προσπάθεια αναγνώρισης του τύπου MIME, χωρίς αποσυμπίεση, χωρίς δεικτοδότηση του περιεχομένου). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>Οι παράμετροι που ακολουθούν έχουν οριστεί είτε στο ανώτερο επίπεδο, αν δεν έχει επιλεγεί τίποτα ή μια κενή γραμμή στο πλαίσιο λίστας παραπάνω, ή για τον επιλεγμένο υποκατάλογο. Μπορείτε να προσθέσετε ή να καταργήσετε καταλόγους κάνοντας κλικ στα πλήκτρα +/. QWidget Create or choose save directory Δημιουργία ή επιλογή του καταλόγου αποθήκευσης Choose exactly one directory Επιλέξτε μόνο έναν κατάλογο Could not read directory: Αδύνατη η ανάγνωση του καταλόγου: Unexpected file name collision, cancelling. Απροσδόκητη σύγκρουση ονομάτων αρχείων, ακύρωση. Cannot extract document: Αδύνατη η εξαγωγή του εγγράφου: &Preview &Προεπισκόπηση &Open Ά&νοιγμα Open With Άνοιγμα με Run Script Εκτέλεση μακροεντολής Copy &File Name Αντιγραφή του ονόματος του α&ρχείου Copy &URL Αντιγραφή του &URL &Write to File &Εγγραφή σε αρχείο Save selection to files Αποθήκευση της επιλογής σε αρχεία Preview P&arent document/folder Προεπισκόπηση του &γονικού εγγράφου/καταλόγου &Open Parent document/folder &Άνοιγμα του γονικού εγγράφου/καταλόγου Find &similar documents Αναζήτηση παρό&μοιων εγγράφων Open &Snippets window Άνοιγμα του παραθύρου απο&σπασμάτων Show subdocuments / attachments Εμφάνιση των υπο-εγγράφων / συνημμένων &Open Parent document &Άνοιγμα γονικού εγγράφου &Open Parent Folder &Άνοιγμα Γονικού Φακέλου Copy Text Αντιγραφή κειμένου Copy &File Path Αντιγραφή της &πλήρους διαδρομής αρχείου Copy File Name Αντιγραφή του ονόματος του αρχείου QxtConfirmationMessage Do not show again. Να μην εμφανιστεί ξανά. RTIToolW Real time indexing automatic start Αυτόματη έναρξη ευρετηρίασης σε πραγμ. χρόνο <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Η ευρετηρίαση του <span style=" font-weight:600;">Recoll</span> μπορεί να έχει ρυθμιστεί να εκτελείται στο παρασκήνιο, ενημερώνοντας το ευρετήριο σταδιακά κατά την τροποποίηση του αρχείου. Επωφελείστε από ένα ευρετήριο πάντα ενημερωμένο, αλλά καταναλώνονται συνέχεια πόροι του συστήματος (μνήμη και επεξεργαστής).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Εκκίνηση του δαίμονα ευρετηρίασης κατά τη σύνδεσή μου. Also start indexing daemon right now. Επίσης να γίνει έναρξη της ευρετηρίασης τώρα. Replacing: Αντικατάσταση του: Replacing file Αντικατάσταση του αρχείου Can't create: Αδυναμία δημιουργίας: Warning Προσοχή Could not execute recollindex Αδυναμία εκτέλεσης του recollindex Deleting: Διαγραφή: Deleting file Διαγραφή του αρχείου Removing autostart Αφαίρεση του autostart Autostart file deleted. Kill current process too ? Το αρχείο autostart διαγράφτηκε. Τερματισμός της διεργασίας σε εξέλιξη; RclCompleterModel Hits RclMain About Recoll Σχετικά με το Recoll Executing: [ Εκτέλεση του: [ Cannot retrieve document info from database Αδύνατη η πρόσβαση στο έγγραφο στη βάση δεδομένων Warning Προσοχή Can't create preview window Αδύνατη η δημιουργία του παραθύρου προεπισκόπησης Query results Αποτελέσματα της αναζήτησης Document history Ιστορικό των ανοιγμένων εγγράφων History data Δεδομένα του ιστορικού Indexing in progress: Ευρετηρίαση σε εξέλιξη: Files Αρχεία Purge Καθαρισμός Stemdb Stemdb Closing Κλείσιμο Unknown Άγνωστο This search is not active any more Η αναζήτηση δεν είναι ενεργή πια Can't start query: Μπορεί't ερώτημα εκκίνησης: Bad viewer command line for %1: [%2] Please check the mimeconf file Κακοδιατυπωμένη εντολή για %1: [%2] Παρακαλώ ελέγξτε το αρχείο mimeconf Cannot extract document or create temporary file Αδύνατη η εξαγωγή του εγγράφου ή η δημιουργία ενός προσωρινού αρχείου (no stemming) (χωρίς επέκταση) (all languages) (όλες οι γλώσσες) error retrieving stemming languages σφάλμα στη λήψη της λίστας των γλωσσών επέκτασης Update &Index Ενημέρωση του &ευρετηρίου Indexing interrupted Η ευρετηρίαση διεκόπη Stop &Indexing Διακοπή της &ευρετηρίασης All Όλα media πολυμέσα message μήνυμα other άλλα presentation παρουσίαση spreadsheet λογιστικό φύλλο text κείμενο sorted ταξινομημένο filtered φιλτραρισμένο External applications/commands needed and not found for indexing your file types: Απαιτούνται εξωτερικές εφαρμογές/εντολές που δεν βρέθηκαν για την ευρετηρίαση των τύπων των αρχείων σας: No helpers found missing Δεν λείπει καμιά εφαρμογή Missing helper programs Ελλείπουσες βοηθητικές εφαρμογές Save file dialog Διάλογος αποθήκευσης αρχείου Choose a file name to save under Επιλέξτε ένα όνομα αρχείου για αποθήκευση στο Document category filter Φίλτρο κατηγοριών των εγγράφων No external viewer configured for mime type [ Κανένας ρυθμισμένος προβολέας για τον τύπο MIME [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Ο καθορισμένος προβολέας στο mimeview για %1: %2 δεν βρέθηκε. Θέλετε να ξεκινήσετε το διάλογο με τις προτιμήσεις; Can't access file: Αδύνατη η πρόσβαση στο αρχείο: Can't uncompress file: Αδύνατη η αποσυμπίεση του αρχείου: Save file Αποθήκευση του αρχείου Result count (est.) Αριθμός αποτελεσμάτων (εκτίμ.) Query details Λεπτομέρειες της αναζήτησης Could not open external index. Db not open. Check external indexes list. Αδύνατο το άνοιγμα ενός εξωτερικού ευρετηρίου. Η βάση δεδομένων δεν είναι ανοιχτή. Ελέγξτε τη λίστα των εξωτερικών ευρετηρίων. No results found Δεν βρέθηκαν αποτελέσματα None Τίποτα Updating Ενημέρωση Done Έγινε Monitor Παρακολούθηση Indexing failed Η ευρετηρίαση απέτυχε The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Η διεργασία ευρετηρίασης σε εξέλιξη δεν ξεκίνησε από αυτή τη διεπαφή. Κάντε κλικ στο Εντάξει για να τη σκοτώσετε όπως και να 'χει, ή στο Ακύρωση για να την αφήσετε ήσυχη Erasing index Διαγραφή του ευρετηρίου Reset the index and start from scratch ? Διαγραφή του ευρετηρίου και επανέναρξη από το μηδέν; Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Αίτημα σε εξέλιξη.<br>Λόγω εσωτερικών περιορισμών,<br>η ακύρωση θα τερματίσει την εκτέλεση του προγράμματος Error Σφάλμα Index not open Το ευρετήριο δεν είναι ανοιχτό Index query error Σφάλμα στην αναζήτηση στο ευρετήριο Indexed Mime Types Τύποι Mime Με Ευρετήριο Content has been indexed for these MIME types: Το περιεχόμενο έχει ευρεθεί για αυτούς τους τύπους MIME: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Το ευρετήριο δεν είναι ενημερωμένο για αυτό το αρχείο. Υπάρχει κίνδυνος εμφάνισης μιας εσφαλμένης καταχώρησης. Κάντε κλικ στο Εντάξει για να ενημερώσετε το ευρετήριο για αυτό το αρχείο, και επανεκκινήστε το αίτημα μετά την ολοκλήρωση της ενημέρωσης του ευρετηρίου. Διαφορετικά, κάντε κλικ στο Ακύρωση. Can't update index: indexer running Αδύνατη η ενημέρωση του ευρετηρίου: μια εργασία ευρετηρίασης βρίσκεται σε εξέλιξη Indexed MIME Types Τύποι MIME με ευρετήριο Bad viewer command line for %1: [%2] Please check the mimeview file Λανθασμένη γραμμή εντολής για %1: [%2] Παρακαλώ ελέγξτε το αρχείο mimeview Viewer command line for %1 specifies both file and parent file value: unsupported Η γραμμή εντολής για %1 καθορίζει την ίδια στιγμή το αρχείο και τον γονέα του: δεν υποστηρίζεται Cannot find parent document Αδύνατη η εύρεση του γονικού εγγράφου Indexing did not run yet Η δεικτοδότηση δεν έχει εκτελεστή ακόμα External applications/commands needed for your file types and not found, as stored by the last indexing pass in Εξωτερικές εφαρμογές και εντολές απαραίτητες για τους τύπους των εγγράφων σας, και που δεν έχουν βρεθεί, όπως έχουν ταξινομηθεί από την τελευταία δεικτοδότηση που έλαβε χώρα στις Index not up to date for this file. Refusing to risk showing the wrong entry. Η δεικτοδότηση δεν είναι ενημερωμένη για αυτό το αρχείο. Πιθανός κίνδυνος εμφάνισης μιας λανθασμένης εισαγωγής. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Κάντε κλικ στο Εντάξει για να ενημερώσετε τη δεικτοδότηση για αυτό το αρχείο, και στη συνέχεια επανεκκινήστε την αναζήτηση όταν θα έχει ολοκληρωθεί η δημιουργία του ευρετηρίου. Διαφορετικά, κλικ στο Ακύρωση. Indexer running so things should improve when it's done Η δημιουργία του ευρετηρίου βρίσκεται σε εξέλιξη, το αρχείο θα ενημερωθεί μετά το πέρας της ενημέρωσης Sub-documents and attachments Υπο-έγγραφα και συνημμένα Document filter Φίλτρο εγγράφου Index not up to date for this file. Refusing to risk showing the wrong entry. Το ευρετήριο δεν είναι ενημερωμένο για αυτό το αρχείο. Άρνηση της διακινδυνευμένης εμφάνισης μιας λανθασμένης καταχώρησης. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Κάντε κλικ στο Εντάξει για να ενημερώσετε το ευρετήριο για αυτό το αρχείο, στη συνέχεια θα πρέπει να εκτελέσετε εκ νέου το ερώτημα μετ το πέρας της δεικτοδότησης. The indexer is running so things should improve when it's done. Τα πράγματα θα βελτιωθούν μετά το πέρας της δεικτοδότησης. The document belongs to an external indexwhich I can't update. Το έγγραφο ανήκει σε ένα εξωτερικό ευρετήριο το οποίο δεν μπορώ να ενημερώσω. Click Cancel to return to the list. Click Ignore to show the preview anyway. Κάντε κλικ στο Ακύρωση για να επιστρέψετε στον κατάλογο. Κάντε κλικ στο Αγνόηση για την εμφάνιση της προεπισκόπησης ούτως ή άλλως. Duplicate documents Διπλότυπα έγγραφα These Urls ( | ipath) share the same content: Αυτά τα Url (| ipath) μοιράζονται το ίδιο περιεχόμενο: Bad desktop app spec for %1: [%2] Please check the desktop file Κακοδιατυπωμένος προσδιορισμός εφαρμογής επιφάνειας εργασίας για το %1: [%2] Παρακαλώ ελέγξτε το αρχείο της επιφάνειας εργασίας Bad paths Λανθασμένες διαδρομές Bad paths in configuration file: Λάθος μονοπάτια στο αρχείο ρυθμίσεων: Selection patterns need topdir Τα μοτίβα επιλογής χρειάζονται topdir Selection patterns can only be used with a start directory Τα μοτίβα επιλογής μπορούν να χρησιμοποιηθούν μόνο με έναν κατάλογο έναρξης No search Καμία αναζήτηση No preserved previous search Δεν υπάρχει διατηρημένη προηγούμενη αναζήτηση Choose file to save Επιλέξτε αρχείο για αποθήκευση Saved Queries (*.rclq) Αποθηκευμένα Ερωτήματα (*.rclq) Write failed Η εγγραφή απέτυχε Could not write to file Αδυναμία εγγραφής στο αρχείο Read failed Η ανάγνωση απέτυχε Could not open file: Αδυναμία ανοίγματος αρχείου: Load error Σφάλμα φόρτωσης Could not load saved query Αδύνατη η φόρτωση του αποθηκευμένου ερωτήματος Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Άνοιγμα ενός προσωρινού αντιγράφου. Οι αλλαγές θα χαθούν αν don't τις αποθηκεύσετε<br/>σε μια μόνιμη θέση. Do not show this warning next time (use GUI preferences to restore). Να μην εμφανιστεί αυτή η προειδοποίηση την επόμενη φορά (χρησιμοποιήστε τις προτιμήσεις GUI για επαναφορά). Disabled because the real time indexer was not compiled in. Απενεργοποιήθηκε επειδή το ευρετήριο πραγματικού χρόνου δεν μεταγλωττίστηκε. This configuration tool only works for the main index. Αυτό το εργαλείο ρύθμισης παραμέτρων λειτουργεί μόνο για τον κύριο δείκτη. The current indexing process was not started from this interface, can't kill it Η τρέχουσα διαδικασία ευρετηρίασης δεν ξεκίνησε από αυτή τη διασύνδεση· αδύνατη η διακοπή της. The document belongs to an external index which I can't update. Το έγγραφο ανήκει σε ένα εξωτερικό ευρετήριο το οποίο δεν μπορώ να ενημερώσω. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Κάντε κλικ στο κουμπί Ακύρωση για να επιστρέψετε στη λίστα. <br>Κάντε κλικ στο κουμπί Παράβλεψη για να εμφανίσετε την προεπισκόπηση ούτως ή άλλως (και θυμηθείτε για αυτή τη συνεδρία). Index scheduling Προγραμματισμός ευρετηρίου Sorry, not available under Windows for now, use the File menu entries to update the index Λυπούμαστε, δεν είναι διαθέσιμο στα Windows για τώρα, χρησιμοποιήστε τις καταχωρήσεις μενού αρχείου για να ενημερώσετε το ευρετήριο Can't set synonyms file (parse error?) Αδύνατος ο ορισμός του αρχείου συνώνυμων (σφάλμα ανάλυσης;) Index locked Το ευρετήριο κλειδώθηκε Unknown indexer state. Can't access webcache file. Άγνωστη κατάσταση ευρετηρίου. Αδύνατη η πρόσβαση στο webcache. Indexer is running. Can't access webcache file. Το ευρετήριο εκτελείται. Αδύνατη η πρόσβαση στο αρχείο webcache. with additional message: με πρόσθετο μήνυμα: Non-fatal indexing message: Μη μοιραίο σφάλμα ευρετηρίασης: Types list empty: maybe wait for indexing to progress? Κενή λίστα τύπων: αναμονή της εξέλιξης της ευρετηρίασης; Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Η γραμμή εντολών προβολής για το %1 καθορίζει το γονικό αρχείο αλλά το URL είναι http[s]: δεν υποστηρίζεται Tools Εργαλεία Results Αποτελέσματα (%d documents/%d files/%d errors/%d total files) (%d έγγραφα/%d αρχεία/%d σφάλματα/%d συνολικά αρχεία) (%d documents/%d files/%d errors) (%d έγγραφα/%d αρχεία/%d σφάλματα) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Κενές ή ανύπαρκτες διαδρομές στο αρχείο ρυθμίσεων. Κάντε κλικ στο κουμπί Εντάξει για να ξεκινήσετε την ευρετηρίαση ούτως ή άλλως (δεν θα εκκαθαριστούν δεδομένα από το ευρετήριο): Indexing done Ολοκλήρωση ευρετηρίου Can't update index: internal error Αδύνατη η ενημέρωση του ευρετηρίου: εσωτερικό σφάλμα Index not up to date for this file.<br> Το ευρετήριο δεν είναι ενημερωμένο για αυτό το αρχείο.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Επίσης, φαίνεται ότι η τελευταία ενημέρωση ευρετηρίου για το αρχείο απέτυχε.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Κάντε κλικ στο κουμπί Εντάξει για να προσπαθήσετε να ενημερώσετε το ευρετήριο για αυτό το αρχείο. Θα πρέπει να εκτελέσετε ξανά το ερώτημα όταν ολοκληρωθεί η ευρετηρίαση.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Κάντε κλικ στο κουμπί Ακύρωση για να επιστρέψετε στη λίστα.<br>Κάντε κλικ στο κουμπί Παράβλεψη για να εμφανίσετε την προεπισκόπηση ούτως ή άλλως (και θυμηθείτε για αυτή τη συνεδρία). Υπάρχει κίνδυνος να παρουσιαστεί λανθασμένη είσοδος.<br/> documents έγγραφα document έγγραφο files αρχεία file αρχείο errors σφάλματα error σφάλμα total files) συνολικά αρχεία) No information: initial indexing not yet performed. Δεν υπάρχουν πληροφορίες: η αρχική ευρετηρίαση δεν έχει πραγματοποιηθεί ακόμα. Batch scheduling Ομαδικός προγραμματισμός The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Χρησιμοποιήστε αυτό το εργαλείο για τον προγραμματισμό εκτέλεσης της ευρετηρίασης. Χρησιμοποιεί τον προγραμματιστή εργασιών των Windows. Confirm Επιβεβαίωση Erasing simple and advanced search history lists, please click Ok to confirm Διαγραφή απλών και προηγμένων λιστών ιστορικού αναζήτησης, παρακαλώ κάντε κλικ στο κουμπί Εντάξει για επιβεβαίωση Could not open/create file Αδυναμία ανοίγματος/δημιουργίας αρχείου F&ilter Φί&λτρο Could not start recollindex (temp file error) Αδυναμία εκκίνησης του recollindex (προσωρινό σφάλμα αρχείου) Could not read: Αδυναμία ανάγνωσης: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Αυτό θα αντικαταστήσει τα τρέχοντα περιεχόμενα της λίστας αποτελεσμάτων συμβολοσειράς κεφαλίδας και το όνομα αρχείου GUI qss. Συνέχεια ? You will need to run a query to complete the display change. Θα χρειαστεί να εκτελέσετε ένα ερώτημα για να ολοκληρώσετε την αλλαγή της οθόνης. Simple search type Απλός τύπος αναζήτησης Any term Οποιοσδήποτε όρος All terms Όλοι οι όροι File name Όνομα του αρχείου Query language Γλώσσα ερωτημάτων Stemming language Γλώσσα για την ανάπτυξη των όρων Main Window Κύριο παράθυρο Focus to Search Εστίαση στην αναζήτηση Focus to Search, alt. Εστίαση στην Αναζήτηση, alt. Clear Search Καθαρισμός αναζήτησης Focus to Result Table Εστίαση στον πίνακα αποτελεσμάτων Clear search Εκκαθάριση αναζήτησης Move keyboard focus to search entry Μετακίνηση εστίασης πληκτρολογίου στην καταχώρηση αναζήτησης Move keyboard focus to search, alt. Μετακίνηση εστίασης πληκτρολογίου στην αναζήτηση, alt. Toggle tabular display Εναλλαγή εμφάνισης πίνακα Move keyboard focus to table Μετακίνηση εστίασης πληκτρολογίου στον πίνακα Flushing Εγγραφή του δείκτη Show menu search dialog Εμφάνιση του διαλόγου του μενού αναζήτησης Duplicates Διπλότυπα Filter directories Φιλτράρισμα καταλόγων Main index open error: Σφάλμα ανοίγματος του βασικού ευρετηρίου: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. . Το ευρετήριο ίσως είναι κατεστραμμένο. Προσπαθήστε να εκτελέσετε το xapian-check ή την επανακατασκευή του ευρετηρίου. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Προηγούμενη σελίδα Next page Επόμενη σελίδα &File &Αρχείο E&xit Έ&ξοδος &Tools &Εργαλεία &Help &Βοήθεια &Preferences &Προτιμήσεις Search tools Εργαλεία αναζήτησης Result list Λίστα αποτελεσμάτων &About Recoll &Σχετικά με το Recoll Document &History &Ιστορικό των εγγράφων Document History Ιστορικό των εγγράφων &Advanced Search &Προχωρημένη αναζήτηση Advanced/complex Search Προχωρημένη αναζήτηση &Sort parameters &Ρυθμίσεις ταξινόμησης Sort parameters Ρυθμίσεις ταξινόμησης Next page of results Επόμενη σελίδα των αποτελεσμάτων Previous page of results Προηγούμενη σελίδα αποτελεσμάτων &Query configuration Δια&μόρφωση της αναζήτησης &User manual Ε&γχειρίδιο Recoll Recoll Ctrl+Q Ctrl+Q Update &index Ε&νημέρωση ευρετηρίου Term &explorer Ε&ξερευνητής του ευρετηρίου Term explorer tool Εργαλείο εξερεύνησης του ευρετηρίου External index dialog Εξωτερικά ευρετήρια &Erase document history &Διαγραφή του ιστορικού εγγράφων First page Πρώτη σελίδα Go to first page of results Μετάβαση στην πρώτη σελίδα αποτελεσμάτων &Indexing configuration Διαμόρφωση ευρετηρίασης All Όλα &Show missing helpers Ε&μφάνιση των ελλειπουσών εφαρμογών PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen Π&λήρης οθόνη F11 F11 Full Screen Πλήρης οθόνη &Erase search history Δια&γραφή του ιστορικού αναζητήσεων sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Ταξινόμηση ανά ημερομηνία από την παλαιότερη στη νεότερη sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Ταξινόμηση ανά ημερομηνία από τη νεότερη στην παλαιότερη Show Query Details Εμφάνιση της αναζήτησης λεπτομερειακά Show results as table Εμφάνιση των αποτελεσμάτων σε πίνακα &Rebuild index Α&νακατασκευή του ευρετηρίου &Show indexed types Εμ&φάνιση των τύπων με ευρετήριο Shift+PgUp Shift+PgUp &Indexing schedule &Προγραμματισμός της ευρετηρίασης E&xternal index dialog Δια&μόρφωση των εξωτερικών ευρετηρίων &Index configuration Διαμόρφωση &ευρετηρίου &GUI configuration Διαμόρφωση &περιβάλλοντος &Results Αποτε&λέσματα Sort by date, oldest first Ταξινόμηση ανά ημερομηνία, τα παλαιότερα πρώτα Sort by date, newest first Ταξινόμηση ανά ημερομηνία, τα νεότερα πρώτα Show as table Εμφάνιση ως πίνακας Show results in a spreadsheet-like table Εμφάνιση των αποτελεσμάτων σε έναν πίνακα ως φύλλο εργασίας Save as CSV (spreadsheet) file Αποθήκευση ως αρχείο CSV (φύλλο εργασίας) Saves the result into a file which you can load in a spreadsheet Αποθηκεύει το αποτέλεσμα σε ένα αρχείο το οποίο μπορείτε να φορτώσετε σε ένα φύλλο εργασίας Next Page Επόμενη σελίδα Previous Page Προηγούμενη σελίδα First Page Πρώτη σελίδα Query Fragments Θραύσματα ερωτήματος With failed files retrying Με επανεπεξεργασία των αποτυχημένων αρχείων Next update will retry previously failed files Η επόμενη ενημέρωση θα επιχειρήσει ξανά με τα αποτυχημένα αρχεία Save last query Αποθήκευση τελευταίου ερωτήματος Load saved query Φόρτωση αποθηκευμένου ερωτήματος Special Indexing Ειδική ευρετηρίαση Indexing with special options Ευρετηρίαση με ειδικές επιλογές Indexing &schedule &Προγραμματισμός ευρετηρίου Enable synonyms Ενεργοποίηση συνώνυμων &View &Προβολή Missing &helpers Ελλείποντες &βοηθοί Indexed &MIME types Τύποι &MIME ευρετηρίου Index &statistics &Στατιστικά ευρετηρίου Webcache Editor Επεξεργαστής Webcache Trigger incremental pass Ενεργοποίηση προοδευτικής ευρετηρίασης E&xport simple search history &Εξαγωγή ιστορικού απλής αναζήτησης Use default dark mode Χρήση προεπιλεγμένης σκοτεινής λειτουργίας Dark mode Σκοτεινή λειτουργία &Query &Ερώτημα Increase results text font size Αύξηση μεγέθους γραμματοσειράς του κειμένου των αποτελεσμάτων Increase Font Size Αύξηση μεγέθους γραμματοσειράς Decrease results text font size Μείωση μεγέθους γραμματοσειράς του κειμένου των αποτελεσμάτων Decrease Font Size Μείωση μεγέθους γραμματοσειράς Start real time indexer Έναρξη ευρετηρίασης ανά διαστήματα Query Language Filters Φίλτρα αναζήτησης (λειτουργία γλώσσας) Filter dates Φίλτρο ημερομηνίας Assisted complex search Υποβοηθούμενη σύνθετη αναζήτηση Filter birth dates RclTrayIcon Restore Επαναφορά Quit Έξοδος RecollModel Abstract Απόσπασμα Author Συγγραφέας Document size Μέγεθος εγγράφου Document date Ημερομηνία εγγράφου File size Μέγεθος αρχείου File name Όνομα αρχείου File date Ημερομηνία αρχείου Ipath Ipath Keywords Λέξεις κλειδιά Mime type Τύπος MIME Original character set Αρχικό σύνολο χαρακτήρων Relevancy rating Εγγύτητα Title Τίτλος URL URL Mtime Ωριαία Date Ημερομηνία Date and time Ημερομηνία και ώρα Ipath Διαδρομή MIME type Τύπος MIME Can't sort by inverse relevance Αδύνατη η ταξινόμηση ανά αντίστροφή εγγύτητα ResList Result list Λίστα αποτελεσμάτων Unavailable document Μη διαθέσιμο έγγραφο Previous Προηγούμενο Next Επόμενο <p><b>No results found</b><br> <p><b>Κανένα αποτέλεσμα</b><br> &Preview &Προεπισκόπηση Copy &URL Αντιγραφή URL Find &similar documents Αναζήτηση παρό&μοιων εγγράφων Query details Λεπτομέρειες της αναζήτησης (show query) (ερώτημα) Copy &File Name Αντιγραφή του ονόματος του α&ρχείου filtered φιλτραρισμένο sorted ταξινομημένο Document history Ιστορικό των ανοιγμένων εγγράφων Preview Προεπισκόπηση Open Άνοιγμα <p><i>Alternate spellings (accents suppressed): </i> <p><i>Προτεινόμενη ορθογραφία (χωρίς τόνους): </i> &Write to File Απο&θήκευση σε Preview P&arent document/folder Προεπισκόπηση του &γονικού εγγράφου/καταλόγου &Open Parent document/folder &Άνοιγμα του γονικού εγγράφου/καταλόγου &Open Ά&νοιγμα Documents Έγγραφα out of at least από τουλάχιστον for για <p><i>Alternate spellings: </i> <p><i>Εναλλακτικά λεξικά: </i> Open &Snippets window Άνοιγμα του παραθύρου απο&σπασμάτων Duplicate documents Διπλότυπα έγγραφα These Urls ( | ipath) share the same content: Αυτά τα URL (| ipath) μοιράζονται το ίδιο περιεχόμενο: Result count (est.) Αριθμός αποτελεσμάτων (εκτίμ.) Snippets Αποσπάσματα This spelling guess was added to the search: Προσθήκη ορθογραφικής προσέγγισης στην αναζήτηση: These spelling guesses were added to the search: Προσθήκες ορθογραφικής προσέγγισης στην αναζήτηση: ResTable &Reset sort &Επαναφορά της ταξινόμησης &Delete column &Αφαίρεση της στήλης Add " Προσθήκη " " column " στήλη Save table to CSV file Αποθήκευση σε ένα αρχείο CSV Can't open/create file: Αδύνατο το άνοιγμα/δημιουργία του αρχείου: &Preview &Προεπισκόπηση &Open Ά&νοιγμα Copy &File Name Αντιγραφή του ονόματος του α&ρχείου Copy &URL Αντιγραφή URL &Write to File Απο&θήκευση σε Find &similar documents Αναζήτηση παρό&μοιων εγγράφων Preview P&arent document/folder Προεπισκόπηση του &γονικού εγγράφου/καταλόγου &Open Parent document/folder &Άνοιγμα του γονικού εγγράφου/καταλόγου &Save as CSV &Αποθήκευση ως CSV Add "%1" column Προσθήκη μιας στήλης «%1» Result Table Πίνακας Αποτελεσμάτων Open Άνοιγμα Open and Quit Άνοιγμα και έξοδος Preview Προεπισκόπηση Show Snippets Εμφάνιση αποσπασμάτων Open current result document Άνοιγμα τρέχοντος εγγράφου αποτελεσμάτων Open current result and quit Άνοιγμα τρέχοντος αποτελέσματος και τερματισμού Show snippets Εμφάνιση αποσπασμάτων Show header Εμφάνιση κεφαλίδας Show vertical header Εμφάνιση κάθετης κεφαλίδας Copy current result text to clipboard Αντιγραφή κειμένου τρέχοντος αποτελέσματος στο πρόχειρο Use Shift+click to display the text instead. Χρησιμοποιήστε Shift +κλικ για την εμφάνιση του κειμένου. %1 bytes copied to clipboard %1 δυφιοσυλλαβές αντιγράφηκαν στο πρόχειρο Copy result text and quit Αντιγραφή του κειμένου του αποτελέσματος και εγκατάλειψη της εφαρμογής ResTableDetailArea &Preview &Προεπισκόπηση &Open Ά&νοιγμα Copy &File Name Αντιγραφή του ονόματος του α&ρχείου Copy &URL Αντιγραφή URL &Write to File Απο&θήκευση σε Find &similar documents Αναζήτηση παρό&μοιων εγγράφων Preview P&arent document/folder Προεπισκόπηση του &γονικού εγγράφου/καταλόγου &Open Parent document/folder &Άνοιγμα του γονικού εγγράφου/καταλόγου ResultPopup &Preview &Προεπισκόπηση &Open Ά&νοιγμα Copy &File Name Αντιγραφή του ονόματος του α&ρχείου Copy &URL Αντιγραφή URL &Write to File Απο&θήκευση σε Save selection to files Αποθήκευση της επιλογής σε αρχεία Preview P&arent document/folder Προεπισκόπηση του &γονικού εγγράφου/καταλόγου &Open Parent document/folder &Άνοιγμα του γονικού εγγράφου/καταλόγου Find &similar documents Αναζήτηση παρό&μοιων εγγράφων Open &Snippets window Άνοιγμα του παραθύρου απο&σπασμάτων Show subdocuments / attachments Εμφάνιση των υπο-εγγράφων / συνημμένων Open With Άνοιγμα με Run Script Εκτέλεση μακροεντολής SSearch Any term Οποιοσδήποτε όρος All terms Όλοι οι όροι File name Όνομα αρχείου Completions Συμπληρώσεις Select an item: Επιλέξτε ένα αντικείμενο: Too many completions Πολλές πιθανές συμπληρώσεις Query language Γλώσσα ερωτημάτων Bad query string Μη αναγνωρισμένο ερώτημα Out of memory Δεν υπάρχει διαθέσιμη μνήμη Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (<F1>) for more detail. Εισάγετε μια έκφραση γλώσσας ερωτήματος. Σκονάκι:<br> <i>term1 term2</i> : 'term1' ΚΑΙ 'term2' σε οποιοδήποτε πεδίο.<br> <i>field:term1</i> : 'term1' αναζήτηση στο πεδίο 'field'.<br> Πρότυπα ονόματα/συνώνυμα πεδίων (χρήση αγγλικών λέξεων):<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Παράδειγμα διαστημάτων ημερομηνιών: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> ΜΗΝ βάλετε τις παρενθέσεις.<br> <i>"term1 term2"</i> : ακριβής πρόταση. Επιλογές:<br> <i>"term1 term2"p</i> : εγγύτητα (χωρίς σειρά).<br> Χρησιμοποιήστε το δεσμό <b>Λεπτομερειακή εμφάνιση του ερωτήματος</b> σε περίπτωση που υπάρχει αμφιβολία στα αποτελέσματα και δείτε το εγχείρίδιο (στα αγγλικά) (<F1>) για περισσότερες λεπτομέρειες. Enter file name wildcard expression. Εισάγετε ένα όνομα αρχείου (επιτρέπονται και σύμβολα υποκατάστασης). Enter search terms here. Type ESC SPC for completions of current term. Εισάγετε εδώ τους όρους της αναζήτησης. Πατήστε ESC SPC για να εμφανίσετε τις λέξεις που αρχίζουν με τον τρέχοντα όρο. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (<F1>) for more detail. Εισαγωγή έκφρασης γλώσσας ερωτήματος. «Σκονάκι»:<br> <i>όρος1 όρος2</i> : 'όρος1' και 'όρος2' σε οποιοδήποτε πεδίο.<br> <i>πεδίο:όρος1</i> : 'όρος1' στο πεδίο 'πεδίο'.<br> Τυπικό πεδίο ονόματα/συνώνυμα:<br> τίτλος/θέμα/υπόμνημα, συγγραφέας/από, παραλήπτης/προς, όνομα αρχείου, επέκταση.<br> Ψευδο-πεδία: κατάλογος, mime/μορφή, τύπος/rclcat, ημερομηνία, μέγεθος.<br> Παραδείγματα δυο διαστημάτων ημερομηνιών: 2009-03-01/2009-05-20 2009-03-01/Π2Μ.<br> <i>όρος1 όρος2 OR όρος3</i> : όρος1 AND (όρος2 OR όρος3).<br> Μπορείτε να χρησιμοποιείτε παρενθέσεις για πιο ευανάγνωστες εκφράσεις.<br> <i>"όρος1 όρος2"</i> : φράση (πρέπει να αντιστοιχεί ακριβώς). Πιθανοί τροποποιητές:<br> <i>"όρος1 όρος2"p</i> : αταξινόμητη και κατά προσέγγιση αναζήτηση με προκαθορισμένη απόσταση.<br> Χρησιμοποιήστε τον δεσμό <b>Εμφάνιση ερωτήματος</b> σε περίπτωση αμφιβολίας σχετικά με το αποτέλεσμα και ανατρέξτε στο εγχειρίδιο χρήσης (<F1>) για περισσότερες λεπτομέρειες. Stemming languages for stored query: Οι γλώσσες ριζικοποίησης για το αποθηκευμένο ερώτημα: differ from current preferences (kept) διαφέρει από τις τρέχουσες προτιμήσεις (διατηρείται) Auto suffixes for stored query: Αυτόματα επιθήματα για αποθηκευμένο ερώτημα: External indexes for stored query: Εξωτερικά ευρετήρια για αποθηκευμένο ερώτημα: Autophrase is set but it was unset for stored query Το Autophrase έχει ρυθμιστεί αλλά δεν έχει οριστεί για αποθηκευμένο ερώτημα Autophrase is unset but it was set for stored query Το Autophrase δεν έχει ρυθμιστεί αλλά έχει οριστεί για αποθηκευμένο ερώτημα Enter search terms here. Εισαγάγετε εδώ τους όρους αναζήτησης. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border-collapse: collapse; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Σκονάκι γλώσσας ερωτημάτων. Σε περίπτωση αμφιβολίας κάντε κλικ στο <b>Εμφάνιση ερωτήματος</b>.&nbsp; You should really look at the manual (F1)</p> Θα πρέπει πραγματικά να εξετάσουμε το εγχειρίδιο (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Τι</th><th>Παραδείγματα</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>And</td><td>ένα δύο&nbsp;&nbsp;&nbsp;ένα AND δύο&nbsp;&nbsp;&nbsp;ένα && δύο</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Or</td><td>ένα OR δύο&nbsp;&nbsp;&nbsp;ένα || δύο</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Σύνθετη boolean. Το OR έχει προτεραιότητα, χρησιμοποιήστε παρένθεση&nbsp; where needed</td><td>(one AND two) OR three</td></tr> αν χρειάζεται</td><td>(ένα AND δύο) OR τρία</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Όχι</td><td>-όρος</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Φράση</td><td>"υπερηφάνεια και προκατάληψη"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Ονόματα πεδίων</td><td>τίτλος/θέμα&nbsp;&nbsp;συγγραφέας/από<br>παραλήπτη/έως&nbsp;&nbsp;όνομα αρχείου&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Φίλτρο διαδρομής καταλόγου</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Φίλτρο τύπου MIME</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size>100k size<1M</td></tr> <tr><td>Μέγεθος</td><td>size>100k size<1M</td></tr> </table></body></html> </table></body></html> Can't open index Αδύνατο το άνοιγμα του ευρετηρίου Could not restore external indexes for stored query:<br> Δεν ήταν δυνατή η επαναφορά εξωτερικών ευρετηρίων για αποθηκευμένο ερώτημα:<br> ??? ;;; Using current preferences. Χρησιμοποιώντας τις τρέχουσες προτιμήσεις. Simple search Απλός τύπος αναζήτησης History Ιστορικό <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> SSearchBase SSearchBase SSearchBase Clear Καθαρισμός Ctrl+S Ctrl+S Erase search entry Καθαρισμός της καταχώρησης Search Αναζήτηση Start query Έναρξη της αναζήτησης Enter search terms here. Type ESC SPC for completions of current term. Εισαγάγετε εδώ τους όρους αναζήτησης. Πατήστε ESC SPC για να εμφανίσετε τις λέξεις που αρχίζουν από τον τρέχοντα όρο. Choose search type. Επιλογή του τύπου αναζήτησης. Show query history Εμφάνιση ιστορικού ερωτημάτων Enter search terms here. Εισάγετε εδώ τους όρους αναζήτησης. Main menu Κεντρικό μενού SearchClauseW SearchClauseW SearchClauseW Any of these Οποιαδήποτε από αυτές All of these Όλα αυτά None of these Κανένα από αυτά This phrase Αυτή η φράση Terms in proximity Όροι εγγύτητας File name matching Ταίριασμα ονόματος αρχείου Select the type of query that will be performed with the words Επιλέξτε τον τύπο του ερωτήματος που θα πραγματοποιηθεί με τις λέξεις Number of additional words that may be interspersed with the chosen ones Αριθμός των επιπρόσθετων λέξεων που μπορούν να βρεθούν μεταξύ των αναζητηθέντων όρων In field Στο πεδίο No field Χωρίς πεδίο Any Οποιοδήποτε All Όλα None Κανένα Phrase Φράση Proximity Εγγύτητα File name Όνομα του αρχείου Snippets Snippets Αποσπάσματα X X Find: Εύρεση: Next Επόμενο Prev Προηγούμενο SnippetsW Search Αναζήτηση <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Λυπάμαι, δεν βρέθηκε μια ακριβής αντιστοιχία εντός ορίων. Πιθανώς το έγγραφο να είναι ογκώδες και ο δημιουργός αποσπασμάτων χάθηκε σε έναν λαβύρινθο...</p> Sort By Relevance Ταξινόμηση ανά συνάφεια Sort By Page Ταξινόμηση ανά σελίδα Snippets Window Παράθυρο αποσπασμάτων Find Αναζήτηση Find (alt) Εύρεση (εναλλακτικό) Find Next Εύρεση επόμενου Find Previous Εύρεση προηγούμενου Hide Απόκρυψη Find next Εύρεση επόμενου Find previous Εύρεση προηγούμενου Close window Κλείσιμο παραθύρου SortForm Date Ημερομηνία Mime type Τύπος MIME SortFormBase Sort Criteria Κριτήρια Ταξινόμησης Sort the Ταξινόμηση του most relevant results by: τα πιο σχετικά αποτελέσματα από: Descending Φθίνουσα Close Κλείσιμο Apply Εφαρμογή SpecIdxW Special Indexing Ειδική ευρετηρίαση Do not retry previously failed files. Μην ξαναπροσπαθήσετε προηγουμένως αποτυχημένα αρχεία. Else only modified or failed files will be processed. Θα γίνει επεξεργασία μόνο τροποποιημένων ή αποτυχημένων αρχείων. Erase selected files data before indexing. Διαγραφή επιλεγμένων αρχείων πριν την ευρετηρίαση. Directory to recursively index Κατάλογος αναδρομικά ευρετηρίου Browse Περιήγηση Start directory (else use regular topdirs): Κατάλογος έναρξης (αλλιώς χρησιμοποιήστε κανονικές κορυφές): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Αφήστε κενό για να επιλέξετε όλα τα αρχεία. Μπορείτε να χρησιμοποιήσετε πολλαπλές σχηματομορφές τύπου κελύφους χωρισμένα με κενό.<br>Σχηματομορφές με ενσωματωμένα κενά θα πρέπει να αναφέρονται με διπλά εισαγωγικά.<br>Μπορεί να χρησιμοποιηθεί μόνο αν έχει οριστεί ο κατάλογος πηγής. Selection patterns: Πρότυπα επιλογής: Top indexed entity Πάνω ευρετήριο οντότητας Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Κατάλογος αναδρομικά δείκτη. Αυτό πρέπει να είναι μέσα στην κανονική περιοχή ευρετηρίου<br> όπως ορίζεται στο αρχείο ρύθμισης παραμέτρων (topdirs). Retry previously failed files. Επανάληψη αποτυχημένων αρχείων. Start directory. Must be part of the indexed tree. We use topdirs if empty. Κατάλογος εκκίνησης. Πρέπει να είναι μέρος του δέντρου ευρετηρίου. Χρησιμοποιούμε topdirs αν είναι κενό. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Κατάλογος έναρξης. Πρέπει να είναι μέρος του δέντρου με δείκτη . Χρησιμοποιήστε την πλήρη ευρετήριο περιοχής αν είναι κενή. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Αρχείο εξόδου διαγνώσεων. Περικομμένο αρχείο με τις διαγνώσεις της ευρετηρίασης (αιτίες μη δεικτοδότησης αρχείων). Diagnostics file Αρχείο διαγνώσεων SpellBase Term Explorer Εξερευνητής όρων &Expand &Ανάπτυξη Alt+E Alt+E &Close &Κλείσιμο Alt+C Alt+C Term Όρος No db info. Δεν υπάρχουν πληροφορίες για τη βάση δεδομένων. Doc. / Tot. Doc. / Tot. Match Ταίριασμα Case Πεζά/κεφαλαία Accents Τόνοι SpellW Wildcards Σύμβολα υποκατάστασης Regexp Κανονική έκφραση Spelling/Phonetic Ορθογραφία/Φωνητική Aspell init failed. Aspell not installed? Σφάλμα στην αρχικοποίηση του aspell. Μήπως δεν είναι εγκατεστημένο; Aspell expansion error. Σφάλμα επέκτασης του aspell. Stem expansion Γραμματική επέκταση error retrieving stemming languages σφάλμα κατά τη λήψη των γλωσσών επέκτασης No expansion found Κανένα αποτέλεσμα Term Όρος Doc. / Tot. Doc. / Tot. Index: %1 documents, average length %2 terms Ευρετήριο: %1 έγγραφα, μέσο μήκος %2 όροι Index: %1 documents, average length %2 terms.%3 results Ευρετήριο: %1 έγγραφα, μέσο μήκος %2 όροι.%3 αποτελέσματα %1 results %1 αποτελέσματα List was truncated alphabetically, some frequent Η λίστα έχει κοπεί αλφαβητικά, μερικοί συχνοί terms may be missing. Try using a longer root. όροι μπορεί να λείπουν. Προσπαθήστε να χρησιμοποιήσετε μια πιο μακριά ρίζα. Show index statistics Εμφάνιση στατιστικών του ευρετηρίου Number of documents Αριθμός εγγράφων Average terms per document Μέσος όρος όρων ανά έγγραφο Smallest document length Μικρότερο μήκος εγγράφου Longest document length Μεγαλύτερο μήκος εγγράφου Database directory size Μέγεθος καταλόγου βάσης δεδομένων MIME types: Τύποι MIME: Item Αντικείμενο Value Τιμή Smallest document length (terms) Μικρότερο μήκος εγγράφου (όροι) Longest document length (terms) Μακρύτερο μήκος εγγράφου (όροι) Results from last indexing: Αποτελέσματα από την τελευταία ευρετηρίαση: Documents created/updated Έγγραφα δημιουργήθηκαν/ενημερώθηκαν Files tested Αρχεία δοκιμάστηκαν Unindexed files Μη δεικτοδοτημένα αρχεία List files which could not be indexed (slow) Εμφάνιση των αρχείων που δεν μπορούν να δεικτοδοτηθούν (αργό) Spell expansion error. Σφάλμα επέκτασης ορθογραφίας. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Ο επιλεγμένος κατάλογος δεν φαίνεται ότι είναι ένα ευρετήριο Xapian This is the main/local index! Αυτό είναι το κύριο ευρετήριο! The selected directory is already in the index list Ο επιλεγμένος κατάλογος βρίσκεται ήδη στη λίστα Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Επιλέξτε έναν κατάλογο που περιέχει ένα ευρετήριο Xapian (π.χ. /home/buddy/.recoll/xapiandb) error retrieving stemming languages σφάλμα κατά τη λήψη των γλωσσών ριζικοποίησης Choose Επιλέξτε Result list paragraph format (erase all to reset to default) Μορφή λίστας παραγράφου αποτελεσμάτων (διαγραφή όλων για επαναφορά στην εξ' ορισμού) Result list header (default is empty) Επικεφαλίδα λίστας αποτελεσμάτων (η εξ' ορισμού είναι κενή) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Επιλέξτε τον κατάλογο διαμόρφωσης του recoll ή του καταλόγου ευρετηρίου του xapian (π.χ.: /home/me/.recoll ή /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Ο επιλεγμένος κατάλογος φαίνεται ως ένας κατάλογος διαμόρφωσης του Recoll αλλά δεν είναι δυνατή η ανάγνωση της διαμόρφωσης At most one index should be selected Ένα περισσότερο ευρετήριο θα πρέπει να επιλεχθεί Cant add index with different case/diacritics stripping option Αδύνατη η προσθήκη ευρετηρίου με διαφορετικές επιλογές διάκρισης πεζών / κεφαλαίων και αποσπασμάτων Default QtWebkit font Γραμματοσειρά εξ ορισμού QtWebkit Any term Οποιοσδήποτε όρος All terms Όλοι οι όροι File name Όνομα του αρχείου Query language Γλώσσα ερωτημάτων Value from previous program exit Τιμή από προηγούμενη έξοδο προγράμματος Context Πλαίσιο Description Περιγραφή Shortcut Συντόμευση Default Προεπιλογή Choose QSS File Επιλογή αρχείου QSS Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Περιβάλλον χρήστη Number of entries in a result page Αριθμός αποτελεσμάτων ανά σελίδα Result list font Γραμματοσειρά λίστας Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Ανοίγει έναν διάλογο για την επιλογή της γραμματοσειράς για τη λίστα αποτελεσμάτων Reset Επαναφορά Resets the result list font to the system default Επαναφέρει τη γραμματοσειρά της λίστας αποτελεσμάτων στην προκαθορισμένη του συστήματος Auto-start simple search on whitespace entry. Αυτόματη έναρξη μιας απλής αναζήτησης όταν εισαχθεί ένα κενό. Start with advanced search dialog open. Εκκίνηση με τον διάλογο της προχωρημένης αναζήτησης ανοιχτό. Start with sort dialog open. Έναρξη με άνοιγμα διαλόγου ταξινόμησης. Search parameters Ρυθμίσεις αναζήτησης Stemming language Γλώσσα για την ανάπτυξη των όρων Dynamically build abstracts Δυναμική δημιουργία των αποσπασμάτων Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Αποφασίζει αν θα δημιουργούνται αποσπάσματα από το περιεχόμενο των όρων αναζήτησης. Μπορεί να επιβραδύνει την απεικόνιση αν τα έγγραφα είναι μεγάλα. Replace abstracts from documents Αντικατάσταση των υπαρχόντων αποσπασμάτων στα έγγραφα Do we synthetize an abstract even if the document seemed to have one? Θα πρέπει να γίνεται σύνθεση μιας σύνοψης ακόμα και αν το αρχικό έγγραφο διαθέτει μια; Synthetic abstract size (characters) Μέγεθος του συνθετικού αποσπάσματος (χαρακτήρες) Synthetic abstract context words Αριθμός σχετικών λέξεων ανά εμφάνιση του όρου στο απόσπασμα External Indexes Εξωτερικά ευρετήρια Add index Προσθήκη ευρετηρίου Select the xapiandb directory for the index you want to add, then click Add Index Επιλέξτε τον κατάλογο xapiandb για το ευρετήριο που θέλετε να προσθέσετε και, στη συνέχεια, κάντε κλικ στο στοιχείο Προσθήκη ευρετηρίου Browse Περιήγηση &OK &Εντάξει Apply changes Εφαρμογή των αλλαγών &Cancel &Ακύρωση Discard changes Απόρριψη των αλλαγών Result paragraph<br>format string Συμβολοσειρά μορφής παράγραφος<br>αποτελέσματος Automatically add phrase to simple searches Προσθήκη αυτόματα μιας φράσης στις απλές αναζητήσεις A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Μια αναζήτηση για [χωριάτικη σαλάτα] (2 όροι) θα συμπληρωθεί ως [χωριάτικη Ή σαλάτα Ή (χωριάτικη ΦΡΑΣΗ 2 σαλάτα)].<br> Αυτό θα πρέπει να αποδώσει μια καλύτερη εγγύτητα των αποτελεσμάτων όπου οι αναζητούμενοι όροι εμφανίζονται ακριβώς με τη σειρά. User preferences Προτιμήσεις χρήστη Use desktop preferences to choose document editor. Χρήση ρυθμίσεων του περιβάλλοντος για την επιλογή της εφαρμογής προβολής. External indexes Εξωτερικοί δείκτες Toggle selected Αλλαγή κατάστασης επιλεγμένων Activate All Ενεργοποίηση όλων Deactivate All Απενεργοποίηση όλων Remove selected Αφαίρεση των επιλεγμένων Remove from list. This has no effect on the disk index. Αφαίρεση από τη λίστα. Δεν έχει επίπτωση στο αποθηκευμένο ευρετήριο. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Καθορίζει τη μορφή για κάθε παράγραφο καταλόγου αποτελεσμάτων. Χρησιμοποιήστε μορφή qt html και αντικαταστάσεις όπως εκτυπώσεις:<br>%A Αφηρημένη<br> %D Ημερομηνία<br> %I Όνομα εικόνας εικονιδίου<br> %K Λέξεις-κλειδιά (αν υπάρχει)<br> %L Προεπισκόπηση και επεξεργασία συνδέσμων<br> %M Τύπος Mime<br> %N Αριθμός αποτελέσματος<br> %R Ποσοστό συνάφειας<br> %S Πληροφορίες μεγέθους<br> %T Τίτλος<br> %U Url<br> Remember sort activation state. Απομνημόνευση της κατάστασης ενεργοποίησης της ταξινόμησης. Maximum text size highlighted for preview (megabytes) Μέγιστο μέγεθος τονισμένων κειμένων προς προεπισκόπηση (MB) Texts over this size will not be highlighted in preview (too slow). Τα κείμενα μεγαλύτερα από αυτό το μέγεθος δεν θα τονιστούν στην προεπισκόπηση (πολύ αργό). Highlight color for query terms Χρώμα τονισμού των όρων αναζήτησης Prefer Html to plain text for preview. Χρήση της μορφής HTML για την προεπισκόπηση. If checked, results with the same content under different names will only be shown once. Εμφανίζει μια μόνο καταχώρηση για τα αποτελέσματα με πανομοιότυπο περιεχόμενο. Hide duplicate results. Απόκρυψη των διπλοεγγραφών. Choose editor applications Επιλέξτε τους επεξεργαστές για τους διάφορους τύπους αρχείων Display category filter as toolbar instead of button panel (needs restart). Εμφάνιση φίλτρου. κατηγορίας ως γραμμή εργαλείων αντί για πίνακα κουμπιών (απαιτεί επανεκκίνηση). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Οι λέξεις στη λίστα θα αλλάξουν αυτόματα σε ρήτρες ext:xxx στις καταχωρήσεις σε γλώσσα ερωτημάτων. Query language magic file name suffixes. Αυτόματα επιθήματα για τη γλώσσα ερωτημάτων. Enable Ενεργοποίηση ViewAction Changing actions with different current values Αλλαγή των ενεργειών με διαφορετικές τρέχουσες τιμές Mime type Τύπος MIME Command Εντολή MIME type Τύπος MIME Desktop Default Προκαθορισμένο Επιφάνειας εργασίας Changing entries with different current values Αλλαγή αντικειμένων με τρέχουσες τιμές διαφορετικές ViewActionBase File type Τύπος αρχείου Action Ενέργεια Select one or several file types, then click Change Action to modify the program used to open them Επιλέξτε έναν ή περισσότερους τύπους αρχείων και κάντε κλικ στο «Αλλαγή» για να αλλάξετε το πρόγραμμα που χρησιμοποιείται για το άνοιγμά τους Change Action Αλλαγή Close Κλείσιμο Native Viewers Εφαρμογές απεικόνισης Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Επιλέξτε έναν ή περισσότερους τύπους MIME και κάντε κλικ στο «Αλλαγή της ενέργειας»<br>Μπορείτε επίσης να κλείσετε το διάλογο και να επιλέξετε «Χρήση των προτιμήσεων του περιβάλλοντος εργασίας»<br>στο κύριο παράθυρο για να αγνοήσετε αυτή τη λίστα. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Επιλέξτε έναν οι περισσότερους τύπους αρχείων, και στη συνέχεια χρησιμοποιήστε τα κουμπιά ελέγχου στο πλαίσιο στο κάτω μέρος για να αλλάξετε τον τρόπο επεξεργασίας. Use Desktop preferences by default Χρήση εξ' ορισμού των προτιμήσεων της Επιφάνειας εργασίας Select one or several file types, then use the controls in the frame below to change how they are processed Επιλέξτε έναν οι περισσότερους τύπους αρχείων, και στη συνέχεια χρησιμοποιήστε τα κουμπιά ελέγχου στο παρακάτω πλαίσιο για να αλλάξετε τον τρόπο επεξεργασίας Exception to Desktop preferences Εξαίρεση των προτιμήσεων Επιφάνειας εργασίας Action (empty -> recoll default) Ενέργεια (κενό -> προκαθορισμένη του recoll) Apply to current selection Εφαρμογή στην τρέχουσα επιλογή Recoll action: Ενέργεια Recoll: current value τρέχουσα τιμή Select same Επιλογή ανά τιμή <b>New Values:</b> <b>Νέες τιμές:</b> Webcache Webcache editor Επεξεργαστής Webcache Search regexp Αναζήτηση regexp TextLabel WebcacheEdit Copy URL Αντιγραφή URL Unknown indexer state. Can't edit webcache file. Άγνωστη κατάσταση ευρετηρίου. Μπορεί't επεξεργάζεται αρχείο webcache. Indexer is running. Can't edit webcache file. Η ευρετηρίαση εκτελείται. Αδύνατη η επεξεργασία του αρχείου webcache. Delete selection Διαγραφή επιλογής Webcache was modified, you will need to run the indexer after closing this window. Η Webcache τροποποιήθηκε, θα πρέπει να εκτελέσετε το ευρετήριο μετά το κλείσιμο αυτού του παραθύρου. Save to File Αποθήκευση σε αρχείο File creation failed: Η δημιουργία του αρχείου απέτυχε: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url URL Date Ημερομηνία Size Μέγεθος URL URL WinSchedToolW Error Σφάλμα Configuration not initialized Οι ρυθμίσεις δεν αρχικοποιήθηκαν <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexing batch scheduling</h3><p>Χρησιμοποιούμε τον τυπικό προγραμματισμό εργασιών των Windows για αυτό. Το πρόγραμμα θα ξεκινήσει όταν κάνετε κλικ στο παρακάτω κουμπί.</p><p>Μπορείτε να χρησιμοποιήσετε είτε την πλήρη διεπαφή (<i>Δημιουργία εργασίας</i> στο μενού στα δεξιά), or the simplified <i>Create Basic task</i> wizard. Και στις δύο περιπτώσεις Αντιγράψτε/Επικολλήστε τη διαδρομή του αρχείου δέσμης εντολών που αναφέρονται παρακάτω ως <i>Ενέργεια</i> που θα εκτελεστεί.</p> Command already started Η εντολή ξεκίνησε ήδη Recoll Batch indexing Recoll ευρετηρίαση παρτίδων Start Windows Task Scheduler tool Ξεκινήστε το εργαλείο προγραμματισμού εργασιών των Windows Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Κλέψιμο της ουράς ευρετηρίασης του Beagle Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Το Beagle ΔΕΝ ΠΡΕΠΕΙ να εκτελείται. Επιτρέπει την επεξεργασία της ουράς του Beagle για ευρετηρίαση του ιστορικού των ιστοσελίδων του Firefox.<br>(θα πρέπει επίσης να εγκαταστήσετε το πρόσθετο του Beagle για το Firefox) Web cache directory name Όνομα καταλόγου προσωρινής μνήμης Web The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Το όνομα ενός καταλόγου στον οποίο θα αποθηκεύεται η προσωρινή μνήμη για επισκέψιμες ιστοσελίδες.<br>Μια μη απόλυτη διαδρομή λαμβάνεται σε σχέση με τον κατάλογο ρυθμίσεων. Max. size for the web cache (MB) Μέγιστο μέγεθος για την προσωρινή μνήμη ιστού (MB) Entries will be recycled once the size is reached Θα γίνεται αντικατάσταση των καταχωρήσεων όταν επιτευχθεί το καθορισμένο μέγεθος Web page store directory name Όνομα καταλόγου αποθήκευσης ιστοσελίδων The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Το όνομα του καταλόγου αποθήκευσης αντιγράφων των επισκεφθέντων ιστοσελίδων.<br>Μια σχετική διαδρομή αναφερόμενη στη διαδρομή διαμόρφωσης. Max. size for the web store (MB) Μέγιστο μέγεθος της κρυφής μνήμης ιστού (MB) Process the WEB history queue Επεξεργασία της ουράς ιστορικού του Ιστού Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Ενεργοποιεί τη δεικτοδότηση των επισκεπτόμενων σελίδων στον Firefox.<br>(θα πρέπει να εγκαταστήσετε και το πρόσθετο Firefox Recoll) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Οι εγγραφές θα ανακυκλωθούν μόλις επιτευχθεί το μέγεθος.<br>Μόνο η αύξηση του μεγέθους έχει πραγματικά νόημα, επειδή η μείωση της τιμής δεν θα περικόψει ένα υπάρχον αρχείο (μόνο χώρο αποβλήτων στο τέλος). confgui::ConfIndexW Can't write configuration file Αδύνατη η εγγραφή του αρχείου διαμόρφωσης Recoll - Index Settings: Recoll - Ρυθμίσεις Ευρετηρίου: confgui::ConfParamFNW Browse Περιήγηση Choose Επιλέξτε confgui::ConfParamSLW + + - - Add entry Προσθήκη καταχώρησης Delete selected entries Διαγραφή επιλεγμένων καταχωρήσεων ~ ~ Edit selected entries Επεξεργασία επιλεγμένων καταχωρήσεων confgui::ConfSearchPanelW Automatic diacritics sensitivity Αυτόματη ευαισθησία στους τόνους <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Αυτόματη εναλλαγή ευαισθησίας τονισμού αν ο όρος αναζήτησης διαθέτει τονισμένους χαρακτήρες (εκτός αυτών του unac_except_trans). Διαφορετικά θα πρέπει να χρησιμοποιήσετε τη γλώσσα της αναζήτησης και τον τροποποιητή <i>D</i> για τον καθορισμό της ευαισθησίας τονισμών. Automatic character case sensitivity Αυτόματη ευαισθησία πεζών/κεφαλαίων <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Αυτόματη εναλλαγή ευαισθησίας διάκρισης πεζών/κεφαλαίων αν η ο όρος αναζήτησης διαθέτει κεφαλαία γράμματα (εκτός του πρώτου γράμματος). Διαφορετικά θα πρέπει να χρησιμοποιήσετε τη γλώσσα της αναζήτησης και τον τροποποιητή <i>C</i> για τον καθορισμό της ευαισθησίας διάκρισης πεζών / κεφαλαίων. Maximum term expansion count Μέγιστο μέγεθος επέκτασης ενός όρου <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Μέγιστος αριθμός επέκτασης για έναν όρο (π.χ.: κατά τη χρήση χαρακτήρων υποκατάστασης). Η προκαθορισμένη τιμή 10000 είναι λογική και θα αποφύγει ερωτήματα που εμφανίζονται σαν παγωμένα την ίδια στιγμή που η μηχανή διαπερνά τη λίστα όρων. Maximum Xapian clauses count Μέγιστος αριθμός ρητρών Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Μέγιστος αριθμός στοιχειωδών ρητρών που προσθέτουμε σε ένα απλό ερώτημα Xapian. Σε μερικές περιπτώσεις, το αποτέλεσμα της επέκτασης των όρων μπορεί να είναι πολλαπλασιαστικό, και θα χρησιμοποιούσε υπερβολική μνήμη. Η προκαθορισμένη τιμή 100000 θα πρέπει να είναι επαρκής και συμβατή με μια τυπική διαμόρφωση υλικού. confgui::ConfSubPanelW Global Γενικά Max. compressed file size (KB) Μεγ.μέγεθος για τα συμπιεσμένα αρχεία (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Αυτή η τιμή καθορίζει ένα όριο πέραν του οποίου τα συμπιεσμένα αρχεία δεν θα επεξεργάζονται. Χρησιμοποιήστε -1 για κανένα όριο, 0 για να μην επεξεργάζονται τα συμπιεσμένα αρχεία. Max. text file size (MB) Μέγιστο μέγεθος αρχείων κειμένου (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Αυτή η τιμή ορίζει ένα όριο πέραν του οποίου δεν θα γίνεται ευρετηρίαση για τα αρχεία κειμένου. Ορίστε -1 για κανένα όριο. Αυτό χρησιμεύει για τον αποκλεισμό από την ευρετηρίαση τεράστιων αρχείων καταγραφών. Text file page size (KB) Μέγεθος κοπής για τα αρχεία κειμένου (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Αν αυτή η τιμή έχει οριστεί και είναι θετική, τα αρχεία κειμένου θα κοπούν σε κομμάτια αυτού του μεγέθους για την ευρετηρίαση. Αυτό βοηθά στη μείωση των καταναλωμένων πόρων από την ευρετηρίαση και βοηθά τη φόρτωση για την προεπισκόπηση. Max. filter exec. time (S) Μέγιστος. χρόνος εκτέλεσης για ένα φίλτρο (Δ) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Τα εξωτερικά φίλτρα σε λειτουργία μεγαλύτερη από αυτό θα διακόπτονται. Χρήσιμο για τη σπάνια περίπτωση (π.χ. postscript) όπου ένα έγγραφο μπορεί να προκαλέσει ένα βρόγχο στο φίλτρο. ορίστε το σε -1 για να αφαιρέσετε το όριο. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Τα εξωτερικά φίλτρα σε λειτουργία μεγαλύτερη από αυτό θα διακόπτονται. Χρήσιμο για τη σπάνια περίπτωση (π.χ. postscript) όπου ένα έγγραφο μπορεί να προκαλέσει ένα βρόγχο στο φίλτρο. Ορίστε το σε -1 για να αφαιρέσετε το όριο. Only mime types Μόνο οι τύποι MIME An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Μια αποκλειστική λίστα δεικτοδοτημένων τύπων mime.<br>Δεν θα δεικτοδοτηθεί τίποτα άλλο. Φυσιολογικά κενό και αδρανές Exclude mime types Αποκλεισμός τύπων αρχείων Mime types not to be indexed Οι τύποι Mime που δεν θα δεικτοδοτηθούν Max. filter exec. time (s) Μέγιστο φίλτρο exec. χρόνος (s) confgui::ConfTopPanelW Top directories Κατάλογοι εκκίνησης The list of directories where recursive indexing starts. Default: your home. Η λίστα των καταλόγων για την έναρξη της αναδρομικής ευρετηρίασης. Προεπιλογή: ο προσωπικός σας κατάλογος. Skipped paths Παραλειπόμενες διαδρομές These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Πρόκειται για ονόματα καταλόγων που δεν θα δεικτοδοτηθούν.<br>Μπορούν να περιέχουν χαρακτήρες υποκατάστασης. Οι διαδρομές πρέπει να αντιστοιχούν με αυτές που είδε ο δεικτοδότης (π.χ: αν ένας κατάλογος έναρξης είναι '/home/me' και το '/home' είναι ένας δεσμός στο '/usr/home', μια σωστή διαδρομή εδώ θα ήταν '/home/me/tmp*' , όχι '/usr/home/me/tmp*') Stemming languages Γλώσσα για την επέκταση των όρων The languages for which stemming expansion<br>dictionaries will be built. Οι γλώσσες για τις οποίες θα δημιουργηθούν τα λεξικά επεκτάσεων<br>των όρων. Log file name Όνομα του αρχείου καταγραφών The file where the messages will be written.<br>Use 'stderr' for terminal output Το αρχείο που θα εγγραφούν τα μηνύματα.<br>Χρησιμοποιήστε 'stderr' για την έξοδο τερματικού Log verbosity level Επίπεδο ανάλυσης των καταγραφών This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Αυτή η τιμή ρυθμίζει την ποσότητα των απεσταλμένων μηνυμάτων,<br>από μόνο τα σφάλματα μέχρι πολλά δεδομένα αποσφαλμάτωσης. Index flush megabytes interval Καθυστέρηση εγγραφής του ευρετηρίου σε megabyte This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Αυτή η τιμή ρυθμίζει την ποσότητα των δεδομένων που δεικτοδοτούνται μεταξύ των εγγραφών στο δίσκο.<br>Βοηθά στον έλεγχο χρήσης της μνήμης. Προεπιλογή: 10MB Max disk occupation (%) Μέγιστη χρήση του δίσκου (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Το ποσοστό χρήσης του δίσκου που θα σταματήσει η ευρετηρίαση (για να αποφευχθεί η υπερβολική πλήρωση).<br>0 σημαίνει χωρίς όριο (προεπιλογή). No aspell usage Χωρίς χρήση του aspell Aspell language Γλώσσα του aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Η γλώσσα για το λεξικό aspell. Αυτό θα πρέπει να είναι του τύπου «en» ή «el» ...<br> Αν αυτή η τιμή δεν οριστεί, χρησιμοποιείται το εθνικό περιβάλλον NLS για να την υπολογίσει, που συνήθως δουλεύει. Για να πάρετε μια ιδέα του τι είναι εγκατεστημένο στο σύστημά σας, πληκτρολογήστε «aspell config» και παρατηρήστε τα αρχεία .dat στον κατάλογο «data-dir». Database directory name Κατάλογος αποθήκευσης του ευρετηρίου The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Το όνομα του καταλόγου αποθήκευσης του ευρετηρίου<br>Μια σχετική διαδρομή αναφερόμενη στη διαδρομή διαμόρφωσης. Η εξ' ορισμού είναι «xapiandb». Use system's 'file' command Χρήση της εντολής 'file' του συστήματος Use the system's 'file' command if internal<br>mime type identification fails. Χρήση της εντολής 'file' αν ο εσωτερικός εντοπισμός<br>του τύπου mime δεν επιφέρει αποτελέσματα. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Απενεργοποιεί τη χρήση του aspell για τη δημιουργία των ορθογραφικών προσεγγίσεων.<br>Χρήσιμο αν το aspell δεν είναι εγκατεστημένο ή δεν λειτουργεί. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Η γλώσσα για το λεξικό aspell. Αυτό θα πρέπει να είναι του τύπου «en» ή «el» ...<br> Αν αυτή η τιμή δεν οριστεί, χρησιμοποιείται το εθνικό περιβάλλον NLS για να την υπολογίσει, που συνήθως δουλεύει. Για να πάρετε μια ιδέα του τι είναι εγκατεστημένο στο σύστημά σας, πληκτρολογήστε «aspell config» και παρατηρήστε τα αρχεία .dat στον κατάλογο «data-dir». The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Το όνομα του καταλόγου αποθήκευσης του ευρετηρίου<br>Μια σχετική διαδρομή αναφερόμενη στη διαδρομή διαμόρφωσης. Η εξ' ορισμού είναι «xapiandb». Unac exceptions Εξαιρέσεις unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Αυτές είναι εξαιρέσεις για τον μηχανισμό unac, ο οποίος εξ' ορισμού, αφαιρεί όλους τους τονισμούς, και πραγματοποιεί κανονική αποσύνθεση. Μπορείτε να αναιρέσετε την αφαίρεση των τονισμών για ορισμένους χαρακτήρες, ανάλογα με τη γλώσσα σας, και διευκρινίστε άλλους αποσυνθέσεις, για παράδειγμα συμπλεγμένους χαρακτήρες. Στη λίστα διαχωρισμένη με κενά, ο πρώτος χαρακτήρας ενός αντικειμένου είναι η πηγή, το υπόλοιπο είναι η μετάφραση. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Αυτά είναι ονόματα διαδρομών καταλόγων που δεν θα εισέλθουν στο ευρετήριο.<br>Τα στοιχεία της διαδρομής μπορεί να περιέχουν μπαλαντέρ (wildcards). Οι καταχωρήσεις πρέπει να ταιριάζουν με τις διαδρομές που βλέπει ο ευρετής (π.χ. εάν οι topdirs περιλαμβάνουν '/home/me' και '/home' είναι στην πραγματικότητα ένας σύνδεσμος με '/usr/home', μια σωστή καταχώρηση skippedPath θα είναι '/home/me/tmp*', όχι '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Μέγιστη κατάληψη δίσκου (%, 0 σημαίνει χωρίς όριο) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Αυτό είναι το ποσοστό χρήσης δίσκου - συνολική χρήση δίσκων, όχι μέγεθος δείκτη - στο οποίο ευρετηρίαση θα αποτύχει και να σταματήσει.<br>Η προεπιλεγμένη τιμή του 0 αφαιρεί οποιοδήποτε όριο. uiPrefsDialogBase User preferences Προτιμήσεις χρήστη User interface Περιβάλλον χρήστη Number of entries in a result page Αριθμός αποτελεσμάτων ανά σελίδα If checked, results with the same content under different names will only be shown once. Εμφανίζει μια μόνο καταχώρηση για τα αποτελέσματα με πανομοιότυπο περιεχόμενο. Hide duplicate results. Απόκρυψη των διπλοεγγραφών. Highlight color for query terms Χρώμα τονισμού των όρων αναζήτησης Result list font Γραμματοσειρά λίστας Opens a dialog to select the result list font Ανοίγει έναν διάλογο για την επιλογή της γραμματοσειράς για τη λίστα αποτελεσμάτων Helvetica-10 Helvetica-10 Resets the result list font to the system default Επαναφέρει τη γραμματοσειρά της λίστας αποτελεσμάτων στην προκαθορισμένη του συστήματος Reset Επαναφορά Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Καθορίζει τη μορφή για κάθε παράγραφο καταλόγου αποτελεσμάτων. Χρησιμοποιήστε μορφή qt html και αντικαταστάσεις όπως εκτυπώσεις:<br>%A Αφηρημένη<br> %D Ημερομηνία<br> %I Όνομα εικόνας εικονιδίου<br> %K Λέξεις-κλειδιά (αν υπάρχει)<br> %L Προεπισκόπηση και επεξεργασία συνδέσμων<br> %M Τύπος Mime<br> %N Αριθμός αποτελέσματος<br> %R Ποσοστό συνάφειας<br> %S Πληροφορίες μεγέθους<br> %T Τίτλος<br> %U Url<br> Result paragraph<br>format string Συμβολοσειρά μορφής παράγραφος<br>αποτελέσματος Texts over this size will not be highlighted in preview (too slow). Τα κείμενα μεγαλύτερα από αυτό το μέγεθος δεν θα επισημαίνονται στην προεπισκόπηση (πολύ αργό). Maximum text size highlighted for preview (megabytes) Μέγιστο. μέγεθος επισημασμένων κειμένων προς προεπισκόπηση (MB) Use desktop preferences to choose document editor. Χρήση ρυθμίσεων του περιβάλλοντος για την επιλογή της εφαρμογής προβολής. Choose editor applications Επιλέξτε τους επεξεργαστές για τους διάφορους τύπους αρχείων Display category filter as toolbar instead of button panel (needs restart). Εμφάνιση φίλτρου. κατηγορίας ως γραμμή εργαλείων αντί για πίνακα κουμπιών (απαιτεί επανεκκίνηση). Auto-start simple search on whitespace entry. Αυτόματη έναρξη μιας απλής αναζήτησης όταν εισαχθεί ένα κενό. Start with advanced search dialog open. Εκκίνηση με τον διάλογο της προχωρημένης αναζήτησης ανοιχτό. Start with sort dialog open. Έναρξη με άνοιγμα διαλόγου ταξινόμησης. Remember sort activation state. Απομνημόνευση της κατάστασης ενεργοποίησης της ταξινόμησης. Prefer Html to plain text for preview. Χρήση της μορφής HTML για την προεπισκόπηση. Search parameters Ρυθμίσεις αναζήτησης Stemming language Γλώσσα για την ανάπτυξη των όρων A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Μια αναζήτηση για [χωριάτικη σαλάτα] (2 όροι) θα συμπληρωθεί ως [χωριάτικη Ή σαλάτα Ή (χωριάτικη ΦΡΑΣΗ 2 σαλάτα)].<br> Αυτό θα πρέπει να αποδώσει μια καλύτερη εγγύτητα των αποτελεσμάτων όπου οι αναζητούμενοι όροι εμφανίζονται ακριβώς με τη σειρά. Automatically add phrase to simple searches Προσθήκη αυτόματα μιας φράσης στις απλές αναζητήσεις Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Αποφασίζει αν θα δημιουργούνται αποσπάσματα από το περιεχόμενο των όρων αναζήτησης. Μπορεί να επιβραδύνει την απεικόνιση αν τα έγγραφα είναι μεγάλα. Dynamically build abstracts Δυναμική δημιουργία των αποσπασμάτων Do we synthetize an abstract even if the document seemed to have one? Θα πρέπει να γίνεται σύνθεση μιας σύνοψης ακόμα και αν το αρχικό έγγραφο διαθέτει μια; Replace abstracts from documents Αντικατάσταση των υπαρχόντων αποσπασμάτων στα έγγραφα Synthetic abstract size (characters) Μέγεθος του συνθετικού αποσπάσματος (χαρακτήρες) Synthetic abstract context words Αριθμός σχετικών λέξεων ανά εμφάνιση του όρου στο απόσπασμα The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Οι λέξεις στη λίστα θα αλλάξουν αυτόματα σε ρήτρες ext:xxx στις καταχωρήσεις σε γλώσσα ερωτημάτων. Query language magic file name suffixes. Αυτόματα επιθήματα για τη γλώσσα ερωτημάτων. Enable Ενεργό External Indexes Εξωτερικά ευρετήρια Toggle selected Αλλαγή κατάστασης επιλεγμένων Activate All Ενεργοποίηση όλων Deactivate All Απενεργοποίηση όλων Remove from list. This has no effect on the disk index. Αφαίρεση από τη λίστα. Δεν έχει επίπτωση στο αποθηκευμένο ευρετήριο. Remove selected Αφαίρεση των επιλεγμένων Click to add another index directory to the list Κάντε κλικ για να προσθέσετε ένα άλλο ευρετήριο στη λίστα Add index Προσθήκη ευρετηρίου Apply changes Εφαρμογή των αλλαγών &OK &Εντάξει Discard changes Απόρριψη των αλλαγών &Cancel &Ακύρωση Abstract snippet separator Διαχωριστής αποσπάσματος Use <PRE> tags instead of <BR>to display plain text as html. Χρησιμοποιήστε <PRE> ετικέτες αντί για <BR>για να εμφανίσετε το απλό κείμενο ως html. Lines in PRE text are not folded. Using BR loses indentation. Γραμμές σε κείμενο PRE δεν διπλώνονται. Χρησιμοποιώντας BR χάνει εσοχή. Style sheet Φύλλο αισθητικής επικάλυψης Opens a dialog to select the style sheet file Ανοίγει έναν διάλογο για την επιλογή ενός αρχείου φύλλου αισθητικής επικάλυψης Choose Επιλογή Resets the style sheet to default Επαναφέρει την προκαθορισμένη τιμή για το φύλλο αισθητικής επικάλυψης Lines in PRE text are not folded. Using BR loses some indentation. Οι γραμμές στις ενότητες PRE δεν δικαιολογούνται. Χρησιμοποιώντας BR χάνονται μερικές εσοχές. Use <PRE> tags instead of <BR>to display plain text as html in preview. Χρήση <PRE> αντί <BR> για απλό κείμενο αντί html στην προεπισκόπηση. Result List Λίστα αποτελεσμάτων Edit result paragraph format string Επεξεργασία της μορφής της παραγράφου αποτελεσμάτων Edit result page html header insert Επεξεργασία του τμήματος για εισαγωγή στην κεφαλίδα HTML Date format (strftime(3)) Μορφή της ημερομηνίας (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Όριο συχνότητας (ποσοστό) πέραν του οποίου οι όροι δεν θα χρησιμοποιούνται. Οι φράσεις που περιέχουν πολύ συχνούς όρους δημιουργούν προβλήματα στην απόδοση. Οι αγνοημένοι όροι αυξάνουν την απόσταση της φράσης, και μειώνουν την αποτελεσματικότητα της λειτουργίας αναζήτησης αυτόματης φράσης. Η προκαθορισμένη τιμή είναι 2%. Autophrase term frequency threshold percentage Όριο συχνότητας του όρου (ποσοστό) για την αυτόματη δημιουργία φράσεων Plain text to HTML line style Στυλ μετάφρασης απλό κείμενο σε HTML Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Οι γραμμές που είναι έγκλειστες στα PRE δεν αναδιπλώνονται. Η χρήση BR οδηγεί σε απώλεια ορισμένων εσοχών. Το στυλ PRE + αναδίπλωση φαίνεται να είναι η καλύτερη επιλογή αλλά η σωστή του λειτουργία εξαρτάται από την έκδοση της Qt. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + αναδίπλωση Exceptions Εξαιρέσεις Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Τύποι Mime που δεν πρέπει να περάσουν στο xdg ανοιχτό ακόμη και όταν "Χρήση προτιμήσεων επιφάνειας εργασίας" έχει οριστεί.<br> Χρήσιμο για να περάσει ο αριθμός σελίδας και τις επιλογές αναζήτησης συμβολοσειρά, π.χ. evince. Disable Qt autocompletion in search entry. Απενεργοποίηση της αυτόματης συμπλήρωσης Qt στην εισαγωγή αναζήτησης. Search as you type. Αναζήτηση κατά την πληκτρολόγηση. Paths translations Διαδρομές μεταφράσεων Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Κάντε κλικ για να προσθέσετε έναν κατάλογο ευρετηρίου στη λίστα. Μπορείτε να επιλέξετε είτε έναν κατάλογο διαμόρφωσης Recoll ή ένα ευρετήριο Xapian. Snippets window CSS file Αρχείο CSS παραθύρου αποσπάσματος Opens a dialog to select the Snippets window CSS style sheet file Ανοίγει έναν διάλογο που σας επιτρέπει την επιλογή του αρχείου στυλ CSS για το αναδυόμενο παράθυρο αποσπασμάτων Resets the Snippets window style Επαναφορά του στυλ του παραθύρου αποσπασμάτων Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Καθορίζει αν τα φίλτρα των εγγράφων θα εμφανίζονται ως κουμπιά επιλογών, γραμμή εργαλείων πλαισίων συνδυασμών, ή μενού. Document filter choice style: Τεχνοτροπία επιλογής φίλτρου εγγράφων: Buttons Panel Πίνακας κουμπιών Toolbar Combobox Γραμμή εργαλείων πλαισίων συνδυασμών Menu Μενού Show system tray icon. Εμφάνιση του εικονιδίου πλαισίου συστήματος. Close to tray instead of exiting. Αντί για έξοδο, καταχώνιασμα στο πλαίσιο συστήματος. Start with simple search mode Έναρξη με απλή λειτουργία αναζήτησης Show warning when opening temporary file. Εμφάνιση προειδοποίησης κατά το άνοιγμα προσωρινού αρχείου. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Στυλ χρήστη που θα εφαρμοστεί στο παράθυρο αποσπάσματα.<br> Σημείωση: το ένθετο κεφαλίδας σελίδας αποτελέσματος περιλαμβάνεται επίσης στην κεφαλίδα του παραθύρου αποκοπής. Synonyms file Αρχείο συνώνυμων Highlight CSS style for query terms Τεχνοτροπία CSS για την επισήμανση των όρων ερωτήματος Recoll - User Preferences Recoll - Προτιμήσεις χρήστη Set path translations for the selected index or for the main one if no selection exists. Ορισμός μεταφράσεων διαδρομής για το επιλεγμένο ευρετήριο ή για το κύριο, αν δεν υπάρχει επιλογή. Activate links in preview. Ενεργοποίηση συνδέσμων στην προεπισκόπηση. Make links inside the preview window clickable, and start an external browser when they are clicked. Κάντε κλικ στους συνδέσμους μέσα στο παράθυρο προεπισκόπησης και ξεκινήστε ένα εξωτερικό πρόγραμμα περιήγησης όταν πατηθούν. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Επισημασμένοι όροι ερωτήματος στα αποτελέσματα. <br>Δοκιμάστε "color:red;background:yellow" για κάτι πιο ζωντανό από το προεπιλεγμένο μπλε... Start search on completer popup activation. Εκκίνηση αναζήτησης σε αναδυόμενη ενεργοποίηση. Maximum number of snippets displayed in the snippets window Μέγιστος αριθμός αποσπασμάτων που εμφανίζονται στο παράθυρο αποσπασμάτων Sort snippets by page number (default: by weight). Ταξινόμηση αποσπασμάτων ανά αριθμό σελίδας (προεπιλογή: κατά βάρος). Suppress all beeps. Αθόρυβη λειτουργία. Application Qt style sheet Φύλλο αισθητικής επικάλυψης Qt για την εφαρμογή Limit the size of the search history. Use 0 to disable, -1 for unlimited. Περιορίζει το μέγεθος του ιστορικού αναζήτησης. Χρησιμοποιήστε το 0 για απενεργοποίηση, το - 1 για απεριόριστα. Maximum size of search history (0: disable, -1: unlimited): Μέγιστο μέγεθος ιστορικού αναζήτησης (0: απενεργοποίηση, -1: απεριόριστος): Generate desktop notifications. Δημιουργία ειδοποιήσεων επιφάνειας εργασίας. Misc Διάφορα Work around QTBUG-78923 by inserting space before anchor text Παράκαμψη του σφάλματος QTBUG-78923 με την εισαγωγή διαστήματος πριν από το κείμενο αγκύρωσης Display a Snippets link even if the document has no pages (needs restart). Εμφάνιση συνδέσμου αποσπασμάτων ακόμα και αν το έγγραφο δεν έχει σελίδες (χρειάζεται επανεκκίνηση). Maximum text size highlighted for preview (kilobytes) Μέγιστο μέγεθος κειμένου επισημασμένο για προεπισκόπηση (kb) Start with simple search mode: Έναρξη με απλή λειτουργία αναζήτησης: Hide toolbars. Απόκρυψη γραμμών εργαλείων. Hide status bar. Απόκρυψη γραμμής κατάστασης. Hide Clear and Search buttons. Απόκρυψη κουμπιών εκκαθάρισης και αναζήτησης. Hide menu bar (show button instead). Απόκρυψη γραμμής μενού (εμφάνιση κουμπιού αντί). Hide simple search type (show in menu only). Απόκρυψη απλού τύπου αναζήτησης (εμφανίζεται μόνο στο μενού). Shortcuts Συντομεύσεις Hide result table header. Απόκρυψη επικεφαλίδας πίνακα αποτελεσμάτων. Show result table row headers. Εμφάνιση κεφαλίδων πίνακα αποτελεσμάτων. Reset shortcuts defaults Επαναφορά προεπιλογών συντομεύσεων Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Απενεργοποιήστε τις συντομεύσεις Ctrl+[0-9]/[a-z] για να μεταβείτε σε γραμμές πίνακα. Use F1 to access the manual Χρησιμοποιήστε το F1 για πρόσβαση στο εγχειρίδιο Hide some user interface elements. Απόκρυψη ορισμένων στοιχείων του περιβάλλοντος χρήστη. Hide: Απόκρυψη: Toolbars Εργαλειοθήκες Status bar Γραμμή κατάστασης Show button instead. Εμφάνιση του κουμπιού. Menu bar Γραμμή μενού Show choice in menu only. Εμφάνιση της επιλογής στο μενού μόνο. Simple search type Απλός τύπος αναζήτησης Clear/Search buttons Κουμπιά εκκαθάρισης/αναζήτησης Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. Απενεργοποιήστε τις συντομεύσεις Ctrl+[0-9]/Shift+[a-z] για να μεταβείτε σε γραμμές πίνακα. None (default) Κανένα (προκαθορισμένο) Uses the default dark mode style sheet Χρήση του προκαθορισμένου φύλλου αισθητικής επικάλυψης σκοτεινής λειτουργίας Dark mode Σκοτεινή λειτουργία Choose QSS File Επιλογή αρχείου QSS To display document text instead of metadata in result table detail area, use: Για την εμφάνιση του κειμένου του εγγράφου αντί των μεταδεδομένων στην περιοχή των λεπτομερειών στον πίνακα των αποτελεσμάτων, χρησιμοποιήστε: left mouse click αριστερό κλικ του ποντικιού Shift+click Shift+κλικ Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Άνοιγμα διαλόγου για την επιλογή του φύλλου αισθητικής επικάλυψης.<br>Ανατρέξτε στο /usr/share/recoll/examples/recoll[-dark].qss για να δείτε ένα παράδειγμα. Result Table Πίνακας αποτελεσμάτων Do not display metadata when hovering over rows. Να μην εμφανίζονται τα μεταδεδομένα κατά το πέρασμα του ποντικιού στις σειρές. Work around Tamil QTBUG-78923 by inserting space before anchor text Παράκαμψη του σφάλματος Ταμίλ QTBUG-78923 με την εισαγωγή διαστήματος πριν από το κείμενο αγκύρωσης The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Αυτό το σφάλμα προκαλεί την εμφάνιση ενός παράξενου χαρακτήρα με κυκλικό σχήμα στο εσωτερικό Ταμίλ λέξεων. Για την παράκαμψη του προβλήματος γίνεται εισαγωγή ενός πρόσθετου χαρακτήρα διαστήματος και όπως φαίνεται λύνει το πρόβλημα. Depth of side filter directory tree Βάθος του δέντρου του φίλτρου των καταλόγων Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Συντελεστής εστίασης για το περιβάλλον χρήστη. Αυτό είναι χρήσιμο όταν η προκαθορισμένη εστίαση δεν αρμόζει στην ανάλυση της οθόνης. Display scale (default 1.0): Κλίμακα προβολής (εξ ορισμού 1.0): Automatic spelling approximation. Αυτόματη προσέγγιση ορθογραφίας. Max spelling distance Μέγιστη απόσταση ορθογραφίας Add common spelling approximations for rare terms. Προσθήκη συχνών όρων και με ορθογραφική προσέγγιση σπάνιων όρων αναζήτησης. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_zh_CN.ts0000644000175000017500000072776314444307651014662 00000000000000 ActSearchDLG Menu search AdvSearch All clauses 全部条件 Any clause 任意条件 texts 文本 spreadsheets 电子表格 presentations 演示文稿 media 多媒体文件 messages 邮件 other 其它 Bad multiplier suffix in size filter 文件尺寸过滤器的后缀单位不正确 text 文本 spreadsheet 电子表格 presentation 演示文档 message 邮件 Advanced Search 高级搜索 History Next 下一历史记录 History Prev 历史记录 Load next stored search 加载下一个存储的搜索 Load previous stored search 加载先前存储的搜索 AdvSearchBase Advanced search 高端搜索 Restrict file types 限定文件类型 Save as default 保存为默认值 Searched file types 将被搜索的文件类型 All ----> 移动全部→ Sel -----> 移动选中项→ <----- Sel ←移动选中项 <----- All ←移动全部 Ignored file types 要忽略的文件类型 Enter top directory for search 输入要搜索的最上层目录 Browse 浏览 Restrict results to files in subtree: 将结果中的文件限定在此子目录树中: Start Search 开始搜索 Search for <br>documents<br>satisfying: 搜索<br>满足以下条件<br>的文档: Delete clause 删除条件 Add clause 添加条件 Check this to enable filtering on file types 选中这个,以便针对文件类型进行过滤 By categories 按大类来过滤 Check this to use file categories instead of raw mime types 选中这个,以便使用较大的分类,而不使用具体的文件类型 Close 关闭 All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. 右边的所有非空字段都会按照逻辑与(“全部条件”选项)或逻辑或(“任意条件”选项)来组合。<br>“任意”“全部”和“无”三种字段类型都接受输入简单词语和双引号引用的词组的组合。<br>空的输入框会被忽略。 Invert 反转过滤条件 Minimum size. You can use k/K,m/M,g/G as multipliers 最小尺寸。你可使用k/K、m/M、g/G作为单位 Min. Size 最小尺寸 Maximum size. You can use k/K,m/M,g/G as multipliers 最大尺寸。你可使用k/K、m/M、g/G作为单位 Max. Size 最大尺寸 Select 选择 Filter 过滤 From To Check this to enable filtering on dates 选中这个,以便针对日期进行过滤 Filter dates 过滤日期 Filter birth dates 过滤创建日期 Find 查找 Check this to enable filtering on sizes 选中这个,以便针对文件尺寸进行过滤 Filter sizes 过滤尺寸 ConfIndexW Can't write configuration file 无法写入配置文件 Global parameters 全局参数 Local parameters 局部参数 Search parameters 搜索参数 Top directories 顶级目录 The list of directories where recursive indexing starts. Default: your home. 索引从这个列表中的目录开始,递归地进行。默认:你的家目录。 Skipped paths 略过的路径 These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') 索引输入的目录的路径名。<br>路径元素可能包含通配符。 条目必须与索引器看到的路径匹配(例如:如果顶级路径包含 '/home/me' ,并且 '/home' 实际上是 '/usr/home' 的链接,则正确的相对路径条目应为 '/home/me/tmp*' ,而不是 '/usr/home/me/tmp*') Stemming languages 词根语言 The languages for which stemming expansion<br>dictionaries will be built. 将会针对这些语言<br>构造词根扩展词典。 Log file name 记录文件名 The file where the messages will be written.<br>Use 'stderr' for terminal output 程序输出的消息会被保存到这个文件。<br>使用'stderr'以表示将消息输出到终端 Log verbosity level 记录的话痨级别 This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. 这个值调整的是输出的消息的数量,<br>其级别从仅输出报错信息到输出一大堆调试信息。 Index flush megabytes interval 刷新索引的间隔,兆字节 This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB 这个值调整的是,当积累咯多少索引数据时,才将数据刷新到硬盘上去。<br>用来控制索引进程的内存占用情况。默认为10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. 这是磁盘使用量(是总磁盘使用量而不是索引大小)的百分比,在该百分比下索引将失败并停止。<br>默认值0将消除所有限制。 No aspell usage 不使用aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. 禁止在词语探索器中使用aspell来生成拼写相近的词语。<br>在没有安装aspell或者它工作不正常时使用这个选项。 Aspell language Aspell语言 The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. aspell词典的语言。表示方式是'en'或'fr'……<br>如果不设置这个值,则会使用系统环境中的自然语言设置信息,而那个通常是正确的。要想查看你的系统中安装咯哪些语言的话,就执行'aspell config',再在'data-dir'目录中找.dat文件。 Database directory name 数据库目录名 The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. 用来储存索引数据的目录的名字<br>如果使用相对路径,则路径会相对于配置目录进行计算。默认值是'xapiandb'。 Unac exceptions Unac例外 <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>这是针对unac机制的例外,默认情况下,该机制会删除所有的判断信息,并进行正规的分解。妳可以按照自己的语言的特点针对某个字符覆盖掉口音解除设置,以及指定额外的分解(例如,针对复数)。在每个由空格分隔的条目中,第一个字符是源字符,剩下的就是翻译。 Process the WEB history queue 处理网页历史队列 Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) 启用对火狐的已访问页面进行索引。<br>(妳还需要安装火狐的Recoll插件) Web page store directory name 网页储存目录名 The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. 用来储存复制过来的已访问网页的目录名。<br>如果使用相对路径,则会相对于配置目录的路径进行处理。 Max. size for the web store (MB) 网页存储的最大尺寸(MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). 一旦大小达到,条目将被回收。<br>仅增加大小确实有意义,因为减小该值不会截断现有文件(最后只是浪费空间而已) Automatic diacritics sensitivity 自动判断大小写 <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>如果搜索语句中包含带有口音特征(不在unac_except_trans中)的话,则自动触发大小写的判断。否则,妳需要使用查询语言和<i>D</i>修饰符来指定对大小写的判断。 Automatic character case sensitivity 自动调整字符的大小写敏感性 <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>如果搜索语句中除首字母之外包含有大写字母的话,则自动触发大小写的判断。否则,妳需要使用查询语言和<i>C</i>修饰符来指定对大小写的判断。 Maximum term expansion count 最大词根扩展数目 <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>针对单个单词的最大词根扩展数目(例如:此选项在使用通配符时会生效)。默认的10000是一个狠合理的值,能够避免当引擎遍历词根列表时引起查询界面假死。 Maximum Xapian clauses count 最大的Xapian子句数目 <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>我们向单个Xapian查询语句中加入的最大的子句数目。某些情况下,词根扩展的结果会是倍增的,而我们想要避免使用过多内存。默认的100000应当既能满足日常的大部分要求,又能与当前的典型硬件配置相兼容。 The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... 生成扩展字典的语言。<br>查看Xapian stammer文档以获取可能的值。例如:英文、 法语、 相关人... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. 所用词典的语言。值是两字母的语言代码,例如 'en', 'fr' 。 。<br>如果未设置此值,NLS环境将被用于计算它,通常是正常工作。 要想了解您的系统上安装了什么,请输入 'aspell 配置' 并寻找。 在 'data dir' 目录内的文件。 Indexer log file name 索引器日志文件名 If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. 如果为空,将使用上面的日志文件名称值。 一个单独的日志用于诊断目的可能有用,因为当<br>GUI启动时,通用日志将被删除。 Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) 磁盘完整的阈值百分比,我们将停止索引<br>例如, 90% 停止为 90% 完整, 0 或 100 表示没有限制) Web history 网页历史 Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types 仅MIME类型 An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive 已索引的MIME类型的排除列表。<br>其他将不被索引。 通常为空且不活动 Exclude mime types 排除MIME类型 Mime types not to be indexed MIME类型将不被索引 Max. compressed file size (KB) 压缩文件最大尺寸(KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. 尺寸大于这个值的压缩文件不会被处理。设置成-1以表示不加任何限制,设置成0以表示根本不处理压缩文件。 Max. text file size (MB) 文本文件最大尺寸(MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. 尺寸大于这个值的文本文件不会被处理。设置成-1以表示不加限制。 其作用是从索引中排除巨型的记录文件。 Text file page size (KB) 文本文件单页尺寸(KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). 如果设置咯这个值(不等于-1),则文本文件会被分割成这么大的块,并且进行索引。 这是用来搜索大型文本文件的(例如记录文件)。 Max. filter exec. time (s) 最大筛选执行时间(秒) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. 工作时间长于这个值的外部过滤器会被中断。这是针对某种特殊情况的,该情况下,一个文档可能引起过滤器无限循环下去(例如:postscript)。设置为-1则表示不设限制。 Global 全局 CronToolW Cron Dialog 计划任务对话框 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T19:47:37" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T19:56:53" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T1 { font-weight:bold; } .T3 { font-style:italic; } .T4 { font-family:Courier New,courier; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1"><span class="T1">Recoll</span> 批量索引计划任务(cron) </p><p class="P1">每个字段都可以包括一个通配符(*)、单个数字值、逗号分隔的列表(1,3,5)和范围(1-7)。更准确地说,这些字段会被<span class="T3">按原样</span>输出到crontab 文件中,因此这里可以使用crontab 的所有语法,参考crontab(5)。</p><p class="P1"><br/>例如,在<span class="T3">日期</span>中输入<span class="T4">*</span>,<span class="T3">小时</span>中输入<span class="T4">12,19</span>,<span class="T3">分钟</span>中输入<span class="T4">15 </span>的话,会在每天的12:15 AM 和7:15 PM启动recollindex。</p><p class="P1">一个频繁执行的计划任务,其性能可能比不上实时索引。</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) 星期日(*或0-7,0或7是指星期天) Hours (* or 0-23) 小时(*或0-23) Minutes (0-59) 分钟(0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T20:08:00" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T20:11:47" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T2 { font-style:italic; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1">点击<span class="T2">禁用</span>以停止进行自动化的批量索引,点击<span class="T2">启用</span>以启用此功能,点击<span class="T2">取消</span>则不改变任何东西。</p></body></html> Enable 启用 Disable 禁用 It seems that manually edited entries exist for recollindex, cannot edit crontab 看起来已经有手动编辑过的recollindex条目了,因此无法编辑crontab Error installing cron entry. Bad syntax in fields ? 插入cron条目时出错。请检查语法。 EditDialog Dialog 对话框 EditTrans Source path 源路径 Local path 本地路径 Config error 配置错误 Original path 原始路径 EditTransBase Path Translations 路径变换 Setting path translations for 针对右侧事务设置路径变换 Select one or several file types, then use the controls in the frame below to change how they are processed 选中一个或多个文件类型,然后使用下面框框中的控件来设置要如何处理它们 Add 添加 Delete 删除 Cancel 取消 Save 保存 FirstIdxDialog First indexing setup 第一次索引设置 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T20:14:44" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T20:23:13" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T2 { font-weight:bold; } .T4 { font-style:italic; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1"><span class="T2">未找到对应于此配置实例的索引数据。</span><br/><br/>如果你只想以一组合理的默认参数来索引你的家目录的话,就直接按<span class="T4">立即开始索引</span>按钮。以后还可以调整配置参数的。</p><p class="P1">如果你想调整某些东西的话,就使用下面的链接来调整其中的索引配置和定时计划吧。</p><p class="P1">这些工具可在以后通过<span class="T4">选项</span>菜单访问。</p></body></html> Indexing configuration 索引配置 This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. 在这里可以调整你想要对其进行索引的目录,以及其它参数,例如:要排除和路径或名字、默认字符集…… Indexing schedule 定时索引任务 This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). 在这里可以选择是要进行批量索引还是实时索引,还可以设置一个自动化的定时(使用cron)批量索引任务。 Start indexing now 立即开始索引 FragButs %1 not found. %1 未找到 %1: %2 %1: %2 Fragment Buttons 片段按钮 Query Fragments 查询片段 IdxSchedW Index scheduling setup 定时索引设置 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T20:27:11" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T20:30:49" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T1 { font-weight:bold; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1"><span class="T1">Recoll</span> 索引程序可持续运行并且在文件发生变化时对其进行索引,也可以间隔一定时间运行一次。</p><p class="P1">你可以读一下手册,以便更好地做出抉择(按F1)。</p><p class="P1">这个工具可帮助你设置一个自动进行批量索引的定时任务,或者设置成当你登录时便启动实时索引(或者两者同时进行,当然那几乎没有意义)。</p></body></html> Cron scheduling 定时任务 The tool will let you decide at what time indexing should run and will install a crontab entry. 这个工具帮助你确定一个让索引运行的时间,它会插入一个crontab条目。 Real time indexing start up 实时索引设置 Decide if real time indexing will be started when you log in (only for the default index). 作出决定,是否要在登录时便启动实时索引(只对默认索引有效)。 ListDialog Dialog 对话框 GroupBox 分组框 Main No db directory in configuration 配置实例中没有数据库目录 Could not open database in 无法打开数据库于 . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. 。 如果您想要在索引开始前编辑配置文件,请点击取消,或者允许它继续。 Configuration problem (dynconf 配置问题 (dynconf "history" file is damaged or un(read)writeable, please check or remove it: "history"文件被损坏,或者不可(读)写,请检查一下或者删除它: "history" file is damaged, please check or remove it: "history"文件被损坏,请检查一下或者删除它: Needs "Show system tray icon" to be set in preferences! Preview &Search for: 搜索(&S): &Next 下一个(&N) &Previous 上一个(&P) Match &Case 匹配大小写(&C) Clear 清空 Creating preview text 正在创建预览文本 Loading preview text into editor 正在将预览文本载入到编辑器中 Cannot create temporary directory 无法创建临时目录 Cancel 取消 Close Tab 关闭标签页 Missing helper program: 缺少辅助程序: Can't turn doc into internal representation for 无法为此文件将文档转换成内部表示方式: Cannot create temporary directory: 无法创建临时目录: Error while loading file 文件载入出错 Form Tab 1 标签页1 Open 打开 Canceled 已取消 Error loading the document: file missing. 加载文档时出错:文件丢失 Error loading the document: no permission. 加载文档时出错:无权限 Error loading: backend not configured. 加载错误:后台未配置 Error loading the document: other handler error<br>Maybe the application is locking the file ? 加载文档时出错:其他处理程序错误<br>也许应用程序正在锁定文件? Error loading the document: other handler error. 加载文档时出错:其他处理程序错误 <br>Attempting to display from stored text. <br>试图从存储的文本中显示 Could not fetch stored text 无法获取存储的文本 Previous result document 上一个结果文档 Next result document 下一个结果文档 Preview Window 预览窗口 Close Window 关闭窗口 Next doc in tab 标签中的下一个 Previous doc in tab 标签中上一个 doc Close tab 关闭标签 Print tab Print tab Close preview window 关闭预览窗口 Show next result 显示下一个结果 Show previous result 显示上一个结果 Print 打印 PreviewTextEdit Show fields 显示字段 Show main text 显示主文本 Print 打印 Print Current Preview 打印当前预览文本 Show image 显示图片 Select All 全选 Copy 复制 Save document to file 将文档保存到文件 Fold lines 自动换行 Preserve indentation 保留缩进符 Open document 打开文档 Reload as Plain Text Reload as HTML QObject Global parameters 全局参数 Local parameters 局部参数 <b>Customised subtrees <b>自定义的子目录树 The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. 这是已索引的目录树中的一些子目录组成的列表<br>,它们的某些参数需要重定义。默认:空白。 <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>以下的参数,当你在上面的列表中不选中任何条目或者选中一个空行时,<br>就是针对顶级目录起作用的,否则便是对选中的子目录起作用的。<br>你可以点击+/-按钮,以便添加或删除目录。 Skipped names 要略过的文件名 These are patterns for file or directory names which should not be indexed. 具有这些模式的文件或目录不会被索引。 Default character set 默认字符集 This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. 这是用来读取那些未标明自身的字符集的文件时所使用的字符集,例如纯文本文件。<br>默认值是空,会使用系统里的自然语言环境参数中的值。 Follow symbolic links 跟踪符号链接 Follow symbolic links while indexing. The default is no, to avoid duplicate indexing 在索引时跟踪符号链接。默认是不跟踪的,以避免重复索引 Index all file names 对所有文件名进行索引 Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true 对那些无法判断或处理其内容(未知类型或其类型不被支持)的文件的名字进行索引。默认为是 Beagle web history Beagle网页历史 Search parameters 搜索参数 Web history 网页历史 Default<br>character set 默认<br>字符集 Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. 这是用来读取那些未标明自身的字符集的文件时所使用的字符集,例如纯文本文件。<br>默认值是空,会使用系统里的自然语言环境参数中的值。 Ignored endings 忽略的结尾 These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. 这些是仅以内容为 索引的文件的文件名结尾(没有MIME 类型识别尝试,没有解密,没有内容索引。 These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). 这些是仅按名称索引的文件的文件名结尾 (没有尝试识别MIME类型,没有解压缩,没有内容索引) <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>以下的参数,当你在上面的列表中不选中任何条目或者选中一个空行时,<br>就是针对顶级目录起作用的,否则便是对选中的子目录起作用的。<br>你可以点击+/-按钮,以便添加或删除目录。 QWidget Create or choose save directory 创建或选择保存目录 Choose exactly one directory 选择(刚好)一个目录 Could not read directory: 无法读取目录: Unexpected file name collision, cancelling. 意外的文件名冲突,取消中... Cannot extract document: 无法提取文档: &Preview 预览(&P) &Open 打开(&O) Open With 打开方式 Run Script 运行脚本 Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) &Write to File 写入文件(&W) Save selection to files 将选中内容保存到文件中 Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) Find &similar documents 查找类似的文档(&s) Open &Snippets window 打开片断窗口(&S) Show subdocuments / attachments 显示子文档/附件 &Open Parent document 打开父文档 &Open Parent Folder 打开父文件夹 Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. 不再显示 RTIToolW Real time indexing automatic start 实时索引自动启动 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T21:00:38" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T21:02:43" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T1 { font-weight:bold; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1"><span class="T1">Recoll</span> 索引程序可以以守护进程的方式运行,在文件发生变化时便实时更新索引。这样你的索引一直是与文件同步的,但是会占用一定的系统资源。</p></body></html> Start indexing daemon with my desktop session. 在我的桌面会话启动时便启动索引进程。 Also start indexing daemon right now. 同时此次也立即启动索引进程。 Replacing: 正在替换: Replacing file 正在替换文件 Can't create: 无法创建: Warning 警告 Could not execute recollindex 无法执行recollindex Deleting: 正在删除: Deleting file 正在删除文件 Removing autostart 正在删除自动启动项 Autostart file deleted. Kill current process too ? 自动启动文件已经删除。也要杀死当前进程吗? RclCompleterModel Hits RclMain About Recoll Recoll说明 Executing: [ 正在执行:[ Cannot retrieve document info from database 无法从数据库获取文档信息 Warning 警告 Can't create preview window 无法创建预览窗口 Query results 查询结果 Document history 文档历史 History data 历史数据 Indexing in progress: 正在索引: Files 文件 Purge 删除 Stemdb Stem数据库 Closing 正在关闭 Unknown 未知 This search is not active any more 这个查询已经不是活跃的了 Can't start query: 可以't 开始查询: Bad viewer command line for %1: [%2] Please check the mimeconf file 针对%1的查看命令[%2]配置出错 请检查mimeconf文件 Cannot extract document or create temporary file 无法提取文档或创建临时文件 (no stemming) (不进行词根计算) (all languages) (对全部语言进行词根计算) error retrieving stemming languages 提取词根语言时出错 Update &Index 更新索引(&I) Indexing interrupted 索引已中止 Stop &Indexing 停止索引(&I) All 全部 media 多媒体文件 message 邮件 other 其它 presentation 演示文档 spreadsheet 电子表格 text 文本文件 sorted 已排序 filtered 已过滤 External applications/commands needed and not found for indexing your file types: 需要用来辅助对你的文件进行索引,却又找不到的外部程序/命令: No helpers found missing 目前不缺少任何辅助程序 Missing helper programs 未找到的辅助程序 Save file dialog 保存文件对话框 Choose a file name to save under 选择要保存的文件名 Document category filter 文档分类过滤器 No external viewer configured for mime type [ 针对此种文件类型没有配置外部查看器[ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? 没有找到mimeview中为%1: %2配置的查看器。 是否要打开选项对话框? Can't access file: 无法访问文件: Can't uncompress file: 无法解压缩此文件: Save file 保存文件 Result count (est.) 结果数(估计值) Query details 查询语句细节 Could not open external index. Db not open. Check external indexes list. 无法打开外部索引。数据库未打开。请检查外部索引列表。 No results found 未找到结果 None Updating 正在更新 Done 已完成 Monitor 监视器 Indexing failed 索引失败 The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone 当前索引进程不是由此界面启动的。点击确定以杀死它,或者点击取消以让它自由运行 Erasing index 正在删除索引 Reset the index and start from scratch ? 从头重新开始索引吗? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program 查询正在进行中。<br>由于索引库的某些限制,<br>取消的话会导致程序退出 Error 错误 Index not open 索引未打开 Index query error 索引查询出错 Indexed Mime Types 索引Mime 类型 Content has been indexed for these MIME types: 已经为这些文件类型索引其内容: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. 此文件的索引已过时。程序拒绝显示错误的条目。请点击确定以更新此文件的索引,等待索引完成之后再查询。或者,取消。 Can't update index: indexer running 无法更新索引:索引程序已在运行 Indexed MIME Types 已索引的文件类型 Bad viewer command line for %1: [%2] Please check the mimeview file 针对%1的查看程序命令不对:%2 请检查mimeview文件 Viewer command line for %1 specifies both file and parent file value: unsupported 针对%1的查看程序命令中同时指定了文件及亲代文件值:这是不支持的 Cannot find parent document 无法找到亲代文档 Indexing did not run yet 还未开始索引 External applications/commands needed for your file types and not found, as stored by the last indexing pass in 在上次的索引过程中发现,针对妳的文件类型,还缺少一些外部的程序/命令,它们储存在右侧文件中 Index not up to date for this file. Refusing to risk showing the wrong entry. 此文件的索引内容不是最新的。如果妳按拒绝,则需要自行承担显示错误条目的风险。 Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. 点击确定来更新此文件的索引,在索引完成之后重新执行此查询。否则,请按取消。 Indexer running so things should improve when it's done 索引器正在运行,所以,当它完毕之后世界将变得更美好 Sub-documents and attachments 子文档及附件 Document filter 文档过滤器 Index not up to date for this file. Refusing to risk showing the wrong entry. 此文件的索引不是最新的。拒绝显示错误条目的风险。 Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. 单击确定以更新此文件的索引,然后您将需要在索引完成时重新运行查询。 The indexer is running so things should improve when it's done. 索引器正在运行,所以,当它完毕之后事情会得到改善 The document belongs to an external indexwhich I can't update. 文档属于外部索引,我可以't 更新。 Click Cancel to return to the list. Click Ignore to show the preview anyway. 点击取消返回列表。点击忽略显示预览。 Duplicate documents 重复文档 These Urls ( | ipath) share the same content: 以下路径(|内部路径)之间共享着相同的内容: Bad desktop app spec for %1: [%2] Please check the desktop file Bad desktop app spec for %1: [%2] 请检查桌面文件 Bad paths 坏路径 Bad paths in configuration file: 配置文件中的路径错误: Selection patterns need topdir 选择模式需要顶级目录 Selection patterns can only be used with a start directory 选择模式只能与起始目录一起使用 No search 无搜索结果 No preserved previous search 没有保留的上一次搜索 Choose file to save 选择要保存的文件 Saved Queries (*.rclq) 已保存的查询 (*.rclq) Write failed 写入失败 Could not write to file 无法写入文件 Read failed 读取失败 Could not open file: 无法打开文件 Load error 加载出错 Could not load saved query 无法加载已保存的查询 Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. 打开一个临时副本,如果不保存它们到一个临时位置,编辑内容将会丢失 Do not show this warning next time (use GUI preferences to restore). 下次不要显示此警告(可通过GUI首选项还原该设置) Disabled because the real time indexer was not compiled in. 已禁用,因为未编译实时索引器。 This configuration tool only works for the main index. 该配置工具仅适用于主索引 The current indexing process was not started from this interface, can't kill it 当前索引过程不是从这个接口开始的,可以't 杀死它 The document belongs to an external index which I can't update. 该文档属于一个我可以't 更新的外部索引。 Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). 点击取消返回列表。 <br>点击忽略显示预览(并记住此会话)。 Index scheduling 定时索引任务 Sorry, not available under Windows for now, use the File menu entries to update the index 对不起,目前在 Windows 下不可用,使用文件菜单项来更新索引 Can't set synonyms file (parse error?) 无法设置同义词文件(解析错误?) Index locked 索引已锁定 Unknown indexer state. Can't access webcache file. 未知的索引器状态, 无法访问网络缓存文件。 Indexer is running. Can't access webcache file. 索引器正在运行,无法访问网络缓存文件。 with additional message: 带有附加消息: Non-fatal indexing message: 非致命的索引消息: Types list empty: maybe wait for indexing to progress? 类型列表为空:也许等待索引进行? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported 针对%1的查看程序命令中指定了父文件但是链接是http[s]:这是不支持的 Tools 工具 Results 结果 (%d documents/%d files/%d errors/%d total files) (%d 文档/%d 文件/%d 错误/%d 文件总数) (%d documents/%d files/%d errors) (%d 文档 /%d 文件/%d 错误) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): 配置文件中的路径为空或不存在。 单击“确定”仍然开始建立索引(缺少的数据将不会从索引中清除): Indexing done 索引已完成 Can't update index: internal error 无法更新索引:内部错误 Index not up to date for this file.<br> 此文件的索引内容不是最新的<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>此外,似乎文件的最后一次索引更新失败</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> 点击确定来更新此文件的索引,在索引完成之后你将需要重新执行此查询。<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> 单击“取消”返回到列表。<br>单击“忽略”以始终显示预览(并记住此会话),有显示错误条目的风险。<br/> documents 文档 document 文档 files 文件 file 文件 errors 错误 error 错误 total files) 文件总数) No information: initial indexing not yet performed. 没有信息:初始索引尚未执行 Batch scheduling 批量计划任务 The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. 该工具将让您决定何时运行索引,它使用Windows任务调度器。 Confirm 确认 Erasing simple and advanced search history lists, please click Ok to confirm 删除简单和高级的搜索历史列表。请单击确定确认 Could not open/create file 无法打开/创建文件 F&ilter Filter Could not start recollindex (temp file error) 无法启动 recollindex (temp 文件错误) Could not read: 无法读取: This will replace the current contents of the result list header string and GUI qss file name. Continue ? 这将替换结果列表头字符串和GUI qss文件名中当前的内容。继续吗? You will need to run a query to complete the display change. 您需要运行查询才能完成显示更改。 Simple search type 简单搜索类型 Any term 任一词语 All terms 全部词语 File name 文件名 Query language 查询语言 Stemming language 词根语言 Main Window 主窗口 Focus to Search 焦点搜索 Focus to Search, alt. 聚焦于搜索,备选案文。 Clear Search 清除搜索 Focus to Result Table 焦点到结果表 Clear search 清除搜索 Move keyboard focus to search entry 移动键盘焦点到搜索项 Move keyboard focus to search, alt. 将键盘焦点移动到搜索中,备选。 Toggle tabular display 切换表格显示 Move keyboard focus to table 移动键盘焦点到表 Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page 上一页 Next page 下一页 &File 文件(&F) E&xit 退出(&x) &Tools 工具(&T) &Help 帮助(&H) &Preferences 选项(&P) Search tools 搜索工具 Result list 结果列表 &About Recoll Recoll说明(&A) Document &History 文档历史(&H) Document History 文档历史 &Advanced Search 高端搜索(&A) Advanced/complex Search 高端/复杂搜索 &Sort parameters 排序参数(&S) Sort parameters 排序参数 Next page of results 下一页结果 Previous page of results 上一页结果 &Query configuration 查询配置(&Q) &User manual 用户手册(&U) Recoll Recoll Ctrl+Q Ctrl+Q Update &index 更新索引(&i) Term &explorer 词语探索器(&e) Term explorer tool 词语探索器 External index dialog 外部索引对话框 &Erase document history 删除文档历史(&E) First page 第一页 Go to first page of results 跳转到结果的第一页 &Indexing configuration 索引配置(&I) All 全部 &Show missing helpers 显示缺少的辅助程序列表(&S) PgDown 向下翻页 Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp 向上翻页 &Full Screen 全屏(&F) F11 F11 Full Screen 全屏 &Erase search history 删除搜索历史(&E) sortByDateAsc 按日期升序排列 Sort by dates from oldest to newest 按日期排列,最旧的在前面 sortByDateDesc 按日期降序排列 Sort by dates from newest to oldest 按日期排列,最新的在前面 Show Query Details 显示查询语句细节 Show results as table 以表格的形式显示结果 &Rebuild index 重新构造索引(&R) &Show indexed types 显示已索引的文件类型(&S) Shift+PgUp Shift+向上翻页 &Indexing schedule 定时索引(&I) E&xternal index dialog 外部索引对话框(&x) &Index configuration 索引设置(&I) &GUI configuration 界面设置(&G) &Results 结果(&R) Sort by date, oldest first 按日期排序,旧文档在前 Sort by date, newest first 按日期排序,新文档在前 Show as table 以表格形式显示 Show results in a spreadsheet-like table 以一个类似于电子表格的形式来显示结果 Save as CSV (spreadsheet) file 保存为CSV(电子表格)文件 Saves the result into a file which you can load in a spreadsheet 将结果保存到一个可用电子表格打开的文件中 Next Page 下一页 Previous Page 上一页 First Page 第一页 Query Fragments 查询片段 With failed files retrying 失败的文件重试中 Next update will retry previously failed files 下次更新将重试先前失败的文件 Save last query 保存上一次查询 Load saved query 加载保存的查询 Special Indexing 特殊索引 Indexing with special options 带有特殊选项的索引 Indexing &schedule 定时索引(&s) Enable synonyms 启用同义词 &View 查看(&V) Missing &helpers 缺少辅助程序 (&h) Indexed &MIME types 已索引的文件类型 (&M) Index &statistics 索引统计 (&s) Webcache Editor Web缓存编辑器 Trigger incremental pass 触发增量通过 E&xport simple search history E&xport 简单搜索历史记录 Use default dark mode 使用默认暗色模式 Dark mode 暗色模式 &Query &查询 Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates 过滤日期 Filter birth dates 过滤创建日期 Assisted complex search RclTrayIcon Restore 恢复 Quit 退出 RecollModel Abstract 摘要 Author 作者 Document size 文档尺寸 Document date 文档日期 File size 文件尺寸 File name 文件名 File date 文件日期 Ipath 内部路径 Keywords 关键词 Mime type Mime 类型 Original character set 原字符集 Relevancy rating 相关度 Title 标题 URL 路径 Mtime 修改时间 Date 日期 Date and time 日期及时间 Ipath 内部路径 MIME type 文件类型 Can't sort by inverse relevance 无法按逆相关性排序 ResList Result list 结果列表 Unavailable document 无法访问文档 Previous 上一个 Next 下一个 <p><b>No results found</b><br> <p><b>未找到结果</b><br> &Preview 预览(&P) Copy &URL 复制路径(&U) Find &similar documents 查找类似的文档(&s) Query details 查询语句细节 (show query) (显示查询语句细节) Copy &File Name 复制文件名(&F) filtered 已过滤 sorted 已排序 Document history 文档历史 Preview 预览 Open 打开 <p><i>Alternate spellings (accents suppressed): </i> <p><i>其它拼写形式(忽视口音):</i> &Write to File 写入文件(&W) Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) &Open 打开(&O) Documents out of at least 个文档,最少共有 for 个文档,查询条件: <p><i>Alternate spellings: </i> <p><i>其它拼写形式:</i> Open &Snippets window 打开片断窗口(&S) Duplicate documents 重复文档 These Urls ( | ipath) share the same content: 以下路径(|内部路径)之间共享着相同的内容: Result count (est.) 结果数(估计值) Snippets 片断 This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort 重置排序条件(&R) &Delete column 删除此列(&D) Add " 添加 " " column " 列 Save table to CSV file 将表格保存成CSV文件 Can't open/create file: 无法打开/创建文件: &Preview 预览(&P) &Open 打开(&O) Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) &Write to File 写入文件(&W) Find &similar documents 查找类似的文档(&s) Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) &Save as CSV 保存为CSV(&S) Add "%1" column 添加"%1"列 Result Table 结果表 Open 打开 Open and Quit 打开并退出 Preview 预览 Show Snippets 显示代码片段 Open current result document 打开当前结果文档 Open current result and quit 打开当前结果并退出 Show snippets 显示代码片段 Show header 显示标题 Show vertical header 显示垂直标题 Copy current result text to clipboard 复制当前结果文本到剪贴板 Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview 预览(&P) &Open 打开(&O) Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) &Write to File 写入文件(&W) Find &similar documents 查找类似的文档(&s) Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) ResultPopup &Preview 预览(&P) &Open 打开(&O) Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) &Write to File 写入文件(&W) Save selection to files 将选中内容保存到文件中 Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) Find &similar documents 查找类似的文档(&s) Open &Snippets window 打开片断窗口(&S) Show subdocuments / attachments 显示子文档/附件 Open With 打开方式 Run Script 运行脚本 SSearch Any term 任一词语 All terms 全部词语 File name 文件名 Completions 补全选项 Select an item: 选择一个条目: Too many completions 有太多与之相关的补全选项啦 Query language 查询语言 Bad query string 查询语言格式不正确 Out of memory 内存不足 Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-23T08:43:25" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-23T09:07:39" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .Standard { font-size:12pt; font-family:Nimbus Roman No9 L; writing-mode:page; } .T1 { font-style:italic; } .T2 { font-style:italic; } .T4 { font-weight:bold; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="Standard">输入查询语言表达式。简要说明:<br/><span class="T2">词语</span><span class="T1">1 </span><span class="T2">词语</span><span class="T1">2</span> : '词语1'和'词语2'同时出现在任意字段中。<br/><span class="T2">字段</span><span class="T1">:</span><span class="T2">词语</span><span class="T1">1</span> : '词语1'出现在字段'字段'中。<br/>标准字段名/同义名:<br/>title/subject/caption、author/from、recipient/to、filename、ext。<br/>伪字段名:dir、mime/format、type/rclcat、date。<br/>日期段的两个示例:2009-03-01/2009-05-20 2009-03-01/P2M。<br/><span class="T2">词语</span><span class="T1">1 </span><span class="T2">词语</span><span class="T1">2 OR </span><span class="T2">词语</span><span class="T1">3</span> : 词语1 <span class="T4">与</span> (词语2 <span class="T4">或</span> 词语3)。<br/>不允许用真正的括号来表示逻辑关系。<br/><span class="T1">"</span><span class="T2">词语</span><span class="T1">1 </span><span class="T2">词语</span><span class="T1">2"</span> : 词组(必须按原样出现)。可用的修饰词:<br/><span class="T1">"</span><span class="T2">词语</span><span class="T1">1 </span><span class="T2">词语</span><span class="T1">2"p</span> : 以默认距离进行的无序近似搜索。<br/>有疑问时可使用<span class="T4">显示查询语句细节</span>链接来查看查询语句的细节,另外请查看手册(&lt;F1&gt;)以了解更多内容。</p></body></html> Enter file name wildcard expression. 输入文件名通配符表达式。 Enter search terms here. Type ESC SPC for completions of current term. 在此输入要搜索的词语。按Esc 空格来查看针对当前词语的补全选项。 Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. 输入查询语言表达式。 作弊表单:<br> <i>term1 termin2</i> : 'term1' 和 'term2' 在任何字段中。<br> <i>field:term 1</i> : 'terms 1' in field 'field'<br> 标准字段名称/同义词:<br> title/subject/caption, author/from recordent/to filename, ext.<br> 伪片段: dir, mime/form, type/rclcat, date, size<br> 日期间隔: 2009-03-01/2009-05-20, 2009-03-01/P2M<br> <i>term 1 term2 or terms 3</i> : terms 1 AND (terms 2 or terms 3)。<br> 您可以使用括号使事情更加清楚。<br> <i>"术语1 术语2"</i> : 短语(必须准确发生)。 可能的修饰符:<br> <i>"term 1 terms 2"p</i> : 无顺序的距离近距离搜索。<br> 当对结果存有疑问时使用 <b>显示查询</b> 链接,看看手册(&lt; 1>获取更多细节。 Stemming languages for stored query: 保存的查询的词根语言 differ from current preferences (kept) 与当前的偏好设置不同(已保留) Auto suffixes for stored query: 保存的查询的自动后缀: External indexes for stored query: 保存的查询的外部索引: Autophrase is set but it was unset for stored query 自动短语设置了,但保存的查询未设置 Autophrase is unset but it was set for stored query 自动短语未设置,但保存的查询设置了 Enter search terms here. 在此处输入搜索词 <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; 边界: 1px 固体黑色; border-collapse: collapse; 边界崩溃:崩溃; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>查询语言作弊表。有疑问:点击 <b>显示查询</b>。&nbsp; You should really look at the manual (F1)</p> 您真的应该看下手册(F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>什么</th><th>示例</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>and</td><td>one two&nbsp;&nbsp;&nbsp;one and two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>或</td><td>或两个&nbsp;&nbsp;&nbsp;一个|| two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>复杂布尔值。或者,使用括号&nbsp; where needed</td><td>(one AND two) OR three</td></tr> 在需要</td><td>(一个或两个) 或三个</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>不</td><td>短期</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>短语</td><td>"自豪感和偏见"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>无序代理。(默认 slack=10)</td><td>"偏见&nbsp;primy"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>无干燥扩展:大写</td><td>地板</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>特定字段</td><td>作者:austen&nbsp;&nbsp;title:imputes</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>和字段内(无订单)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>或在字段内</td><td>作者:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>字段名称</td><td>标题/主题/标题&nbsp;&nbsp;作者/来自<br>收件者/to&nbsp;&nbsp;文件名&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>目录路径过滤</td><td>dir:home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME 类型过滤器</td><td>mime:text/plem mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>日期间隔</td><td>日期: 2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> 日期:2018&nbsp;&nbsp;日期:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index 可以't 打开索引 Could not restore external indexes for stored query:<br> 无法恢复存储查询的外部索引:<br> ??? ??? Using current preferences. 使用当前首选项。 Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear 清空 Ctrl+S Ctrl+S Erase search entry 删除搜索条目 Search 搜索 Start query 开始查询 Enter search terms here. Type ESC SPC for completions of current term. 在此输入要搜索的词语。按Esc 空格来查看针对当前词语的补全选项。 Choose search type. 选择搜索类型。 Show query history 显示查询语句历史 Enter search terms here. 在此处输入搜索词 Main menu 主菜单 SearchClauseW SearchClauseW 搜索条款 Any of these 其中任意一个 All of these 所有这些内容 None of these 所有这些都没有 This phrase 这个短语 Terms in proximity 接近条款 File name matching 文件名匹配 Select the type of query that will be performed with the words 选择要对右边的词语进行的查询类型 Number of additional words that may be interspersed with the chosen ones 允许在选中的词语之间出现的额外词语的个数 In field 在字段 No field 不限字段 Any 任意 All 全部 None Phrase 词组 Proximity 近似 File name 文件名 Snippets Snippets 片断 X X Find: 查找: Next 下一个 Prev 上一个 SnippetsW Search 搜索 <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>很抱歉,在限制范围内未找到完全匹配的内容。 可能是文档很大,片段生成器迷失在迷宫了。</ p> Sort By Relevance 按相关性排序 Sort By Page 按页数排序 Snippets Window 代码片段窗口 Find 查找 Find (alt) 查找(Alt) Find Next 查找下一个 Find Previous 查找上一个 Hide 隐藏 Find next 查找下一个 Find previous 查找上一个 Close window 关闭窗口 SortForm Date 日期 Mime type Mime 类型 SortFormBase Sort Criteria 排序条件 Sort the 排序 most relevant results by: 最相关的成果包括: Descending 降序 Close 关闭 Apply 应用 SpecIdxW Special Indexing 特殊索引 Do not retry previously failed files. 不要重试以前失败的文件。 Else only modified or failed files will be processed. 否则,仅处理已修改或失败的文件。 Erase selected files data before indexing. 索引前擦除所选文件的数据 Directory to recursively index 递归索引目录 Browse 浏览 Start directory (else use regular topdirs): 启动目录 (否则使用普通的顶部目录): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. 留空以选择所有文件。 您可以使用多个以空格分隔的shell类型的模式。<br>带有嵌入式空格的模式应该用双引号括起来。<br>仅在设置了起始目标时才能使用 Selection patterns: 选择模式: Top indexed entity 最上面的索引实体 Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). 要递归索引的目录。它必须位于配置文件(topdirs)中定义的常规索引区域中。 Retry previously failed files. 重试之前失败的文件 Start directory. Must be part of the indexed tree. We use topdirs if empty. 启动目录。必须是索引树的一部分。如果为空,我们使用顶级目录。 Start directory. Must be part of the indexed tree. Use full indexed area if empty. 启动目录。 必须是索引树的一部分。 如果为空,请使用完整的索引区域。 Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer 词语探索器 &Expand 展开(&E) Alt+E Alt+E &Close 关闭(&C) Alt+C Alt+C Term 词语 No db info. 未找到数据库信息。 Doc. / Tot. 文档数/总数 Match 匹配 Case 大小写 Accents 口音 SpellW Wildcards 通配符 Regexp 正则表达式 Spelling/Phonetic 拼写/发音检查 Aspell init failed. Aspell not installed? Aspell初始化失败。是否未安装Aspell? Aspell expansion error. Aspell扩展出错。 Stem expansion 词根扩展 error retrieving stemming languages 提取词根语言时出错 No expansion found 未找到扩展 Term 词语 Doc. / Tot. 文档数/总数 Index: %1 documents, average length %2 terms 索引:%1个文档,平均长度为%2个词语 Index: %1 documents, average length %2 terms.%3 results 索引:%1个文档,平均长度为%2个单词。%3个结果 %1 results %1个结果 List was truncated alphabetically, some frequent 列表已按字母顺序截断,某个常见 terms may be missing. Try using a longer root. 的单词可能会缺失。请尝试使用一个更长的词根。 Show index statistics 显示索引统计信息 Number of documents 文档个数 Average terms per document 每个文档中的平均单词个数 Smallest document length 最小文档长度 Longest document length 最大文档长度 Database directory size 数据库目录尺寸 MIME types: 多媒体文档类型列表: Item 条目 Value Smallest document length (terms) 最小文档长度(词语) Longest document length (terms) 最大文档长度(词语) Results from last indexing: 上一次索引的结果: Documents created/updated 文档已创建/更新 Files tested 文件已测试 Unindexed files 未索引的文件 List files which could not be indexed (slow) 列出无法建立索引的文件(缓慢) Spell expansion error. 拼写扩展出错 Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index 选中的目录不是Xapian索引 This is the main/local index! 这是主要/本地索引! The selected directory is already in the index list 选中的目录已经在索引列表中 Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) 选择xapian索引目录(例如:/home/buddy/.recoll/xapiandb) error retrieving stemming languages 提取词根语言时出错 Choose 选择 Result list paragraph format (erase all to reset to default) 结果列表的段落格式(删除全部内容即可重置为默认状态) Result list header (default is empty) 结果列表表头(默认为空) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) 选择recoll配置目录或xapian索引目录(例如:/home/me/.recoll 或 /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read 所选中的目录看起来像是一个Recoll配置目录,但是其中的配置内容无法读取 At most one index should be selected 最多应当选中一个索引 Cant add index with different case/diacritics stripping option 无法添加带有不同的大小写/诊断信息裁剪方式的索引 Default QtWebkit font 默认QtWebkit字体 Any term 任一词语 All terms 全部词语 File name 文件名 Query language 查询语言 Value from previous program exit 上一个程序退出的值 Context 二. 背景 Description 描述 Shortcut 快捷方式 Default 默认设置 Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface 用户界面 Number of entries in a result page 一个结果页面中显示的结果条数 Result list font 结果列表字体 Helvetica-10 文泉驿微米黑-12 Opens a dialog to select the result list font 打开一个对话框,以选择用于结果列表的字体 Reset 重置 Resets the result list font to the system default 将结果列表中的字体重设为系统默认值 Auto-start simple search on whitespace entry. 输入空格时自动开始进行简单搜索。 Start with advanced search dialog open. 启动时打开高端搜索对话框。 Start with sort dialog open. 开始时打开排序对话框。 Search parameters 搜索参数 Stemming language 词根语言 Dynamically build abstracts 动态构造摘要 Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. 是否要使用查询词语周围的上下文来构造结果列表条目中的摘要? 对于大的文档可能会很慢。 Replace abstracts from documents 取代文档中自带的摘要 Do we synthetize an abstract even if the document seemed to have one? 即使文档本身拥有一个摘要,我们仍然自行合成摘要信息? Synthetic abstract size (characters) 合成摘要长度(字符个数) Synthetic abstract context words 合成摘要上下文 External Indexes 外部索引 Add index 添加索引 Select the xapiandb directory for the index you want to add, then click Add Index 选择您想要添加的索引的 xapiandb 目录,然后单击添加索引 Browse 浏览 &OK 确定(&O) Apply changes 使改变生效 &Cancel 取消(&C) Discard changes 放弃这些改变 Result paragraph<br>format string 结果段落<br>格式字符串 Automatically add phrase to simple searches 自动将词组添加到简单搜索中 A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. 对[滚 石] (2个词语)的搜索会变成[滚 or 石 or (滚 2个词语 石)]。 对于那些搜索词语在其中按照原样出现的结果,其优先级会高一些。 User preferences 用户选项 Use desktop preferences to choose document editor. 使用桌面系统的设置来选择文档编辑器。 External indexes 外部索引 Toggle selected 切换选中项 Activate All 全部激活 Deactivate All 全部禁用 Remove selected 删除选中项 Remove from list. This has no effect on the disk index. 从列表中删除。这不会对硬盘上的索引造成损害。 Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> 定义每个结果列表段落的格式。 使用 qt html 格式和像打印一样的替换:<br>%A 摘要<br> %D 日期<br> %I 图标图像名称<br> %K 关键字(如果有的话)<br> %L 预览并编辑链接<br> %M Mime 类型<br> %N 结果编号<br> %R 相关性百分比<br> %S 大小信息<br> %T 标题<br> %U Url<br> Remember sort activation state. 记住排序状态。 Maximum text size highlighted for preview (megabytes) 在预览中对其进行高亮显示的最大文本尺寸(兆字节) Texts over this size will not be highlighted in preview (too slow). 超过这个长度的文本不会在预览窗口里高亮显示(太慢)。 Highlight color for query terms 查询词语的高亮颜色 Prefer Html to plain text for preview. 预览中优先使用Html。 If checked, results with the same content under different names will only be shown once. 如果选中这个,则拥有相同文件内容的不同文件名只会显示一个。 Hide duplicate results. 隐藏重复结果。 Choose editor applications 选择编辑器程序 Display category filter as toolbar instead of button panel (needs restart). 将文件类型过滤器显示成工具条,而不是按钮面板(需要重启程序)。 The words in the list will be automatically turned to ext:xxx clauses in the query language entry. 这个列表中的词语会在查询语言输入框里自动变成ext:xxx语句。 Query language magic file name suffixes. 查询语言神奇文件名后缀。 Enable 启用 ViewAction Changing actions with different current values 正在针对不同的当前值而改变动作 Mime type Mime 类型 Command 命令 MIME type 文件类型 Desktop Default 桌面默认值 Changing entries with different current values 正在使用不同的当前值来修改条目 ViewActionBase File type 文件类型 Action 行 动 Select one or several file types, then click Change Action to modify the program used to open them 选中一个或多个文件类型,然后点击“修改动作”来修改用来打开这些文件的程序 Change Action 修改动作 Close 关闭 Native Viewers 本地查看器 Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. 选中一个或多个文件类型祟点击“修改动作”<br>或者可以关闭这个对话框,而在主面板中选中“使用桌面默认设置”<br>那样就会无视这个列表而使用桌面的默认设置。 Select one or several mime types then use the controls in the bottom frame to change how they are processed. 选中一个或多个文件类型,然后使用下面框框中的控件来设置要如何处理它们。 Use Desktop preferences by default 默认使用桌面本身的设置 Select one or several file types, then use the controls in the frame below to change how they are processed 选中一个或多个文件类型,然后使用下面框框中的控件来设置要如何处理它们 Exception to Desktop preferences 针对桌面默认值的例外 Action (empty -> recoll default) 动作(空白则表示使用recoll的默认值) Apply to current selection 应用到当前选中项上 Recoll action: Recoll动作: current value 当前值 Select same 选中相同的项 <b>New Values:</b> <b>新的值:</b> Webcache Webcache editor Webcache编辑器 Search regexp 搜索正则表达式 TextLabel WebcacheEdit Copy URL 复制路径 Unknown indexer state. Can't edit webcache file. 未知的索引器状态,无法编辑webcache文件 Indexer is running. Can't edit webcache file. 索引器正在运行,无法编辑webcache文件 Delete selection 删除选中内容 Webcache was modified, you will need to run the indexer after closing this window. Webcache已修改,您将需要在关闭此窗口后运行索引器 Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME 文件类型 Url 路径 Date 日期 Size URL 路径 WinSchedToolW Error 错误 Configuration not initialized 配置未初始化 <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>重新索引批处理计划</h3><p>我们为此使用标准的 Windows 任务调度器。 当您点击下面的按钮时,程序将启动。</p><p>您可以使用完整界面 (<i>在右边的菜单中创建任务</i> ), 或简化的 <i>创建基本任务</i> 向导。 在这两种情况下,下面列出的批处理文件路径为 <i>操作</i> 需要执行。</p> Command already started 命令已经开始 Recoll Batch indexing 重新批量索引 Start Windows Task Scheduler tool 启动 Windows 任务计划工具 Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue 窃取Beagle索引队列 Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) 不可运行Beagle。启用对beagle队列的处理,以索引火狐网页历史。<br>(你还需要安装火狐Beagle插件) Web cache directory name 网页缓存目录名称 The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. 用于存储访问网页缓存的目录名。<br>相对于配置目录使用非绝对路径。 Max. size for the web cache (MB) 网页缓存最大大小 (MB) Entries will be recycled once the size is reached 当尺寸达到设定值时,这些条目会被循环使用 Web page store directory name 网页储存目录名 The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. 用来储存复制过来的已访问网页的目录名。<br>如果使用相对路径,则会相对于配置目录的路径进行处理。 Max. size for the web store (MB) 网页存储的最大尺寸(MB) Process the WEB history queue 处理网页历史队列 Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) 启用对火狐的已访问页面进行索引。<br>(妳还需要安装火狐的Recoll插件) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). 一旦大小达到,条目将被回收。<br>仅增加大小确实有意义,因为减小该值不会截断现有文件(最后只是浪费空间而已) confgui::ConfIndexW Can't write configuration file 无法写入配置文件 Recoll - Index Settings: Recoll - 索引设置: confgui::ConfParamFNW Browse 浏览 Choose 选择 confgui::ConfParamSLW + + - - Add entry 添加条目 Delete selected entries 删除选中的条目 ~ ~ Edit selected entries 编辑选中的条目 confgui::ConfSearchPanelW Automatic diacritics sensitivity 自动判断大小写 <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>如果搜索语句中包含带有口音特征(不在unac_except_trans中)的话,则自动触发大小写的判断。否则,妳需要使用查询语言和<i>D</i>修饰符来指定对大小写的判断。 Automatic character case sensitivity 自动调整字符的大小写敏感性 <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>如果搜索语句中除首字母之外包含有大写字母的话,则自动触发大小写的判断。否则,妳需要使用查询语言和<i>C</i>修饰符来指定对大小写的判断。 Maximum term expansion count 最大词根扩展数目 <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>针对单个单词的最大词根扩展数目(例如:此选项在使用通配符时会生效)。默认的10000是一个狠合理的值,能够避免当引擎遍历词根列表时引起查询界面假死。 Maximum Xapian clauses count 最大的Xapian子句数目 <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>我们向单个Xapian查询语句中加入的最大的子句数目。某些情况下,词根扩展的结果会是倍增的,而我们想要避免使用过多内存。默认的100000应当既能满足日常的大部分要求,又能与当前的典型硬件配置相兼容。 confgui::ConfSubPanelW Global 全局 Max. compressed file size (KB) 压缩文件最大尺寸(KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. 尺寸大于这个值的压缩文件不会被处理。设置成-1以表示不加任何限制,设置成0以表示根本不处理压缩文件。 Max. text file size (MB) 文本文件最大尺寸(MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. 尺寸大于这个值的文本文件不会被处理。设置成-1以表示不加限制。 其作用是从索引中排除巨型的记录文件。 Text file page size (KB) 文本文件单页尺寸(KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). 如果设置咯这个值(不等于-1),则文本文件会被分割成这么大的块,并且进行索引。 这是用来搜索大型文本文件的(例如记录文件)。 Max. filter exec. time (S) 过滤器的最长执行时间(S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. 外部过滤器的执行时间如果超过这个值,则会被强行中断。在罕见的情况下,某些文档(例如postscript)会导致过滤器陷入死循环。设置成-1以表示不加限制。 External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. 工作时间长于这个值的外部过滤器会被中断。这是针对某种特殊情况的,该情况下,一个文档可能引起过滤器无限循环下去(例如:postscript)。设置为-1则表示不设限制。 Only mime types 仅MIME类型 An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive 已索引的MIME类型的排除列表。<br>其他将不被索引。 通常为空且不活动 Exclude mime types 排除MIME类型 Mime types not to be indexed MIME类型将不被索引 Max. filter exec. time (s) 最大筛选执行时间(秒) confgui::ConfTopPanelW Top directories 顶级目录 The list of directories where recursive indexing starts. Default: your home. 索引从这个列表中的目录开始,递归地进行。默认:你的家目录。 Skipped paths 略过的路径 These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') 索引进程不会进入具有这些名字的目录。<br>可以包含通配符。必须匹配索引进程自身所见到的路径(例如:如果topdirs包含'/home/me',而实际上'/home'是到'/usr/home'的链接,则正确的skippedPath条目应当是'/home/me/tmp*',而不是'/usr/home/me/tmp*') Stemming languages 词根语言 The languages for which stemming expansion<br>dictionaries will be built. 将会针对这些语言<br>构造词根扩展词典。 Log file name 记录文件名 The file where the messages will be written.<br>Use 'stderr' for terminal output 程序输出的消息会被保存到这个文件。<br>使用'stderr'以表示将消息输出到终端 Log verbosity level 记录的话痨级别 This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. 这个值调整的是输出的消息的数量,<br>其级别从仅输出报错信息到输出一大堆调试信息。 Index flush megabytes interval 刷新索引的间隔,兆字节 This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB 这个值调整的是,当积累咯多少索引数据时,才将数据刷新到硬盘上去。<br>用来控制索引进程的内存占用情况。默认为10MB Max disk occupation (%) 最大硬盘占用率(%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). 当硬盘的占用率达到这个数时,索引会失败并且停止(以避免塞满你的硬盘)。<br>设为0则表示不加限制(这是默认值)。 No aspell usage 不使用aspell Aspell language Aspell语言 The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. aspell词典的语言。表示方式是'en'或'fr'……<br>如果不设置这个值,则会使用系统环境中的自然语言设置信息,而那个通常是正确的。要想查看你的系统中安装咯哪些语言的话,就执行'aspell config',再在'data-dir'目录中找.dat文件。 Database directory name 数据库目录名 The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. 用来储存索引数据的目录的名字<br>如果使用相对路径,则路径会相对于配置目录进行计算。默认值是'xapiandb'。 Use system's 'file' command 使用系统里的'file'命令 Use the system's 'file' command if internal<br>mime type identification fails. 当内部的文件类型识别功能失效时<br>使用系统里的'file'命令。 Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. 禁止在词语探索器中使用aspell来生成拼写相近的词语。<br>在没有安装aspell或者它工作不正常时使用这个选项。 The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. aspell词典的语言。表示方式是'en'或'fr'……<br>如果不设置这个值,则会使用系统环境中的自然语言设置信息,而那个通常是正确的。要想查看你的系统中安装咯哪些语言的话,就执行'aspell config',再在'data-dir'目录中找.dat文件。 The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. 用来储存索引数据的目录的名字<br>如果使用相对路径,则路径会相对于配置目录进行计算。默认值是'xapiandb'。 Unac exceptions Unac例外 <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>这是针对unac机制的例外,默认情况下,该机制会删除所有的判断信息,并进行正规的分解。妳可以按照自己的语言的特点针对某个字符覆盖掉口音解除设置,以及指定额外的分解(例如,针对复数)。在每个由空格分隔的条目中,第一个字符是源字符,剩下的就是翻译。 These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') 索引输入的目录的路径名。<br>路径元素可能包含通配符。 条目必须与索引器看到的路径匹配(例如:如果顶级路径包含 '/home/me' ,并且 '/home' 实际上是 '/usr/home' 的链接,则正确的相对路径条目应为 '/home/me/tmp*' ,而不是 '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) 最大硬盘占用率(%, 0代表没有限制) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. 这是磁盘使用量(是总磁盘使用量而不是索引大小)的百分比,在该百分比下索引将失败并停止。<br>默认值0将消除所有限制。 uiPrefsDialogBase User preferences 用户选项 User interface 用户界面 Number of entries in a result page 一个结果页面中显示的结果条数 If checked, results with the same content under different names will only be shown once. 如果选中这个,则拥有相同文件内容的不同文件名只会显示一个。 Hide duplicate results. 隐藏重复结果。 Highlight color for query terms 查询词语的高亮颜色 Result list font 结果列表字体 Opens a dialog to select the result list font 打开一个对话框,以选择用于结果列表的字体 Helvetica-10 文泉驿微米黑-12 Resets the result list font to the system default 将结果列表中的字体重设为系统默认值 Reset 重置 Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> 定义每个结果列表段落的格式。 使用 qt html 格式和像打印一样的替换:<br>%A 摘要<br> %D 日期<br> %I 图标图像名称<br> %K 关键字(如果有的话)<br> %L 预览并编辑链接<br> %M Mime 类型<br> %N 结果编号<br> %R 相关性百分比<br> %S 大小信息<br> %T 标题<br> %U Url<br> Result paragraph<br>format string 结果段落<br>格式字符串 Texts over this size will not be highlighted in preview (too slow). 超过这个长度的文本不会在预览窗口里高亮显示(太慢)。 Maximum text size highlighted for preview (megabytes) 在预览中对其进行高亮显示的最大文本尺寸(兆字节) Use desktop preferences to choose document editor. 使用桌面系统的设置来选择文档编辑器。 Choose editor applications 选择编辑器程序 Display category filter as toolbar instead of button panel (needs restart). 将文件类型过滤器显示成工具条,而不是按钮面板(需要重启程序)。 Auto-start simple search on whitespace entry. 输入空格时自动开始进行简单搜索。 Start with advanced search dialog open. 启动时打开高端搜索对话框。 Start with sort dialog open. 开始时打开排序对话框。 Remember sort activation state. 记住排序状态。 Prefer Html to plain text for preview. 预览中优先使用Html。 Search parameters 搜索参数 Stemming language 词根语言 A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. 对[滚 石] (2个词语)的搜索会变成[滚 or 石 or (滚 2个词语 石)]。 对于那些搜索词语在其中按照原样出现的结果,其优先级会高一些。 Automatically add phrase to simple searches 自动将词组添加到简单搜索中 Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. 是否要使用查询词语周围的上下文来构造结果列表条目中的摘要? 对于大的文档可能会很慢。 Dynamically build abstracts 动态构造摘要 Do we synthetize an abstract even if the document seemed to have one? 即使文档本身拥有一个摘要,我们仍然自行合成摘要信息? Replace abstracts from documents 取代文档中自带的摘要 Synthetic abstract size (characters) 合成摘要长度(字符个数) Synthetic abstract context words 合成摘要上下文 The words in the list will be automatically turned to ext:xxx clauses in the query language entry. 这个列表中的词语会在查询语言输入框里自动变成ext:xxx语句。 Query language magic file name suffixes. 查询语言神奇文件名后缀。 Enable 启用 External Indexes 外部索引 Toggle selected 切换选中项 Activate All 全部激活 Deactivate All 全部禁用 Remove from list. This has no effect on the disk index. 从列表中删除。这不会对硬盘上的索引造成损害。 Remove selected 删除选中项 Click to add another index directory to the list 点击这里,以将另一个索引目录添加到列表中 Add index 添加索引 Apply changes 使改变生效 &OK 确定(&O) Discard changes 放弃这些改变 &Cancel 取消(&C) Abstract snippet separator 摘要中的片段的分隔符 Use <PRE> tags instead of <BR>to display plain text as html. 使用 <PRE> 标签而不是 <BR>来显示纯文本为 html 。 Lines in PRE text are not folded. Using BR loses indentation. PRE 文本中的行未折叠。使用 BR 丢失缩进. Style sheet 样式单 Opens a dialog to select the style sheet file 打开一个对话框,以选择样式单文件 Choose 选择 Resets the style sheet to default 将样式单重置为默认值 Lines in PRE text are not folded. Using BR loses some indentation. PRE中的文字不会换行。使用BR的话会使一些缩进失效。 Use <PRE> tags instead of <BR>to display plain text as html in preview. 在将纯文本显示成html预览的时候,使用<PRE>标签,而不是<BR>标签。 Result List 结果列表 Edit result paragraph format string 编辑结果段落的格式字符串 Edit result page html header insert 编辑结果页面的html头部插入项 Date format (strftime(3)) 日期格式(strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). 这是一个频率阈值,超过这个值的话,我们就不会把词语放到自动词组中。 高频词语是词组中性能问题的主要来源。 略过的词语会增加词组的空缺值,因此会降低自动词组功能的效率。 默认值是2(百分比)。 Autophrase term frequency threshold percentage 自动词组频率阈值百分比 Plain text to HTML line style 纯文本转换为HTML换行符的风格 Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. PRE文本中的那些行是不会被折叠的。使用BR会丢失一些缩进信息。PRE+换行风格可能才是妳想要的。 <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE>+换行 Exceptions 例外 Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. 即使设置了 "使用桌面首选项" 时也不应传递到 xdg-open 的 Mime 类型。<br> 将页码和搜索字符串选项传递给例如evince。 Disable Qt autocompletion in search entry. 禁止在查询输入框中使用Qt的自动补全 Search as you type. 在输入的同时进行搜索。 Paths translations 路径变换 Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. 点击此处以向列表中加入另一个索引目录。妳可以选择一个Recoll配置目录或一个Xapian索引。 Snippets window CSS file 片断窗口的CSS文件 Opens a dialog to select the Snippets window CSS style sheet file 打开一个对话框,以选择片断窗口的CSS样式单文件 Resets the Snippets window style 重置片断窗口的样式 Decide if document filters are shown as radio buttons, toolbar combobox, or menu. 确定文档过滤器是否显示为单选按钮,工具栏组合框或菜单。 Document filter choice style: 文档过滤器选择样式: Buttons Panel 按钮面板 Toolbar Combobox 工具栏组合框 Menu 菜单 Show system tray icon. 显示系统托盘图标 Close to tray instead of exiting. 关闭到托盘而不是退出 Start with simple search mode 从简单的搜索模式开始 Show warning when opening temporary file. 打开临时文件时显示警告 User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. 应用于代码片段窗口的用户样式。<br>注意:结果页面标题插入也包含在代码片段窗口标题中。 Synonyms file 同义词文件 Highlight CSS style for query terms 查询词语的高亮CSS样式 Recoll - User Preferences Recoll - 用户选项 Set path translations for the selected index or for the main one if no selection exists. 如果没有选择,则为所选索引或主索引设置路径转换。 Activate links in preview. 在预览窗口中激活链接 Make links inside the preview window clickable, and start an external browser when they are clicked. 使预览窗口中的链接可点击,并在点击时启动外部浏览器。 Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... 查询结果中突出显示的字词。 <br>也许可以尝试使用 "color:red;background:yellow" 之类的,而不是默认的蓝色。 Start search on completer popup activation. 完成程序弹出窗口激活时开始搜索 Maximum number of snippets displayed in the snippets window 代码片段窗口中显示的片段最大数 Sort snippets by page number (default: by weight). 按页数排序代码片段 (默认按大小排序) Suppress all beeps. 禁止所有蜂鸣声 Application Qt style sheet 应用程序 Qt 样式表 Limit the size of the search history. Use 0 to disable, -1 for unlimited. 限制搜索历史的数目。使用 0 禁用, -1 无限制 Maximum size of search history (0: disable, -1: unlimited): 搜索历史的最大值(0: 禁用, -1: 无限制): Generate desktop notifications. 使用桌面通知 Misc 杂项 Work around QTBUG-78923 by inserting space before anchor text 通过在锚文本前插入空格临时解决QTBUG-78923 Display a Snippets link even if the document has no pages (needs restart). 即使文档没有页面显示片断链接(需要重启)。 Maximum text size highlighted for preview (kilobytes) 预览的最大文本大小(千字节) Start with simple search mode: 从简单的搜索模式开始: Hide toolbars. 隐藏工具栏。 Hide status bar. 隐藏状态栏。 Hide Clear and Search buttons. 隐藏清除和搜索按钮。 Hide menu bar (show button instead). 隐藏菜单栏 (显示按钮)。 Hide simple search type (show in menu only). 隐藏简单的搜索类型 (仅在菜单中显示)。 Shortcuts 快捷键 Hide result table header. 隐藏结果表标题 Show result table row headers. 显示结果表行标题 Reset shortcuts defaults 重置快捷方式默认值 Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. 禁用 Ctrl+[0-9]/[a-z] 快捷键以跳转到表格行。 Use F1 to access the manual 使用 F1 访问手册 Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type 简单搜索类型 Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode 暗色模式 Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table 结果表 Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_lt.ts0000644000175000017500000071772014444307651014270 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Visos sąlygos Any clause Bet kuri sąlyga texts tekstai spreadsheets skaičiuoklės presentations prezentacijos media media messages žinutės other kita Bad multiplier suffix in size filter Bad multiplier suffix in size filter text tekstas spreadsheet skaičiuoklės presentation prezentacijos message pranešimas Advanced Search Išsamesnė Paieška History Next History Next History Prev History Prev Load next stored search Load next stored search Load previous stored search Load previous stored search AdvSearchBase Advanced search Išsamesnė paieška Restrict file types Apriboti bylų tipus Save as default Išsaugoti kaip numatytąjį Searched file types Ieškota bylų tipų All ----> Visi ----> Sel -----> Pas -----> <----- Sel <----- Pas <----- All <----- Visi Ignored file types Ignoruoti bylų tipai Enter top directory for search Įrašykite viršutinio lygio direktoriją paieškai Browse Naršyti Restrict results to files in subtree: Pateikti rezultatus byloms submedyje: Start Search Pradėti paiešką Search for <br>documents<br>satisfying: Ieškoti <br>dokumentų<br>tenkinančių: Delete clause Ištrinti sąlygą Add clause Pridėti sąlygą Check this to enable filtering on file types Pažymėti, jei norite filtruoti pagal bylų tipus By categories Pagal kategorijas Check this to use file categories instead of raw mime types Pažymėti, jei norite naudoti bylų kategorijas vietoje mime tipų Close Uždaryti All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Visi kairėje esantys netušti laukai bus sujungiami AND (visi) arba OR (bet kuris) pagalba. <br> "Bet kuris" "Visi" ir "Nei vienas" laukų tipai gali priimti paprastų žodžių mišinį ir frazes pažymėtas dvigubomis kabutėmis. <br> Tušti laukeliai ignoruojami. Invert Invert Minimum size. You can use k/K,m/M,g/G as multipliers Minimum size. You can use k/K,m/M,g/G as multipliers Min. Size Min. Size Maximum size. You can use k/K,m/M,g/G as multipliers Maximum size. You can use k/K,m/M,g/G as multipliers Max. Size Max. Size Select Select Filter Filter From From To To Check this to enable filtering on dates Check this to enable filtering on dates Filter dates Filter dates Find Find Check this to enable filtering on sizes Check this to enable filtering on sizes Filter sizes Filter sizes Filter birth dates ConfIndexW Can't write configuration file Nepavyksta įrašyti nustatymų bylos Global parameters Globalūs parametrai Local parameters Lokalūs parametrai Search parameters Paieškos parametrai Top directories Aukščiausio lygmens direktorijos<br>kuriose vykdomas indeksavimas The list of directories where recursive indexing starts. Default: your home. Direktorijų, kuriose pradedamas rekursinis indeksavimas, sąrašas. Numatytoji: namų direktorija. Skipped paths Direktorijų, kurių turinys nein-<br>deksuojamas, sąrašas These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Stemming languages Kalbos naudojamos stemming<br> procesui The languages for which stemming expansion<br>dictionaries will be built. Kalbos, kurioms bus sukurti stemming <br>expansion žodynai. Log file name Log bylos vardas The file where the messages will be written.<br>Use 'stderr' for terminal output Byla, kurioje bus įrašomos žinutės.<br>Naudokite 'stderr' norėdami išvesti į terminalo langą Log verbosity level Log išsamumo lygmuo This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Vertė nustato žiniučių apimtį, nuo vien tik <br>klaidų fiksavimo iki didelės apimties duomenų skirtų debugging. Index flush megabytes interval Indekso dalių, įrašomų į diską, dydis (MB) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Vertė nustato duomenų, kurie indeksuojami tarp įrašymo į diską, apimtį.<br>Padeda valdyti indeksavimo dalies atminties naudojimą. Numatyta vertė yra 10 MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. No aspell usage Aspell nebus naudojama Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Nurodo nenaudoti aspell programos kuriant tarimo aproksimacijas raktinių žodžių tyrinėjimo įrankyje.<br>Naudinga, jei aspell neveikia arba neįdiegta. Aspell language Aspell kalba The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Database directory name Duomenų bazės direktorijos vardas The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Unac exceptions Unac exceptions <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Process the WEB history queue Process the WEB history queue Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Web page store directory name Web page store directory name The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Max. size for the web store (MB) Max. size for the web store (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Automatic diacritics sensitivity Automatic diacritics sensitivity <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Automatic character case sensitivity Automatic character case sensitivity <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. Maximum term expansion count Maximum term expansion count <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Maximum Xapian clauses count Maximum Xapian clauses count <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Indexer log file name Indexer log file name If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Web history Web history Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Only mime types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Exclude mime types Exclude mime types Mime types not to be indexed Mime types not to be indexed Max. compressed file size (KB) Didžiausias suspaustų bylų dydis (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Viršijus pasirinktą suspaustų bylų dydį, jie nebus indeksuojami. Pasirinkite -1 jei nenorite nurodyti ribos, 0, jei nenorite, jog suspaustos bylos būtų indeksuojamos. Max. text file size (MB) Didžiausias tekstinės bylos dydis (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Viršijus pasirinktą tekstinių bylų dydį, jie nebus indeksuojami. Pasirinkite -1 jei nenorite nurodyti ribos, 0, jei nenorite, jog suspaustos bylos būtų indeksuojamos. Text file page size (KB) Tekstinės bylos dydis (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Jei vertė nurodyta (nelgyi -1) tekstinės bylos bus suskaidytos į nurodyto dydžio bylas, kurios bus atskirai indeksuojamos. Naudinga atliekant paiešką labai dideliose tekstinėse bylose (pav. log bylose). Max. filter exec. time (s) Max. filter exec. time (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Global Globalus CronToolW Cron Dialog Cron Dialog <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Days of week (* or 0-7, 0 or 7 is Sunday) Hours (* or 0-23) Hours (* or 0-23) Minutes (0-59) Minutes (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> Enable Enable Disable Disable It seems that manually edited entries exist for recollindex, cannot edit crontab It seems that manually edited entries exist for recollindex, cannot edit crontab Error installing cron entry. Bad syntax in fields ? Error installing cron entry. Bad syntax in fields ? EditDialog Dialog Dialog EditTrans Source path Source path Local path Local path Config error Config error Original path Original path EditTransBase Path Translations Path Translations Setting path translations for Setting path translations for Select one or several file types, then use the controls in the frame below to change how they are processed Select one or several file types, then use the controls in the frame below to change how they are processed Add Add Delete Delete Cancel Atšaukti Save Save FirstIdxDialog First indexing setup First indexing setup <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> Indexing configuration Indexing configuration This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Indexing schedule Indexing schedule This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Start indexing now Start indexing now FragButs %1 not found. %1 not found. %1: %2 %1: %2 Fragment Buttons Fragment Buttons Query Fragments Query Fragments IdxSchedW Index scheduling setup Index scheduling setup <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> Cron scheduling Cron scheduling The tool will let you decide at what time indexing should run and will install a crontab entry. The tool will let you decide at what time indexing should run and will install a crontab entry. Real time indexing start up Real time indexing start up Decide if real time indexing will be started when you log in (only for the default index). Decide if real time indexing will be started when you log in (only for the default index). ListDialog Dialog Dialog GroupBox GroupBox Main No db directory in configuration Nustatymuose nerandama duomenų bazės bylos Could not open database in Nepavyko atidaryti duomenų bazės . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. Configuration problem (dynconf Nustatymų bėda (dynconf "history" file is damaged or un(read)writeable, please check or remove it: "history" file is damaged or un(read)writeable, please check or remove it: "history" file is damaged, please check or remove it: "history" file is damaged, please check or remove it: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Ieškoti: &Next &Sekantis &Previous &Ankstesnis Match &Case Atitaikyti &Atvejį Clear Išvalyti Creating preview text Kuriamas peržvalgos tekstas Loading preview text into editor Įkeliamas į redaktorių peržvalgos tekstas Cannot create temporary directory Nepavyksta sukurti laikinos direktorijos Cancel Atšaukti Close Tab Uždarykite auselę Missing helper program: Trūksta pagalbinės programos: Can't turn doc into internal representation for Nepavyksta pervesti dokumento į vidinę buseną Cannot create temporary directory: Cannot create temporary directory: Error while loading file Error while loading file Form Form Tab 1 Tab 1 Open Atidaryti Canceled Canceled Error loading the document: file missing. Error loading the document: file missing. Error loading the document: no permission. Error loading the document: no permission. Error loading: backend not configured. Error loading: backend not configured. Error loading the document: other handler error<br>Maybe the application is locking the file ? Error loading the document: other handler error<br>Maybe the application is locking the file ? Error loading the document: other handler error. Error loading the document: other handler error. <br>Attempting to display from stored text. <br>Attempting to display from stored text. Could not fetch stored text Could not fetch stored text Previous result document Previous result document Next result document Next result document Preview Window Preview Window Close Window Close Window Next doc in tab Next doc in tab Previous doc in tab Previous doc in tab Close tab Close tab Print tab Print tab Close preview window Close preview window Show next result Show next result Show previous result Show previous result Print Spausdinti PreviewTextEdit Show fields Rodyti laukus Show main text Rodyti pagrindinį tekstą Print Spausdinti Print Current Preview Spausdinti kaip matoma peržiūroje Show image Show image Select All Select All Copy Copy Save document to file Save document to file Fold lines Fold lines Preserve indentation Preserve indentation Open document Open document Reload as Plain Text Reload as HTML QObject Global parameters Globalūs parametrai Local parameters Lokalūs parametrai <b>Customised subtrees <b>Pritaikyti direktorijų<br> submedžiai The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Subdirektorijų, kuriose dalį parametrų reikia pakeisti, sąrašas.<br> Numatytoji reikšmė: tuščia. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Nurodyti parametrai taikomi arba visoms direktorijoms, arba subdirektorijoms,<br> jei kuri jų prieš tai pažymimos. Pridėti ir ištrinti direktorijų vardus galite<br> spausdami +/- mygtukus. Skipped names Neįtraukti vardai These are patterns for file or directory names which should not be indexed. Bylų arba direktorijų, kurių nedera indeksuoti, vardų šablonai. Default character set Numatytoji simbolių aibė This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Pasirinkta simbolių aibė bus naudojama skaityti bylų, kurių simbolių aibės nepavyksta nustatyti, turiniui.<br>Numatytoji vertė yra nepasirinkti konkrečios simbolių aibės - tokiu atveju naudojama NLS aplinkos vertė. Follow symbolic links Sekti simbolines nuorodas Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Indeksavimo metu sekti simbolines nuorodas. Numatytasis elgesys yra nesekti, bandant išvengti dvigubo indeksavimo Index all file names Indeksuoti visų bylų vardus Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indeksuoti bylų, kurių turinio nepavyksta perskaityti, vardus. Numatytoji reikšmė: teisybė Beagle web history Beagle tinklo istorija Search parameters Paieškos parametrai Web history Web history Default<br>character set Default<br>character set Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Ignored endings Ignored endings These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. QWidget Create or choose save directory Create or choose save directory Choose exactly one directory Choose exactly one directory Could not read directory: Could not read directory: Unexpected file name collision, cancelling. Unexpected file name collision, cancelling. Cannot extract document: Cannot extract document: &Preview &Peržiūra &Open &Open Open With Open With Run Script Run Script Copy &File Name Kopijuoti &Bylos vardą Copy &URL Kopijuoti &URL &Write to File &Įrašyti į bylą Save selection to files Save selection to files Preview P&arent document/folder Peržiūrėti &Aukštesnio lygio dokumentus/direktorijas &Open Parent document/folder Atidaryti &Aukštesnio lygio dokumentus/direktorijas Find &similar documents Rasti &panašius dokumentus Open &Snippets window Open &Snippets window Show subdocuments / attachments Show subdocuments / attachments &Open Parent document &Open Parent document &Open Parent Folder &Open Parent Folder Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Do not show again. RTIToolW Real time indexing automatic start Real time indexing automatic start <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Start indexing daemon with my desktop session. Also start indexing daemon right now. Also start indexing daemon right now. Replacing: Replacing: Replacing file Replacing file Can't create: Can't create: Warning Įspėjimas Could not execute recollindex Could not execute recollindex Deleting: Deleting: Deleting file Deleting file Removing autostart Removing autostart Autostart file deleted. Kill current process too ? Autostart file deleted. Kill current process too ? RclCompleterModel Hits RclMain About Recoll Apie Recoll Executing: [ Vykdoma: [ Cannot retrieve document info from database Nepavyksta išgauti iš duomenų bazės informacijos apie dokumentą Warning Įspėjimas Can't create preview window Nepavyksta sukurti peržiūros lango Query results Užklausos rezultatai Document history Dokumentų istorija History data Istorijos duomenys Indexing in progress: Indeksuojama: Files Failai Purge Išvalyti Stemdb Stemdb Closing Uždaroma Unknown Nežinoma This search is not active any more Ši paieška daugiau nevykdoma Can't start query: Nepavyksta pradėti vykdyti užklausą: Bad viewer command line for %1: [%2] Please check the mimeconf file Netinkamos peržiūros komandinė eilutė %1: [%2] Prašome patikrinti mimeconf bylą Cannot extract document or create temporary file Nepavyksta perskaityti dokumento arba sukurti laikinos bylos (no stemming) (no stemming) (all languages) (visos kalbos) error retrieving stemming languages error retrieving stemming languages Update &Index Atnaujinti &Indeksą Indexing interrupted indeksavimas pertrauktas Stop &Indexing Sustabdyti &Indeksavimą All Visi media media message pranešimas other kita presentation prezentacijos spreadsheet skaičiuoklės text tekstas sorted surūšiuota filtered filtruotas External applications/commands needed and not found for indexing your file types: Reikalingos pilnam indeksavimui, tačiau nerandamos išorinės programos/komandos: No helpers found missing Randamos visos reikalingos pagalbinės programos Missing helper programs Trūksta pagalbinių programų Save file dialog Išsaugoti failą forma Choose a file name to save under Pasirinkite bylos vardą, kuriuo išsaugosite bylą Document category filter Dokumentų kategorijų filtras No external viewer configured for mime type [ Nustatymuose nenumatyta jokia išorinė peržiūros programa šiam mime tipui [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Nurodyta peržiūros programa šiam mime tipui %1: %2 nerandama. Ar norėtumete iššaukti nustatymų langą? Can't access file: Can't access file: Can't uncompress file: Can't uncompress file: Save file Save file Result count (est.) Result count (est.) Query details Užklausos detalės Could not open external index. Db not open. Check external indexes list. Could not open external index. Db not open. Check external indexes list. No results found No results found None None Updating Updating Done Done Monitor Monitor Indexing failed Indexing failed The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Erasing index Erasing index Reset the index and start from scratch ? Reset the index and start from scratch ? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Error Error Index not open Index not open Index query error Index query error Indexed Mime Types Indexed Mime Types Content has been indexed for these MIME types: Content has been indexed for these MIME types: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Can't update index: indexer running Can't update index: indexer running Indexed MIME Types Indexed MIME Types Bad viewer command line for %1: [%2] Please check the mimeview file Bad viewer command line for %1: [%2] Please check the mimeview file Viewer command line for %1 specifies both file and parent file value: unsupported Viewer command line for %1 specifies both file and parent file value: unsupported Cannot find parent document Cannot find parent document Indexing did not run yet Indexing did not run yet External applications/commands needed for your file types and not found, as stored by the last indexing pass in External applications/commands needed for your file types and not found, as stored by the last indexing pass in Index not up to date for this file. Refusing to risk showing the wrong entry. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Indexer running so things should improve when it's done Indexer running so things should improve when it's done Sub-documents and attachments Sub-documents and attachments Document filter Document filter Index not up to date for this file. Refusing to risk showing the wrong entry. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. The indexer is running so things should improve when it's done. The indexer is running so things should improve when it's done. The document belongs to an external indexwhich I can't update. The document belongs to an external indexwhich I can't update. Click Cancel to return to the list. Click Ignore to show the preview anyway. Click Cancel to return to the list. Click Ignore to show the preview anyway. Duplicate documents Duplicate documents These Urls ( | ipath) share the same content: These Urls ( | ipath) share the same content: Bad desktop app spec for %1: [%2] Please check the desktop file Bad desktop app spec for %1: [%2] Please check the desktop file Bad paths Bad paths Bad paths in configuration file: Bad paths in configuration file: Selection patterns need topdir Selection patterns need topdir Selection patterns can only be used with a start directory Selection patterns can only be used with a start directory No search No search No preserved previous search No preserved previous search Choose file to save Choose file to save Saved Queries (*.rclq) Saved Queries (*.rclq) Write failed Write failed Could not write to file Could not write to file Read failed Read failed Could not open file: Could not open file: Load error Load error Could not load saved query Could not load saved query Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Do not show this warning next time (use GUI preferences to restore). Do not show this warning next time (use GUI preferences to restore). Disabled because the real time indexer was not compiled in. Disabled because the real time indexer was not compiled in. This configuration tool only works for the main index. This configuration tool only works for the main index. The current indexing process was not started from this interface, can't kill it The current indexing process was not started from this interface, can't kill it The document belongs to an external index which I can't update. The document belongs to an external index which I can't update. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Index scheduling Index scheduling Sorry, not available under Windows for now, use the File menu entries to update the index Sorry, not available under Windows for now, use the File menu entries to update the index Can't set synonyms file (parse error?) Can't set synonyms file (parse error?) Index locked Index locked Unknown indexer state. Can't access webcache file. Unknown indexer state. Can't access webcache file. Indexer is running. Can't access webcache file. Indexer is running. Can't access webcache file. with additional message: with additional message: Non-fatal indexing message: Non-fatal indexing message: Types list empty: maybe wait for indexing to progress? Types list empty: maybe wait for indexing to progress? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Tools Įrankiai Results Results (%d documents/%d files/%d errors/%d total files) (%d documents/%d files/%d errors/%d total files) (%d documents/%d files/%d errors) (%d documents/%d files/%d errors) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Indexing done Indexing done Can't update index: internal error Can't update index: internal error Index not up to date for this file.<br> Index not up to date for this file.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Also, it seems that the last index update for the file failed.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> documents dokumentai document document files failai file byla errors errors error error total files) total files) No information: initial indexing not yet performed. No information: initial indexing not yet performed. Batch scheduling Batch scheduling The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Confirm Confirm Erasing simple and advanced search history lists, please click Ok to confirm Erasing simple and advanced search history lists, please click Ok to confirm Could not open/create file Could not open/create file F&ilter F&ilter Could not start recollindex (temp file error) Could not start recollindex (temp file error) Could not read: Could not read: This will replace the current contents of the result list header string and GUI qss file name. Continue ? This will replace the current contents of the result list header string and GUI qss file name. Continue ? You will need to run a query to complete the display change. You will need to run a query to complete the display change. Simple search type Simple search type Any term Bet kuris raktinis žodis All terms Visi raktiniai žodžiai File name Bylos vardas Query language Užklausų kalba Stemming language Stemming kalba Main Window Main Window Focus to Search Focus to Search Focus to Search, alt. Focus to Search, alt. Clear Search Clear Search Focus to Result Table Focus to Result Table Clear search Clear search Move keyboard focus to search entry Move keyboard focus to search entry Move keyboard focus to search, alt. Move keyboard focus to search, alt. Toggle tabular display Toggle tabular display Move keyboard focus to table Move keyboard focus to table Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Prieš tai buvęs puslapis Next page Sekantis puslapis &File &Byla E&xit I&šeiti &Tools &Įrankiai &Help &Pagalba &Preferences &Nustatymai Search tools Paieškos įrankiai Result list Rezultatų sąrašas &About Recoll &Apie Recoll Document &History Dokumentų &Istorija Document History Dokumentų Istorija &Advanced Search &Išsamesnė Paieška Advanced/complex Search Išsamesnė Paieška &Sort parameters &Surūšiuoti parametrai Sort parameters Surūšiuoti parametrus Next page of results Sekantis rezultatų puslapis Previous page of results Ankstesnis rezultatų puslapis &Query configuration &Užklausų nustatymai &User manual &Vartotojo vadovas Recoll Recoll Ctrl+Q Ctrl+Q Update &index Atnaujinti &Indeksą Term &explorer Raktinių žodžių &tyrinėtojas Term explorer tool Raktinių žodžių tyrinėjimo įrankis External index dialog Išorinių indeksų langas &Erase document history &Ištrinti dokumentų istoriją First page Pirmas puslapis Go to first page of results Pereiti į pirmą rezultatų puslapį &Indexing configuration &Indeksavimo nustatymai All Visi &Show missing helpers &Trūkstamos pagalbinės programos PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen &Full Screen F11 F11 Full Screen Full Screen &Erase search history &Erase search history sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Sort by dates from oldest to newest sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Sort by dates from newest to oldest Show Query Details Show Query Details Show results as table Show results as table &Rebuild index &Rebuild index &Show indexed types &Show indexed types Shift+PgUp Shift+PgUp &Indexing schedule &Indexing schedule E&xternal index dialog E&xternal index dialog &Index configuration &Index configuration &GUI configuration &GUI configuration &Results &Results Sort by date, oldest first Sort by date, oldest first Sort by date, newest first Sort by date, newest first Show as table Show as table Show results in a spreadsheet-like table Show results in a spreadsheet-like table Save as CSV (spreadsheet) file Save as CSV (spreadsheet) file Saves the result into a file which you can load in a spreadsheet Saves the result into a file which you can load in a spreadsheet Next Page Next Page Previous Page Previous Page First Page First Page Query Fragments Query Fragments With failed files retrying With failed files retrying Next update will retry previously failed files Next update will retry previously failed files Save last query Save last query Load saved query Load saved query Special Indexing Special Indexing Indexing with special options Indexing with special options Indexing &schedule Indexing &schedule Enable synonyms Enable synonyms &View &View Missing &helpers Missing &helpers Indexed &MIME types Indexed &MIME types Index &statistics Index &statistics Webcache Editor Webcache Editor Trigger incremental pass Trigger incremental pass E&xport simple search history E&xport simple search history Use default dark mode Use default dark mode Dark mode Dark mode &Query &Query Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Filter dates Assisted complex search Filter birth dates RclTrayIcon Restore Restore Quit Quit RecollModel Abstract Abstract Author Author Document size Document size Document date Document date File size File size File name Bylos vardas File date File date Ipath Ipath Keywords Keywords Mime type Mime tipas Original character set Original character set Relevancy rating Relevancy rating Title Title URL URL Mtime Mtime Date Data Date and time Date and time Ipath Ipath MIME type MIME type Can't sort by inverse relevance Can't sort by inverse relevance ResList Result list Rezultatų sąrašas Unavailable document Neprieinamas dokumentas Previous Ankstesnis Next Kitas <p><b>No results found</b><br> <p><b>Nerasta rezultatų</b><br> &Preview &Peržiūra Copy &URL Kopijuoti &URL Find &similar documents Rasti &panašius dokumentus Query details Užklausos detalės (show query) (rodyti užklausą) Copy &File Name Kopijuoti &Bylos vardą filtered išfiltruota sorted surūšiuota Document history Dokumentų istorija Preview Peržiūra Open Atidaryti <p><i>Alternate spellings (accents suppressed): </i> <p><i>Kiti galimi tarimai (be akcentų): </i> &Write to File &Įrašyti į bylą Preview P&arent document/folder Peržiūrėti &Aukštesnio lygio dokumentus/direktorijas &Open Parent document/folder Atidaryti &Aukštesnio lygio dokumentus/direktorijas &Open &Atidaryti Documents Dokumentai out of at least iš bent for for <p><i>Alternate spellings: </i> <p><i>Alternate spellings: </i> Open &Snippets window Open &Snippets window Duplicate documents Duplicate documents These Urls ( | ipath) share the same content: These Urls ( | ipath) share the same content: Result count (est.) Result count (est.) Snippets Snippets This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Reset sort &Delete column &Delete column Add " Add " " column " column Save table to CSV file Save table to CSV file Can't open/create file: Can't open/create file: &Preview &Peržiūra &Open &Atidaryti Copy &File Name Kopijuoti &Bylos vardą Copy &URL Kopijuoti &URL &Write to File &Įrašyti į bylą Find &similar documents Rasti &panašius dokumentus Preview P&arent document/folder Peržiūrėti &Aukštesnio lygio dokumentus/direktorijas &Open Parent document/folder Atidaryti &Aukštesnio lygio dokumentus/direktorijas &Save as CSV &Save as CSV Add "%1" column Add "%1" column Result Table Result Table Open Atidaryti Open and Quit Open and Quit Preview Peržiūra Show Snippets Show Snippets Open current result document Open current result document Open current result and quit Open current result and quit Show snippets Show snippets Show header Show header Show vertical header Show vertical header Copy current result text to clipboard Copy current result text to clipboard Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Peržiūra &Open &Atidaryti Copy &File Name Kopijuoti &Bylos vardą Copy &URL Kopijuoti &URL &Write to File &Įrašyti į bylą Find &similar documents Rasti &panašius dokumentus Preview P&arent document/folder Peržiūrėti &Aukštesnio lygio dokumentus/direktorijas &Open Parent document/folder Atidaryti &Aukštesnio lygio dokumentus/direktorijas ResultPopup &Preview &Peržiūra &Open &Atidaryti Copy &File Name Kopijuoti &Bylos vardą Copy &URL Kopijuoti &URL &Write to File &Įrašyti į bylą Save selection to files Save selection to files Preview P&arent document/folder Peržiūrėti &Aukštesnio lygio dokumentus/direktorijas &Open Parent document/folder Atidaryti &Aukštesnio lygio dokumentus/direktorijas Find &similar documents Rasti &panašius dokumentus Open &Snippets window Open &Snippets window Show subdocuments / attachments Show subdocuments / attachments Open With Open With Run Script Run Script SSearch Any term Bet kuris raktinis žodis All terms Visi raktiniai žodžiai File name Bylos vardas Completions Užbaigimai Select an item: Pasirinkti įrašą: Too many completions Per daug galimų užbaigimų Query language Užklausų kalba Bad query string Netinkamai pateikta užklausa Out of memory Nepakanka atminties Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter file name wildcard expression. Enter file name wildcard expression. Enter search terms here. Type ESC SPC for completions of current term. Čia įveskite paieškos raktinius žodžius. Įrašykite ESC SPC rašomo termino užbaigimui. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Stemming languages for stored query: Stemming languages for stored query: differ from current preferences (kept) differ from current preferences (kept) Auto suffixes for stored query: Auto suffixes for stored query: External indexes for stored query: External indexes for stored query: Autophrase is set but it was unset for stored query Autophrase is set but it was unset for stored query Autophrase is unset but it was set for stored query Autophrase is unset but it was set for stored query Enter search terms here. Enter search terms here. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border-collapse: collapse; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; You should really look at the manual (F1)</p> You should really look at the manual (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>What</th><th>Examples</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; where needed</td><td>(one AND two) OR three</td></tr> where needed</td><td>(one AND two) OR three</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Can't open index Could not restore external indexes for stored query:<br> Could not restore external indexes for stored query:<br> ??? ??? Using current preferences. Using current preferences. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Išvalyti Ctrl+S Ctrl+S Erase search entry Ištrinti paieškos įrašą Search Ieškoti Start query Pradėti užklausą Enter search terms here. Type ESC SPC for completions of current term. Čia įveskite paieškos raktinius žodžius. Įrašykite ESC SPC rašomo termino užbaigimui. Choose search type. Pasirinkite paieškos tipą. Show query history Show query history Enter search terms here. Enter search terms here. Main menu Main menu SearchClauseW SearchClauseW SearchClauseW Any of these Bet kuris šių All of these Visi šie None of these Nei vienas šių This phrase Ši frazė Terms in proximity Artimi raktiniai žodžiai File name matching Bylos vardą atitinka Select the type of query that will be performed with the words Pasirinkite užklausos tipą atliekamą su žodžiais Number of additional words that may be interspersed with the chosen ones Papildomų žodžių skaičius kurie gali interspersed with the chosen ones In field In field No field No field Any Any All Visi None None Phrase Phrase Proximity Proximity File name Bylos vardas Snippets Snippets Snippets X X Find: Find: Next Kitas Prev Prev SnippetsW Search Ieškoti <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> Sort By Relevance Sort By Relevance Sort By Page Sort By Page Snippets Window Snippets Window Find Find Find (alt) Find (alt) Find Next Find Next Find Previous Find Previous Hide Hide Find next Find next Find previous Find previous Close window Close window SortForm Date Data Mime type Mime tipas SortFormBase Sort Criteria Rūšiavimo kriterijus Sort the Rūšiuoti most relevant results by: tinkamiausi rezultatai pagal: Descending Mažėjimo tvarka Close Uždaryti Apply Pritaikyti SpecIdxW Special Indexing Special Indexing Do not retry previously failed files. Do not retry previously failed files. Else only modified or failed files will be processed. Else only modified or failed files will be processed. Erase selected files data before indexing. Erase selected files data before indexing. Directory to recursively index Directory to recursively index Browse Naršyti Start directory (else use regular topdirs): Start directory (else use regular topdirs): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Selection patterns: Selection patterns: Top indexed entity Top indexed entity Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Retry previously failed files. Retry previously failed files. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Raktinių žodžių tyrinėjimas &Expand &Išplėsti Alt+E Alt+E &Close &Uždaryti Alt+C Alt+C Term Raktinis žodis No db info. No db info. Doc. / Tot. Doc. / Tot. Match Match Case Case Accents Accents SpellW Wildcards Wildcards Regexp Regexp Spelling/Phonetic Tarimas/Fonetika Aspell init failed. Aspell not installed? Aspell iššaukimas nepavyko. Aspell programa neįdiegta? Aspell expansion error. Aspell praplėtimų klaida. Stem expansion Stem expansion error retrieving stemming languages error retrieving stemming languages No expansion found Nerasta praplėtimų Term Raktinis žodis Doc. / Tot. Doc. / Tot. Index: %1 documents, average length %2 terms Index: %1 documents, average length %2 terms Index: %1 documents, average length %2 terms.%3 results Index: %1 documents, average length %2 terms.%3 results %1 results %1 results List was truncated alphabetically, some frequent List was truncated alphabetically, some frequent terms may be missing. Try using a longer root. terms may be missing. Try using a longer root. Show index statistics Show index statistics Number of documents Number of documents Average terms per document Average terms per document Smallest document length Smallest document length Longest document length Longest document length Database directory size Database directory size MIME types: MIME types: Item Item Value Value Smallest document length (terms) Smallest document length (terms) Longest document length (terms) Longest document length (terms) Results from last indexing: Results from last indexing: Documents created/updated Documents created/updated Files tested Files tested Unindexed files Unindexed files List files which could not be indexed (slow) List files which could not be indexed (slow) Spell expansion error. Spell expansion error. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Atrodo, jog pasirinkta direktorija nėra Xapian indekso direktorija This is the main/local index! Pagrindinis/localus indekas! The selected directory is already in the index list Pasirinkta direktorija jau yra indekso sąraše Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Pasirinkite Xapian indekso direktoriją (pav: /home/buddy/.recoll/xapiandb) error retrieving stemming languages error retrieving stemming languages Choose Naršyti Result list paragraph format (erase all to reset to default) Result list paragraph format (erase all to reset to default) Result list header (default is empty) Result list header (default is empty) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read The selected directory looks like a Recoll configuration directory but the configuration could not be read At most one index should be selected At most one index should be selected Cant add index with different case/diacritics stripping option Cant add index with different case/diacritics stripping option Default QtWebkit font Default QtWebkit font Any term Bet kuris raktinis žodis All terms Visi raktiniai žodžiai File name Bylos vardas Query language Užklausų kalba Value from previous program exit Value from previous program exit Context Context Description Description Shortcut Shortcut Default Default Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Vartotoja aplinka Number of entries in a result page Įrašų skaičius rezultatų puslapyje Result list font Rezultatų sąrašo šriftas Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Pasirinkite rezultatų sąrašo šriftą Reset Gražinti numatytąją formą Resets the result list font to the system default Gražina numatytąją rezultatų sąrašo srifto vertę Auto-start simple search on whitespace entry. Pradėti paprastąją paiešką įvedus tuščio tarpelio simoblį. Start with advanced search dialog open. Pradėti nuo išsamesnės paieškos lango. Start with sort dialog open. Pradėti su atidarytu rūšiavimo langu. Search parameters Paieškos parametrai Stemming language Stemming kalba Dynamically build abstracts Dinamiškai sukurti santraukas Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Ar pabandome sukurti santraukas remdamiesi užklausų raktinių žodžių kontekstu? Didelės apimties dokumentams gali lėtai veikti. Replace abstracts from documents Pakeisti dokumentuose randamas santraukas Do we synthetize an abstract even if the document seemed to have one? Ar sukuriame dirbtinę santrauką, jei dokumente jau ji yra? Synthetic abstract size (characters) Dirbtinės santraukos dydis (simbolių skaičius) Synthetic abstract context words Dirbtinės santraukos konteksto žodžiai External Indexes Išoriniai indeksai Add index Pridėti indeksą Select the xapiandb directory for the index you want to add, then click Add Index Pasirinkti xapiandb direktoriją kurios indeką norite pridėti, tada paspauskite Pridėti Indeksą Browse Naršyti &OK &Gerai Apply changes Pritaikyti pakeitimus &Cancel &Atšaukti Discard changes Panaikinti pakeitimus Result paragraph<br>format string Rezultatų paragrafo<br>formatas Automatically add phrase to simple searches Pridėti prie paprastos paieškos frazę A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Paieška bus pakeista (pav. rolling stones -> rolling or stones or (rolling phrase 2 stones)). Teikiama aiški pirmenybė rezultatams kuriuose rasti raktiniai žodžiai atitinka įvestus. User preferences Vartotojo nustatymai Use desktop preferences to choose document editor. Naudoti darbalaukio nustatymus parenkant dokumentų redaktorių. External indexes Išoriniai indeksai Toggle selected Įjungti/Išjungti pasirinktą Activate All Visus aktyvuoti Deactivate All Visus deaktyvuoti Remove selected Pažymėtus pašalinti Remove from list. This has no effect on the disk index. Pašalinti iš sąrašo. Neturi jokio poveikio indeksui diske. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Apibūdina kiekvieno rezultatų įrašo formatą:<br>%A Santrauka<br> %D Data<br> %I Ikona<br> %K Raktiniai žodžiai (jei yra)<br> %L Peržiūros ir Redagavimo nuorodos<br> %M Mime tipai<br> %N Rezultų skaičius<br> %R Tinkamumas procentais<br> %S Informacija apie dydį<br> %T Pavadinimas<br> %U Url<br> Remember sort activation state. Įsiminti rūšiavimo pasirinkimus (nedings perkrovus). Maximum text size highlighted for preview (megabytes) Didžiausia teksto, pažymėto peržiūrai, apimtis (megabaitai) Texts over this size will not be highlighted in preview (too slow). Tekstai viršijantys šį dydį nebus nuspalvinami peržiūros metu (per didelė apkrova). Highlight color for query terms Užklausų raktinių žodžių žymėjimo spalvos Prefer Html to plain text for preview. Pirmenybę teikti Html formatui peržiūros metu. If checked, results with the same content under different names will only be shown once. Pažymėjus, bus rodoma tik viena iš bylų su tuo pačiu turiniu, tačiau skirtingais vardais. Hide duplicate results. Slėpti pasikartojančius rezultatus. Choose editor applications Pasirinkite redaktorių programas Display category filter as toolbar instead of button panel (needs restart). Kategorijų filtrą rodyti kaip įrankų juostą (reikalauja perkrovimo). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Query language magic file name suffixes. Query language magic file name suffixes. Enable Enable ViewAction Changing actions with different current values Pakeisti veiksmus su skirtingomis dabartinėmis vertėmis Mime type Mime tipas Command Command MIME type MIME type Desktop Default Desktop Default Changing entries with different current values Changing entries with different current values ViewActionBase File type Bylos tipas Action Veiksmas Select one or several file types, then click Change Action to modify the program used to open them Pasirinkite vieną ar kelis bylų tipus, tada paspauskite Keisti Veiksmus norėdami keisti kaip programa juos atidaro Change Action Pakeisti veiksmą Close Uždaryti Native Viewers Sistemos peržiūros programos Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Pasirinkite vieną ar kelis mime tipus tada spauskite "Keisti Veiksmus"<br>Taip pat galite uždaryti šį langą ir patikrinti "Naudoti darbalaukio nustatymus"<br>pagrindinėje panelėje? norėdami ignoruoti šį sąrašą ir naudoti numatytasias darbalaukio. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Use Desktop preferences by default Use Desktop preferences by default Select one or several file types, then use the controls in the frame below to change how they are processed Select one or several file types, then use the controls in the frame below to change how they are processed Exception to Desktop preferences Exception to Desktop preferences Action (empty -> recoll default) Action (empty -> recoll default) Apply to current selection Apply to current selection Recoll action: Recoll action: current value current value Select same Select same <b>New Values:</b> <b>New Values:</b> Webcache Webcache editor Webcache editor Search regexp Search regexp TextLabel WebcacheEdit Copy URL Copy URL Unknown indexer state. Can't edit webcache file. Unknown indexer state. Can't edit webcache file. Indexer is running. Can't edit webcache file. Indexer is running. Can't edit webcache file. Delete selection Delete selection Webcache was modified, you will need to run the indexer after closing this window. Webcache was modified, you will need to run the indexer after closing this window. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url Url Date Data Size URL URL WinSchedToolW Error Error Configuration not initialized Configuration not initialized <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> Command already started Command already started Recoll Batch indexing Recoll Batch indexing Start Windows Task Scheduler tool Start Windows Task Scheduler tool Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Įtraukti Beagle paruoštus duomenis Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) BEAGLE programa TURI neveikti. Įgalina peržiūrėti beagle paruoštą medžiagą bandant indeksuoti Firefox naršymo<br> istoriją (papildomai reikia įdiegti Firefox Beagle priedą) Web cache directory name Naršymo tinkle cache direktorijos vardas The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Direktorijos, kurioje saugoma lankytų tinklo svetainių cache, vardas.<br>Santykinis kelias prasideda nuo nustatymų direktorijos. Max. size for the web cache (MB) Didžiausias tinklo naršymo cache dydis (MB) Entries will be recycled once the size is reached Įrašai bus trinami pasiekus nurodytą dydį Web page store directory name Web page store directory name The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Max. size for the web store (MB) Max. size for the web store (MB) Process the WEB history queue Process the WEB history queue Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). confgui::ConfIndexW Can't write configuration file Nepavyksta įrašyti nustatymų bylos Recoll - Index Settings: Recoll - Index Settings: confgui::ConfParamFNW Browse Naršyti Choose Naršyti confgui::ConfParamSLW + + - - Add entry Add entry Delete selected entries Delete selected entries ~ ~ Edit selected entries Edit selected entries confgui::ConfSearchPanelW Automatic diacritics sensitivity Automatic diacritics sensitivity <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Automatic character case sensitivity Automatic character case sensitivity <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. Maximum term expansion count Maximum term expansion count <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Maximum Xapian clauses count Maximum Xapian clauses count <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. confgui::ConfSubPanelW Global Globalus Max. compressed file size (KB) Didžiausias suspaustų bylų dydis (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Viršijus pasirinktą suspaustų bylų dydį, jie nebus indeksuojami. Pasirinkite -1 jei nenorite nurodyti ribos, 0, jei nenorite, jog suspaustos bylos būtų indeksuojamos. Max. text file size (MB) Didžiausias tekstinės bylos dydis (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Viršijus pasirinktą tekstinių bylų dydį, jie nebus indeksuojami. Pasirinkite -1 jei nenorite nurodyti ribos, 0, jei nenorite, jog suspaustos bylos būtų indeksuojamos. Text file page size (KB) Tekstinės bylos dydis (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Jei vertė nurodyta (nelgyi -1) tekstinės bylos bus suskaidytos į nurodyto dydžio bylas, kurios bus atskirai indeksuojamos. Naudinga atliekant paiešką labai dideliose tekstinėse bylose (pav. log bylose). Max. filter exec. time (S) Ilgiausias filtrų veikimo laikas (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Išorinių filtrų, dirbančių ilgiau nei numatyta, darbas bus nutraukiamas. Taikoma retiems atvejas (pav. postscript) kada dokumentas galėtų priversti filtrą kartoti veiksmus be galo ilgai. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Only mime types Only mime types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Exclude mime types Exclude mime types Mime types not to be indexed Mime types not to be indexed Max. filter exec. time (s) Max. filter exec. time (s) confgui::ConfTopPanelW Top directories Aukščiausio lygmens direktorijos<br>kuriose vykdomas indeksavimas The list of directories where recursive indexing starts. Default: your home. Direktorijų, kuriose pradedamas rekursinis indeksavimas, sąrašas. Numatytoji: namų direktorija. Skipped paths Direktorijų, kurių turinys nein-<br>deksuojamas, sąrašas These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Direktorijų, kurių turinys nebus indeksuojamas, vardai.<br> Vardo dalis gali būti wildcards. Turi atitikti programos matomus kelius iki direktorijų (pav. jei indeksuoti pradedama nuo '/home/me', o '/home' yra nuoroda į '/usr/home', teisinga vertė bus '/home/me/tmp*', o ne '/usr/home/me/tm*') Stemming languages Kalbos naudojamos stemming<br> procesui The languages for which stemming expansion<br>dictionaries will be built. Kalbos, kurioms bus sukurti stemming <br>expansion žodynai. Log file name Log bylos vardas The file where the messages will be written.<br>Use 'stderr' for terminal output Byla, kurioje bus įrašomos žinutės.<br>Naudokite 'stderr' norėdami išvesti į terminalo langą Log verbosity level Log išsamumo lygmuo This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Vertė nustato žiniučių apimtį, nuo vien tik <br>klaidų fiksavimo iki didelės apimties duomenų skirtų debugging. Index flush megabytes interval Indekso dalių, įrašomų į diską, dydis (MB) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Vertė nustato duomenų, kurie indeksuojami tarp įrašymo į diską, apimtį.<br>Padeda valdyti indeksavimo dalies atminties naudojimą. Numatyta vertė yra 10 MB Max disk occupation (%) Didžiausia disko atminties naudojimo dalis (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Viršijus (procentine išraiška) disko atminties panaudojimą indeksavimas bus sustabdytas (vengiant pilnai užpildyti diską).<br>0 reiškia, jog ribos nėra (numatytoji vertė). No aspell usage Aspell nebus naudojama Aspell language Aspell kalba The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Aspell žodyno kalba ('en', 'fr' ar kita).<br>Jei vertė nenurodyta NLS aplinka pabandys nustatyti tinkamą kalbą (paprastai teisingai). Norėdami sužinoti kas įrašyta Jūsų sistemoje įrašykite 'aspell-config' ir žiūrėkite į dat bylas 'data-dir' direktorijoje. Database directory name Duomenų bazės direktorijos vardas The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Direktorijos, kurioje bus saugomas indeksas, vardas<br>Laikoma, jog santykinio keliio iki direktorijos pradžia yra nustatymų direktorija. Numatytoji yra 'xapiandb'. Use system's 'file' command Naudoti sistemos 'file' komandą Use the system's 'file' command if internal<br>mime type identification fails. Jei nepavyks atpažinti mime tipo<br>naudoti sistemos 'file' komandą. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Nurodo nenaudoti aspell programos kuriant tarimo aproksimacijas raktinių žodžių tyrinėjimo įrankyje.<br>Naudinga, jei aspell neveikia arba neįdiegta. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Unac exceptions Unac exceptions <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Max disk occupation (%, 0 means no limit) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. uiPrefsDialogBase User preferences Vartotojo nustatymai User interface Vartotoja aplinka Number of entries in a result page Įrašų skaičius rezultatų puslapyje If checked, results with the same content under different names will only be shown once. Pažymėjus, bus rodoma tik viena iš bylų su tuo pačiu turiniu, tačiau skirtingais vardais. Hide duplicate results. Slėpti pasikartojančius rezultatus. Highlight color for query terms Užklausų raktinių žodžių žymėjimo spalvos Result list font Rezultatų sąrašo šriftas Opens a dialog to select the result list font Pasirinkite rezultatų sąrašo šriftą Helvetica-10 Helvetica-10 Resets the result list font to the system default Gražina numatytąją rezultatų sąrašo srifto vertę Reset Gražinti numatytąją formą Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Apibūdina kiekvieno rezultatų įrašo formatą:<br>%A Santrauka<br> %D Data<br> %I Ikona<br> %K Raktiniai žodžiai (jei yra)<br> %L Peržiūros ir Redagavimo nuorodos<br> %M Mime tipai<br> %N Rezultų skaičius<br> %R Tinkamumas procentais<br> %S Informacija apie dydį<br> %T Pavadinimas<br> %U Url<br> Result paragraph<br>format string Rezultatų paragrafo<br>formatas Texts over this size will not be highlighted in preview (too slow). Tekstai viršijantys šį dydį nebus nuspalvinami peržiūros metu (per didelė apkrova). Maximum text size highlighted for preview (megabytes) Didžiausia teksto, pažymėto peržiūrai, apimtis (megabaitai) Use desktop preferences to choose document editor. Naudoti darbalaukio nustatymus parenkant dokumentų redaktorių. Choose editor applications Pasirinkite redaktorių programas Display category filter as toolbar instead of button panel (needs restart). Kategorijų filtrą rodyti kaip įrankų juostą (reikalauja perkrovimo). Auto-start simple search on whitespace entry. Pradėti paprastąją paiešką įvedus tuščio tarpelio simoblį. Start with advanced search dialog open. Pradėti nuo išsamesnės paieškos lango. Start with sort dialog open. Pradėti su atidarytu rūšiavimo langu. Remember sort activation state. Įsiminti rūšiavimo pasirinkimus (nedings perkrovus). Prefer Html to plain text for preview. Pirmenybę teikti Html formatui peržiūros metu. Search parameters Paieškos parametrai Stemming language Stemming kalba A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Paieška bus pakeista (pav. rolling stones -> rolling or stones or (rolling phrase 2 stones)). Teikiama aiški pirmenybė rezultatams kuriuose rasti raktiniai žodžiai atitinka įvestus. Automatically add phrase to simple searches Pridėti prie paprastos paieškos frazę Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Ar pabandome sukurti santraukas remdamiesi užklausų raktinių žodžių kontekstu? Didelės apimties dokumentams gali lėtai veikti. Dynamically build abstracts Dinamiškai sukurti santraukas Do we synthetize an abstract even if the document seemed to have one? Ar sukuriame dirbtinę santrauką, jei dokumente jau ji yra? Replace abstracts from documents Pakeisti dokumentuose randamas santraukas Synthetic abstract size (characters) Dirbtinės santraukos dydis (simbolių skaičius) Synthetic abstract context words Dirbtinės santraukos konteksto žodžiai The words in the list will be automatically turned to ext:xxx clauses in the query language entry. The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Query language magic file name suffixes. Query language magic file name suffixes. Enable Enable External Indexes Išoriniai indeksai Toggle selected Įjungti/Išjungti pasirinktą Activate All Visus aktyvuoti Deactivate All Visus deaktyvuoti Remove from list. This has no effect on the disk index. Pašalinti iš sąrašo. Neturi jokio poveikio indeksui diske. Remove selected Pažymėtus pašalinti Click to add another index directory to the list Click to add another index directory to the list Add index Pridėti indeksą Apply changes Pritaikyti pakeitimus &OK &Gerai Discard changes Panaikinti pakeitimus &Cancel &Atšaukti Abstract snippet separator Abstract snippet separator Use <PRE> tags instead of <BR>to display plain text as html. Use <PRE> tags instead of <BR>to display plain text as html. Lines in PRE text are not folded. Using BR loses indentation. Lines in PRE text are not folded. Using BR loses indentation. Style sheet Style sheet Opens a dialog to select the style sheet file Opens a dialog to select the style sheet file Choose Naršyti Resets the style sheet to default Resets the style sheet to default Lines in PRE text are not folded. Using BR loses some indentation. Lines in PRE text are not folded. Using BR loses some indentation. Use <PRE> tags instead of <BR>to display plain text as html in preview. Use <PRE> tags instead of <BR>to display plain text as html in preview. Result List Result List Edit result paragraph format string Edit result paragraph format string Edit result page html header insert Edit result page html header insert Date format (strftime(3)) Date format (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Autophrase term frequency threshold percentage Autophrase term frequency threshold percentage Plain text to HTML line style Plain text to HTML line style Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + wrap Exceptions Exceptions Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Disable Qt autocompletion in search entry. Disable Qt autocompletion in search entry. Search as you type. Search as you type. Paths translations Paths translations Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Snippets window CSS file Snippets window CSS file Opens a dialog to select the Snippets window CSS style sheet file Opens a dialog to select the Snippets window CSS style sheet file Resets the Snippets window style Resets the Snippets window style Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Document filter choice style: Document filter choice style: Buttons Panel Buttons Panel Toolbar Combobox Toolbar Combobox Menu Menu Show system tray icon. Show system tray icon. Close to tray instead of exiting. Close to tray instead of exiting. Start with simple search mode Start with simple search mode Show warning when opening temporary file. Show warning when opening temporary file. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Synonyms file Synonyms file Highlight CSS style for query terms Highlight CSS style for query terms Recoll - User Preferences Recoll - User Preferences Set path translations for the selected index or for the main one if no selection exists. Set path translations for the selected index or for the main one if no selection exists. Activate links in preview. Activate links in preview. Make links inside the preview window clickable, and start an external browser when they are clicked. Make links inside the preview window clickable, and start an external browser when they are clicked. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Start search on completer popup activation. Start search on completer popup activation. Maximum number of snippets displayed in the snippets window Maximum number of snippets displayed in the snippets window Sort snippets by page number (default: by weight). Sort snippets by page number (default: by weight). Suppress all beeps. Suppress all beeps. Application Qt style sheet Application Qt style sheet Limit the size of the search history. Use 0 to disable, -1 for unlimited. Limit the size of the search history. Use 0 to disable, -1 for unlimited. Maximum size of search history (0: disable, -1: unlimited): Maximum size of search history (0: disable, -1: unlimited): Generate desktop notifications. Generate desktop notifications. Misc Misc Work around QTBUG-78923 by inserting space before anchor text Work around QTBUG-78923 by inserting space before anchor text Display a Snippets link even if the document has no pages (needs restart). Display a Snippets link even if the document has no pages (needs restart). Maximum text size highlighted for preview (kilobytes) Maximum text size highlighted for preview (kilobytes) Start with simple search mode: Start with simple search mode: Hide toolbars. Hide toolbars. Hide status bar. Hide status bar. Hide Clear and Search buttons. Hide Clear and Search buttons. Hide menu bar (show button instead). Hide menu bar (show button instead). Hide simple search type (show in menu only). Hide simple search type (show in menu only). Shortcuts Shortcuts Hide result table header. Hide result table header. Show result table row headers. Show result table row headers. Reset shortcuts defaults Reset shortcuts defaults Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Use F1 to access the manual Use F1 to access the manual Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Simple search type Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Dark mode Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Result Table Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_tr.ts0000644000175000017500000071631414444307651014274 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Tüm ifadeler Any clause İfadelerin herhangi biri texts metinler spreadsheets hesap tabloları presentations sunumlar media ortamlar messages iletiler other diğer Bad multiplier suffix in size filter Bad multiplier suffix in size filter text text spreadsheet spreadsheet presentation presentation message message Advanced Search Gelişmiş arama History Next History Next History Prev History Prev Load next stored search Load next stored search Load previous stored search Load previous stored search AdvSearchBase Advanced search Gelişmiş arama Restrict file types Dosya tiplerini sınırlandır Save as default öntanımlı olarak kaydet Searched file types Aranan dosya tipleri All ----> Tümü ----> Sel -----> Seç -----> <----- Sel <----- Seç <----- All <----- Tümü Ignored file types Yoksayılan dosya tipleri Enter top directory for search Arama için en üst dizini girin Browse Gözat Restrict results to files in subtree: Arama sonuçlarını bu dizin ve aşağısı ile sınırlandır: Start Search Aramayı Başlat Search for <br>documents<br>satisfying: Uyan <br>belgeleri<br>ara: Delete clause İfadeyi sil Add clause İfade ekle Check this to enable filtering on file types Dosya tipleri üzerinde filtreleme kullanmak için bunu işaretleyin By categories Kategorilere göre Check this to use file categories instead of raw mime types Dosya tipleri yerine ham mime tipleri üzerinde filtreleme kullanmak için bunu işaretleyin Close Kapat All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Invert Invert Minimum size. You can use k/K,m/M,g/G as multipliers Minimum size. You can use k/K,m/M,g/G as multipliers Min. Size Min. Size Maximum size. You can use k/K,m/M,g/G as multipliers Maximum size. You can use k/K,m/M,g/G as multipliers Max. Size Max. Size Select Select Filter Filter From From To To Check this to enable filtering on dates Check this to enable filtering on dates Filter dates Filter dates Find Find Check this to enable filtering on sizes Check this to enable filtering on sizes Filter sizes Filter sizes Filter birth dates ConfIndexW Can't write configuration file Yapılandırma dosyası yazılamadı Global parameters Genel parametreler Local parameters Yerel parametreler Search parameters Arama parametreleri Top directories Üst dizinler The list of directories where recursive indexing starts. Default: your home. Özyinelemeli indesklemenin başlayacağı dizinlerin listesi. Öntanımlı: ev dizininiz. Skipped paths Atlanan yollar These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Stemming languages Sözcük kökleri ayrıştırılabilir diller The languages for which stemming expansion<br>dictionaries will be built. Kök ayrıştırma genişlemesi için sözlükleri<br>inşa edilecek olan diller. Log file name Günlük dosyasının adı The file where the messages will be written.<br>Use 'stderr' for terminal output İletilerin yazılacağı dosya.<br>Uçbirim çıktısı için 'stderr' kullanın Log verbosity level Günlük dosyası ayrıntı düzeyi This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Bu değer ileti boyutunu ayarlar,<br>sadece hatalardan hata ayıklama verilerine kadar. Index flush megabytes interval İndex düzeltme MB aralığı This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Bu değer diske gönderilecek indekslenmiş veri miktarını ayarlar.<br>Bu indeksleyicinin bellek kullanımını kontrol etmeye yarar. Öntanımlı 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. No aspell usage Aspell kullanımı yok Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Aspell language Aspell dili The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Database directory name Veritabanı dizininin adı The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Unac exceptions Unac exceptions <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Process the WEB history queue Process the WEB history queue Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Web page store directory name Web page store directory name The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Max. size for the web store (MB) Max. size for the web store (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Automatic diacritics sensitivity Automatic diacritics sensitivity <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Automatic character case sensitivity Automatic character case sensitivity <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. Maximum term expansion count Maximum term expansion count <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Maximum Xapian clauses count Maximum Xapian clauses count <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Indexer log file name Indexer log file name If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Web history Web history Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Only mime types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Exclude mime types Exclude mime types Mime types not to be indexed Mime types not to be indexed Max. compressed file size (KB) Max. compressed file size (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Max. text file size (MB) Max. text file size (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Text file page size (KB) Text file page size (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Max. filter exec. time (s) Max. filter exec. time (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Global Genel CronToolW Cron Dialog Cron Dialog <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Days of week (* or 0-7, 0 or 7 is Sunday) Hours (* or 0-23) Hours (* or 0-23) Minutes (0-59) Minutes (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> Enable Enable Disable Disable It seems that manually edited entries exist for recollindex, cannot edit crontab It seems that manually edited entries exist for recollindex, cannot edit crontab Error installing cron entry. Bad syntax in fields ? Error installing cron entry. Bad syntax in fields ? EditDialog Dialog Dialog EditTrans Source path Source path Local path Local path Config error Config error Original path Original path EditTransBase Path Translations Path Translations Setting path translations for Setting path translations for Select one or several file types, then use the controls in the frame below to change how they are processed Select one or several file types, then use the controls in the frame below to change how they are processed Add Add Delete Delete Cancel İptal Save Save FirstIdxDialog First indexing setup First indexing setup <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> Indexing configuration Indexing configuration This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Indexing schedule Indexing schedule This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Start indexing now Start indexing now FragButs %1 not found. %1 not found. %1: %2 %1: %2 Fragment Buttons Fragment Buttons Query Fragments Query Fragments IdxSchedW Index scheduling setup Index scheduling setup <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> Cron scheduling Cron scheduling The tool will let you decide at what time indexing should run and will install a crontab entry. The tool will let you decide at what time indexing should run and will install a crontab entry. Real time indexing start up Real time indexing start up Decide if real time indexing will be started when you log in (only for the default index). Decide if real time indexing will be started when you log in (only for the default index). ListDialog Dialog Dialog GroupBox GroupBox Main No db directory in configuration Yapılandırma içerisinde veritabanı dizini yok Could not open database in Veritabanı açılamadı . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . İndekseleme başlamadan yapılandırmayı düzenlemek için İptal düğmesine basın ya da Tamam düğmesine basarak işleme izin verin. Configuration problem (dynconf Yapılandırma sorunu "history" file is damaged or un(read)writeable, please check or remove it: "history" file is damaged or un(read)writeable, please check or remove it: "history" file is damaged, please check or remove it: "history" file is damaged, please check or remove it: Needs "Show system tray icon" to be set in preferences! Preview &Search for: A&ra: &Next &Sonraki &Previous &Önceki Match &Case Eşleşme Şa&rtı Clear Temizle Creating preview text Önizleme metni oluşturuluyor Loading preview text into editor Önizleme metni düzenleyiciye yükleniyor Cannot create temporary directory Geçici dizin oluşturulamadı Cancel İptal Close Tab Sekmeyi Kapat Missing helper program: Yardımcı program kayıp: Can't turn doc into internal representation for Şunun için iç gösterim yapılamıyor Cannot create temporary directory: Cannot create temporary directory: Error while loading file Error while loading file Form Form Tab 1 Tab 1 Open Open Canceled Canceled Error loading the document: file missing. Error loading the document: file missing. Error loading the document: no permission. Error loading the document: no permission. Error loading: backend not configured. Error loading: backend not configured. Error loading the document: other handler error<br>Maybe the application is locking the file ? Error loading the document: other handler error<br>Maybe the application is locking the file ? Error loading the document: other handler error. Error loading the document: other handler error. <br>Attempting to display from stored text. <br>Attempting to display from stored text. Could not fetch stored text Could not fetch stored text Previous result document Previous result document Next result document Next result document Preview Window Preview Window Close Window Close Window Next doc in tab Next doc in tab Previous doc in tab Previous doc in tab Close tab Close tab Print tab Print tab Close preview window Close preview window Show next result Show next result Show previous result Show previous result Print Print PreviewTextEdit Show fields Show fields Show main text Show main text Print Print Print Current Preview Print Current Preview Show image Show image Select All Select All Copy Copy Save document to file Save document to file Fold lines Fold lines Preserve indentation Preserve indentation Open document Open document Reload as Plain Text Reload as HTML QObject Global parameters Genel parametreler Local parameters Yerel parametreler <b>Customised subtrees <b>Özelleştirilmiş alt ağaçlar The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. İndekslenmiş sıralama içerisindeki alt dizinlerin listesi <br>ki burada bazı parametrelerin yeniden tanımlanması gerekir. Öntanımlı: boş. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Aşağıdaki parametreler, ya seçili alt dizin için uygulanır ya da üst düzeyde veya üstteki metin kutusunda hiçbir şey seçilmediğinde yada boş bir satır seçildiğinde uygulanır.<br>+/- düğmelerine tıklayarak dizinleri ekleyip çıkarabilirsiniz. Skipped names Atlanan isimler These are patterns for file or directory names which should not be indexed. Bu nitelikler insekslenmemesi gereken dosya ve dizinler içindir. Default character set Öntanımlı karakter seti This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Bu karakter seti, karakter kodlaması uygulama tarafından belirlenemeyen dosyalar için kulanılır, Örneğin salt metin dosyaları.<br>Öntanımlı değer boştur ve NLS çevresel değişkeni kullanılır. Follow symbolic links Sembolik bağlantıları izle Follow symbolic links while indexing. The default is no, to avoid duplicate indexing İndekslerken sembolik bağlantıları izle. Aynı ögelerin yeniden indekslenmesinden kaçınmak için öntanımlı değer hayır Index all file names Tüm dosya isimlerini indeksle Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true İçeriği tanınmayan ya da işlenemeyen (ya da desteklenmeyen mime tipi) dosyaları indeksle. Öntanımlı evet Beagle web history Beagle web history Search parameters Arama parametreleri Web history Web history Default<br>character set Default<br>character set Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Ignored endings Ignored endings These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. QWidget Create or choose save directory Create or choose save directory Choose exactly one directory Choose exactly one directory Could not read directory: Could not read directory: Unexpected file name collision, cancelling. Unexpected file name collision, cancelling. Cannot extract document: Cannot extract document: &Preview &Önizle &Open &Open Open With Open With Run Script Run Script Copy &File Name &Dosya Adını Kopyala Copy &URL &Adresi Kopyala &Write to File &Write to File Save selection to files Save selection to files Preview P&arent document/folder Preview P&arent document/folder &Open Parent document/folder &Open Parent document/folder Find &similar documents Benzer belgeleri &bul Open &Snippets window Open &Snippets window Show subdocuments / attachments Show subdocuments / attachments &Open Parent document &Open Parent document &Open Parent Folder &Open Parent Folder Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Do not show again. RTIToolW Real time indexing automatic start Real time indexing automatic start <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Start indexing daemon with my desktop session. Also start indexing daemon right now. Also start indexing daemon right now. Replacing: Replacing: Replacing file Replacing file Can't create: Can't create: Warning Uyarı Could not execute recollindex Could not execute recollindex Deleting: Deleting: Deleting file Deleting file Removing autostart Removing autostart Autostart file deleted. Kill current process too ? Autostart file deleted. Kill current process too ? RclCompleterModel Hits RclMain About Recoll Recoll Hakkında Executing: [ Çalıştırılıyor: [ Cannot retrieve document info from database Veritabanından belge bilgileri alınamadı Warning Uyarı Can't create preview window Önizleme penceresi oluşturulamıyor Query results Arama Sonuçları Document history Belge geçmişi History data Geçmiş verileri Indexing in progress: İndeksleme devam ediyor: Files Dosyalar Purge Temizle Stemdb KökAyrıştırmaVeritabanı Closing Kapatılıyor Unknown Bilinmeyen This search is not active any more Bu arama atrık etkin değil Can't start query: Sorgu başlatılamadı: Bad viewer command line for %1: [%2] Please check the mimeconf file %1 için uygun olmayan komut: [%2] Lütfen mimeconf dosyasını kontrol edin Cannot extract document or create temporary file Belge açılamadı ya da geçici dosya oluşturulamadı (no stemming) (kök ayrıştırma kullanma) (all languages) (tüm diller) error retrieving stemming languages sözcük kökleri ayrıştırılabilir diller alınırken hata oluştu Update &Index Update &Index Indexing interrupted Indexing interrupted Stop &Indexing Stop &Indexing All All media ortamlar message message other diğer presentation presentation spreadsheet spreadsheet text text sorted sorted filtered filtered External applications/commands needed and not found for indexing your file types: External applications/commands needed and not found for indexing your file types: No helpers found missing No helpers found missing Missing helper programs Missing helper programs Save file dialog Save file dialog Choose a file name to save under Choose a file name to save under Document category filter Document category filter No external viewer configured for mime type [ No external viewer configured for mime type [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Can't access file: Can't access file: Can't uncompress file: Can't uncompress file: Save file Save file Result count (est.) Result count (est.) Query details Sorgu detayları Could not open external index. Db not open. Check external indexes list. Could not open external index. Db not open. Check external indexes list. No results found No results found None None Updating Updating Done Done Monitor Monitor Indexing failed Indexing failed The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Erasing index Erasing index Reset the index and start from scratch ? Reset the index and start from scratch ? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Error Error Index not open Index not open Index query error Index query error Indexed Mime Types Indexed Mime Types Content has been indexed for these MIME types: Content has been indexed for these MIME types: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Can't update index: indexer running Can't update index: indexer running Indexed MIME Types Indexed MIME Types Bad viewer command line for %1: [%2] Please check the mimeview file Bad viewer command line for %1: [%2] Please check the mimeview file Viewer command line for %1 specifies both file and parent file value: unsupported Viewer command line for %1 specifies both file and parent file value: unsupported Cannot find parent document Cannot find parent document Indexing did not run yet Indexing did not run yet External applications/commands needed for your file types and not found, as stored by the last indexing pass in External applications/commands needed for your file types and not found, as stored by the last indexing pass in Index not up to date for this file. Refusing to risk showing the wrong entry. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Indexer running so things should improve when it's done Indexer running so things should improve when it's done Sub-documents and attachments Sub-documents and attachments Document filter Document filter Index not up to date for this file. Refusing to risk showing the wrong entry. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. The indexer is running so things should improve when it's done. The indexer is running so things should improve when it's done. The document belongs to an external indexwhich I can't update. The document belongs to an external indexwhich I can't update. Click Cancel to return to the list. Click Ignore to show the preview anyway. Click Cancel to return to the list. Click Ignore to show the preview anyway. Duplicate documents Duplicate documents These Urls ( | ipath) share the same content: These Urls ( | ipath) share the same content: Bad desktop app spec for %1: [%2] Please check the desktop file Bad desktop app spec for %1: [%2] Please check the desktop file Bad paths Bad paths Bad paths in configuration file: Bad paths in configuration file: Selection patterns need topdir Selection patterns need topdir Selection patterns can only be used with a start directory Selection patterns can only be used with a start directory No search No search No preserved previous search No preserved previous search Choose file to save Choose file to save Saved Queries (*.rclq) Saved Queries (*.rclq) Write failed Write failed Could not write to file Could not write to file Read failed Read failed Could not open file: Could not open file: Load error Load error Could not load saved query Could not load saved query Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Do not show this warning next time (use GUI preferences to restore). Do not show this warning next time (use GUI preferences to restore). Disabled because the real time indexer was not compiled in. Disabled because the real time indexer was not compiled in. This configuration tool only works for the main index. This configuration tool only works for the main index. The current indexing process was not started from this interface, can't kill it The current indexing process was not started from this interface, can't kill it The document belongs to an external index which I can't update. The document belongs to an external index which I can't update. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Index scheduling Index scheduling Sorry, not available under Windows for now, use the File menu entries to update the index Sorry, not available under Windows for now, use the File menu entries to update the index Can't set synonyms file (parse error?) Can't set synonyms file (parse error?) Index locked Index locked Unknown indexer state. Can't access webcache file. Unknown indexer state. Can't access webcache file. Indexer is running. Can't access webcache file. Indexer is running. Can't access webcache file. with additional message: with additional message: Non-fatal indexing message: Non-fatal indexing message: Types list empty: maybe wait for indexing to progress? Types list empty: maybe wait for indexing to progress? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Tools Araçlar Results Results (%d documents/%d files/%d errors/%d total files) (%d documents/%d files/%d errors/%d total files) (%d documents/%d files/%d errors) (%d documents/%d files/%d errors) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Indexing done Indexing done Can't update index: internal error Can't update index: internal error Index not up to date for this file.<br> Index not up to date for this file.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Also, it seems that the last index update for the file failed.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> documents documents document document files dosyalar file dosya errors errors error error total files) total files) No information: initial indexing not yet performed. No information: initial indexing not yet performed. Batch scheduling Batch scheduling The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Confirm Confirm Erasing simple and advanced search history lists, please click Ok to confirm Erasing simple and advanced search history lists, please click Ok to confirm Could not open/create file Could not open/create file F&ilter F&ilter Could not start recollindex (temp file error) Could not start recollindex (temp file error) Could not read: Could not read: This will replace the current contents of the result list header string and GUI qss file name. Continue ? This will replace the current contents of the result list header string and GUI qss file name. Continue ? You will need to run a query to complete the display change. You will need to run a query to complete the display change. Simple search type Simple search type Any term Sözcüklerin herhangi biri All terms Tüm sözcükler File name Dosya adı Query language Arama dili Stemming language Kök ayrıştırma dili Main Window Main Window Focus to Search Focus to Search Focus to Search, alt. Focus to Search, alt. Clear Search Clear Search Focus to Result Table Focus to Result Table Clear search Clear search Move keyboard focus to search entry Move keyboard focus to search entry Move keyboard focus to search, alt. Move keyboard focus to search, alt. Toggle tabular display Toggle tabular display Move keyboard focus to table Move keyboard focus to table Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Önceki sayfa Next page Sonraki sayfa &File &Dosya E&xit &Çık &Tools &Araçlar &Help &Yardım &Preferences &Tercihler Search tools Arama araçları Result list Sonuç listesi &About Recoll &Recoll Hakkında Document &History Belge &Geçmişi Document History Belge Geçmişi &Advanced Search &Gelişmiş arama Advanced/complex Search Gelişmiş/karmaşık Arama &Sort parameters &Sıralama Ölçütleri Sort parameters Sıralama ölçütleri Next page of results Sonuçların sonraki sayfası Previous page of results Sonuçların önceki sayfası &Query configuration &Sorgu yapılandırması &User manual &Kullanıcı El Kitabı Recoll Recoll Ctrl+Q Ctrl+Q Update &index İndeksi g&üncelle Term &explorer İfade g&österici Term explorer tool İfade gösterme aracı External index dialog Dış indeksler penceresi &Erase document history &Belge geçmişini temizle First page İlk sayfa Go to first page of results Sonuçların ilk sayfasına git &Indexing configuration İ&ndeksleme yapılandırması All All &Show missing helpers &Show missing helpers PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen &Full Screen F11 F11 Full Screen Full Screen &Erase search history &Erase search history sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Sort by dates from oldest to newest sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Sort by dates from newest to oldest Show Query Details Show Query Details Show results as table Show results as table &Rebuild index &Rebuild index &Show indexed types &Show indexed types Shift+PgUp Shift+PgUp &Indexing schedule &Indexing schedule E&xternal index dialog E&xternal index dialog &Index configuration &Index configuration &GUI configuration &GUI configuration &Results &Results Sort by date, oldest first Sort by date, oldest first Sort by date, newest first Sort by date, newest first Show as table Show as table Show results in a spreadsheet-like table Show results in a spreadsheet-like table Save as CSV (spreadsheet) file Save as CSV (spreadsheet) file Saves the result into a file which you can load in a spreadsheet Saves the result into a file which you can load in a spreadsheet Next Page Next Page Previous Page Previous Page First Page First Page Query Fragments Query Fragments With failed files retrying With failed files retrying Next update will retry previously failed files Next update will retry previously failed files Save last query Save last query Load saved query Load saved query Special Indexing Special Indexing Indexing with special options Indexing with special options Indexing &schedule Indexing &schedule Enable synonyms Enable synonyms &View &View Missing &helpers Missing &helpers Indexed &MIME types Indexed &MIME types Index &statistics Index &statistics Webcache Editor Webcache Editor Trigger incremental pass Trigger incremental pass E&xport simple search history E&xport simple search history Use default dark mode Use default dark mode Dark mode Dark mode &Query &Query Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Filter dates Assisted complex search Filter birth dates RclTrayIcon Restore Restore Quit Quit RecollModel Abstract Abstract Author Author Document size Document size Document date Document date File size File size File name Dosya adı File date File date Ipath Ipath Keywords Keywords Mime type Mime Tipi Original character set Original character set Relevancy rating Relevancy rating Title Title URL URL Mtime Mtime Date Tarih Date and time Date and time Ipath Ipath MIME type MIME type Can't sort by inverse relevance Can't sort by inverse relevance ResList Result list Sonuç listesi Unavailable document Erişilemez belge Previous Önceki Next Sonraki <p><b>No results found</b><br> <p><b>Sonuç bulunamadı</b><br> &Preview &Önizle Copy &URL &Adresi Kopyala Find &similar documents Benzer belgeleri &bul Query details Sorgu detayları (show query) (sorguyu göster) Copy &File Name &Dosya Adını Kopyala filtered filtered sorted sorted Document history Belge geçmişi Preview Önizle Open Open <p><i>Alternate spellings (accents suppressed): </i> <p><i>Alternate spellings (accents suppressed): </i> &Write to File &Write to File Preview P&arent document/folder Preview P&arent document/folder &Open Parent document/folder &Open Parent document/folder &Open &Open Documents Documents out of at least out of at least for for <p><i>Alternate spellings: </i> <p><i>Alternate spellings: </i> Open &Snippets window Open &Snippets window Duplicate documents Duplicate documents These Urls ( | ipath) share the same content: These Urls ( | ipath) share the same content: Result count (est.) Result count (est.) Snippets Snippets This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Reset sort &Delete column &Delete column Add " Add " " column " column Save table to CSV file Save table to CSV file Can't open/create file: Can't open/create file: &Preview &Önizle &Open &Open Copy &File Name &Dosya Adını Kopyala Copy &URL &Adresi Kopyala &Write to File &Write to File Find &similar documents Benzer belgeleri &bul Preview P&arent document/folder Preview P&arent document/folder &Open Parent document/folder &Open Parent document/folder &Save as CSV &Save as CSV Add "%1" column Add "%1" column Result Table Result Table Open Open Open and Quit Open and Quit Preview Önizle Show Snippets Show Snippets Open current result document Open current result document Open current result and quit Open current result and quit Show snippets Show snippets Show header Show header Show vertical header Show vertical header Copy current result text to clipboard Copy current result text to clipboard Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Önizle &Open &Open Copy &File Name &Dosya Adını Kopyala Copy &URL &Adresi Kopyala &Write to File &Write to File Find &similar documents Benzer belgeleri &bul Preview P&arent document/folder Preview P&arent document/folder &Open Parent document/folder &Open Parent document/folder ResultPopup &Preview &Önizle &Open &Open Copy &File Name &Dosya Adını Kopyala Copy &URL &Adresi Kopyala &Write to File &Write to File Save selection to files Save selection to files Preview P&arent document/folder Preview P&arent document/folder &Open Parent document/folder &Open Parent document/folder Find &similar documents Benzer belgeleri &bul Open &Snippets window Open &Snippets window Show subdocuments / attachments Show subdocuments / attachments Open With Open With Run Script Run Script SSearch Any term Sözcüklerin herhangi biri All terms Tüm sözcükler File name Dosya adı Completions Tamamlamalar Select an item: Bir öge seçin: Too many completions Çok fazla tamamlama Query language Arama dili Bad query string Uygunsuz arama ifadesi Out of memory Yetersiz bellek Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter file name wildcard expression. Enter file name wildcard expression. Enter search terms here. Type ESC SPC for completions of current term. Aranacak ifadeleri buraya girin. Geçerli sözcüğün tamamlanması için ESC SPACE kullanın. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Stemming languages for stored query: Stemming languages for stored query: differ from current preferences (kept) differ from current preferences (kept) Auto suffixes for stored query: Auto suffixes for stored query: External indexes for stored query: External indexes for stored query: Autophrase is set but it was unset for stored query Autophrase is set but it was unset for stored query Autophrase is unset but it was set for stored query Autophrase is unset but it was set for stored query Enter search terms here. Enter search terms here. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border-collapse: collapse; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; You should really look at the manual (F1)</p> You should really look at the manual (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>What</th><th>Examples</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; where needed</td><td>(one AND two) OR three</td></tr> where needed</td><td>(one AND two) OR three</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Can't open index Could not restore external indexes for stored query:<br> Could not restore external indexes for stored query:<br> ??? ??? Using current preferences. Using current preferences. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Temizle Ctrl+S Ctrl+S Erase search entry Arama girdisini temizle Search Ara Start query Sorguyu başlat Enter search terms here. Type ESC SPC for completions of current term. Aranacak ifadeleri buraya girin. Geçerli sözcüğün tamamlanması için ESC SPACE kullanın. Choose search type. Choose search type. Show query history Show query history Enter search terms here. Enter search terms here. Main menu Main menu SearchClauseW SearchClauseW SearchClauseW Any of these Bunların herhangi biri All of these Bunların tümü None of these Bunların hiçbiri This phrase Tam olarak bu ifade Terms in proximity Yakın ifadeler File name matching Dosya adı eşleşen Select the type of query that will be performed with the words Sözcükler ile kullanılacak sorgu biçimini seç Number of additional words that may be interspersed with the chosen ones Seçilen sözcüklerin arasında yer alabilecek ek sözcüklerin sayısı In field In field No field No field Any Any All All None None Phrase Phrase Proximity Proximity File name Dosya adı Snippets Snippets Snippets X X Find: Find: Next Sonraki Prev Prev SnippetsW Search Ara <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> Sort By Relevance Sort By Relevance Sort By Page Sort By Page Snippets Window Snippets Window Find Find Find (alt) Find (alt) Find Next Find Next Find Previous Find Previous Hide Hide Find next Find next Find previous Find previous Close window Close window SortForm Date Tarih Mime type Mime Tipi SortFormBase Sort Criteria Sıralama Ölçütü Sort the Sırala most relevant results by: en uygun sonuç veren: Descending Azalan Close Kapat Apply Uygula SpecIdxW Special Indexing Special Indexing Do not retry previously failed files. Do not retry previously failed files. Else only modified or failed files will be processed. Else only modified or failed files will be processed. Erase selected files data before indexing. Erase selected files data before indexing. Directory to recursively index Directory to recursively index Browse Gözat Start directory (else use regular topdirs): Start directory (else use regular topdirs): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Selection patterns: Selection patterns: Top indexed entity Top indexed entity Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Retry previously failed files. Retry previously failed files. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer İfade Gösterici &Expand &Genişlet Alt+E Alt+G &Close &Kapat Alt+C Alt+K Term İfade No db info. No db info. Doc. / Tot. Doc. / Tot. Match Match Case Case Accents Accents SpellW Wildcards Özel karakterler Regexp Düzenli ifade Spelling/Phonetic Heceleme/Fonetik Aspell init failed. Aspell not installed? Aspell başlatılamadı. Yüklenmemiş olabilir mi? Aspell expansion error. Aspell heceleme genişlemesi hatası. Stem expansion Kök ayrıştırma genişlemesi error retrieving stemming languages sözcük kökleri ayrıştırılabilir diller alınırken hata oluştu No expansion found Hiç genişleme bulunamadı Term İfade Doc. / Tot. Doc. / Tot. Index: %1 documents, average length %2 terms Index: %1 documents, average length %2 terms Index: %1 documents, average length %2 terms.%3 results Index: %1 documents, average length %2 terms.%3 results %1 results %1 results List was truncated alphabetically, some frequent List was truncated alphabetically, some frequent terms may be missing. Try using a longer root. terms may be missing. Try using a longer root. Show index statistics Show index statistics Number of documents Number of documents Average terms per document Average terms per document Smallest document length Smallest document length Longest document length Longest document length Database directory size Database directory size MIME types: MIME types: Item Item Value Value Smallest document length (terms) Smallest document length (terms) Longest document length (terms) Longest document length (terms) Results from last indexing: Results from last indexing: Documents created/updated Documents created/updated Files tested Files tested Unindexed files Unindexed files List files which could not be indexed (slow) List files which could not be indexed (slow) Spell expansion error. Spell expansion error. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Seçilen dizin bir Xapian indeks dizini gibi görünmüyor This is the main/local index! Bu ana/yerel veritabanı! The selected directory is already in the index list Seçilen dizin zaten indeks listesinde var Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Xapian indeks dizinini seç (/home/kullanıcı_adınız/.recoll/xapiandb gibi.) error retrieving stemming languages sözcük kökleri ayrıştırılabilir diller alınırken hata oluştu Choose Gözat Result list paragraph format (erase all to reset to default) Result list paragraph format (erase all to reset to default) Result list header (default is empty) Result list header (default is empty) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read The selected directory looks like a Recoll configuration directory but the configuration could not be read At most one index should be selected At most one index should be selected Cant add index with different case/diacritics stripping option Cant add index with different case/diacritics stripping option Default QtWebkit font Default QtWebkit font Any term Sözcüklerin herhangi biri All terms Tüm sözcükler File name Dosya adı Query language Arama dili Value from previous program exit Value from previous program exit Context Context Description Description Shortcut Shortcut Default Default Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Kullanıcı arayüzü Number of entries in a result page Bir sonuç sayfasındaki sonuç sayısı Result list font Sonuç listesi yazıtipi Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Sonuç listesi yazıtipini seçmek için bir pencere açar Reset Sıfırla Resets the result list font to the system default Sonuç listesi yazıtipini sistem ayarlarına döndür Auto-start simple search on whitespace entry. Beyaz alan girdisi olduğunda basit aramayı otomatik olarak başlat. Start with advanced search dialog open. Gelişmiş arama penceresi ile başla. Start with sort dialog open. Sıralama penceresi ile başla. Search parameters Arama parametreleri Stemming language Kök ayrıştırma dili Dynamically build abstracts Özetleri dinamik olarak oluştur Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Sorgu sözcükleri kullanılarak sonuç listesi girdileri için özet oluşturulsun mu ? Büyük boyutlu belgelerde yavaş olabilir. Replace abstracts from documents Belgelerden özetleri kaldır Do we synthetize an abstract even if the document seemed to have one? Belgenin bir özeti varsa bile bir yapay özet oluşturulsun mu? Synthetic abstract size (characters) Yapay özet boyutu (karakter sayısı) Synthetic abstract context words Yapay özet sözcükleri External Indexes Dış indeksler Add index İndeks ekle Select the xapiandb directory for the index you want to add, then click Add Index İstediğiniz indeksi eklemek için xapiandb (veritabanı) dizinini seçin ve İndeks Ekle düğmesine tıklayın Browse Gözat &OK &TAMAM Apply changes Değişiklikleri uygula &Cancel &İptal Discard changes Değişiklikleri sil Result paragraph<br>format string Sonuç paragrafı<br>biçimlendirme ifadesi Automatically add phrase to simple searches Basit aramalara ifadeyi otomatik olarak ekle A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. [linux kernel] (2 sözcük) araması [linux veya kernel veya (linux ifadesi 2 tane kernel)] olarak değiştirilecektir. Bu, aranacak sözcüklerin tam olarak girildiği gibi görüntülendiği sonuçlara yüksek öncelik verilmesini sağlayacaktır. User preferences Kullanıcı tercihleri Use desktop preferences to choose document editor. Belge düzenleyiciyi seçmek için masaüstü tercihlerini kullan. External indexes Dış indeksler Toggle selected Seç /Bırak Activate All Tümünü Etkinleştir Deactivate All Tümünü Pasifleştir Remove selected Seçileni sil Remove from list. This has no effect on the disk index. Listeden sil. Bu diskteki indeksi etkilemez. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Tüm sonuç listesi paragraflarını tanımlar. Qt html biçimini ve printf benzeri yer değiştiricileri kullanın:<br>%A Özet<br> %D Tarih<br> %I Simge resminin adı<br> %K Anahtar sözcükler (eğer varsa)<br> %L Önizle ve Düzenle bağlantıları<br> %M Mime tipi<br> %N Sonuç sayısı<br> %R Uyum yüzdesi<br> %S Boyut bilgileri<br> %T Başlık<br> %U Url<br> Remember sort activation state. Sıralama kurallarını hatırla. Maximum text size highlighted for preview (megabytes) Önizlemede vurgulanacak en fazla metin boyutu (MB) Texts over this size will not be highlighted in preview (too slow). Bu boyuttan büyük metinler önizlemede vurgulanmayacak (çok yavaş). Highlight color for query terms Highlight color for query terms Prefer Html to plain text for preview. Prefer Html to plain text for preview. If checked, results with the same content under different names will only be shown once. If checked, results with the same content under different names will only be shown once. Hide duplicate results. Hide duplicate results. Choose editor applications Choose editor applications Display category filter as toolbar instead of button panel (needs restart). Display category filter as toolbar instead of button panel (needs restart). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Query language magic file name suffixes. Query language magic file name suffixes. Enable Enable ViewAction Changing actions with different current values Farklı değerlerle eylemler değiştiriliyor Mime type Mime Tipi Command Command MIME type MIME type Desktop Default Desktop Default Changing entries with different current values Changing entries with different current values ViewActionBase File type Dosya tipi Action Davranış Select one or several file types, then click Change Action to modify the program used to open them Bir ya da birkaç dosya tipi seçin ve Eylemi Değiştir düğmesine tıklayarak hangi uygulama ile açılacağını değiştirin Change Action Davranışı Değiştir Close Kapat Native Viewers Doğal Göstericiler Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Use Desktop preferences by default Use Desktop preferences by default Select one or several file types, then use the controls in the frame below to change how they are processed Select one or several file types, then use the controls in the frame below to change how they are processed Exception to Desktop preferences Exception to Desktop preferences Action (empty -> recoll default) Action (empty -> recoll default) Apply to current selection Apply to current selection Recoll action: Recoll action: current value current value Select same Select same <b>New Values:</b> <b>New Values:</b> Webcache Webcache editor Webcache editor Search regexp Search regexp TextLabel WebcacheEdit Copy URL Copy URL Unknown indexer state. Can't edit webcache file. Unknown indexer state. Can't edit webcache file. Indexer is running. Can't edit webcache file. Indexer is running. Can't edit webcache file. Delete selection Delete selection Webcache was modified, you will need to run the indexer after closing this window. Webcache was modified, you will need to run the indexer after closing this window. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url Url Date Tarih Size URL URL WinSchedToolW Error Error Configuration not initialized Configuration not initialized <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> Command already started Command already started Recoll Batch indexing Recoll Batch indexing Start Windows Task Scheduler tool Start Windows Task Scheduler tool Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Steal Beagle indexing queue Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Web cache directory name Web cache directory name The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Max. size for the web cache (MB) Max. size for the web cache (MB) Entries will be recycled once the size is reached Entries will be recycled once the size is reached Web page store directory name Web page store directory name The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Max. size for the web store (MB) Max. size for the web store (MB) Process the WEB history queue Process the WEB history queue Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). confgui::ConfIndexW Can't write configuration file Yapılandırma dosyası yazılamadı Recoll - Index Settings: Recoll - Index Settings: confgui::ConfParamFNW Browse Gözat Choose Gözat confgui::ConfParamSLW + + - - Add entry Add entry Delete selected entries Delete selected entries ~ ~ Edit selected entries Edit selected entries confgui::ConfSearchPanelW Automatic diacritics sensitivity Automatic diacritics sensitivity <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Automatic character case sensitivity Automatic character case sensitivity <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. Maximum term expansion count Maximum term expansion count <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Maximum Xapian clauses count Maximum Xapian clauses count <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. confgui::ConfSubPanelW Global Genel Max. compressed file size (KB) Max. compressed file size (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Max. text file size (MB) Max. text file size (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Text file page size (KB) Text file page size (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Max. filter exec. time (S) Max. filter exec. time (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Only mime types Only mime types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Exclude mime types Exclude mime types Mime types not to be indexed Mime types not to be indexed Max. filter exec. time (s) Max. filter exec. time (s) confgui::ConfTopPanelW Top directories Üst dizinler The list of directories where recursive indexing starts. Default: your home. Özyinelemeli indesklemenin başlayacağı dizinlerin listesi. Öntanımlı: ev dizininiz. Skipped paths Atlanan yollar These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Bunlar indekslemenin girmeyeceği dizinlerin adlarıdır.<br> * gibi özel karakterler içerebilir. İndeksleyici tarafından görülen yollar ile eşleşmelidir (örneğin: eğer en üst dizinler '/home/ben' ve '/home' içeriyorsa ve home '/usr/home' dizinine bağlantılı ise atlanacak dizin yolu '/home/me/tmp*' olmalıdır, '/usr/home/me/tmp*' değil) Stemming languages Sözcük kökleri ayrıştırılabilir diller The languages for which stemming expansion<br>dictionaries will be built. Kök ayrıştırma genişlemesi için sözlükleri<br>inşa edilecek olan diller. Log file name Günlük dosyasının adı The file where the messages will be written.<br>Use 'stderr' for terminal output İletilerin yazılacağı dosya.<br>Uçbirim çıktısı için 'stderr' kullanın Log verbosity level Günlük dosyası ayrıntı düzeyi This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Bu değer ileti boyutunu ayarlar,<br>sadece hatalardan hata ayıklama verilerine kadar. Index flush megabytes interval İndex düzeltme MB aralığı This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Bu değer diske gönderilecek indekslenmiş veri miktarını ayarlar.<br>Bu indeksleyicinin bellek kullanımını kontrol etmeye yarar. Öntanımlı 10MB Max disk occupation (%) En yüksek disk kullanımı (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Bu disk kullanımının yüzdesidir ki bu orana erişildiğinde indeksleme durdurulur (diskin doldurulmasını engellemek için).<br>0 kısıtlama yok demektir (Öntanımlı). No aspell usage Aspell kullanımı yok Aspell language Aspell dili The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Aspell sözlükleri için dil. Bu 'en' ya da 'fr' gibi olmalıdır ...<br>Eğer bu değer ayarlanmazsa şimdi kullandığnız NLS çevresel değişkeni kullanılacaktır. Sisteminizde neyin yüklü olduğu hakkında bilgi almak için 'aspell config' yazıp 'data-dir' içerisindeki .dat dosyalarına bakın. Database directory name Veritabanı dizininin adı The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. İndeksin duracağı dizinin adı<br>Eğer tam yol verilmezse yol yapılandırma dizinine göre belirlenecek. Öntanımlı dizin adı 'xapiandb'. Use system's 'file' command Sistemdeki 'file' komutunu kullan Use the system's 'file' command if internal<br>mime type identification fails. İç mime tipi belirleme işlemi başarısız olursa<br> sistemdeki 'file' komutunu kullan. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Unac exceptions Unac exceptions <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Max disk occupation (%, 0 means no limit) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. uiPrefsDialogBase User preferences Kullanıcı tercihleri User interface Kullanıcı arayüzü Number of entries in a result page Bir sonuç sayfasındaki sonuç sayısı If checked, results with the same content under different names will only be shown once. If checked, results with the same content under different names will only be shown once. Hide duplicate results. Hide duplicate results. Highlight color for query terms Highlight color for query terms Result list font Sonuç listesi yazıtipi Opens a dialog to select the result list font Sonuç listesi yazıtipini seçmek için bir pencere açar Helvetica-10 Helvetica-10 Resets the result list font to the system default Sonuç listesi yazıtipini sistem ayarlarına döndür Reset Sıfırla Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Tüm sonuç listesi paragraflarını tanımlar. Qt html biçimini ve printf benzeri yer değiştiricileri kullanın:<br>%A Özet<br> %D Tarih<br> %I Simge resminin adı<br> %K Anahtar sözcükler (eğer varsa)<br> %L Önizle ve Düzenle bağlantıları<br> %M Mime tipi<br> %N Sonuç sayısı<br> %R Uyum yüzdesi<br> %S Boyut bilgileri<br> %T Başlık<br> %U Url<br> Result paragraph<br>format string Sonuç paragrafı<br>biçimlendirme ifadesi Texts over this size will not be highlighted in preview (too slow). Bu boyuttan büyük metinler önizlemede vurgulanmayacak (çok yavaş). Maximum text size highlighted for preview (megabytes) Önizlemede vurgulanacak en fazla metin boyutu (MB) Use desktop preferences to choose document editor. Belge düzenleyiciyi seçmek için masaüstü tercihlerini kullan. Choose editor applications Choose editor applications Display category filter as toolbar instead of button panel (needs restart). Display category filter as toolbar instead of button panel (needs restart). Auto-start simple search on whitespace entry. Beyaz alan girdisi olduğunda basit aramayı otomatik olarak başlat. Start with advanced search dialog open. Gelişmiş arama penceresi ile başla. Start with sort dialog open. Sıralama penceresi ile başla. Remember sort activation state. Sıralama kurallarını hatırla. Prefer Html to plain text for preview. Prefer Html to plain text for preview. Search parameters Arama parametreleri Stemming language Kök ayrıştırma dili A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. [linux kernel] (2 sözcük) araması [linux veya kernel veya (linux ifadesi 2 tane kernel)] olarak değiştirilecektir. Bu, aranacak sözcüklerin tam olarak girildiği gibi görüntülendiği sonuçlara yüksek öncelik verilmesini sağlayacaktır. Automatically add phrase to simple searches Basit aramalara ifadeyi otomatik olarak ekle Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Sorgu sözcükleri kullanılarak sonuç listesi girdileri için özet oluşturulsun mu ? Büyük boyutlu belgelerde yavaş olabilir. Dynamically build abstracts Özetleri dinamik olarak oluştur Do we synthetize an abstract even if the document seemed to have one? Belgenin bir özeti varsa bile bir yapay özet oluşturulsun mu? Replace abstracts from documents Belgelerden özetleri kaldır Synthetic abstract size (characters) Yapay özet boyutu (karakter sayısı) Synthetic abstract context words Yapay özet sözcükleri The words in the list will be automatically turned to ext:xxx clauses in the query language entry. The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Query language magic file name suffixes. Query language magic file name suffixes. Enable Enable External Indexes Dış indeksler Toggle selected Seç /Bırak Activate All Tümünü Etkinleştir Deactivate All Tümünü Pasifleştir Remove from list. This has no effect on the disk index. Listeden sil. Bu diskteki indeksi etkilemez. Remove selected Seçileni sil Click to add another index directory to the list Click to add another index directory to the list Add index İndeks ekle Apply changes Değişiklikleri uygula &OK &TAMAM Discard changes Değişiklikleri sil &Cancel &İptal Abstract snippet separator Abstract snippet separator Use <PRE> tags instead of <BR>to display plain text as html. Use <PRE> tags instead of <BR>to display plain text as html. Lines in PRE text are not folded. Using BR loses indentation. Lines in PRE text are not folded. Using BR loses indentation. Style sheet Style sheet Opens a dialog to select the style sheet file Opens a dialog to select the style sheet file Choose Gözat Resets the style sheet to default Resets the style sheet to default Lines in PRE text are not folded. Using BR loses some indentation. Lines in PRE text are not folded. Using BR loses some indentation. Use <PRE> tags instead of <BR>to display plain text as html in preview. Use <PRE> tags instead of <BR>to display plain text as html in preview. Result List Result List Edit result paragraph format string Edit result paragraph format string Edit result page html header insert Edit result page html header insert Date format (strftime(3)) Date format (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Autophrase term frequency threshold percentage Autophrase term frequency threshold percentage Plain text to HTML line style Plain text to HTML line style Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + wrap Exceptions Exceptions Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Disable Qt autocompletion in search entry. Disable Qt autocompletion in search entry. Search as you type. Search as you type. Paths translations Paths translations Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Snippets window CSS file Snippets window CSS file Opens a dialog to select the Snippets window CSS style sheet file Opens a dialog to select the Snippets window CSS style sheet file Resets the Snippets window style Resets the Snippets window style Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Document filter choice style: Document filter choice style: Buttons Panel Buttons Panel Toolbar Combobox Toolbar Combobox Menu Menu Show system tray icon. Show system tray icon. Close to tray instead of exiting. Close to tray instead of exiting. Start with simple search mode Start with simple search mode Show warning when opening temporary file. Show warning when opening temporary file. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Synonyms file Synonyms file Highlight CSS style for query terms Highlight CSS style for query terms Recoll - User Preferences Recoll - User Preferences Set path translations for the selected index or for the main one if no selection exists. Set path translations for the selected index or for the main one if no selection exists. Activate links in preview. Activate links in preview. Make links inside the preview window clickable, and start an external browser when they are clicked. Make links inside the preview window clickable, and start an external browser when they are clicked. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Start search on completer popup activation. Start search on completer popup activation. Maximum number of snippets displayed in the snippets window Maximum number of snippets displayed in the snippets window Sort snippets by page number (default: by weight). Sort snippets by page number (default: by weight). Suppress all beeps. Suppress all beeps. Application Qt style sheet Application Qt style sheet Limit the size of the search history. Use 0 to disable, -1 for unlimited. Limit the size of the search history. Use 0 to disable, -1 for unlimited. Maximum size of search history (0: disable, -1: unlimited): Maximum size of search history (0: disable, -1: unlimited): Generate desktop notifications. Generate desktop notifications. Misc Misc Work around QTBUG-78923 by inserting space before anchor text Work around QTBUG-78923 by inserting space before anchor text Display a Snippets link even if the document has no pages (needs restart). Display a Snippets link even if the document has no pages (needs restart). Maximum text size highlighted for preview (kilobytes) Maximum text size highlighted for preview (kilobytes) Start with simple search mode: Start with simple search mode: Hide toolbars. Hide toolbars. Hide status bar. Hide status bar. Hide Clear and Search buttons. Hide Clear and Search buttons. Hide menu bar (show button instead). Hide menu bar (show button instead). Hide simple search type (show in menu only). Hide simple search type (show in menu only). Shortcuts Shortcuts Hide result table header. Hide result table header. Show result table row headers. Show result table row headers. Reset shortcuts defaults Reset shortcuts defaults Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Use F1 to access the manual Use F1 to access the manual Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Simple search type Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Dark mode Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Result Table Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_zh.ts0000644000175000017500000054727214427373216014276 00000000000000 ActSearchDLG Menu search AdvSearch All clauses 全部条件 Any clause 任意条件 texts 文本 spreadsheets 电子表格 presentations 演示文稿 media 多媒体文件 messages 邮件 other 其它 Bad multiplier suffix in size filter 文件尺寸过滤器的后缀单位不正确 text 文本文件 spreadsheet 电子表格 presentation 演示文档 message 邮件 Advanced Search Load next stored search Load previous stored search AdvSearchBase Advanced search 高端搜索 Search for <br>documents<br>satisfying: 搜索<br>满足以下条件<br>的文档: Delete clause 删除条件 Add clause 添加条件 Restrict file types 限定文件类型 Check this to enable filtering on file types 选中这个,以便针对文件类型进行过滤 By categories 按大类来过滤 Check this to use file categories instead of raw mime types 选中这个,以便使用较大的分类,而不使用具体的文件类型 Save as default 保存为默认值 Searched file types 将被搜索的文件类型 All ----> 移动全部→ Sel -----> 移动选中项→ <----- Sel ←移动选中项 <----- All ←移动全部 Ignored file types 要忽略的文件类型 Enter top directory for search 输入要搜索的最上层目录 Browse 浏览 Restrict results to files in subtree: 将结果中的文件限定在此子目录树中: Start Search 开始搜索 Close 关闭 All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. 右边的所有非空字段都会按照逻辑与(“全部条件”选项)或逻辑或(“任意条件”选项)来组合。<br>“任意”“全部”和“无”三种字段类型都接受输入简单词语和双引号引用的词组的组合。<br>空的输入框会被忽略。 Invert 反转过滤条件 Minimum size. You can use k/K,m/M,g/G as multipliers 最小尺寸。你可使用k/K、m/M、g/G作为单位 Min. Size 最小尺寸 Maximum size. You can use k/K,m/M,g/G as multipliers 最大尺寸。你可使用k/K、m/M、g/G作为单位 Max. Size 最大尺寸 Filter 过滤 From To Check this to enable filtering on dates 选中这个,以便针对日期进行过滤 Filter dates 过滤日期 Filter birth dates 过滤创建日期 Find 查找 Check this to enable filtering on sizes 选中这个,以便针对文件尺寸进行过滤 Filter sizes 过滤尺寸 ConfIndexW Can't write configuration file 无法写入配置文件 Global parameters 全局参数 Local parameters 局部参数 Search parameters 搜索参数 Top directories 顶级目录 The list of directories where recursive indexing starts. Default: your home. 索引从这个列表中的目录开始,递归地进行。默认:你的家目录。 Skipped paths 略过的路径 These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Stemming languages 词根语言 The languages for which stemming expansion<br>dictionaries will be built. 将会针对这些语言<br>构造词根扩展词典。 Log file name 记录文件名 The file where the messages will be written.<br>Use 'stderr' for terminal output 程序输出的消息会被保存到这个文件。<br>使用'stderr'以表示将消息输出到终端 Log verbosity level 记录的话痨级别 This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. 这个值调整的是输出的消息的数量,<br>其级别从仅输出报错信息到输出一大堆调试信息。 Index flush megabytes interval 刷新索引的间隔,兆字节 This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB 这个值调整的是,当积累咯多少索引数据时,才将数据刷新到硬盘上去。<br>用来控制索引进程的内存占用情况。默认为10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. No aspell usage 不使用aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. 禁止在词语探索器中使用aspell来生成拼写相近的词语。<br>在没有安装aspell或者它工作不正常时使用这个选项。 Aspell language Aspell语言 Database directory name 数据库目录名 The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Unac exceptions <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Web page store directory name 网页储存目录名 The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. 用来储存复制过来的已访问网页的目录名。<br>如果使用相对路径,则会相对于配置目录的路径进行处理。 Max. size for the web store (MB) 网页存储的最大尺寸(MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Automatic diacritics sensitivity <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Automatic character case sensitivity <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. Maximum term expansion count <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Maximum Xapian clauses count <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Indexer log file name If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Web history Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 ConfSubPanelW Only mime types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Exclude mime types Mime types not to be indexed Max. compressed file size (KB) 压缩文件最大尺寸(KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. 尺寸大于这个值的压缩文件不会被处理。设置成-1以表示不加任何限制,设置成0以表示根本不处理压缩文件。 Max. text file size (MB) 文本文件最大尺寸(MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. 尺寸大于这个值的文本文件不会被处理。设置成-1以表示不加限制。 其作用是从索引中排除巨型的记录文件。 Text file page size (KB) 文本文件单页尺寸(KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). 如果设置咯这个值(不等于-1),则文本文件会被分割成这么大的块,并且进行索引。 这是用来搜索大型文本文件的(例如记录文件)。 Max. filter exec. time (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Global 全局 CronToolW Cron Dialog 计划任务对话框 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T19:47:37" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T19:56:53" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T1 { font-weight:bold; } .T3 { font-style:italic; } .T4 { font-family:Courier New,courier; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1"><span class="T1">Recoll</span> 批量索引计划任务(cron) </p><p class="P1">每个字段都可以包括一个通配符(*)、单个数字值、逗号分隔的列表(1,3,5)和范围(1-7)。更准确地说,这些字段会被<span class="T3">按原样</span>输出到crontab 文件中,因此这里可以使用crontab 的所有语法,参考crontab(5)。</p><p class="P1"><br/>例如,在<span class="T3">日期</span>中输入<span class="T4">*</span>,<span class="T3">小时</span>中输入<span class="T4">12,19</span>,<span class="T3">分钟</span>中输入<span class="T4">15 </span>的话,会在每天的12:15 AM 和7:15 PM启动recollindex。</p><p class="P1">一个频繁执行的计划任务,其性能可能比不上实时索引。</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) 星期日(*或0-7,0或7是指星期天) Hours (* or 0-23) 小时(*或0-23) Minutes (0-59) 分钟(0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T20:08:00" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T20:11:47" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T2 { font-style:italic; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1">点击<span class="T2">禁用</span>以停止进行自动化的批量索引,点击<span class="T2">启用</span>以启用此功能,点击<span class="T2">取消</span>则不改变任何东西。</p></body></html> Enable 启用 Disable 禁用 It seems that manually edited entries exist for recollindex, cannot edit crontab 看起来已经有手动编辑过的recollindex条目了,因此无法编辑crontab Error installing cron entry. Bad syntax in fields ? 插入cron条目时出错。请检查语法。 EditDialog Dialog 对话框 EditTrans Source path Local path Config error Original path EditTransBase Path Translations Setting path translations for Select one or several file types, then use the controls in the frame below to change how they are processed Add Delete Cancel 取消 Save FirstIdxDialog First indexing setup 第一次索引设置 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T20:14:44" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T20:23:13" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T2 { font-weight:bold; } .T4 { font-style:italic; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1"><span class="T2">未找到对应于此配置实例的索引数据。</span><br/><br/>如果你只想以一组合理的默认参数来索引你的家目录的话,就直接按<span class="T4">立即开始索引</span>按钮。以后还可以调整配置参数的。</p><p class="P1">如果你想调整某些东西的话,就使用下面的链接来调整其中的索引配置和定时计划吧。</p><p class="P1">这些工具可在以后通过<span class="T4">选项</span>菜单访问。</p></body></html> Indexing configuration 索引配置 This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. 在这里可以调整你想要对其进行索引的目录,以及其它参数,例如:要排除和路径或名字、默认字符集…… Indexing schedule 定时索引任务 This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). 在这里可以选择是要进行批量索引还是实时索引,还可以设置一个自动化的定时(使用cron)批量索引任务。 Start indexing now 立即开始索引 FragButs %1 not found. %1: %2 Query Fragments IdxSchedW Index scheduling setup 定时索引设置 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T20:27:11" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T20:30:49" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T1 { font-weight:bold; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1"><span class="T1">Recoll</span> 索引程序可持续运行并且在文件发生变化时对其进行索引,也可以间隔一定时间运行一次。</p><p class="P1">你可以读一下手册,以便更好地做出抉择(按F1)。</p><p class="P1">这个工具可帮助你设置一个自动进行批量索引的定时任务,或者设置成当你登录时便启动实时索引(或者两者同时进行,当然那几乎没有意义)。</p></body></html> Cron scheduling 定时任务 The tool will let you decide at what time indexing should run and will install a crontab entry. 这个工具帮助你确定一个让索引运行的时间,它会插入一个crontab条目。 Real time indexing start up 实时索引设置 Decide if real time indexing will be started when you log in (only for the default index). 作出决定,是否要在登录时便启动实时索引(只对默认索引有效)。 ListDialog Dialog 对话框 GroupBox 分组框 Main No db directory in configuration 配置实例中没有数据库目录 "history" file is damaged or un(read)writeable, please check or remove it: "history"文件被损坏,或者不可(读)写,请检查一下或者删除它: "history" file is damaged, please check or remove it: Preview Close Tab 关闭标签页 Cancel 取消 Missing helper program: 缺少辅助程序: Can't turn doc into internal representation for 无法为此文件将文档转换成内部表示方式: Creating preview text 正在创建预览文本 Loading preview text into editor 正在将预览文本载入到编辑器中 &Search for: 搜索(&S): &Next 下一个(&N) &Previous 上一个(&P) Clear 清空 Match &Case 匹配大小写(&C) Cannot create temporary directory: 无法创建临时目录: Error while loading file 文件载入出错 Form Tab 1 Open 打开 Canceled Error loading the document: file missing. Error loading the document: no permission. Error loading: backend not configured. Error loading the document: other handler error<br>Maybe the application is locking the file ? Error loading the document: other handler error. <br>Attempting to display from stored text. Could not fetch stored text Previous result document Next result document Preview Window Close tab Close preview window Show next result Show previous result Print 打印 PreviewTextEdit Show fields 显示字段 Show main text 显示主文本 Print 打印 Print Current Preview 打印当前预览文本 Show image 显示图片 Select All 全选 Copy 复制 Save document to file 将文档保存到文件 Fold lines 自动换行 Preserve indentation 保留缩进符 Open document QObject Global parameters 全局参数 Local parameters 局部参数 <b>Customised subtrees <b>自定义的子目录树 The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. 这是已索引的目录树中的一些子目录组成的列表<br>,它们的某些参数需要重定义。默认:空白。 <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>以下的参数,当你在上面的列表中不选中任何条目或者选中一个空行时,<br>就是针对顶级目录起作用的,否则便是对选中的子目录起作用的。<br>你可以点击+/-按钮,以便添加或删除目录。 Skipped names 要略过的文件名 These are patterns for file or directory names which should not be indexed. 具有这些模式的文件或目录不会被索引。 Default character set 默认字符集 This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. 这是用来读取那些未标明自身的字符集的文件时所使用的字符集,例如纯文本文件。<br>默认值是空,会使用系统里的自然语言环境参数中的值。 Follow symbolic links 跟踪符号链接 Follow symbolic links while indexing. The default is no, to avoid duplicate indexing 在索引时跟踪符号链接。默认是不跟踪的,以避免重复索引 Index all file names 对所有文件名进行索引 Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true 对那些无法判断或处理其内容(未知类型或其类型不被支持)的文件的名字进行索引。默认为是 Beagle web history Beagle网页历史 Search parameters 搜索参数 Default<br>character set Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Ignored endings These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. QWidget Create or choose save directory Choose exactly one directory Could not read directory: Unexpected file name collision, cancelling. Cannot extract document: &Preview 预览(&P) &Open 打开(&O) Open With Run Script Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) &Write to File 写入文件(&W) Save selection to files Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) Find &similar documents 查找类似的文档(&s) Open &Snippets window Show subdocuments / attachments &Open Parent document &Open Parent Folder Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. RTIToolW Real time indexing automatic start 实时索引自动启动 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-22T21:00:38" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-22T21:02:43" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .P1 { font-size:12pt; margin-bottom:0cm; margin-top:0cm; font-family:Nimbus Roman No9 L; writing-mode:page; margin-left:0cm; margin-right:0cm; text-indent:0cm; } .T1 { font-weight:bold; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="P1"><span class="T1">Recoll</span> 索引程序可以以守护进程的方式运行,在文件发生变化时便实时更新索引。这样你的索引一直是与文件同步的,但是会占用一定的系统资源。</p></body></html> Start indexing daemon with my desktop session. 在我的桌面会话启动时便启动索引进程。 Also start indexing daemon right now. 同时此次也立即启动索引进程。 Replacing: 正在替换: Replacing file 正在替换文件 Can't create: 无法创建: Warning 警告 Could not execute recollindex 无法执行recollindex Deleting: 正在删除: Deleting file 正在删除文件 Removing autostart 正在删除自动启动项 Autostart file deleted. Kill current process too ? 自动启动文件已经删除。也要杀死当前进程吗? RclMain (no stemming) (不进行词根计算) (all languages) (对全部语言进行词根计算) error retrieving stemming languages 提取词根语言时出错 Indexing in progress: 正在索引: Purge 删除 Stemdb Stem数据库 Closing 正在关闭 Unknown 未知 Query results 查询结果 Cannot retrieve document info from database 无法从数据库获取文档信息 Warning 警告 Can't create preview window 无法创建预览窗口 This search is not active any more 这个查询已经不是活跃的了 Bad viewer command line for %1: [%2] Please check the mimeconf file 针对%1的查看命令[%2]配置出错 请检查mimeconf文件 Cannot extract document or create temporary file 无法提取文档或创建临时文件 Executing: [ 正在执行:[ About Recoll Recoll说明 History data 历史数据 Document history 文档历史 Update &Index 更新索引(&I) Stop &Indexing 停止索引(&I) All 全部 media 多媒体文件 message 邮件 other 其它 presentation 演示文档 spreadsheet 电子表格 text 文本文件 sorted 已排序 filtered 已过滤 External applications/commands needed and not found for indexing your file types: 需要用来辅助对你的文件进行索引,却又找不到的外部程序/命令: No helpers found missing 目前不缺少任何辅助程序 Missing helper programs 未找到的辅助程序 Document category filter 文档分类过滤器 No external viewer configured for mime type [ 针对此种文件类型没有配置外部查看器[ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? 没有找到mimeview中为%1: %2配置的查看器。 是否要打开选项对话框? Can't access file: 无法访问文件: Can't uncompress file: 无法解压缩此文件: Save file 保存文件 Result count (est.) 结果数(估计值) Query details 查询语句细节 Could not open external index. Db not open. Check external indexes list. 无法打开外部索引。数据库未打开。请检查外部索引列表。 No results found 未找到结果 None Updating 正在更新 Done 已完成 Monitor 监视器 Indexing failed 索引失败 The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone 当前索引进程不是由此界面启动的。点击确定以杀死它,或者点击取消以让它自由运行 Erasing index 正在删除索引 Reset the index and start from scratch ? 从头重新开始索引吗? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program 查询正在进行中。<br>由于索引库的某些限制,<br>取消的话会导致程序退出 Error 错误 Index not open 索引未打开 Index query error 索引查询出错 Content has been indexed for these mime types: 已经为这些文件类型索引其内容: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. 此文件的索引已过时。程序拒绝显示错误的条目。请点击确定以更新此文件的索引,等待索引完成之后再查询。或者,取消。 Can't update index: indexer running 无法更新索引:索引程序已在运行 Indexed MIME Types 已索引的文件类型 Bad viewer command line for %1: [%2] Please check the mimeview file Viewer command line for %1 specifies both file and parent file value: unsupported Cannot find parent document External applications/commands needed for your file types and not found, as stored by the last indexing pass in Sub-documents and attachments Document filter The indexer is running so things should improve when it's done. Bad desktop app spec for %1: [%2] Please check the desktop file Indexing interrupted Bad paths Selection patterns need topdir Selection patterns can only be used with a start directory No search No preserved previous search Choose file to save Saved Queries (*.rclq) Write failed Could not write to file Read failed Could not open file: Load error Could not load saved query Disabled because the real time indexer was not compiled in. This configuration tool only works for the main index. Can't set synonyms file (parse error?) The document belongs to an external index which I can't update. Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Do not show this warning next time (use GUI preferences to restore). Index locked Unknown indexer state. Can't access webcache file. Indexer is running. Can't access webcache file. with additional message: Non-fatal indexing message: Types list empty: maybe wait for indexing to progress? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Tools Results Content has been indexed for these MIME types: Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Indexing done Can't update index: internal error Index not up to date for this file.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> documents document files file errors error total files) No information: initial indexing not yet performed. Batch scheduling The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Confirm Erasing simple and advanced search history lists, please click Ok to confirm Could not open/create file F&ilter Simple search type Any term 任一词语 All terms 全部词语 File name 文件名 Query language 查询语言 Stemming language 词根语言 Main Window Clear search Move keyboard focus to search entry Move keyboard focus to search, alt. Toggle tabular display Move keyboard focus to table Flushing Show menu search dialog Duplicates Filter directories RclMainBase Recoll Recoll Search tools 搜索工具 Result list 结果列表 &File 文件(&F) &Tools 工具(&T) &Preferences 选项(&P) &Help 帮助(&H) E&xit 退出(&x) Ctrl+Q Ctrl+Q Update &index 更新索引(&i) &Erase document history 删除文档历史(&E) &About Recoll Recoll说明(&A) &User manual 用户手册(&U) Document &History 文档历史(&H) Document History 文档历史 &Advanced Search 高端搜索(&A) Advanced/complex Search 高端/复杂搜索 &Sort parameters 排序参数(&S) Sort parameters 排序参数 Term &explorer 词语探索器(&e) Term explorer tool 词语探索器 Next page 下一页 Next page of results 下一页结果 First page 第一页 Go to first page of results 跳转到结果的第一页 Previous page 上一页 Previous page of results 上一页结果 &Query configuration 查询配置(&Q) External index dialog 外部索引对话框 &Indexing configuration 索引配置(&I) All 全部 &Show missing helpers 显示缺少的辅助程序列表(&S) PgDown 向下翻页 PgUp 向上翻页 &Full Screen 全屏(&F) F11 F11 Full Screen 全屏 &Erase search history 删除搜索历史(&E) sortByDateAsc 按日期升序排列 Sort by dates from oldest to newest 按日期排列,最旧的在前面 sortByDateDesc 按日期降序排列 Sort by dates from newest to oldest 按日期排列,最新的在前面 Show Query Details 显示查询语句细节 Show results as table 以表格的形式显示结果 &Rebuild index 重新构造索引(&R) &Show indexed types 显示已索引的文件类型(&S) Shift+PgUp Shift+向上翻页 &Indexing schedule 定时索引(&I) E&xternal index dialog 外部索引对话框(&x) &Index configuration &GUI configuration &Results Sort by date, oldest first Sort by date, newest first Show as table Show results in a spreadsheet-like table Save as CSV (spreadsheet) file Saves the result into a file which you can load in a spreadsheet Next Page Previous Page First Page Query Fragments With failed files retrying Next update will retry previously failed files Indexing &schedule Enable synonyms Save last query Load saved query Special Indexing Indexing with special options &View Missing &helpers Indexed &MIME types Index &statistics Webcache Editor Trigger incremental pass E&xport simple search history &Query Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates 过滤日期 Filter birth dates 过滤创建日期 Assisted complex search RclTrayIcon Restore Quit RecollModel Abstract 摘要 Author 作者 Document size 文档尺寸 Document date 文档日期 File size 文件尺寸 File name 文件名 File date 文件日期 Keywords 关键词 Original character set 原字符集 Relevancy rating 相关度 Title 标题 URL 路径 Mtime 修改时间 Date 日期 Date and time 日期及时间 Ipath 内部路径 MIME type 文件类型 Can't sort by inverse relevance ResList Result list 结果列表 (show query) (显示查询语句细节) &Preview 预览(&P) Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) Find &similar documents 查找类似的文档(&s) Document history 文档历史 <p><b>No results found</b><br> <p><b>未找到结果</b><br> Previous 上一页 Next 下一页 Unavailable document 无法访问文档 Preview 预览 Open 打开 <p><i>Alternate spellings (accents suppressed): </i> <p><i>其它拼写形式(忽视口音):</i> &Write to File 写入文件(&W) Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) &Open 打开(&O) Documents out of at least 个文档,最少共有 for 个文档,查询条件: <p><i>Alternate spellings: </i> Result count (est.) 结果数(估计值) Query details 查询语句细节 Snippets ResTable &Reset sort 重置排序条件(&R) &Delete column 删除此列(&D) Save table to CSV file 将表格保存成CSV文件 Can't open/create file: 无法打开/创建文件: &Preview 预览(&P) &Open 打开(&O) Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) &Write to File 写入文件(&W) Find &similar documents 查找类似的文档(&s) Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) &Save as CSV 保存为CSV(&S) Add "%1" column 添加"%1"列 Result Table Open 打开 Preview 预览 Open current result document Open current result and quit Show snippets Show header Show vertical header Copy current result text to clipboard Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview 预览(&P) &Open 打开(&O) Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) &Write to File 写入文件(&W) Find &similar documents 查找类似的文档(&s) Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) ResultPopup &Preview 预览(&P) &Open 打开(&O) Copy &File Name 复制文件名(&F) Copy &URL 复制路径(&U) &Write to File 写入文件(&W) Preview P&arent document/folder 预览上一级文档/目录(&a) &Open Parent document/folder 打开上一级文档/目录(&O) Find &similar documents 查找类似的文档(&s) SSearch Any term 任一词语 All terms 全部词语 File name 文件名 Query language 查询语言 Bad query string 查询语言格式不正确 Out of memory 内存不足 Too many completions 有太多与之相关的补全选项啦 Completions 补全选项 Select an item: 选择一个条目: Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by OpenOffice.org - see http://xml.openoffice.org/odf2xhtml for more info.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xmlns:ns_1="http://www.w3.org/XML/1998/namespace" ns_1:lang="en-US">- no title specified</title><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.title" content="" ns_1:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2012-03-23T08:43:25" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.modified" content="2012-03-23T09:07:39" scheme="DCTERMS.W3CDTF"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.provenance" content="" ns_1:lang="en-US"/><meta xmlns:ns_1="http://www.w3.org/XML/1998/namespace" name="DCTERMS.subject" content="," ns_1:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css"> @page { } table { border-collapse:collapse; border-spacing:0; empty-cells:show } td, th { vertical-align:top; font-size:12pt;} h1, h2, h3, h4, h5, h6 { clear:both } ol, ul { margin:0; padding:0;} li { list-style: none; margin:0; padding:0;} <!-- "li span.odfLiEnd" - IE 7 issue--> li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; } span.footnodeNumber { padding-right:1em; } span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; } * { margin:0;} .Standard { font-size:12pt; font-family:Nimbus Roman No9 L; writing-mode:page; } .T1 { font-style:italic; } .T2 { font-style:italic; } .T4 { font-weight:bold; } <!-- ODF styles with no properties representable as CSS --> { } </style></head><body dir="ltr" style="max-width:21.001cm;margin-top:2cm; margin-bottom:2cm; margin-left:2cm; margin-right:2cm; writing-mode:lr-tb; "><p class="Standard">输入查询语言表达式。简要说明:<br/><span class="T2">词语</span><span class="T1">1 </span><span class="T2">词语</span><span class="T1">2</span> : '词语1'和'词语2'同时出现在任意字段中。<br/><span class="T2">字段</span><span class="T1">:</span><span class="T2">词语</span><span class="T1">1</span> : '词语1'出现在字段'字段'中。<br/>标准字段名/同义名:<br/>title/subject/caption、author/from、recipient/to、filename、ext。<br/>伪字段名:dir、mime/format、type/rclcat、date。<br/>日期段的两个示例:2009-03-01/2009-05-20 2009-03-01/P2M。<br/><span class="T2">词语</span><span class="T1">1 </span><span class="T2">词语</span><span class="T1">2 OR </span><span class="T2">词语</span><span class="T1">3</span> : 词语1 <span class="T4">与</span> (词语2 <span class="T4">或</span> 词语3)。<br/>不允许用真正的括号来表示逻辑关系。<br/><span class="T1">"</span><span class="T2">词语</span><span class="T1">1 </span><span class="T2">词语</span><span class="T1">2"</span> : 词组(必须按原样出现)。可用的修饰词:<br/><span class="T1">"</span><span class="T2">词语</span><span class="T1">1 </span><span class="T2">词语</span><span class="T1">2"p</span> : 以默认距离进行的无序近似搜索。<br/>有疑问时可使用<span class="T4">显示查询语句细节</span>链接来查看查询语句的细节,另外请查看手册(&lt;F1&gt;)以了解更多内容。</p></body></html> Enter file name wildcard expression. 输入文件名通配符表达式。 Enter search terms here. Type ESC SPC for completions of current term. 在此输入要搜索的词语。按Esc 空格来查看针对当前词语的补全选项。 Stemming languages for stored query: differ from current preferences (kept) Auto suffixes for stored query: Autophrase is set but it was unset for stored query Autophrase is unset but it was set for stored query Enter search terms here. <html><head><style> table, th, td { border: 1px solid black; border-collapse: collapse; } th,td { text-align: center; </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; You should really look at the manual (F1)</p> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; where needed</td><td>(one AND two) OR three</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> Can't open index Could not restore external indexes for stored query:<br> ??? Using current preferences. Simple search History SSearchBase SSearchBase SSearchBase Clear 清空 Ctrl+S Ctrl+S Erase search entry 删除搜索条目 Search 搜索 Start query 开始查询 Enter search terms here. Type ESC SPC for completions of current term. 在此输入要搜索的词语。按Esc 空格来查看针对当前词语的补全选项。 Choose search type. 选择搜索类型。 Show query history Main menu SearchClauseW Select the type of query that will be performed with the words 选择要对右边的词语进行的查询类型 Number of additional words that may be interspersed with the chosen ones 允许在选中的词语之间出现的额外词语的个数 No field 不限字段 Any 任意 All 全部 None Phrase 词组 Proximity 近似 File name 文件名 Snippets Snippets Find: Next 下一页 Prev SnippetsW Search 搜索 <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> Sort By Relevance Sort By Page Snippets Window Find 查找 Find (alt) Find next Find previous Close window SpecIdxW Special Indexing Else only modified or failed files will be processed. Erase selected files data before indexing. Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Browse 浏览 Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Selection patterns: Top indexed entity Retry previously failed files. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer 词语探索器 &Expand 展开(&E) Alt+E Alt+E &Close 关闭(&C) Alt+C Alt+C No db info. 未找到数据库信息。 Match Case Accents SpellW Wildcards 通配符 Regexp 正则表达式 Stem expansion 词根扩展 Spelling/Phonetic 拼写/发音检查 error retrieving stemming languages 提取词根语言时出错 Aspell init failed. Aspell not installed? Aspell初始化失败。是否未安装Aspell? Aspell expansion error. Aspell扩展出错。 No expansion found 未找到扩展 Term 词语 Doc. / Tot. 文档数/总数 Index: %1 documents, average length %2 terms 索引:%1个文档,平均长度为%2个词语 Index: %1 documents, average length %2 terms.%3 results %1 results List was truncated alphabetically, some frequent terms may be missing. Try using a longer root. Show index statistics Number of documents Average terms per document Database directory size MIME types: Item Value Smallest document length (terms) Longest document length (terms) Results from last indexing: Documents created/updated Files tested Unindexed files List files which could not be indexed (slow) Spell expansion error. UIPrefsDialog error retrieving stemming languages 提取词根语言时出错 The selected directory does not appear to be a Xapian index 选中的目录不是Xapian索引 This is the main/local index! 这是主要/本地索引! The selected directory is already in the index list 选中的目录已经在索引列表中 Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) 选择xapian索引目录(例如:/home/buddy/.recoll/xapiandb) Choose 选择 Result list paragraph format (erase all to reset to default) Result list header (default is empty) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read At most one index should be selected Cant add index with different case/diacritics stripping option Default QtWebkit font Any term 任一词语 All terms 全部词语 File name 文件名 Query language 查询语言 Value from previous program exit Context Description Shortcut Default Choose QSS File ViewAction Changing actions with different current values 正在针对不同的当前值而改变动作 Command 命令 MIME type 文件类型 Desktop Default Changing entries with different current values ViewActionBase Native Viewers 本地查看器 Select one or several file types, then click Change Action to modify the program used to open them 选中一个或多个文件类型,然后点击“修改动作”来修改用来打开这些文件的程序 Change Action 修改动作 Close 关闭 Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. 选中一个或多个文件类型祟点击“修改动作”<br>或者可以关闭这个对话框,而在主面板中选中“使用桌面默认设置”<br>那样就会无视这个列表而使用桌面的默认设置。 Select one or several mime types then use the controls in the bottom frame to change how they are processed. Use Desktop preferences by default Select one or several file types, then use the controls in the frame below to change how they are processed Exception to Desktop preferences Action (empty -> recoll default) Apply to current selection Recoll action: current value Select same <b>New Values:</b> Webcache Webcache editor Search regexp WebcacheEdit Copy URL Unknown indexer state. Can't edit webcache file. Indexer is running. Can't edit webcache file. Delete selection Webcache was modified, you will need to run the indexer after closing this window. Save to File File creation failed: WebcacheModel MIME Url Date 日期 Size WinSchedToolW Error 错误 confgui::ConfBeaglePanelW Steal Beagle indexing queue 窃取Beagle索引队列 Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) 不可运行Beagle。启用对beagle队列的处理,以索引火狐网页历史。<br>(你还需要安装火狐Beagle插件) Entries will be recycled once the size is reached 当尺寸达到设定值时,这些条目会被循环使用 Web page store directory name 网页储存目录名 The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. 用来储存复制过来的已访问网页的目录名。<br>如果使用相对路径,则会相对于配置目录的路径进行处理。 Max. size for the web store (MB) 网页存储的最大尺寸(MB) confgui::ConfIndexW Can't write configuration file 无法写入配置文件 confgui::ConfParamFNW Choose 选择 confgui::ConfParamSLW + + - - Add entry Delete selected entries ~ Edit selected entries confgui::ConfSubPanelW Global 全局 Max. compressed file size (KB) 压缩文件最大尺寸(KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. 尺寸大于这个值的压缩文件不会被处理。设置成-1以表示不加任何限制,设置成0以表示根本不处理压缩文件。 Max. text file size (MB) 文本文件最大尺寸(MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. 尺寸大于这个值的文本文件不会被处理。设置成-1以表示不加限制。 其作用是从索引中排除巨型的记录文件。 Text file page size (KB) 文本文件单页尺寸(KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). 如果设置咯这个值(不等于-1),则文本文件会被分割成这么大的块,并且进行索引。 这是用来搜索大型文本文件的(例如记录文件)。 Max. filter exec. time (S) 过滤器的最长执行时间(S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. 外部过滤器的执行时间如果超过这个值,则会被强行中断。在罕见的情况下,某些文档(例如postscript)会导致过滤器陷入死循环。设置成-1以表示不加限制。 confgui::ConfTopPanelW Top directories 顶级目录 The list of directories where recursive indexing starts. Default: your home. 索引从这个列表中的目录开始,递归地进行。默认:你的家目录。 Skipped paths 略过的路径 These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') 索引进程不会进入具有这些名字的目录。<br>可以包含通配符。必须匹配索引进程自身所见到的路径(例如:如果topdirs包含'/home/me',而实际上'/home'是到'/usr/home'的链接,则正确的skippedPath条目应当是'/home/me/tmp*',而不是'/usr/home/me/tmp*') Stemming languages 词根语言 The languages for which stemming expansion<br>dictionaries will be built. 将会针对这些语言<br>构造词根扩展词典。 Log file name 记录文件名 The file where the messages will be written.<br>Use 'stderr' for terminal output 程序输出的消息会被保存到这个文件。<br>使用'stderr'以表示将消息输出到终端 Log verbosity level 记录的话痨级别 This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. 这个值调整的是输出的消息的数量,<br>其级别从仅输出报错信息到输出一大堆调试信息。 Index flush megabytes interval 刷新索引的间隔,兆字节 This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB 这个值调整的是,当积累咯多少索引数据时,才将数据刷新到硬盘上去。<br>用来控制索引进程的内存占用情况。默认为10MB Max disk occupation (%) 最大硬盘占用率(%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). 当硬盘的占用率达到这个数时,索引会失败并且停止(以避免塞满你的硬盘)。<br>设为0则表示不加限制(这是默认值)。 No aspell usage 不使用aspell Aspell language Aspell语言 The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. aspell词典的语言。表示方式是'en'或'fr'……<br>如果不设置这个值,则会使用系统环境中的自然语言设置信息,而那个通常是正确的。要想查看你的系统中安装咯哪些语言的话,就执行'aspell config',再在'data-dir'目录中找.dat文件。 Database directory name 数据库目录名 The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. 用来储存索引数据的目录的名字<br>如果使用相对路径,则路径会相对于配置目录进行计算。默认值是'xapiandb'。 Use system's 'file' command 使用系统里的'file'命令 Use the system's 'file' command if internal<br>mime type identification fails. 当内部的文件类型识别功能失效时<br>使用系统里的'file'命令。 Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. 禁止在词语探索器中使用aspell来生成拼写相近的词语。<br>在没有安装aspell或者它工作不正常时使用这个选项。 uiPrefsDialogBase User preferences 用户选项 User interface 用户界面 Number of entries in a result page 一个结果页面中显示的结果条数 If checked, results with the same content under different names will only be shown once. 如果选中这个,则拥有相同文件内容的不同文件名只会显示一个。 Hide duplicate results. 隐藏重复结果。 Highlight color for query terms 查询词语的高亮颜色 Result list font 结果列表字体 Opens a dialog to select the result list font 打开一个对话框,以选择用于结果列表的字体 Helvetica-10 文泉驿微米黑-12 Resets the result list font to the system default 将结果列表中的字体重设为系统默认值 Reset 重置 Texts over this size will not be highlighted in preview (too slow). 超过这个长度的文本不会在预览窗口里高亮显示(太慢)。 Maximum text size highlighted for preview (megabytes) 在预览中对其进行高亮显示的最大文本尺寸(兆字节) Use desktop preferences to choose document editor. 使用桌面系统的设置来选择文档编辑器。 Choose editor applications 选择编辑器程序 Display category filter as toolbar instead of button panel (needs restart). 将文件类型过滤器显示成工具条,而不是按钮面板(需要重启程序)。 Auto-start simple search on whitespace entry. 输入空格时自动开始进行简单搜索。 Start with advanced search dialog open. 启动时打开高端搜索对话框。 Remember sort activation state. 记住排序状态。 Prefer Html to plain text for preview. 预览中优先使用Html。 Search parameters 搜索参数 Stemming language 词根语言 A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. 对[滚 石] (2个词语)的搜索会变成[滚 or 石 or (滚 2个词语 石)]。 对于那些搜索词语在其中按照原样出现的结果,其优先级会高一些。 Automatically add phrase to simple searches 自动将词组添加到简单搜索中 Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. 是否要使用查询词语周围的上下文来构造结果列表条目中的摘要? 对于大的文档可能会很慢。 Dynamically build abstracts 动态构造摘要 Do we synthetize an abstract even if the document seemed to have one? 即使文档本身拥有一个摘要,我们仍然自行合成摘要信息? Replace abstracts from documents 取代文档中自带的摘要 Synthetic abstract size (characters) 合成摘要长度(字符个数) Synthetic abstract context words 合成摘要上下文 The words in the list will be automatically turned to ext:xxx clauses in the query language entry. 这个列表中的词语会在查询语言输入框里自动变成ext:xxx语句。 Query language magic file name suffixes. 查询语言神奇文件名后缀。 Enable 启用 External Indexes 外部索引 Toggle selected 切换选中项 Activate All 全部激活 Deactivate All 全部禁用 Remove from list. This has no effect on the disk index. 从列表中删除。这不会对硬盘上的索引造成损害。 Remove selected 删除选中项 Click to add another index directory to the list 点击这里,以将另一个索引目录添加到列表中 Add index 添加索引 Apply changes 使改变生效 &OK 确定(&O) Discard changes 放弃这些改变 &Cancel 取消(&C) Abstract snippet separator 摘要中的片段的分隔符 Style sheet 样式单 Opens a dialog to select the style sheet file 打开一个对话框,以选择样式单文件 Choose 选择 Resets the style sheet to default 将样式单重置为默认值 Lines in PRE text are not folded. Using BR loses some indentation. PRE中的文字不会换行。使用BR的话会使一些缩进失效。 Use <PRE> tags instead of <BR>to display plain text as html in preview. 在将纯文本显示成html预览的时候,使用<PRE>标签,而不是<BR>标签。 Result List 结果列表 Edit result paragraph format string 编辑结果段落的格式字符串 Edit result page html header insert 编辑结果页面的html头部插入项 Date format (strftime(3)) 日期格式(strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). 这是一个频率阈值,超过这个值的话,我们就不会把词语放到自动词组中。 高频词语是词组中性能问题的主要来源。 略过的词语会增加词组的空缺值,因此会降低自动词组功能的效率。 默认值是2(百分比)。 Autophrase term frequency threshold percentage 自动词组频率阈值百分比 Plain text to HTML line style Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. <BR> <PRE> <PRE> + wrap Disable Qt autocompletion in search entry. Paths translations Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Snippets window CSS file Opens a dialog to select the Snippets window CSS style sheet file Resets the Snippets window style Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Document filter choice style: Buttons Panel Toolbar Combobox Menu Show system tray icon. Close to tray instead of exiting. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Synonyms file Show warning when opening temporary file. Highlight CSS style for query terms Recoll - User Preferences Set path translations for the selected index or for the main one if no selection exists. Activate links in preview. Make links inside the preview window clickable, and start an external browser when they are clicked. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Start search on completer popup activation. Maximum number of snippets displayed in the snippets window Suppress all beeps. Application Qt style sheet Limit the size of the search history. Use 0 to disable, -1 for unlimited. Maximum size of search history (0: disable, -1: unlimited): Generate desktop notifications. Sort snippets by page number (default: by weight). Misc Display a Snippets link even if the document has no pages (needs restart). Maximum text size highlighted for preview (kilobytes) Start with simple search mode: Shortcuts Hide result table header. Show result table row headers. Reset shortcuts defaults Use F1 to access the manual Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): recoll-1.36.1/qtgui/i18n/recoll_ko.ts0000644000175000017500000072034414444307651014256 00000000000000 ActSearchDLG Menu search AdvSearch All clauses 모든 조건 만족 Any clause 하나라도 만족 texts 텍스트들 spreadsheets 스프레트쉬트들 presentations presentations media 미디어 messages messages other 기타 Bad multiplier suffix in size filter 사이즈 필터의 접미사 배수기능 상태 나쁨 text 텍스트 spreadsheet 스프레드쉬트 presentation 프레젠테이션 message 메세지 Advanced Search 고급 검색 History Next History Next History Prev History Prev Load next stored search 다음 저장된 검색 로드 Load previous stored search 이전 저장된 검색 로드 AdvSearchBase Advanced search 고급검색 Restrict file types 파일 형식 제한 Save as default 기본값으로 저장 Searched file types 검색된 파일 형식들 All ----> 모두 ----> Sel -----> 선택 -----> <----- Sel <----- 선택 <----- All <----- 모두 Ignored file types 무시할 파일 형식 Enter top directory for search 검색을 위해 최상위 폴더를 입력하십시오. Browse 탐색 Restrict results to files in subtree: 선택한 폴더의 하위 파일들의 결과들을 제한합니다: Start Search 검색 시작 Search for <br>documents<br>satisfying: 다음을<br>만족: Delete clause 조건 삭제 Add clause 조건 추가 Check this to enable filtering on file types 파일 형식에 대한 필터링을 활성화하려면 이것을 선택하십시오. By categories 카테고리 별로 Check this to use file categories instead of raw mime types MIME 형식 대신 파일 카테고리를 사용하려면 이것을 선택하십시오. Close 닫기 All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. "모든 조건 만족"을 선택하면, 오른쪽에 입력된 모든 조건은 AND로 결합되며, "하나라도 만족"을 선택하면 OR로 결합됩니다.<br>"하나라도", "모두", 그리고 "없음" 유형들은 단순 단어들의 혼합도, 큰 따옴표로 묶은 어구도 받아들일 수 있으며, <br> 입력되지 않은 필드들은 무시됩니다. Invert 역순 Minimum size. You can use k/K,m/M,g/G as multipliers 최소 용량을 지정합니다. k/K, m/M, g/G 단위를 사용할 수 있습니다. Min. Size 최소 용량 Maximum size. You can use k/K,m/M,g/G as multipliers 최대 용량을 지정합니다. k/K, m/M, g/G 단위를 사용할 수 있습니다. Max. Size 최대 용량 Select Select Filter 필터 From 언제부터 To 까지 Check this to enable filtering on dates 날짜 필터링을 활성화하려면 이 옵션을 선택하십시오. Filter dates 날짜 필터 Find 찾기 Check this to enable filtering on sizes 용량을 필터링하려면이 옵션을 선택하십시오. Filter sizes 용량 필터 Filter birth dates ConfIndexW Can't write configuration file 환경설정 파일을 쓸 수 없습니다 Global parameters 광역 환경설정 Local parameters 지역 환경설정 Search parameters 검색 매개변수들 Top directories 색인할 최상위 폴더 The list of directories where recursive indexing starts. Default: your home. 색인 작성이 시작되는 폴더 목록. 기본값 : home(리눅스). Skipped paths 제외할 폴더 These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') 색인 작업이 실행되지 않게 될 폴더 경로들입니다.<br>경로에는 와일드 카드가 포함될 수 있습니다. 항목들은 색인자가 보는 경로와 일치해야합니다 (예 : 최상위 폴더에 '/home /me'가 포함되어 있고 '/home'이 실제로 '/usr/home'에 대한 링크인 경우 올바른 '건너뛴 경로들' 항목은 '/home/me'입니다. '/usr/home/me/tmp*'가 아닌 /tmp*') Stemming languages 형태소 언어 The languages for which stemming expansion<br>dictionaries will be built. 형태소 확장 사전을 만들 언어가<br>작성됩니다. Log file name 로그 파일 이름 The file where the messages will be written.<br>Use 'stderr' for terminal output 메시지가 기록 될 파일입니다.<br>터미널에 출력하려면 'stderr'을 입력하십시오. Log verbosity level 상세 로그 수준 This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. 이 값은 메시지의 범위를<br>단순 오류에서부터 많은 디버깅 데이터에까지 조정합니다. Index flush megabytes interval 색인 정비 간격(MB) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB 이 값은 색인되는 데이터의 양을 적절한 값으로 조정합니다.<br>색인 작업자가 메모리 사용을 제어하는 데 도움이 됩니다. 기본 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. 색인을 실패시키고 중지시킬 디스크 크기의 백분율입니다.<br>기준은 색인 크기가 아닌 전체 디스크 사용량입니다.<br>기본값인 0은 제한을 두지 않습니다. No aspell usage 철자검색기 사용안함 Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. 용어 탐색기 도구에서 유사한 철자를 발생시키는 철자 사용을 비활성화 합니다. <br> 철자가 없거나 작동하지 않는 경우에 유용합니다. Aspell language 철자검색기 언어 The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. 철자 사전의 언어입니다. 이것은 'en'이나 'fr' 등으로 나타나야 합니다.<br>만약 이 값이 지정되어있지 않다면, NLS 환경설정이 일반적으로 사용되는 값을 찾을 것입니다. 시스템에 무엇이 설치되어 있는지 이해하려면 'aspell config'를 입력하고 'data-dir'디렉토리에서 .dat 파일을 찾으십시오. Database directory name 데이터베이스 폴더 이름 The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. 색인을 저장할 폴더 이름<br>상대 경로일 경우, 환경설정 폴더를 기본으로 합니다. 기본값은 'xapiandb'입니다. Unac exceptions Unac 예외 <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p> 이것은 기본적으로 모든 분음 부호를 제거하고 정식 분해를 수행하는 unac 메커니즘에 대한 예외를 설정합니다. 언어에 따라 일부 문자의 강조를 무시, 추가, 분해를 지정할 수 있습니다(예 : 합자의 경우. 공백으로 구분된 각 항목에서 첫 번째 문자는 원본이고 나머지는 번역입니다). Process the WEB history queue 웹 기록 큐 처리 Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Firefox 방문 페이지 색인 작성을 활성화합니다.<br>(Firefox Recoll 플러그인도 설치필요) Web page store directory name 웹 페이지 저장소 디렉토리 이름 The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. 방문한 웹 페이지의 사본을 저장할 디렉토리의 이름을 지정해줍니다.<br>상대적인 경로를 입력한 경우, 환경설정 폴더를 기준으로 합니다. Max. size for the web store (MB) 웹 저장소의 최대 용량(MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). 용량이 한계에 도달하면 항목들이 재활용됩니다.<br>값을 줄이면 기존 파일이 잘리지 않기 때문에 크기를 늘리는 것만으로도 의미가 있습니다(끝 공간만 낭비됩니다). Automatic diacritics sensitivity 발음구별 부호 자동 감도 <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. 검색어에 악센트 부호가 있는 문자(unac_except_trans에 없는)가 있으면 분음 부호 민감도를 자동으로 조정합니다. 그렇지 않고 분음 부호 민감도를 직접 지정하려면 검색어 언어와 <i>D</i> 수정자를 사용해야합니다. Automatic character case sensitivity 대소문자 자동 구분 <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p> 항목 중 첫 글자 이외에 대문자가 있으면 대소문자 구분을 자동으로 처리합니다. 그렇지 않으면 검색어 언어와 <i>C</i> 수정자를 사용하여 대소문자 구분을 지정해야합니다. Maximum term expansion count 용어 확장 최대값 <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p> 단일 용어의 최대 확장 횟수(예 : 와일드 카드 사용시). 기본값인 10,000은 합리적이며, 엔진이 그 확장된 용어 목록을 처리하는 동안 처리할 수 없는 용어는 피합니다. Maximum Xapian clauses count Xapian의 절 계수의 최대값 <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. 단일 Xapian 검색 요청에 넣을 수 있는 절 숫자의 최대값입니다. 경우에 따라 확장된 용어의 결과가 곱해질 수 있기에 과도한 메모리 사용을 피하려고합니다. 기본값 100 000은 대부분의 경우에 충분하며, 하드웨어 구성과 호환되어야합니다. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... 형태소 확장 사전을 빌드할 언어.<br>가능한 값은 Xapian 형태소 분석기 문서를 참조하십시오. 예: 영어, 프랑스어, 독일어... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. 철자 검색기 사전의 언어입니다. 값은 2 글자 언어 코드입니다 (예 : 'en', 'fr'... <br>이 값을 설정하지 않으면 NLS 환경이 언어 코드를 계산하는 데 사용되며 일반적으로 작동합니다. 시스템에 무엇이 설치되어 있는지 이해하려면 'aspell config'를 입력하고 'data-dir'디렉토리에서 .dat 파일을 찾으십시오. Indexer log file name 색인 로그 파일명 If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) 인덱싱을 중지할 디스크 전체 임계값 백분율<br>(E.g. 90% 로 설정하면 90% 도달시 멈춤, 0 또는 100 은 제한이 없는 것을 의미) Web history 웹 기록 Process the Web history queue 웹 기록 큐 처리하기 (by default, aspell suggests mispellings when a query has no results). 디폴트로, 검색에 대한 결과가 없을 때 aspell 이 올바른 맞춤법을 제시합니다 Page recycle interval 페이지 재활용 간격 <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. 디폴트로, 캐시에는 하나의 URL 인스턴스만 보관됩니다. 여러 인스턴스를 보관하는 빈도를 설정해 이를 변경할 수 있습니다 ('day', 'week', 'month', 'year'). 간격을 늘린다고 기존 항목이 지워지는 것은 아닙니다. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 참고: 최대 크기에 도달하면 이전 페이지가 지워져 새 페이지를 위한 공간을 만듭니다. 현재 크기: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types 특정 MIME만 색인 An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive 여기서 설정된 MIME 유형들만 색인합니다.<br>보통은 비어있음(비활성화). Exclude mime types 제외할 MIME 유형 Mime types not to be indexed 색인처리되지 않는 MIME 유형 Max. compressed file size (KB) 압축된 파일 용량의 최대값(KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. 이 값은 압축 파일이 처리되지 않게 할 임계 값을 설정합니다. 제한이 없으면 -1로, 압축 해제를 하지 않으려면 0으로 설정하십시오. Max. text file size (MB) 텍스트 파일 용량의 최대값(MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. 이 값은 텍스트 파일이 처리되지 않게 할 임계 값을 설정합니다. 제한이 없으면 -1로 설정하십시오. 이것은 색인에서 너무 큰 로그 파일을 제외하기 위한 것입니다. Text file page size (KB) 텍스트 파일 페이지 용량(KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). 이 값을 설정하면(-1이 아닌) 텍스트 파일이 색인을 위해 설정한 크기 단위로 분할됩니다. 이는 매우 큰 텍스트 파일(예 : 로그 파일)을 검색하는 데 도움이 됩니다. Max. filter exec. time (s) 필터 최대 실행시간(초) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. 이 값보다 오래 작동하는 외부 필터는 중단됩니다. 문서가 필터를 무한 반복하게 만들 수 있는 드문 경우에 사용됩니다(예 : 포스트 스크립트). 제한이 없으면 -1로 설정하십시오. Global 광역 CronToolW Cron Dialog 예약 대화창 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> 일괄 색인 작성 일정(예약-cron:리눅스 프로그램 실행 예약 도구-역자 주) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">각 필드는 한 개의 와일드카드(*), 단일 숫자값, 콤마로 분리된 리스트(1,3,5), 그리고 범위들(1-7)을 포함할 수 있습니다. 필드가 더 일반적으로 사용됩니다. <span style=" font-style:italic;">현재 그대로</span> crontab 파일 내부에서 전체 crontab 구문을 사용할 수 있습니다 (도움말 중 crontab (5) 참조).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />예를 들어, <span style=" font-style:italic;">날짜들 </span> 항목에 <span style=" font-family:'Courier New,courier';">*</span>를 입력하고, <span style=" font-style:italic;">시간</span> 항목에 <span style=" font-family:'Courier New,courier';">12,19</span>를 입력하고, <span style=" font-style:italic;">분</span> 항목에 <span style=" font-family:'Courier New,courier';">15</span>를 입력하면, recollindex(Recoll 색인 작성 프로그램-역자 주)은 매일 오전 12:15와 7:15 PM에 시작될 것입니다.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">일정을 활성화를 너무 자주하는 것보다는, 실시간 색인 작성이 더 효율적입니다.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) 주간 요일 지정 (* 혹은 0-7. 0이나 7은 주일) Hours (* or 0-23) 시간 (* 혹은 0-23) Minutes (0-59) 분 (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">일괄 자동 색인 작성을 중지하려면 <span style=" font-style:italic;">비활성화</span>를 클릭, 활성화 하려면<span style=" font-style:italic;">활성화</span>를 클릭, 아무 것도 변경하지 않으려면 <span style=" font-style:italic;">취소</span>를 클릭하십시오.</p></body></html> Enable 활성화 Disable 비활성화 It seems that manually edited entries exist for recollindex, cannot edit crontab recollindex를 수동으로 편집한 항목이 있으므로 crontab을 편집할 수 없습니다. Error installing cron entry. Bad syntax in fields ? cron 항목을 설치하는 중에 오류가 발생했습니다. 필드에 잘못된 구문이 있습니까? EditDialog Dialog 대화창 EditTrans Source path 소스 경로 Local path 로컬 경로 Config error 설정 오류 Original path 원본 경로 EditTransBase Path Translations 경로 변경 Setting path translations for 다음 파일을 위한 경로를 변경해주십시오 : Select one or several file types, then use the controls in the frame below to change how they are processed 하나 혹은 복수의 파일 형식을 선택한 다음, 아래 프레임의 컨트롤을 사용하여 처리 방식을 변경하십시오. Add 추가 Delete 삭제 Cancel 취소 Save 저장 FirstIdxDialog First indexing setup 최초 색인 작성 설정 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">이 구성에 대한 색인이 존재하지 않습니다.</span><br /><br />단지 적절한 기본값들로 구성하여 HOME 디렉토리를 색인하고자 한다면, <span style=" font-style:italic;">지금 색인 시작</span> 버튼을 누르십시오. 나중에 세부 항목을 조정할 수 있습니다. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">더 많은 제어가 필요한 경우 다음 링크로 따라가서 색인 구성 및 일정을 조정하십시오.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">이 도구들은 추후에 <span style=" font-style:italic;">환경설정</span> 메뉴를 통해서 접근할 수 있습니다.</p></body></html> Indexing configuration 색인 작성 구성 This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. 이것은 색인을 생성하려는 디렉토리, 제외할 파일 경로 또는 이름, 기본 문자 집합 등과 같은 기타 매개 변수를 조정하도록 도와줍니다. Indexing schedule 색인 작성 일정 This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). 이것은 일괄 혹은 실시간 색인 작성 중 선택을 돕습니다. 그리고 일괄 자동 색인 작성 일정을 설정할 수 있습니다 (cron 사용). Start indexing now 지금 색인 작성 시작 FragButs %1 not found. %1을 찾을 수 없습니다. %1: %2 %1: %2 Fragment Buttons Fragment Buttons Query Fragments 쿼리 프래그먼트 IdxSchedW Index scheduling setup 색인 예약 설정 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> 색인 작성은 실시간으로 실행되거나, 파일이 변경될 때마다 실행되거나, 불규칙한 간격으로 실행될 수 있습니다. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">설명서를 읽으면 접근 방식을 결정하는 데 도움이 될 수 있습니다 (설명서를 보시려면 F1을 누르세요-영문판만 지원). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">이 도구는 일괄 자동 색인 작성을 예약하거나, 로그인 할 때마다 실시간 색인 작성을 시작하는 것을 설정하도록 도와줍니다. 참고로, 일괄과 실시간 색인 작성을 함께 설정 가능하지만 일반적인 경우에서는 불필요합니다. </p></body></html> Cron scheduling Cron 예약 The tool will let you decide at what time indexing should run and will install a crontab entry. 이 도구는 언제 색인을 작성할지 결정하도록 돕고, crontab 내 항목을 설치합니다. Real time indexing start up 실시간 색인 작성 시작 Decide if real time indexing will be started when you log in (only for the default index). 로그인 할 때 실시간 색인 작성을 시작할 것인지 결정합니다(기본 색인에만 해당). ListDialog Dialog 대화창 GroupBox 그룹박스 Main No db directory in configuration 설정에 DB 디렉토리가 없습니다. Could not open database in Could not open database in . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. Configuration problem (dynconf Configuration problem (dynconf "history" file is damaged or un(read)writeable, please check or remove it: "history" file is damaged or un(read)writeable, please check or remove it: "history" file is damaged, please check or remove it: "history" 파일이 손상되었습니다. 점검 혹은 삭제해주세요. Needs "Show system tray icon" to be set in preferences! Preview &Search for: 검색(&S) : &Next 다음(&N) &Previous 이전(&P) Match &Case 대소문자 일치(&C) Clear 검색어 삭제 Creating preview text 미리보기 텍스트를 만드는 중입니다. Loading preview text into editor 편집기에 미리보기 텍스트를 불러오는 중입니다. Cannot create temporary directory Cannot create temporary directory Cancel 취소 Close Tab Close Tab Missing helper program: 도우미 프로그램이 없습니다 : Can't turn doc into internal representation for 이 프로그램 내에서 표시할 수 없는 문서 : Cannot create temporary directory: Cannot create temporary directory: Error while loading file Error while loading file Form 양식 Tab 1 탭 1 Open 열기 Canceled 취소됨 Error loading the document: file missing. 문서 로딩 중 오류 : 파일이 없습니다. Error loading the document: no permission. 문서 로딩 중 오류 : 권한이 없습니다. Error loading: backend not configured. 로딩 오류 : 백앤드가 구성되지 않았습니다. Error loading the document: other handler error<br>Maybe the application is locking the file ? 문서 로딩 오류 : 다른 핸들러 오류<br> 프로그램이 파일을 잠그고 있지는 않습니까? Error loading the document: other handler error. 문서 로딩 오류 : 다른 핸들러 오류 <br>Attempting to display from stored text. <br>저장되었던 텍스트 표시를 시도 중 Could not fetch stored text 저장된 텍스트를 가져올 수 없습니다. Previous result document 이전 결과 문서 Next result document 다음 결과 문서 Preview Window 미리보기 화면 Close Window Close Window Next doc in tab Next doc in tab Previous doc in tab Previous doc in tab Close tab 탭 닫기 Print tab Print tab Close preview window 미리보기 화면 닫기 Show next result 다음 결과 보기 Show previous result 이전 결과 보기 Print 인쇄 PreviewTextEdit Show fields 필드 보이기 Show main text 메인 텍스트 보이기 Print 인쇄 Print Current Preview 현재 미리보기를 인쇄 Show image 이미지 보이기 Select All 모두 선택 Copy 복사 Save document to file 문서를 파일로 저장 Fold lines 줄 내리기 Preserve indentation 들여쓰기 유지 Open document 문서 열기 Reload as Plain Text Reload as HTML QObject Global parameters 광역 환경설정 Local parameters 지역 환경설정 <b>Customised subtrees <b>폴더별 환경설정 The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. 색인 구조 중 하위 디렉터리 목록을 선택하십시오.<br>일부 환경 설정는 재정의해야합니다.<br>기본값 : 비어 있음 <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. Skipped names 생략할 이름 These are patterns for file or directory names which should not be indexed. 파일 또는 디렉토리 이름 중 색인 작성해서는 안되는 패턴을 설정합니다. Default character set Default character set This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Follow symbolic links 심볼릭 링크 따라가기 Follow symbolic links while indexing. The default is no, to avoid duplicate indexing 이중 색인 작성을 피하기 위해서, 심볼릭 링크의 본래 경로로 색인합니다. 기본값은 no. Index all file names 모든 파일 이름을 색인하기 Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true 내용들을 파악할 수 없거나 처리할 수 없는 파일들(확장자가 없거나 지원하지 않는 MIME 형식)의 이름들도 색인합니다. 기본값: true Beagle web history Beagle web history Search parameters 매개변수들을 검색 Web history 웹 기록 Default<br>character set 기본값<br>문자 설정 Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. 문자 설정은 내부적으로 문자를 인식할 수 없는 파일들을 읽을 때 사용됩니다. 예, 순수한 텍스트 파일들.<br> 기본값은 비어있으며, NLS 환경으로부터 받은 값은 사용됩니다. Ignored endings 무시할 확장자 These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). 파일 이름만 색인하고, 파일 내용은 색인하지 않습니다. (지원 안되는 MIME 유형, 압축 내 파일, 색인할 컨텐츠 없음) <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>위에 추가할 폴더는 '광역 환경설정'에서 정해준 '색인할 최상위 폴더'의 하위 폴더이어야 합니다. 위 목록 상자에서 아무것도 추가하지 않았거나, 빈 줄을 선택한다면, 아래의 설정들은 최상위 레벨에서 설정됩니다. 추가한 하위 폴더를 선택하였다면, 아래 설정한 내용은 해당 폴더에만 적용됩니다. +/- 버튼을 클릭하여 폴더들을 추가하거나 지울 수 있습니다.</i> QWidget Create or choose save directory 저장할 폴더를 만들거나 선택하십시오. Choose exactly one directory 한 개의 폴더를 선택하십시오. Could not read directory: 폴더를 읽을 수 없습니다: Unexpected file name collision, cancelling. 예기치 않은 파일 이름 충돌 발생, 취소 중. Cannot extract document: 문서를 추출할 수 없습니다. &Preview 미리보기(&P) &Open 열기(&O) Open With 함께 열기 Run Script 스크립트 실행 Copy &File Name 파일 이름 복사(&F) Copy &URL 웹 주소 복사(&U) &Write to File 파일에 기록(&W) Save selection to files 선택한 것을 파일들에 저장 Preview P&arent document/folder 상위 문서/폴더 미리보기(&a) &Open Parent document/folder 상위 문서/폴더 열기(&O) Find &similar documents 유사한 문서들 검색(&S) Open &Snippets window 문서별 검색창 열기(&S) Show subdocuments / attachments 하위 문서들/첨부내용들 보기 &Open Parent document &Open Parent document &Open Parent Folder &Open Parent Folder Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. 이제 보지 않겠습니다. RTIToolW Real time indexing automatic start 실시간 자동 색인 작성 시작 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> 색인 작성은 데몬으로서 실행할 수 있으며, 파일이 변경되었을 때, 혹은 실시간으로 작동하도록 설정할 수 있습니다. 항상 색인을 최신상태로 유지할 수 있지만, 시스템 자원은 계속해서 사용됩니다.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. 색인 작성을 데스크톱 세션과 함께 데몬으로 실행합니다. Also start indexing daemon right now. 또한 색인 작성 데몬을 지금 바로 시작합니다. Replacing: 교체: Replacing file 교체 파일 Can't create: 만들 수 없습니다: Warning 경고 Could not execute recollindex recollindex를 실행할 수 없습니다. Deleting: 삭제 중 : Deleting file 파일 삭제 중 Removing autostart 자동실행 제거 중 Autostart file deleted. Kill current process too ? 자동실행 파일을 삭제하였습니다. 현재 프로세스도 종료할까요? RclCompleterModel Hits RclMain About Recoll Recoll에 대하여 Executing: [ 실행중: [ Cannot retrieve document info from database 데이터베이스에서 문서 정보를 검색할 수 없습니다. Warning 경고 Can't create preview window 미리보기 창을 만들 수 없습니다. Query results 검색 결과 Document history 문서 기록 History data 데이터 역사 Indexing in progress: 색인 작성이 진행 중입니다: Files Files Purge 완전삭제 Stemdb 형태소 DB Closing 종료하는 중 Unknown 알려지지 않은 This search is not active any more 이 검색은 더 이상 유효하지 않습니다. Can't start query: Can't start query: Bad viewer command line for %1: [%2] Please check the mimeconf file Bad viewer command line for %1: [%2] Please check the mimeconf file Cannot extract document or create temporary file 문서를 추출하거나 임시 파일을 만들 수 없습니다. (no stemming) (형태소 없음) (all languages) (모든 언어들) error retrieving stemming languages 언어 형태소 분석 오류 Update &Index 색인 업데이트(&I) Indexing interrupted 색인 작업 멈춤 Stop &Indexing 색인 작성 중지(&I) All 모두 media 미디어 message 메세지 other 기타 presentation 프레젠테이션 spreadsheet 스프레드쉬트 text 텍스트 sorted 정렬된 filtered 필터된 External applications/commands needed and not found for indexing your file types: External applications/commands needed and not found for indexing your file types: No helpers found missing 누락된 도우미 프로그램이 없습니다. Missing helper programs 누락된 도우미 프로그램들 Save file dialog Save file dialog Choose a file name to save under Choose a file name to save under Document category filter Document category filter No external viewer configured for mime type [ 다음 MIME 형식에 대해 구성된 외부 뷰어가 없습니다: [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? %1: %2를 위한 뷰어(mimeview 파일에 기록된)를 찾을 수 없습니다. 환경설정 대화창을 시작하기 원하십니까? Can't access file: 파일 접근 불가 Can't uncompress file: 압축해제 할 수 없는 파일: Save file 파일 저장 Result count (est.) 결과 목록 갯수 (추정값) Query details 쿼리 상세보기 Could not open external index. Db not open. Check external indexes list. 외부 색인을 열 수 없습니다. DB를 열 수 없습니다. 외부 색인 목록을 확인하십시오. No results found 아무 결과가 없습니다. None 없음 Updating 업데이트 중 Done 완료 Monitor 모니터 Indexing failed 색인 작성 실패 The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone 이 인터페이스에서 현재 색인 작성 프로세스가 시작되지 않았습니다. 확인을 클릭하여 종료하거나, 취소하십시오. Erasing index 색인 삭제 Reset the index and start from scratch ? 색인을 재설정하고 다시 작성하시겠습니까? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program 검색어 요청이 진행중입니다.<br>취소되면 색인 작업 보관용량의 제한 때문에<br>프로그램이 종료될 것입니다. Error 오류 Index not open Index not open Index query error 색인 요청 오류 Indexed Mime Types Indexed Mime Types Content has been indexed for these MIME types: 다음 MIME 유형 컨텐츠가 색인되었습니다: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Can't update index: indexer running 색인을 업데이트 할 수 없습니다: 색인 작성자가 실행중입니다. Indexed MIME Types 색인된 MIME 목록 Bad viewer command line for %1: [%2] Please check the mimeview file %1: [%2]에 대한 뷰어 명령줄이 잘못되었습니다. mimeview 파일을 확인하십시오. Viewer command line for %1 specifies both file and parent file value: unsupported %1에 대한 뷰어 명령줄은 파일 및 상위 파일 값을 모두 지정합니다: 지원되지 않음 Cannot find parent document 상위 문서를 찾을 수 없습니다. Indexing did not run yet Indexing did not run yet External applications/commands needed for your file types and not found, as stored by the last indexing pass in 최근 색인 처리가 되어 저장된 일부 파일 유형에 외부(도우미) 프로그램들/명령들이 필요하지만, 찾을 수 없습니다. Index not up to date for this file. Refusing to risk showing the wrong entry. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Indexer running so things should improve when it's done Indexer running so things should improve when it's done Sub-documents and attachments 하위 문서들과 첨부 문서들 Document filter 문서 필터 Index not up to date for this file. Refusing to risk showing the wrong entry. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. The indexer is running so things should improve when it's done. 색인 작성자가 실행 중이므로, 작업이 완료되면 결과가 개선될 것입니다. The document belongs to an external indexwhich I can't update. The document belongs to an external indexwhich I can't update. Click Cancel to return to the list. Click Ignore to show the preview anyway. Click Cancel to return to the list. Click Ignore to show the preview anyway. Duplicate documents 문서들 복제하기 These Urls ( | ipath) share the same content: 이 웹 주소들( | ipath)는 같은 내용을 공유합니다: Bad desktop app spec for %1: [%2] Please check the desktop file %1: [%2]에 지정된 데스크톱 프로그램이 올바르지 않습니다. 데스크톱 파일을 확인하십시오. Bad paths 옳지 않은 경로들 Bad paths in configuration file: Bad paths in configuration file: Selection patterns need topdir 패턴 선택은 최상위 폴더를 필요로 합니다. Selection patterns can only be used with a start directory 패턴 선택은 오직 시작 폴더에서만 사용할 수 있습니다. No search 검색 안함 No preserved previous search 이전 검색을 보존하지 않음 Choose file to save 저장할 파일을 선택하세요. Saved Queries (*.rclq) 저장된 쿼리들 (*.rclq) Write failed 쓰기 실패 Could not write to file 파일을 기록할 수 없습니다. Read failed 읽기 실패 Could not open file: 파일을 열 수 없습니다: Load error 불러오기 오류 Could not load saved query 저장된 쿼리들을 불러올 수 없습니다. Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. 임시 복사본을 여는 중입니다. 다른 곳에 저장하지 않으면<br/>변경내용이 손실됩니다. Do not show this warning next time (use GUI preferences to restore). 이 경고를 다음부터 띄우지 않습니다(GUI 설정에서 변경할 수 있습니다). Disabled because the real time indexer was not compiled in. 비활성화. 실시간 색인 기록자가 컴파일되지 않음. This configuration tool only works for the main index. 이 설정 도구는 오직 주 색인에서만 작동합니다. The current indexing process was not started from this interface, can't kill it The current indexing process was not started from this interface, can't kill it The document belongs to an external index which I can't update. 그 문서는 업데이트 할 수 없는 외부 색인에 속해있습니다. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Index scheduling 색인 예약 Sorry, not available under Windows for now, use the File menu entries to update the index 죄송합니다, 지금 윈도우에서는 지원되지 않습니다. 색인을 업데이트 하려면 '파일' 메뉴를 사용하십시오. Can't set synonyms file (parse error?) 동의어 파일을 설정할 수 없습니다(구문 분석 오류?). Index locked 잠긴 색인 Unknown indexer state. Can't access webcache file. 인식할 수 없는 색인 작성기 상태. 웹 캐시 파일에 접근할 수 없습니다. Indexer is running. Can't access webcache file. 색인 작성이 진행되고 있습니다. 웹 캐시 파일에 접근할 수 없습니다. with additional message: 부가 메세지 : Non-fatal indexing message: 치명적이지 않은 색인 작성 메세지: Types list empty: maybe wait for indexing to progress? 형식 리스트가 비어있습니다: 색인 작성이 완료될 때까지 기다려주십시오. Viewer command line for %1 specifies parent file but URL is http[s]: unsupported %1를 위한 뷰어 명령줄은 상위 파일 특정짓지만, http[s]는 지원되지 않습니다. Tools 도구 Results 검색 결과 (%d documents/%d files/%d errors/%d total files) (%d documents/%d files/%d errors/%d total files) (%d documents/%d files/%d errors) (%d documents/%d files/%d errors) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): 비어있거나 존재하지 않는 경로가 환경설정 파일에 있습니다. 그래도 색인 작성을 시작하려면 OK를 클릭하십시오(비어있는 데이터는 인덱스에서 지워지지 않습니다). Indexing done 색인 작성 완료 Can't update index: internal error 색인을 업데이트 할 수 없습니다: 내부 오류 Index not up to date for this file.<br> 이 파일에 대해 색인이 업데이트 되지 않았습니다. <em>Also, it seems that the last index update for the file failed.</em><br/> <em>또한, 최근 그 파일에 대한 색인 업데이트가 실패한 것으로 보입니다.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> OK를 클릭하여 이 파일에 대한 색인 업데이트를 시도하십시오. 검색은 색인 작성이 끝난 후에 다시 해야할 것입니다. Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> 목록으로 돌아가려면 'Cancel'을 클릭하십시오.<br>그래도 미리보기를 표시하려면 'Ignore'을 클릭하십시오(이 세션을 기억하십시오). 이것은 잘못된 항목을 보여줄 위험이 있습니다.<br/> documents 문서들 document 문서 files 파일들 file 파일 errors 오류들 error 오류 total files) 파일 총계) No information: initial indexing not yet performed. 정보가 없습니다: 색인 작성이 한 번도 진행되지 않았습니다. Batch scheduling 일괄 작업 예약 The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. 이 도구를 사용하면 색인 작성 시간을 결정할 수 있습니다. Windows 작업 스케줄러를 사용합니다. Confirm 승인 Erasing simple and advanced search history lists, please click Ok to confirm 단순, 혹은 고급 검색 기록들을 지우려면, 확인을 클릭하여 승인하십시오. Could not open/create file 파일 열기/만들기 불가 F&ilter 필터(&i) Could not start recollindex (temp file error) Could not start recollindex (temp file error) Could not read: Could not read: This will replace the current contents of the result list header string and GUI qss file name. Continue ? This will replace the current contents of the result list header string and GUI qss file name. Continue ? You will need to run a query to complete the display change. You will need to run a query to complete the display change. Simple search type Simple search type Any term 하나라도 포함 All terms 모두 포함 File name 파일 이름 Query language 쿼리 언어 Stemming language 형태소 언어 Main Window 메인 화면 Focus to Search Focus to Search Focus to Search, alt. Focus to Search, alt. Clear Search Clear Search Focus to Result Table Focus to Result Table Clear search 검색 화면 지우기 Move keyboard focus to search entry 키보드 포커스를 검색창으로 이동 Move keyboard focus to search, alt. 키보드 포커스를 검색창으로 이동, 표식 표시 아닐때. Toggle tabular display 표식 표시 전환 Move keyboard focus to table 키보드 포커스를 테이블로 이동 Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page 이전 페이지 Next page 다음 페이지 &File 파일(&F) E&xit 종료(&x) &Tools 도구(&T) &Help 도움말(&H) &Preferences 환경설정(&P) Search tools Search tools Result list 결과 목록 &About Recoll Recoll에 대하여(&A) Document &History 문서 기록(&H) Document History 문서 기록 &Advanced Search 고급검색(&A) Advanced/complex Search 고급/복합 검색 &Sort parameters 매개변수 정렬(&S) Sort parameters 매개변수 정렬 Next page of results 다음 페이지 결과들 Previous page of results 검색 결과의 이전 페이지 &Query configuration &Query configuration &User manual (영어)사용자 메뉴얼(&U) Recoll Recoll Ctrl+Q Ctrl+Q Update &index 색인 업데이트(&i) Term &explorer 용어 탐색기(&e) Term explorer tool 용어 탐색기 도구 External index dialog 외부 색인 대화창 &Erase document history 문서 기록 삭제(&E) First page 첫 페이지 Go to first page of results 검색결과의 첫 페이지로 가기 &Indexing configuration &색인 작성 구성 All 모두 &Show missing helpers &Show missing helpers PgDown 페이지 다운 Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp 페이지 업 &Full Screen 전체화면(&F) F11 F11 Full Screen 전체화면 &Erase search history 검색 기록 삭제(&E) sortByDateAsc sortByDateAsc Sort by dates from oldest to newest 날짜 오름차순 정렬 sortByDateDesc sortByDateDesc Sort by dates from newest to oldest 날짜 내림차순 정렬 Show Query Details 쿼리 상세보기 Show results as table Show results as table &Rebuild index 색인 재구축(&R) &Show indexed types &Show indexed types Shift+PgUp Shift+페이지 업 &Indexing schedule &색인 작성 일정 E&xternal index dialog 외부 색인 대화창(&E) &Index configuration 색인 구성(&I) &GUI configuration GUI 환경설정(&G) &Results 검색 결과(&R) Sort by date, oldest first 날짜 오름차순 정렬 Sort by date, newest first 날짜 내림차순 정렬 Show as table 표 형식으로 보여주기 Show results in a spreadsheet-like table 표 형식으로 검색 결과들을 보여주기 Save as CSV (spreadsheet) file CSV (스프레드쉬트) 파일로 저장 Saves the result into a file which you can load in a spreadsheet 검색 결과를 읽어올 수 있는 스프레드쉬트 파일로 저장합니다. Next Page 다음 페이지 Previous Page 이전 페이지 First Page 첫 페이지 Query Fragments 쿼리 프래그먼트 With failed files retrying 파일들 재시도 실패 Next update will retry previously failed files 이전에 실패한 파일들을 다음 업데이트 시 재시도 Save last query 최근 검색어 저장 Load saved query 저장된 검색어 불러오기 Special Indexing 특별한 색인 Indexing with special options 특별한 옵션과 함께 색인 작성 Indexing &schedule 색인 작성 예약(&s) Enable synonyms 동의어들 활성화 &View 보기(&V) Missing &helpers 누락된 도우미 프로그램들(&h) Indexed &MIME types 색인된 MIME 유형들(&M) Index &statistics 색인 현황(&s) Webcache Editor 웹 캐시 편집기 Trigger incremental pass 증가분 패스 시도 E&xport simple search history 단순 검색어 기록 내보내기(&x) Use default dark mode Use default dark mode Dark mode Dark mode &Query 쿼리(&Q) Increase results text font size 결과 화면 글꼴 키우기 Increase Font Size Decrease results text font size 결과 화면 글꼴 줄이기 Decrease Font Size Start real time indexer Query Language Filters Filter dates 날짜 필터 Assisted complex search Filter birth dates RclTrayIcon Restore 복구 Quit 종료 RecollModel Abstract 발췌 Author 저자 Document size 문서 크기 Document date 문서 날짜 File size 파일 크기 File name 파일 이름 File date 파일 날짜 Ipath I경로 Keywords 키워드들 Mime type Mime type Original character set 본래 문자 집합 Relevancy rating 관련성 등급 Title 제목 URL 웹 주소 Mtime M시간 Date 날짜 Date and time 날짜와 시간 Ipath I경로 MIME type MIME 유형 Can't sort by inverse relevance 관련성 역순으로 정렬할 수 없습니다. ResList Result list 결과 목록 Unavailable document 사용할 수 없는 문서 Previous 이전 Next 다음 <p><b>No results found</b><br> <p><b>아무 결과도 없습니다</b><br> &Preview 미리보기(&P) Copy &URL 웹 주소 복사(&U) Find &similar documents 유사한 문서들 검색(&S) Query details 쿼리 상세보기 (show query) (쿼리 보기) Copy &File Name 파일 이름 복사(&F) filtered 필터된 sorted 정렬된 Document history 문서 기록 Preview 미리보기 Open 열기 <p><i>Alternate spellings (accents suppressed): </i> <p><i>대체 철자들 (엑센트 무시): </i> &Write to File 파일에 기록(&W) Preview P&arent document/folder 상위 문서/폴더 미리보기(&a) &Open Parent document/folder 상위 문서/폴더 열기(&O) &Open 열기(&O) Documents 문서 번호 out of at least , 총 갯수는 최소 for <p><i>Alternate spellings: </i> <p><i>대체 철자들: </i> Open &Snippets window 문서별 검색창 열기(&S) Duplicate documents 문서들 복제하기 These Urls ( | ipath) share the same content: 이 웹 주소들( | ipath)는 같은 내용을 공유합니다: Result count (est.) 결과 계수 (추정값) Snippets 문서별 검색창 This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort 정렬 초기화(&R) &Delete column 줄 삭제(&D) Add " Add " " column " column Save table to CSV file 표를 CSV 파일로 저장 Can't open/create file: 파일을 열거나 만들 수 없습니다: &Preview 미리보기(&P) &Open 열기(&O) Copy &File Name 파일 이름 복사(&F) Copy &URL 웹 주소 복사(&U) &Write to File 파일에 기록(&W) Find &similar documents 유사한 문서들 검색(&S) Preview P&arent document/folder 상위 문서/폴더 미리보기(&a) &Open Parent document/folder 상위 문서/폴더 열기(&O) &Save as CSV CSV로 저장(&S) Add "%1" column "%1"줄 추가 Result Table 결과 테이블 Open 열기 Open and Quit Open and Quit Preview 미리보기 Show Snippets Show Snippets Open current result document 현재 결과 문서 열기 Open current result and quit 현재 결과 문서 열고 종료 Show snippets 부분 발췌 보기 Show header 헤더 보이기 Show vertical header 수직 헤더 보이기 Copy current result text to clipboard 현재 결과 텍스트를 클립보드에 복사하기 Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview 미리보기(&P) &Open 열기(&O) Copy &File Name 파일 이름 복사(&F) Copy &URL 웹 주소 복사(&U) &Write to File 파일에 기록(&W) Find &similar documents 유사한 문서들 검색(&S) Preview P&arent document/folder 상위 문서/폴더 미리보기(&a) &Open Parent document/folder 상위 문서/폴더 열기(&O) ResultPopup &Preview 미리보기(&P) &Open 열기(&O) Copy &File Name 파일 이름 복사(&F) Copy &URL 웹 주소 복사(&U) &Write to File 파일에 기록(&W) Save selection to files 선택한 것을 파일들에 저장 Preview P&arent document/folder 상위 문서/폴더 미리보기(&a) &Open Parent document/folder 상위 문서/폴더 열기(&O) Find &similar documents 유사한 문서들 검색(&S) Open &Snippets window 문서별 검색창 열기(&S) Show subdocuments / attachments 하위 문서들/첨부내용들 보기 Open With 함께 열기 Run Script 스크립트 실행 SSearch Any term 하나라도 포함 All terms 모두 포함 File name 파일 이름 Completions Completions Select an item: Select an item: Too many completions Too many completions Query language 쿼리 언어 Bad query string 옳지 않은 검색어 명령 문자열 Out of memory 메모리 초과 Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter file name wildcard expression. 파일 이름 와일드 카드 표현식을 입력하십시오. Enter search terms here. Type ESC SPC for completions of current term. Enter search terms here. Type ESC SPC for completions of current term. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. 쿼리 언어 표현식을 입력하십시오:<br> <i>용어1 용어2</i> : 어느 곳에 있든 '용어1'과 '용어2'.<br> <i>필드:용어1</i> : '용어1'은 '필드'필드에 있음.<br> 일반적인 필드 이름/동음어들:<br> 제목/주제/표제, 저자/출처, 수취인/발송지, 파일이름, 그 외.<br> 유사 필드: 폴더, MIME/확장자, 형식/rclcat, 날짜, 크기.<br> 두 날짜 간격 예제들: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>용어1 용어2 OR 용어3</i> : 용어1 AND (용어2 OR 용어3).<br> 이것들을 명확하게 만들기 위해 매개변수를 사용할 수 있습니다.<br> <i>"용어1 용어2"</i> : 어구 (정확히 발생해야 합니다). 가능한 수정자:<br> <i>"용어1 용어2"p</i> : 기본 거리를 사용한 정렬되지 않은 근접 검색<br> 결과에 대해 의심스럽다면 <b>"쿼리 보기"</b> 링크를 사용하십시오. 더 자세한 내용을 위해 메뉴얼을 참고할 수 있습니다(&lt;F1>). Stemming languages for stored query: 저장된 쿼리에 대한 형태소 언어들 differ from current preferences (kept) 현재 환경설정과 다르게 유지합니다. Auto suffixes for stored query: 저장된 쿼리에 대한 자동 접미사: External indexes for stored query: 저장된 쿼리에 대한 외부 색인: Autophrase is set but it was unset for stored query 자동 어구가 설정되지만, 저장된 쿼리에는 설정되지 않습니다. Autophrase is unset but it was set for stored query 자동 어구는 설정되지 않습니다만, 저장된 쿼리에 대해서는 설정합니다. Enter search terms here. 여기에 검색 용어를 입력하세요. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border-collapse: collapse; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>쿼리 언어 힌트. 결과에 대해 의심스럽다면 <b>쿼리 보기</b>.&nbsp; 링크를 사용하십시오. You should really look at the manual (F1)</p> 꼭 메뉴얼을 보셔야 합니다.(F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>어떤</th><th>예제들</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>And</td><td>하나 둘&nbsp;&nbsp;&nbsp;하나 AND 둘&nbsp;&nbsp;&nbsp;하나 && 둘</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Or</td><td>하나 OR 둘&nbsp;&nbsp;&nbsp;하나 || 둘</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>복잡한 불리언(boolean). 'OR'이 우선권을 가지니, 괄호를 사용하세요.&nbsp; where needed</td><td>(one AND two) OR three</td></tr> 필요한 곳에</td><td>(하나 AND 둘) OR 셋</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Not</td><td>-용어</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>문구</td><td>"자존심 and 편견"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>정렬된 근접성(slack:느슨함=1)</td><td>"자존심 편견"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>정렬되지 않은 근접성 (slack:느슨함=1)</td><td>"편견 자존심"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>정렬되지 않은 근접성. (기본 slack:느슨함 값=10)</td><td>"편견&nbsp;자존심"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>형태소 확장 없음: 대문자화가 </td><td>너무 많습니다.</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>특정 필드</td><td>저자:오스틴&nbsp;&nbsp;제목:편견</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND 내부 필드 (정렬 없음 )</td><td>저자:제인,오스틴</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR 내부 필드</td><td>저자:오스틴/브론테</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>필드 이름들</td><td>제목/주제/표제&nbsp;&nbsp;저자/출처<br>수취인/수신자&nbsp;&nbsp;파일이름&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>폴더 경로 필터</td><td>폴더:/home/me&nbsp;&nbsp;폴더:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME 유형 필터</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>날짜 간격</td><td>날짜:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> 날짜:2018&nbsp;&nbsp;날짜:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>용량</td><td>용량&gt;100k 용량&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Can't open index Could not restore external indexes for stored query:<br> Could not restore external indexes for stored query:<br> ??? ??? Using current preferences. Using current preferences. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase S검색 베이스 Clear 목록 지우기 Ctrl+S Ctrl+S Erase search entry 검색 항목 지우기 Search 검색 Start query 검색어 요청 시작 Enter search terms here. Type ESC SPC for completions of current term. Enter search terms here. Type ESC SPC for completions of current term. Choose search type. 검색 형식을 지정해주세요. Show query history 검색어 요청 기록 보기 Enter search terms here. 여기에 검색 용어를 입력하세요. Main menu Main menu SearchClauseW SearchClauseW SearchClauseW Any of these Any of these All of these All of these None of these None of these This phrase This phrase Terms in proximity Terms in proximity File name matching File name matching Select the type of query that will be performed with the words 검색을 수행할 검색어 요청의 유형을 선택해주세요. Number of additional words that may be interspersed with the chosen ones 선택한 단어와 함께 산재시킬 수 있는 추가적인 단어들의 숫자 In field In field No field 필드 없음 Any 하나라도 All 모두 None 없음 Phrase 어구(Phrase) Proximity 근사치 File name 파일 이름 Snippets Snippets 문서별 검색창 X X Find: 찾기: Next 다음 Prev 이전 SnippetsW Search 검색 <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>죄송합니다. 한도 내에서 정확히 일치하는 것을 발견하지 못했습니다. 아마도 문서가 매우 크거나, 스니펫 생성기가 미로에서 길을 잃었을 것입니다...</p> Sort By Relevance 관련성 별로 정리 Sort By Page 페이지 별로 정리 Snippets Window 부분 발췌 화면 Find 찾기 Find (alt) 찾기 (alt) Find Next Find Next Find Previous Find Previous Hide Hide Find next 다음 찾기 Find previous 이전 찾기 Close window 창 닫기 SortForm Date 날짜 Mime type Mime type SortFormBase Sort Criteria Sort Criteria Sort the Sort the most relevant results by: most relevant results by: Descending Descending Close 닫기 Apply Apply SpecIdxW Special Indexing 특별한 색인 작성 Do not retry previously failed files. Do not retry previously failed files. Else only modified or failed files will be processed. 체크하지 않으면 수정되거나 실패한 파일만 처리됩니다. Erase selected files data before indexing. 색인 작업 전에 선택된 파일들의 데이터를 지웁니다. Directory to recursively index Directory to recursively index Browse 탐색 Start directory (else use regular topdirs): Start directory (else use regular topdirs): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. 모든 파일을 선택하려면 비워 두십시오. 공백으로 구분된 쉘 유형 패턴을 여러 개 사용할 수 있습니다.<br>공간이 포함된 패턴은 큰 따옴표로 묶어야합니다.<br>시작 대상이 설정된 경우에만 사용할 수 있습니다. Selection patterns: 패턴 선택 Top indexed entity 최상위 색인된 항목 Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). 재귀적으로 색인할 폴더입니다. 최상위 폴더에 있는 환경설정 파일에 정의된대로<br> 정규 색인 영역 안에 있어야합니다. Retry previously failed files. 이전에 실패한 파일을 재시도하기. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Must be part of the indexed tree. Use full indexed area if empty. 폴더를 지정하십시오. 색인된 폴더들 중 하나여야합니다. 만일 지정하지 않는다면, 전체 색인 영역이 사용됩니다. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). 진단결과 파일. 지정한 위치에 진단결과(파일이 인덱싱되지 않은 이유) 파일이 생성됩니다. Diagnostics file 진단결과 파일 SpellBase Term Explorer 용어 탐색기 &Expand 확장(&E) Alt+E Alt+E &Close 닫기(&C) Alt+C Alt+C Term 용어 No db info. DB정보 없음 Doc. / Tot. 문서 / 총계 Match 일치 Case 대소문자 Accents 액센트 SpellW Wildcards 와일드카드 Regexp 정규식 Spelling/Phonetic 철자/음성 Aspell init failed. Aspell not installed? Aspell init failed. Aspell not installed? Aspell expansion error. Aspell expansion error. Stem expansion 용어 확장 error retrieving stemming languages 언어 형태소 분석 오류 No expansion found 발견된 확장이 없음 Term 용어 Doc. / Tot. 문서 / 총계 Index: %1 documents, average length %2 terms Index: %1 documents, average length %2 terms Index: %1 documents, average length %2 terms.%3 results 색인: %1 문서, 평균 길이 %2 용어들.%3 결과들 %1 results %1 결과들 List was truncated alphabetically, some frequent 목록이 알파벳 순으로 잘렸습니다. terms may be missing. Try using a longer root. 용어가 아마 누락된 것 같습니다. 더 긴 어근을 사용하여 시도하십시오. Show index statistics 색인 현황 보기 Number of documents 문서 개수 Average terms per document 문서당 평균 용어 개수 Smallest document length Smallest document length Longest document length Longest document length Database directory size 데이터베이스 폴더 크기 MIME types: 색인된 MIME 유형: Item 아이템 Value Smallest document length (terms) 가장 작은 문서 길이(용어 개수) Longest document length (terms) 가장 긴 문서 길이(용어 개수) Results from last indexing: 최근 색인 작성 결과들 Documents created/updated 만들어진/업데이트된 문서들 Files tested 테스트된 파일들 Unindexed files 색인되지 않은 파일들 List files which could not be indexed (slow) 색인할 수 없는 파일들 목록(느림) Spell expansion error. 철자 오류입니다. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index 선택한 디렉토리는 Xapian index가 아닌 것으로 보입니다. This is the main/local index! 이것은 주/지역 색인입니다! The selected directory is already in the index list 선택한 폴더는 이미 색인 목록에 있습니다. Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) error retrieving stemming languages 형태소 분석 언어 오류 Choose 선택 Result list paragraph format (erase all to reset to default) 결과 목록 단락 형식 (모두 삭제하여 기본값으로 재설정) Result list header (default is empty) 결과 목록 헤더 (기본값은 비어 있음) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) recoll 환경설정 폴더 또는 xapian 색인 폴더를 선택하십시오 (예 : /home/me/.recoll 또는 /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read 선택한 디렉토리는 Recoll 환경설정 디렉토리처럼 보이지만 환경설정을 읽을 수 없습니다. At most one index should be selected 한 개의 색인만 선택되어야 합니다. Cant add index with different case/diacritics stripping option 다른 대소문자 / 분음 부호 제거 옵션으로 색인을 추가 할 수 없습니다. Default QtWebkit font QtWebkit 기본 폰트 Any term 하나라도 포함 All terms 모두 포함 File name 파일 이름 Query language 쿼리 언어 Value from previous program exit 이전 프로그램 종료 값 Context 컨텍스트 Description 설명 Shortcut 단축키 Default 기본값 Choose QSS File QSS 파일 선택하기 Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface 사용자 인터페이스 Number of entries in a result page 결과 페이지 당 표시할 자료 갯수 Result list font 검색 결과 목록 폰트 Helvetica-10 Helvetica-10 Opens a dialog to select the result list font 결과 목록 폰트를 선택하기 위해 검색창을 엽니다. Reset 재설정 Resets the result list font to the system default 검색 결과 목록 폰트를 시스템 기본값으로 재설정 Auto-start simple search on whitespace entry. Auto-start simple search on whitespace entry. Start with advanced search dialog open. Recoll을 시작할 때마다 고급 검색창을 엽니다. Start with sort dialog open. Start with sort dialog open. Search parameters 매개변수들을 검색 Stemming language 형태소 언어 Dynamically build abstracts 검색 결과에 나타나는 문서 내용에 검색어를 표시합니다. Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. 검색 결과에서, 검색된 단어들을 문맥적으로 표시하도록 할까요? 큰 문서들은 아마 느려질 것입니다. Replace abstracts from documents 문서들로부터 검색된 단어들을 재배치합니다. Do we synthetize an abstract even if the document seemed to have one? 검색어가 문서 내 단 하나만 있어도 추출한 단어를 합성할까요?(무슨 기능인지 잘 모르겠음-역자 주) Synthetic abstract size (characters) 결과 보기의 미리보기 글자 수 분량 Synthetic abstract context words 문서별 검색창의 미리보기 단어 수 External Indexes 외부 색인들 Add index 색인 추가 Select the xapiandb directory for the index you want to add, then click Add Index Select the xapiandb directory for the index you want to add, then click Add Index Browse 탐색 &OK &OK Apply changes 변경사항 적용 &Cancel 취소(&C) Discard changes 변경 내역 취소 Result paragraph<br>format string Result paragraph<br>format string Automatically add phrase to simple searches 단순한 검색을 수행할 시, 자동으로 검색어를 추가합니다. A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. [rolling stones]을 검색했다면, (2 음절들)은 [rolling] 혹은 [stones] 혹은 [rolling (절 2개) stones]로 변환됩니다. 입력한 검색어대로 정확히 보여주는 결과에 더 높은 우선순위가 부여됩니다. User preferences User preferences Use desktop preferences to choose document editor. Use desktop preferences to choose document editor. External indexes External indexes Toggle selected 선택된 항목 Activate All 모두 활성화 Deactivate All 모두 비활성화 Remove selected 선택 삭제 Remove from list. This has no effect on the disk index. 목록에서 삭제. 디스크 색인에 영향 없음. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Remember sort activation state. 검색 결과창의 정렬 기준을 기억합니다. Maximum text size highlighted for preview (megabytes) 미리보기를 위한 강조된 글자 수 크기의 최대값(MB) Texts over this size will not be highlighted in preview (too slow). 이 크기를 초과하는 글자들은 미리보기에서 강조(Highlight)가 되지 않습니다. Highlight color for query terms Highlight color for query terms Prefer Html to plain text for preview. 미리보기에서 텍스트보다 HTML을 우선합니다. If checked, results with the same content under different names will only be shown once. 이 옵션을 선택하면, 이름이 달라도 내용이 동일할 경우 결과가 한 번만 표시됩니다. Hide duplicate results. 중복된 결과들을 숨깁니다. Choose editor applications MIME별 실행 프로그램 선택 창 Display category filter as toolbar instead of button panel (needs restart). Display category filter as toolbar instead of button panel (needs restart). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. 목록 내 단어들은 검색어 언어 항목 안에서 자동으로 'ext"xxx' 절들로 전환됩니다. Query language magic file name suffixes. 언어 매직 파일 이름 접미사를 검색하십시오. Enable 활성화 ViewAction Changing actions with different current values Changing actions with different current values Mime type Mime type Command 명령 MIME type MIME 타입 Desktop Default 데스크톱 기본값 Changing entries with different current values 현재 값과 다른 항목을 변경함 ViewActionBase File type File type Action Action Select one or several file types, then click Change Action to modify the program used to open them Select one or several file types, then click Change Action to modify the program used to open them Change Action Change Action Close 닫기 Native Viewers 내장 뷰어들 Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Select one or several mime types then use the controls in the bottom frame to change how they are processed. 하나 또는 여러 개의 MIME 유형을 선택한 다음, 창 하단에 있는 명령을 수정하여 파일 실행 방식을 변경할 수 있습니다. Use Desktop preferences by default 데스크톱 설정들을 기본값으로 사용합니다. Select one or several file types, then use the controls in the frame below to change how they are processed 하나 혹은 복수의 파일 형식을 선택한 다음, 아래 프레임의 컨트롤을 사용하여 처리 방식을 변경하십시오. Exception to Desktop preferences 기본값을 사용하지 않으려면 선택하십시오. Action (empty -> recoll default) 명령 (비어있음 -> Recoll 기본값) : Apply to current selection 명령을 선택한 곳에 적용합니다(기본값을 사용하지 않아야 적용됩니다). Recoll action: 현재 명령 : current value 현재값 Select same 같은 것 선택 <b>New Values:</b> <b>새 값:</b> Webcache Webcache editor 웹 캐시 편집기 Search regexp 정규식 검색 TextLabel WebcacheEdit Copy URL 웹 주소 복사 Unknown indexer state. Can't edit webcache file. 인덱서의 상태를 알 수 없습니다. 웹 캐시 파일을 수정할 수 없습니다. Indexer is running. Can't edit webcache file. 인덱서가 실행중입니다. 웹 캐시 파일을 수정할 수 없습니다. Delete selection 선택 삭제 Webcache was modified, you will need to run the indexer after closing this window. 웹 캐시가 수정되었으므로 이 창을 닫은 후에 인덱서를 실행해야 합니다. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url 웹 주소 Date 날짜 Size URL 웹 주소 WinSchedToolW Error 오류 Configuration not initialized 구성이 초기화되지 않았습니다. <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll 색인 일괄 예약</h3><p>우리는 이 작업을 위해 기본적으로 '윈도우즈 작업 스케쥴러'를 사용합니다. 아래 버튼을 클릭하여 스캐쥴러를 시작할 수 있습니다.</p><p>당신은 오른쪽의 메뉴 안에 <i>작업 만들기</i>로 전체 인터페이스를 사용할 수 있으며, 혹은 간단하게 <i> 기본 작업 만들기</i> 마법사를 사용할 수 있습니다.<p> 작업을 수행하기 위하여 아래 나열된 일괄 파일 경로를 복사/붙여넣기 하십시오. Command already started 명령이 이미 시작되었습니다. Recoll Batch indexing Recoll 일괄 색인 Start Windows Task Scheduler tool 윈도우즈 작업 스캐쥴러 도구 시작 Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Steal Beagle indexing queue Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Web cache directory name Web cache directory name The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Max. size for the web cache (MB) Max. size for the web cache (MB) Entries will be recycled once the size is reached Entries will be recycled once the size is reached Web page store directory name 웹 페이지 저장소 디렉토리 이름 The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. 방문한 웹 페이지의 사본을 저장할 디렉토리의 이름.<br>상대적인 경로인 경우 환경설정 폴더를 기준으로합니다. Max. size for the web store (MB) 웹 저장소의 최대 용량(MB) Process the WEB history queue 웹 히스토리 큐 처리 Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Firefox 방문 페이지 색인 작성을 활성화합니다.<br>(Firefox Recoll 플러그인도 설치필요) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). 용량이 한계에 도달하면 항목들이 재활용됩니다.<br>값을 줄이면 기존 파일이 잘리지 않기 때문에 크기를 늘리는 것만으로도 의미가 있습니다(끝 공간만 낭비됩니다). confgui::ConfIndexW Can't write configuration file 환경설정 파일을 쓸 수 없습니다 Recoll - Index Settings: Recoll - 색인 설정: confgui::ConfParamFNW Browse 탐색 Choose 선택 confgui::ConfParamSLW + + - - Add entry 항목 추가 Delete selected entries 선택된 항목 삭제 ~ ~ Edit selected entries 선택된 항목 편집 confgui::ConfSearchPanelW Automatic diacritics sensitivity 발음구별 부호 자동 감도 <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. 검색어에 악센트 부호가 있는 문자(unac_except_trans에 없는)가 있으면 분음 부호 민감도를 자동으로 조정합니다. 그렇지 않고 분음 부호 민감도를 직접 지정하려면 검색어 언어와 <i>D</i> 수정자를 사용해야합니다. Automatic character case sensitivity 대소문자 자동 구분 <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p> 항목 중 첫 글자 이외에 대문자가 있으면 대소문자 구분을 자동으로 처리합니다. 그렇지 않으면 검색어 언어와 <i>C</i> 수정자를 사용하여 대소문자 구분을 지정해야합니다. Maximum term expansion count 용어 확장 최대값 <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p> 단일 용어의 최대 확장 횟수(예 : 와일드 카드 사용시). 기본값인 10,000은 합리적이며, 엔진이 그 확장된 용어 목록을 처리하는 동안 처리할 수 없는 용어는 피합니다. Maximum Xapian clauses count Xapian의 절 계수의 최대값 <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. 단일 Xapian 검색 요청에 넣을 수 있는 절 숫자의 최대값입니다. 경우에 따라 확장된 용어의 결과가 곱해질 수 있기에 과도한 메모리 사용을 피하려고합니다. 기본값 100 000은 대부분의 경우에 충분하며, 하드웨어 구성과 호환되어야합니다. confgui::ConfSubPanelW Global 광역 Max. compressed file size (KB) 압축된 파일 용량의 최대값(KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. 이 값은 압축 파일이 처리되지 않게 할 임계 값을 설정합니다. 제한이 없으면 -1로, 압축 해제가 없으면 0으로 설정하십시오. Max. text file size (MB) 텍스트 파일 용량의 최대값(MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. 이 값은 텍스트 파일이 처리되지 않게 할 임계 값을 설정합니다. 제한이 없으면 -1로 설정하십시오. 이것은 색인에서 너무 큰 로그 파일을 제외하기위한 것입니다. Text file page size (KB) 텍스트 파일 페이지 용량(KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). 이 값을 설정하면(-1이 아닌) 텍스트 파일이 색인을 위해 설정한 크기 단위로 분할됩니다. 이는 매우 큰 텍스트 파일(예 : 로그 파일)을 검색하는 데 도움이 됩니다. Max. filter exec. time (S) Max. filter exec. time (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. 이 값보다 오래 작동하는 외부 필터는 중단됩니다. 문서가 필터를 무한 반복하게 만들 수 있는 드문 경우에 사용됩니다(예 : 포스트 스크립트). 제한이 없으면 -1로 설정하십시오. Only mime types 오직 MIME 유형만 An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive 색인 처리된 MIME 유형들의 독점 목록입니다.<br>다른 것들은 색인되지 않을 것입니다. 보통 비어있음, 비활성화. Exclude mime types 제외된 MIME 유형들 Mime types not to be indexed 색인처리되지 않는 MIME 유형 Max. filter exec. time (s) 필터 최대 실행시간 confgui::ConfTopPanelW Top directories 최상위 폴더들 The list of directories where recursive indexing starts. Default: your home. 재귀 색인 작성이 시작되는 폴더 목록. 기본값 : home. Skipped paths 건너뛴 경로들 These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Stemming languages 형태소 언어 The languages for which stemming expansion<br>dictionaries will be built. 형태소 확장 사전을 만들 언어가<br>작성됩니다. Log file name 로그 파일 이름 The file where the messages will be written.<br>Use 'stderr' for terminal output 메시지가 기록 될 파일입니다.<br>터미널 출력에 'stderr'을 사용하십시오. Log verbosity level 로그 상세 수준 This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. 이 값은 메시지의 분량을<br>오류에서 많은 디버깅 데이터에 이르기까지 조정합니다. Index flush megabytes interval 색인 정비 간격(MB) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB 이 값은 색인되는 데이터의 양을 적절한 값으로 조정합니다.<br>색인 작업자가 메모리 사용을 제어하는 데 도움이 됩니다. 기본 10MB Max disk occupation (%) Max disk occupation (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). No aspell usage 철자 사용법 없음 Aspell language 철자 언어 The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Database directory name 데이터베이스 폴더 이름 The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Use system's 'file' command Use system's 'file' command Use the system's 'file' command if internal<br>mime type identification fails. Use the system's 'file' command if internal<br>mime type identification fails. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. 용어 탐색기 도구에서 유사한 철자를 발생시키는 철자 사용을 비활성화 합니다. <br> 철자가 없거나 작동하지 않는 경우에 유용합니다. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. 철자 사전의 언어입니다. 이것은 'en'이나 'fr' 등으로 나타나야 합니다.<br>만약 이 값이 지정되어있지 않다면, NLS 환경설정이 일반적으로 사용되는 값을 찾을 것입니다. 시스템에 무엇이 설치되어 있는지 이해하려면 'aspell config'를 입력하고 'data-dir'디렉토리에서 .dat 파일을 찾으십시오. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. 색인을 저장할 폴더 이름<br>상대 경로는 환경설정 폴더를 기본으로 합니다. 기본값은 'xapiandb'입니다. Unac exceptions Unac 예외 <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p> 이것은 기본적으로 모든 분음 부호를 제거하고 정식 분해를 수행하는 unac 메커니즘에 대한 예외를 설정합니다. 언어에 따라 일부 문자의 강조를 무시, 추가, 분해를 지정할 수 있습니다(예 : 합자의 경우. 공백으로 구분된 각 항목에서 첫 번째 문자는 원본이고 나머지는 번역입니다). These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') 색인 작업이 실행되지 않게 될 폴더 경로들입니다.<br>경로에는 와일드 카드가 포함될 수 있습니다. 항목들은 색인자가 보는 경로와 일치해야합니다 (예 : 최상위 폴더에 '/home /me'가 포함되어 있고 '/home'이 실제로 '/usr/home'에 대한 링크인 경우 올바른 '건너뛴 경로들' 항목은 '/home/me'입니다. '/usr/home/me/tmp*'가 아닌 /tmp*') Max disk occupation (%, 0 means no limit) 최대 디스크 점유율(%, 0은 제한없음) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. 색인을 실패시키고 중지할 디스크 크기(색인 크기가 아닌 전체 디스크 사용량)의 백분율입니다.<br>기본값 0은 제한을 두지 않습니다. uiPrefsDialogBase User preferences User preferences User interface 사용자 인터페이스 Number of entries in a result page 결과 페이지 당 표시할 자료 갯수 If checked, results with the same content under different names will only be shown once. 이 옵션을 선택하면, 이름이 달라도 내용이 동일할 경우 결과가 한 번만 표시됩니다. Hide duplicate results. 중복된 결과들을 숨깁니다. Highlight color for query terms Highlight color for query terms Result list font 검색 결과 목록 폰트 Opens a dialog to select the result list font 결과 목록 폰트를 선택하기 위해 검색창을 엽니다. Helvetica-10 Helvetica-10 Resets the result list font to the system default 검색 결과 목록 폰트를 시스템 기본값으로 재설정 Reset 재설정 Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Result paragraph<br>format string Result paragraph<br>format string Texts over this size will not be highlighted in preview (too slow). 이 크기를 초과하는 글자들은 미리보기에서 강조(Highlight)가 되지 않습니다. Maximum text size highlighted for preview (megabytes) 미리보기를 위한 강조된 글자 수 크기의 최대값(MB) Use desktop preferences to choose document editor. Use desktop preferences to choose document editor. Choose editor applications MIME별 실행 프로그램 선택 창 Display category filter as toolbar instead of button panel (needs restart). Display category filter as toolbar instead of button panel (needs restart). Auto-start simple search on whitespace entry. Auto-start simple search on whitespace entry. Start with advanced search dialog open. Recoll을 시작할 때마다 고급 검색창을 엽니다. Start with sort dialog open. Start with sort dialog open. Remember sort activation state. 검색 결과창의 정렬 기준을 기억합니다. Prefer Html to plain text for preview. 미리보기에서 텍스트보다 HTML을 우선합니다. Search parameters 검색 매개변수 Stemming language 형태소 언어 A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. [rolling stones]을 검색했다면, (2 음절들)은 [rolling] 혹은 [stones] 혹은 [rolling (절 2개) stones]로 변환됩니다. 입력한 검색어대로 정확히 보여주는 결과에 더 높은 우선순위가 부여됩니다. Automatically add phrase to simple searches 단순한 검색을 수행할 시, 자동으로 검색어를 추가합니다. Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. 검색 결과에서, 검색된 단어들을 문맥적으로 표시하도록 할까요? 큰 문서들은 아마 느려질 것입니다. Dynamically build abstracts 검색 결과에 나타나는 문서 내용에 검색어를 표시합니다. Do we synthetize an abstract even if the document seemed to have one? 검색어가 문서 내 단 하나만 있어도 추출한 단어를 합성할까요?(무슨 기능인지 잘 모르겠음-역자 주) Replace abstracts from documents 문서들로부터 검색된 단어들을 재배치합니다. Synthetic abstract size (characters) 결과 보기의 미리보기 글자 수 분량 Synthetic abstract context words 문서별 검색창의 미리보기 단어 수 The words in the list will be automatically turned to ext:xxx clauses in the query language entry. 목록 내 단어들은 검색어 언어 항목 안에서 자동으로 'ext"xxx' 절들로 전환됩니다. Query language magic file name suffixes. 언어 매직 파일 이름 접미사를 검색하십시오. Enable 사용함 External Indexes 외부 색인들 Toggle selected 선택된 항목 Activate All 모두 활성화 Deactivate All 모두 비활성화 Remove from list. This has no effect on the disk index. 목록에서 삭제. 디스크 색인에 영향 없음. Remove selected 선택 삭제 Click to add another index directory to the list Click to add another index directory to the list Add index 색인 추가 Apply changes 변경사항 적용 &OK 적용(&O) Discard changes 변경 내역 취소 &Cancel 취소(&C) Abstract snippet separator 문서별 검색 내용 분리자 Use <PRE> tags instead of <BR>to display plain text as html. Use <PRE> tags instead of <BR>to display plain text as html. Lines in PRE text are not folded. Using BR loses indentation. Lines in PRE text are not folded. Using BR loses indentation. Style sheet 스타일 시트 Opens a dialog to select the style sheet file 스타일 시트 파일을 선택하기 위해 대화창을 엽니다. Choose 선택 Resets the style sheet to default 스타일 시트를 기본값으로 재설정 Lines in PRE text are not folded. Using BR loses some indentation. Lines in PRE text are not folded. Using BR loses some indentation. Use <PRE> tags instead of <BR>to display plain text as html in preview. Use <PRE> tags instead of <BR>to display plain text as html in preview. Result List 검색 결과 목록 Edit result paragraph format string 결과 목록을 구성하는 표와 글서식 편집 Edit result page html header insert 검색 결과 페이지의 HTML Header에 삽입할 내용 편집 Date format (strftime(3)) 날짜 서식 (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). 백분율 값을 높이면, 자주 사용되는 단어를 구문검색에 활용하지 않습니다. 이것은 성능 향상에 도움을 줄 수는 있지만, 원하는 문장을 찾는데에는 지장을 줄 수 있습니다. 기본값은 2 입니다.(단위는 퍼센트) Autophrase term frequency threshold percentage 용어를 검색 빈도수에 따라서 자동 문구 생성에 활용합니다(백분율). Plain text to HTML line style 단순 텍스트를 HTML 스타일로 변경할 때 Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. PRE 텍스트의 줄은 접히지 않습니다. BR은 들여쓰기가 손실됩니다. PRE + Wrap 스타일을 원하고 있을지도 모릅니다. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + wrap Exceptions Exceptions Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Disable Qt autocompletion in search entry. 검색어 입력창에서 자동완성을 끕니다. Search as you type. Search as you type. Paths translations 변경된 경로들 Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. 목록에 다른 색인 폴더를 추가하려면 클릭하십시오. Recoll 환경설정 폴더 또는 Xapian 색인을 선택할 수 있습니다. Snippets window CSS file 문서별 검색창을 위한 CSS 파일 Opens a dialog to select the Snippets window CSS style sheet file 문서별 검색창의 CSS 스타일 시트 파일을 선택하기 위해 대화창을 엽니다. Resets the Snippets window style 문서별 검색창 스타일을 재설정 Decide if document filters are shown as radio buttons, toolbar combobox, or menu. 검색 결과의 문서 필터를 라디오 버튼으로 볼지, 툴바 콤보 박스로 볼지, 혹은 메뉴로 볼지 결정하십시오. Document filter choice style: 검색된 자료의 필터 선택 스타일: Buttons Panel 버튼 패널 Toolbar Combobox 툴바 콤보박스 Menu 메뉴 Show system tray icon. 시스템 트레이에 아이콘을 표시합니다. Close to tray instead of exiting. 프로그램 종료 대신, 작업표시줄의 트레이로 최소화합니다. Start with simple search mode 단순 검색의 검색 기준 기본값: Show warning when opening temporary file. 임시 파일이 열렸을 때 경고창을 봅니다. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. 문서별 검색창에 적용할 사용자 스타일입니다.<br> 참고: 결과 페이지의 헤더에 삽입한 값들도 문서 검색창 헤더에 같이 포함됩니다. Synonyms file 동의어 파일 Highlight CSS style for query terms 검색어 단어를 위한 강조 CSS 스타일 Recoll - User Preferences Recoll - 사용자 환경설정 Set path translations for the selected index or for the main one if no selection exists. 선택된 색인의 경로 지시, 혹은 선택한 색인이 없을 때 주 색인의 경로를 설정하십시오. Activate links in preview. 미리보기에서 링크를 활성화합니다. Make links inside the preview window clickable, and start an external browser when they are clicked. 미리보기에 외부 브라우저 시작이 가능한 링크를 만듭니다. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... 검색 결과에서 검색어 강조. <br> 예를 들어, 기본값은 파란색이지만, "color:red;background:yellow"를 설정하면 더 돋보일 것입니다. Start search on completer popup activation. 자동완성 항목 선택 시 검색을 즉각 시작합니다. Maximum number of snippets displayed in the snippets window 문서별 검색창 내에 표시되는 검색결과의 최대 수 Sort snippets by page number (default: by weight). 문서별 검색 결과를 페이지 순서대로 정리합니다(기본값: 검색어 관련도). Suppress all beeps. 모든 경고음을 억제합니다. Application Qt style sheet 응용 프로그램 Qt 스타일 시트 Limit the size of the search history. Use 0 to disable, -1 for unlimited. 검색 기록의 크기를 제한하십시오. 0:비활성화, -1:무제한. Maximum size of search history (0: disable, -1: unlimited): 검색 기록 최대 크기 (0:사용 안함, -1:무제한): Generate desktop notifications. 데스크탑 알림을 생성합니다. Misc 기타 Work around Tamil QTBUG-78923 by inserting space before anchor text 앵커 텍스트 앞에 공백을 삽입하여 Tamil QTBUG-78923을 해결합니다. Display a Snippets link even if the document has no pages (needs restart). 문서에 페이지가 없더라도 문서별 검색기 링크를 표시합니다(다시 시작해야 함). Maximum text size highlighted for preview (kilobytes) 미리보기를 위한 하이라이트 처리된 글자의 최대 용량(KB) Start with simple search mode: 단순 검색의 검색 기준 기본값: Hide toolbars. Hide toolbars. Hide status bar. Hide status bar. Hide Clear and Search buttons. Hide Clear and Search buttons. Hide menu bar (show button instead). Hide menu bar (show button instead). Hide simple search type (show in menu only). Hide simple search type (show in menu only). Shortcuts 단축키 Hide result table header. 결과 테이블의 헤더 숨기기. Show result table row headers. 결과 테이블의 행 헤더 표시. Reset shortcuts defaults 단축키 초기화 Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Use F1 to access the manual F1을 사용하여 설명서에 액세스합니다. Hide some user interface elements. Hide: 숨기기: Toolbars 툴바 Status bar 상태바 Show button instead. 메뉴바 대신 버튼이 보여집니다. Menu bar 메뉴바 Show choice in menu only. 메뉴를 통해서만 유형을 선택합니다. Simple search type 단순 검색 유형 Clear/Search buttons 지우기/검색 버튼 Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. 테이블 행으로 이동하는 Ctrl+[0-9]/Shift+[a-z] 단축키 비활성화 None (default) None (default) Uses the default dark mode style sheet 스타일 시트를 기본 다크모드로 설정 Dark mode Dark mode Choose QSS File QSS 파일 선택하기 To display document text instead of metadata in result table detail area, use: 결과 테이블에서 메타데이터가 아닌 문서 텍스트를 보려면: left mouse click 마우스 클릭 Shift+click Shift+클릭 Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. 파일탐색기를 열어 스타일시트 파일을 선택합니다.<br>예시로 /usr/share/recoll/examples/recoll[-dark].qss 를 참고하세요. Result Table 결과 테이블 Do not display metadata when hovering over rows. 마우스가 행 위에 있을 때 메타데이터 표시하지 않기. The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_hu.ts0000644000175000017500000071017014444307651014255 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Minden feltétel Any clause Bármely feltétel texts texts spreadsheets spreadsheets presentations presentations media Média messages messages other Egyéb Bad multiplier suffix in size filter Hibás sokszorozó utótag a méretszűrőben! text Szöveg spreadsheet Munkafüzet presentation Prezentáció message Üzenet Advanced Search Advanced Search History Next History Next History Prev History Prev Load next stored search Load next stored search Load previous stored search Load previous stored search AdvSearchBase Advanced search Összetett keresés Restrict file types Fájltípus Save as default Mentés alapértelmezettként Searched file types Keresett fájltípusok All ----> Mind -----> Sel -----> Kijelölt -----> <----- Sel <----- Kijelölt <----- All <----- Mind Ignored file types Kizárt fájltípusok Enter top directory for search A keresés kezdő könyvtárának megadása Browse Tallózás Restrict results to files in subtree: Keresés az alábbi könyvtárból indulva: Start Search A keresés indítása Search for <br>documents<br>satisfying: A keresés módja: Delete clause Feltétel törlése Add clause Új feltétel Check this to enable filtering on file types A találatok szűrése a megadott fájltípusokra By categories Kategória Check this to use file categories instead of raw mime types A találatok szűrése MIME típus helyett fájlkategóriára Close Bezárás All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. A jobb oldali nem üres mezők „Minden feltétel” választásakor ÉS, „Bármely feltétel” választásakor VAGY kapcsolatban lesznek.<br>A „Bármely szó”, „Minden szó” és az „Egyik sem” típusú mezőkben szavak és idézőjelbe tett részmondatok kombinációja adható meg.<br>Az üres mezők figyelmen kívül lesznek hagyva. Invert Megfordítás Minimum size. You can use k/K,m/M,g/G as multipliers Minimális méret, sokszorozó utótag lehet a k/K, m/M, g/G Min. Size legalább Maximum size. You can use k/K,m/M,g/G as multipliers Maximális méret, sokszorozó utótag lehet a k/K, m/M, g/G Max. Size legfeljebb Select Select Filter Szűrők From ettől To eddig Check this to enable filtering on dates A találatok szűrése a fájlok dátuma alapján Filter dates Dátum Find Keresés Check this to enable filtering on sizes A találatok szűrése a fájlok mérete alapján Filter sizes Méret Filter birth dates ConfIndexW Can't write configuration file A beállítófájl írása sikertelen Global parameters Általános beállítások Local parameters Helyi beállítások Search parameters Keresési beállítások Top directories Kezdő könyvtárak The list of directories where recursive indexing starts. Default: your home. A megadott könyvtárak rekurzív indexelése. Alapértelmezett értéke a saját könyvtár. Skipped paths Kizárt elérési utak These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Stemming languages A szótőképzés nyelve The languages for which stemming expansion<br>dictionaries will be built. Ezen nyelvekhez készüljön szótövező és -toldalékoló szótár Log file name A naplófájl neve The file where the messages will be written.<br>Use 'stderr' for terminal output Az üzenetek kiírásának a helye.<br>A „stderr” a terminálra küldi az üzeneteket. Log verbosity level A naplózás szintje This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Az üzenetek mennyiségének szabályozása,<br>a hibaüzenetekre szorítkozótól a részletes hibakeresésig. Index flush megabytes interval Indexírási intervallum (MB) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Az az adatmennyiség, melyet két lemezre írás között az indexelő feldolgoz.<br>Segíthet kézben tartani a memóriafoglalást. Alapértelmezett: 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. No aspell usage Az aspell mellőzése Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. A szóvizsgálóban az aspell használatának mellőzése a hasonló szavak keresésekor.<br>Hasznos, ha az aspell nincs telepítve vagy nem működik. Aspell language Az aspell nyelve The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Az aspell szótár nyelve. pl. „en” vagy „hu”...<br>Ha nincs megadva, akkor az NLS környezet alapján lesz beállítva, ez általában megfelelő. A rendszerre telepített nyelveket az „aspell config” parancs kiadása után a „data-dir” könyvtárban található .dat fájlokból lehet megtudni. Database directory name Az adatbázis könyvtárneve The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Az indexet tartalmazó könyvtár neve.<br>Relatív elérési út a beállítási könyvtárhoz képest értendő. Alapértelmezett: „xapiandb”. Unac exceptions Unac kivételek <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Az unac alapértelmezetten eltávolít minden ékezetet és szétbontja a ligatúrákat. Az itt megadott kivételekkel lehetőség van adott karakterek esetén tiltani a műveletet, ha a használt nyelv ezt szükségessé teszi. Ezen kívül előírhatók további felbontandó karakterek is. Az egyes elemeket egymástól szóközzel kell elválasztani. Egy elem első karaktere az eredetit, a további karakterek a várt eredményt határozzák meg. Process the WEB history queue A webes előzmények feldolgozása Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) A Firefoxszal látogatott oldalak indexelése<br>(a Firefox Recoll kiegészítőjét is telepíteni kell) Web page store directory name A weblapokat tároló könyvtár neve The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. A látogatott weblapok másolatát tároló könyvtár neve.<br>Relatív elérési út a beállításokat tároló könyvtárhoz képest értendő. Max. size for the web store (MB) A webes tároló max. mérete (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). A méret elérésekor a legkorábbi bejegyzések törlődnek.<br>Csak a növelésnek van haszna, mivel csökkentéskor a már létező fájl nem lesz kisebb (csak egy része állandóan kihasználatlan marad). Automatic diacritics sensitivity Automatikus ékezetérzékenység <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatikusan különbözőnek tekinti az ékezetes betűket az ékezet nélküli párjuktól, ha tartalmaz ékezetes betűt a kifejezés (az unac_except_trans kivételével). Egyébként a keresőnyelv <i>D</i> módosítójával érhető el ugyanez. Automatic character case sensitivity Kis-és nagybetűk automatikus megkülönböztetése <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatikusan különbözőnek tekinti a kis-és nagybetűket, ha az első karakter kivételével bárhol tartalmaz nagybetűt a kifejezés. Egyébként a keresőnyelv <i>C</i> módosítójával érhető el ugyanez. Maximum term expansion count A toldalékok maximális száma <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Egy szó toldalékainak maximális száma (pl. helyettesítő karakterek használatakor). Az alapértelmezett 10 000 elfogadható érték, és elkerülhető vele a felhasználói felület időleges válaszképtelensége is. Maximum Xapian clauses count A Xapian feltételek maximális száma <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. Egy Xapian kereséshez felhasználható elemi feltételek maximális száma. Néha a szavak toldalékolása szorzó hatású, ami túlzott memóriahasználathoz vezethet. Az alapértelmezett 100 000 a legtöbb esetben elegendő, de nem is támaszt különleges igényeket a hardverrel szemben. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Indexer log file name Indexer log file name If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Web history Webes előzmények Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types MIME típusok An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Az indexelendő MIME típusok listája.<br>Csak ezek a típusok kerülnek az indexbe. Rendesen üres és inaktív. Exclude mime types Kizárt MIME típusok Mime types not to be indexed Ezek a MIME típusok kimaradnak az indexelésből Max. compressed file size (KB) A tömörített fájlok max. mérete (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. A tömörített fájlok indexbe kerülésének határértéke. -1 esetén nincs korlát. 0 esetén soha nem történik kicsomagolás. Max. text file size (MB) Szövegfájl max. mérete (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. A szövegfájlok indexbe kerülésének határértéke. -1 esetén nincs korlát. Az óriásira nőtt naplófájlok feldolgozása kerülhető el így. Text file page size (KB) Szövegfájl lapmérete (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Ha be van állítva (nem egyenlő -1), akkor a szövegfájlok indexelése ilyen méretű darabokban történik. Ez segítséget nyújt a nagyon nagy méretű szövegfájlokban (pl. naplófájlok) való kereséshez. Max. filter exec. time (s) Max. filter exec. time (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. A túl hosszú ideig futó külső szűrők leállítása Néha előfordul (pl. postscript esetén), hogy a szűrő végtelen ciklusba kerül. -1 esetén nincs korlát. Global Minden könyvtárra vonatkozik CronToolW Cron Dialog Cron időzítő <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A <span style=" font-weight:600;">Recoll</span> indexelő időzítése (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Minden mezőben megadható csillag (*), szám, számok listája (1,3,5) vagy számtartomány (1-7). Általánosabban, a mezők jelentése ugyanaz, <span style=" font-style:italic;">mint</span> a crontab fájlban, és a teljes crontab szintaxis használható, lásd a crontab(5) kézikönyvlapot.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Például <span style=" font-family:'Courier New,courier';">*</span>-ot írva a <span style=" font-style:italic;">naphoz, </span><span style=" font-family:'Courier New,courier';">12,19</span>-et az <span style=" font-style:italic;">órához</span> és <span style=" font-family:'Courier New,courier';">15</span>-öt a <span style=" font-style:italic;">perchez</span>, a recollindex minden nap 12:15-kor és du. 7:15-kor fog elindulni.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Túl gyakori ütemezés helyett célszerűbb lehet a valós idejű indexelés engedélyezése.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) A hét napja (* vagy 0-7, 0 vagy 7 a vasárnap) Hours (* or 0-23) Óra (* vagy 0-23) Minutes (0-59) Perc (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A <span style=" font-style:italic;">Kikapcsolás</span> megszünteti, a <span style=" font-style:italic;">Bekapcsolás</span> aktiválja az időzített indexelést, a <span style=" font-style:italic;">Mégsem</span> nem változtat a beállításon.</p></body></html> Enable Bekapcsolás Disable Kikapcsolás It seems that manually edited entries exist for recollindex, cannot edit crontab Úgy tűnik, egy kézi bejegyzése van a recollindexnek, nem sikerült a crontab szerkesztése! Error installing cron entry. Bad syntax in fields ? Hiba a cron bejegyzés hozzáadásakor! Rossz szintaxis a mezőkben? EditDialog Dialog Párbeszédablak EditTrans Source path Eredeti elérési út Local path Helyi elérési út Config error Beállítási hiba Original path Eredeti elérési út EditTransBase Path Translations Elérési út átalakítása Setting path translations for Elérési út-átalakítás ehhez: Select one or several file types, then use the controls in the frame below to change how they are processed Kijelölhető egy vagy több elérési út is Add Hozzáadás Delete Törlés Cancel Mégsem Save Mentés FirstIdxDialog First indexing setup Az indexelés beállítása első induláskor <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">A jelenlegi beállításokhoz még nem tartozik index.</span><br /><br />A saját mappa indexelése javasolt alapbeállításokkal az <span style=" font-style:italic;">Indexelés indítása most</span> gombbal indítható. A beállítások később módosíthatók.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Az alábbi hivatkozások az indexelés finomhangolására és időzítésére szolgálnak.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ezek a lehetőségek később a <span style=" font-style:italic;">Beállítások</span> menüből is elérhetők.</p></body></html> Indexing configuration Az indexelés beállításai This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Megadható az indexelendő könyvtárak köre és egyéb paraméterek, például kizárt elérési utak vagy fájlnevek, alapértelmezett betűkészlet stb. Indexing schedule Az időzítés beállításai This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Lehetőség van ütemezett indításra és valós idejű indexelésre, az előbbi időzítése is beállítható (a cron segítségével). Start indexing now Indexelés indítása most FragButs %1 not found. A fájl nem található: %1. %1: %2 %1: %2 Fragment Buttons Fragment Buttons Query Fragments Statikus szűrők IdxSchedW Index scheduling setup Az indexelés időzítése <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A <span style=" font-weight:600;">Recoll</span> indexelő futhat folyamatosan, így a fájlok változásakor az index is azonnal frissül, vagy indulhat meghatározott időközönként.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A kézikönyv segítséget nyújt a két eljárás közül a megfelelő kiválasztásához (F1).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Lehetőség van az időzített indexelés ütemezésére, vagy a valós idejű indexelő automatikus indítására bejelentkezéskor (vagy mindkettőre, bár ez ritkán célszerű).</p></body></html> Cron scheduling Cron időzítő The tool will let you decide at what time indexing should run and will install a crontab entry. Az indexelés kezdő időpontjainak beállítása egy crontab bejegyzés által. Real time indexing start up Valós idejű indexelés indítása Decide if real time indexing will be started when you log in (only for the default index). A valós idejű indexelés indítása bejelentkezéskor (csak az alapértelmezett indexhez). ListDialog Dialog Párbeszédablak GroupBox GroupBox Main No db directory in configuration Nincs adatbáziskönyvtár a beállítófájlban Could not open database in Could not open database in . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. Configuration problem (dynconf Configuration problem (dynconf "history" file is damaged or un(read)writeable, please check or remove it: Az előzmények fájlja sérült vagy nem lehet írni/olvasni, ellenőrizni vagy törölni kell: "history" file is damaged, please check or remove it: "history" file is damaged, please check or remove it: Needs "Show system tray icon" to be set in preferences! Preview &Search for: Kere&sés: &Next &Következő &Previous &Előző Match &Case Kis- és &nagybetűk Clear Törlés Creating preview text Előnézet létrehozása Loading preview text into editor Az előnézet betöltése a megjelenítőbe Cannot create temporary directory Cannot create temporary directory Cancel Mégsem Close Tab Lap bezárása Missing helper program: Hiányzó segédprogram: Can't turn doc into internal representation for Nem sikerült értelmezni: Cannot create temporary directory: Cannot create temporary directory: Error while loading file Hiba a fájl betöltése közben! Form Form Tab 1 Tab 1 Open Megnyitás Canceled Canceled Error loading the document: file missing. Error loading the document: file missing. Error loading the document: no permission. Error loading the document: no permission. Error loading: backend not configured. Error loading: backend not configured. Error loading the document: other handler error<br>Maybe the application is locking the file ? Error loading the document: other handler error<br>Maybe the application is locking the file ? Error loading the document: other handler error. Error loading the document: other handler error. <br>Attempting to display from stored text. <br>Attempting to display from stored text. Could not fetch stored text Could not fetch stored text Previous result document Previous result document Next result document Next result document Preview Window Preview Window Close Window Close Window Next doc in tab Next doc in tab Previous doc in tab Previous doc in tab Close tab Close tab Print tab Print tab Close preview window Close preview window Show next result Show next result Show previous result Show previous result Print Nyomtatás PreviewTextEdit Show fields Mezők Show main text Tartalom Print Nyomtatás Print Current Preview A jelenlegi nézet nyomtatása Show image Kép Select All Mindent kijelöl Copy Másolás Save document to file Mentés fájlba Fold lines Sortörés Preserve indentation Eredeti tördelés Open document Open document Reload as Plain Text Reload as HTML QObject Global parameters Általános beállítások Local parameters Helyi beállítások <b>Customised subtrees <b>Egyedi alkönyvtárak The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Az indexelt hierarchián belüli alkönyvtárak listája,<br> melyekre eltérő beállítások vonatkoznak. Alapértelmezetten üres. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Ha a fenti listából semmi vagy egy üres sor van kijelölve, úgy a következő jellemzők<br>az indexelendő legfelső szintű, egyébként a kijelölt mappára vonatkoznak.<br>A +/- gombokkal lehet a listához könyvtárakat adni vagy onnan törölni. Skipped names Kizárt nevek These are patterns for file or directory names which should not be indexed. Mintával megadható fájl- és könyvtárnevek, melyeket nem kell indexelni Default character set Default character set This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Follow symbolic links Szimbolikus linkek követése Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Indexeléskor kövesse a szimbolikus linkeket.<br>Alapértelmezetten ki van kapcsolva, elkerülendő a dupla indexelést. Index all file names Minden fájlnév indexelése Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true A Recoll számára ismeretlen típusú vagy értelmezhetetlen fájlok nevét is indexelje.<br>Alapértelmezetten engedélyezve van. Beagle web history Beagle web history Search parameters Keresési beállítások Web history Webes előzmények Default<br>character set Alapértelmezett<br>karakterkódolás Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. A karakterkódolásról információt nem tároló fájlok (például egyszerű szöveges fájlok) kódolása.<br>Alapértelmezetten nincs megadva, és a nyelvi környezet (NLS) alapján lesz beállítva. Ignored endings Kizárt kiterjesztések These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Az ilyen fájlnévvégződésű fájlok csak a nevük alapján indexelendők (nem történik MIME típusfelismerés, kicsomagolás és tartalomindexelés sem). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. QWidget Create or choose save directory Mentési könyvtár megadása Choose exactly one directory Csak pontosan egy könyvtár adható meg! Could not read directory: A könyvtár nem olvasható: Unexpected file name collision, cancelling. A fájl már létezik, ezért ki lesz hagyva. Cannot extract document: Nem sikerült kicsomagolni a fájlt: &Preview &Előnézet &Open &Megnyitás Open With Megnyitás ezzel: Run Script Szkript futtatása Copy &File Name &Fájlnév másolása Copy &URL &URL másolása &Write to File Menté&s fájlba Save selection to files A kijelölés mentése fájlba Preview P&arent document/folder A szülő előné&zete &Open Parent document/folder A szülő megnyi&tása Find &similar documents &Hasonló dokumentum keresése Open &Snippets window Ér&demi részek Show subdocuments / attachments Aldokumentumok / csatolmányok &Open Parent document &Open Parent document &Open Parent Folder &Open Parent Folder Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Ne jelenjen meg újra. RTIToolW Real time indexing automatic start A valós idejű indexelés automatikus indítása <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A <span style=" font-weight:600;">Recoll</span> indexelője indítható szolgáltatásként, így az index minden fájlváltozáskor azonnal frissül. Előnye a mindig naprakész index, de folyamatosan igénybe veszi az erőforrásokat.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Az indexelő szolgáltatás indítása a munkamenettel Also start indexing daemon right now. Az indexelő szolgáltatás indítása most Replacing: Csere: Replacing file Fájl cseréje Can't create: Nem sikerült létrehozni: Warning Figyelmeztetés Could not execute recollindex A recollindex indítása sikertelen Deleting: Törlés: Deleting file Fájl törlése Removing autostart Az autostart kikapcsolása Autostart file deleted. Kill current process too ? Az autostart fájl törölve lett. A most futó indexelőt is le kell állítani? RclCompleterModel Hits RclMain About Recoll A Recoll névjegye Executing: [ Végrehajtás: [ Cannot retrieve document info from database Nem sikerült az adatbázisban információt találni a dokumentumról. Warning Figyelmeztetés Can't create preview window Nem sikerült létrehozni az előnézetet Query results A keresés eredménye Document history Előzmények History data Előzményadatok Indexing in progress: Az indexelés folyamatban: Files Files Purge törlés Stemdb szótövek adatbázisa Closing lezárás Unknown ismeretlen This search is not active any more Ez a keresés már nem aktív. Can't start query: Can't start query: Bad viewer command line for %1: [%2] Please check the mimeconf file Bad viewer command line for %1: [%2] Please check the mimeconf file Cannot extract document or create temporary file Nem sikerült a kicsomagolás vagy az ideiglenes fájl létrehozása. (no stemming) (nincs szótőképzés) (all languages) (minden nyelv) error retrieving stemming languages hiba a szótőképzés nyelvének felismerésekor Update &Index &Index frissítése Indexing interrupted Az indexelés megszakadt. Stop &Indexing Indexelé&s leállítása All Mind media Média message Üzenet other Egyéb presentation Prezentáció spreadsheet Munkafüzet text Szöveg sorted rendezett filtered szűrt External applications/commands needed and not found for indexing your file types: External applications/commands needed and not found for indexing your file types: No helpers found missing Nincs hiányzó segédprogram. Missing helper programs Hiányzó segédprogramok Save file dialog Save file dialog Choose a file name to save under Choose a file name to save under Document category filter Document category filter No external viewer configured for mime type [ Nincs külső megjelenítő beállítva ehhez a MIME típushoz [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? A mimeview fájlban megadott megjelenítő ehhez: %1: %2 nem található. Megnyissuk a beállítások ablakát? Can't access file: A fájl nem elérhető: Can't uncompress file: Nem sikerült kicsomagolni a fájlt: Save file Fájl mentése Result count (est.) Találatok száma (kb.) Query details A keresés részletei Could not open external index. Db not open. Check external indexes list. Egy külső index megnyitása nem sikerült. Ellenőrizni kell a külső indexek listáját. No results found Nincs találat None semmi Updating frissítés Done kész Monitor figyelés Indexing failed Sikertelen indexelés The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone A jelenleg futó indexelő nem erről a felületről lett indítva.<br>Az OK gombbal kilőhető, a Mégsem gombbal meghagyható. Erasing index Index törlése Reset the index and start from scratch ? Indulhat az index törlése és teljes újraépítése? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program A keresés folyamatban van.<br>Az indexelő korlátozásai miatt<br>megszakításkor a program kilép. Error Hiba Index not open Nincs megnyitott index Index query error Indexlekérdezési hiba Indexed Mime Types Indexed Mime Types Content has been indexed for these MIME types: Content has been indexed for these MIME types: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Can't update index: indexer running Nem sikerült frissíteni az indexet: az indexelő már fut. Indexed MIME Types Indexelt MIME típusok Bad viewer command line for %1: [%2] Please check the mimeview file Hibás a megjelenítő parancssor ehhez: %1: [%2] Ellenőrizni kell a mimeview fájlt! Viewer command line for %1 specifies both file and parent file value: unsupported %1 megjelenítő parancssora fájlt és szülőt is megad: ez nem támogatott. Cannot find parent document Nem található a szülődokumentum. Indexing did not run yet Az indexelő jelenleg nem fut. External applications/commands needed for your file types and not found, as stored by the last indexing pass in Az alábbi külső alkalmazások/parancsok hiányoznak a legutóbbi indexelés során keletkezett napló alapján -----> Index not up to date for this file. Refusing to risk showing the wrong entry. A fájl bejegyzése az indexben elavult. Esetlegesen téves adatok megjelenítése helyett kihagyva. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Indexer running so things should improve when it's done Indexer running so things should improve when it's done Sub-documents and attachments Aldokumentumok és csatolmányok Document filter Dokumentumszűrő Index not up to date for this file. Refusing to risk showing the wrong entry. A fájl bejegyzése az indexben elavult. Esetlegesen téves adatok megjelenítése helyett kihagyva. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Az OK-ra kattintva frissíthető a fájl indexbejegyzése, ennek végeztével újra kell futtatni a keresést. The indexer is running so things should improve when it's done. Az indexelő fut, ennek végeztére a dolgok még helyreállhatnak. The document belongs to an external indexwhich I can't update. The document belongs to an external indexwhich I can't update. Click Cancel to return to the list. Click Ignore to show the preview anyway. Click Cancel to return to the list. Click Ignore to show the preview anyway. Duplicate documents Másodpéldányok These Urls ( | ipath) share the same content: Ezek az URL-ek (| ipath) azonos tartalmúak: Bad desktop app spec for %1: [%2] Please check the desktop file Hibás alkalmazásbeállítás ehhez:%1: [%2] Ellenőrizni kell az asztali beállítófájlt! Bad paths Hibás elérési utak Bad paths in configuration file: Hibás elérési utak a beállítófájlban: Selection patterns need topdir A mintához kezdő könyvtár szükséges Selection patterns can only be used with a start directory Minta használatához kezdő könyvtárt is meg kell adni. No search Nincs keresés No preserved previous search Nincs előzőleg mentett keresés Choose file to save Mentés ide Saved Queries (*.rclq) Mentett keresések (*.rclq) Write failed Sikertelen írásművelet Could not write to file A fájl írása sikertelen Read failed Sikertelen olvasás Could not open file: Nem sikerült megnyitni a fájlt: Load error Betöltési hiba Could not load saved query Nem sikerült betölteni a mentett keresést Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Egy ideiglenes másolat lesz megnyitva. A módosítások<br/>megőrzéséhez a fájlt el kell menteni máshová. Do not show this warning next time (use GUI preferences to restore). Ne jelenjen meg többször (a GUI beállításaiban visszaállítható). Disabled because the real time indexer was not compiled in. Nem elérhető, mert a valós idejű indexelés nincs a programba fordítva. This configuration tool only works for the main index. Ez a beállítóeszköz csak az elsődleges indexszel használható. The current indexing process was not started from this interface, can't kill it A jelenleg futó indexelő nem erről a felületről lett indítva, nem állítható le. The document belongs to an external index which I can't update. A dokumentum külső indexhez tartozik, mely innen nem frissíthető. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Visszatérés a listához: Mégsem.<b>Az előnézet megnyitása mindenképp (és megjegyzés erre a munkamenetre): Mellőzés. Index scheduling Az időzítés beállításai Sorry, not available under Windows for now, use the File menu entries to update the index Sajnos Windows rendszeren még nem vehető igénybe, a Fájl menüből lehet frissíteni az indexet. Can't set synonyms file (parse error?) Nem lehet betölteni a szinonímafájlt (értelmezési hiba?) Index locked Az index zárolva van Unknown indexer state. Can't access webcache file. Az indexelő állapota ismeretlen. A webes gyorstár nem hozzáférhető. Indexer is running. Can't access webcache file. Az indexelő fut. A webes gyorstár nem hozzáférhető. with additional message: with additional message: Non-fatal indexing message: Non-fatal indexing message: Types list empty: maybe wait for indexing to progress? Types list empty: maybe wait for indexing to progress? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Tools Eszközök Results Találatok (%d documents/%d files/%d errors/%d total files) (%d documents/%d files/%d errors/%d total files) (%d documents/%d files/%d errors) (%d documents/%d files/%d errors) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Indexing done Indexing done Can't update index: internal error Can't update index: internal error Index not up to date for this file.<br> Index not up to date for this file.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Also, it seems that the last index update for the file failed.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> documents találatok a lapon: document document files files file fájl errors errors error hiba total files) total files) No information: initial indexing not yet performed. No information: initial indexing not yet performed. Batch scheduling Batch scheduling The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Confirm Confirm Erasing simple and advanced search history lists, please click Ok to confirm Erasing simple and advanced search history lists, please click Ok to confirm Could not open/create file Could not open/create file F&ilter F&ilter Could not start recollindex (temp file error) Could not start recollindex (temp file error) Could not read: Could not read: This will replace the current contents of the result list header string and GUI qss file name. Continue ? This will replace the current contents of the result list header string and GUI qss file name. Continue ? You will need to run a query to complete the display change. You will need to run a query to complete the display change. Simple search type Simple search type Any term Bármely szó All terms Minden szó File name Fájlnév Query language Keresőnyelv Stemming language A szótőképzés nyelve Main Window Main Window Focus to Search Focus to Search Focus to Search, alt. Focus to Search, alt. Clear Search Clear Search Focus to Result Table Focus to Result Table Clear search Clear search Move keyboard focus to search entry Move keyboard focus to search entry Move keyboard focus to search, alt. Move keyboard focus to search, alt. Toggle tabular display Toggle tabular display Move keyboard focus to table Move keyboard focus to table Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Előző oldal Next page Következő oldal &File &Fájl E&xit &Kilépés &Tools &Eszközök &Help &Súgó &Preferences &Beállítások Search tools Search tools Result list Találati lista &About Recoll A Recoll &névjegye Document &History &Előzmények Document History Előzmények &Advanced Search Összetett &keresés Advanced/complex Search Összetett keresés &Sort parameters &Rendezési beállítások Sort parameters Rendezési beállítások Next page of results Következő oldal Previous page of results Előző oldal &Query configuration &Query configuration &User manual &Felhasználói kézikönyv Recoll Recoll Ctrl+Q Ctrl+Q Update &index Az &index frissítése Term &explorer &Szóvizsgáló Term explorer tool Szóvizsgáló External index dialog Külső indexek &Erase document history &Előzmények törlése First page Első oldal Go to first page of results Első oldal &Indexing configuration &Az indexelés beállításai All Mind &Show missing helpers &Show missing helpers PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen &Teljes képernyő F11 F11 Full Screen Teljes képernyő &Erase search history Keresé&si előzmények törlése sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Növekvő rendezés dátum szerint sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Csökkenő rendezés dátum szerint Show Query Details A keresés részletei Show results as table Show results as table &Rebuild index Index új&raépítése &Show indexed types &Show indexed types Shift+PgUp Shift+PgUp &Indexing schedule &Az időzítés beállításai E&xternal index dialog &Külső indexek &Index configuration &Indexelés &GUI configuration &Felhasználói felület &Results &Találatok Sort by date, oldest first Növekvő rendezés dátum szerint Sort by date, newest first Csökkenő rendezés dátum szerint Show as table Táblázatos nézet Show results in a spreadsheet-like table A találatok megjelenítése táblázatban Save as CSV (spreadsheet) file Mentés CSV (strukturált szöveg) fájlba Saves the result into a file which you can load in a spreadsheet A találatok mentése egy táblázatkezelővel megnyitható fájlba Next Page Következő oldal Previous Page Előző oldal First Page Első oldal Query Fragments Statikus szűrők With failed files retrying A sikerteleneket újra Next update will retry previously failed files A következő frissítéskor újra próbálja a sikertelenül indexelt fájlokat Save last query A legutóbbi keresés mentése Load saved query Mentett keresés betöltése Special Indexing Egyedi indexelés Indexing with special options Indexelés egyedi beállításokkal Indexing &schedule Időzí&tés Enable synonyms Szinonímák engedélyezése &View &Nézet Missing &helpers &Hiányzó segédprogramok Indexed &MIME types Indexelt &MIME típusok Index &statistics &Statisztika Webcache Editor Webes gyorstár szerkesztése Trigger incremental pass Trigger incremental pass E&xport simple search history E&xport simple search history Use default dark mode Use default dark mode Dark mode Dark mode &Query &Query Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Dátum Assisted complex search Filter birth dates RclTrayIcon Restore A Recoll megjelenítése Quit Kilépés RecollModel Abstract Tartalmi kivonat Author Szerző Document size A dokumentum mérete Document date A dokumentum dátuma File size A fájl mérete File name Fájlnév File date A fájl dátuma Ipath Belső elérési út Keywords Kulcsszavak Mime type Mime type Original character set Eredeti karakterkódolás Relevancy rating Relevancia Title Cím URL URL Mtime Módosítás ideje Date Dátum Date and time Dátum és idő Ipath Belső elérési út MIME type MIME típus Can't sort by inverse relevance Can't sort by inverse relevance ResList Result list Találati lista Unavailable document Elérhetetlen dokumentum Previous Előző Next Következő <p><b>No results found</b><br> <p><b>Nincs találat.</b><br> &Preview &Előnézet Copy &URL &URL másolása Find &similar documents &Hasonló dokumentum keresése Query details A keresés részletei (show query) (a&nbsp;keresés&nbsp;részletei) Copy &File Name &Fájlnév másolása filtered szűrt sorted rendezett Document history Előzmények Preview Előnézet Open Megnyitás <p><i>Alternate spellings (accents suppressed): </i> <p><i>Alternatív írásmód (ékezetek nélkül): </i> &Write to File Menté&s fájlba Preview P&arent document/folder A szülő előné&zete &Open Parent document/folder A szülő megnyi&tása &Open &Megnyitás Documents Találatok a lapon: out of at least • Az összes találat: for for <p><i>Alternate spellings: </i> <p><i>Alternatív írásmód: </i> Open &Snippets window Ér&demi részek Duplicate documents Másodpéldányok These Urls ( | ipath) share the same content: Ezek az URL-ek (| ipath) azonos tartalmúak: Result count (est.) Találatok száma (kb.) Snippets Érdemi részek This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Rendezés alaphelyzetbe &Delete column Oszlop &törlése Add " Add " " column " column Save table to CSV file A táblázat mentése CSV fájlba Can't open/create file: Nem sikerült megnyitni/létrehozni: &Preview &Előnézet &Open &Megnyitás Copy &File Name &Fájlnév másolása Copy &URL &URL másolása &Write to File Menté&s fájlba Find &similar documents &Hasonló dokumentum keresése Preview P&arent document/folder A szülő előné&zete &Open Parent document/folder A szülő megnyi&tása &Save as CSV &Mentés CSV fájlba Add "%1" column „%1” oszlop hozzáadása Result Table Result Table Open Megnyitás Open and Quit Open and Quit Preview Előnézet Show Snippets Show Snippets Open current result document Open current result document Open current result and quit Open current result and quit Show snippets Show snippets Show header Show header Show vertical header Show vertical header Copy current result text to clipboard Copy current result text to clipboard Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Előnézet &Open &Megnyitás Copy &File Name &Fájlnév másolása Copy &URL &URL másolása &Write to File Menté&s fájlba Find &similar documents &Hasonló dokumentum keresése Preview P&arent document/folder A szülő előné&zete &Open Parent document/folder A szülő megnyi&tása ResultPopup &Preview &Előnézet &Open &Megnyitás Copy &File Name &Fájlnév másolása Copy &URL &URL másolása &Write to File Menté&s fájlba Save selection to files A kijelölés mentése fájlba Preview P&arent document/folder A szülő előné&zete &Open Parent document/folder A szülő megnyi&tása Find &similar documents &Hasonló dokumentum keresése Open &Snippets window Ér&demi részek Show subdocuments / attachments Aldokumentumok / csatolmányok Open With Megnyitás ezzel: Run Script Szkript futtatása SSearch Any term Bármely szó All terms Minden szó File name Fájlnév Completions Completions Select an item: Select an item: Too many completions Too many completions Query language Keresőnyelv Bad query string Hibás keresőkifejezés Out of memory Elfogyott a memória Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Enter file name wildcard expression. A fájlnév megadásához helyettesítő karakterek is használhatók Enter search terms here. Type ESC SPC for completions of current term. Ide kell írni a keresőszavakat. ESC SZÓKÖZ billentyűsorozat: a szó lehetséges kiegészítéseit ajánlja fel. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Keresőnyelvi kifejezés megadása. Segítség:<br> <i>szo1 szo2</i> : 'szo1' és 'szo2' bármely mezőben.<br> <i>mezo:szo1</i> : 'szo1' a 'mezo' nevű mezőben.<br> Szabványos mezőnevek/szinonímák:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pszeudómezők: dir, mime/format, type/rclcat, date, size.<br> Péda dátumtartományra: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>szo1 szo2 OR szo3</i> : szo1 AND (szo2 OR szo3).<br> A jobb olvashatóság érdekében használhatók zárójelek.<br> <i>"szo1 szo2"</i> : részmondat (pontosan így kell előfordulnia). Lehetséges módosítók:<br> <i>"szo1 szo2"p</i> : szavak egymáshoz közel, bármilyen sorrendben, alapértelmezett távolsággal.<br> <b>A keresés részletei</b> segíthet feltárni a nem várt találatok okát. Részletesebb leírás a kézikönyvben (&lt;F1>) található. Stemming languages for stored query: A mentett keresés szótőképző nyelve: differ from current preferences (kept) eltér a jelenlegi beállítástól (megtartva). Auto suffixes for stored query: A mentett keresés automatikus toldalékolása: External indexes for stored query: A mentett keresés külső indexe: Autophrase is set but it was unset for stored query Az „automatikus részmondat” be van kapcsolva, de a keresés mentésekor tiltva volt. Autophrase is unset but it was set for stored query Az „automatikus részmondat” ki van kapcsolva, de a keresés mentésekor engedélyezve volt. Enter search terms here. Enter search terms here. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; border: 1px solid black; border-collapse: collapse; border-collapse: collapse; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; You should really look at the manual (F1)</p> You should really look at the manual (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>What</th><th>Examples</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; where needed</td><td>(one AND two) OR three</td></tr> where needed</td><td>(one AND two) OR three</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Can't open index Could not restore external indexes for stored query:<br> Could not restore external indexes for stored query:<br> ??? ??? Using current preferences. Using current preferences. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Törlés Ctrl+S Ctrl+S Erase search entry A keresőmező törlése Search Keresés Start query A keresés indítása Enter search terms here. Type ESC SPC for completions of current term. Ide kell írni a keresőszavakat. ESC SZÓKÖZ billentyűsorozat: a szó lehetséges kiegészítéseit ajánlja fel. Choose search type. A keresés módjának kiválasztása Show query history Show query history Enter search terms here. Enter search terms here. Main menu Main menu SearchClauseW SearchClauseW SearchClauseW Any of these Any of these All of these All of these None of these None of these This phrase This phrase Terms in proximity Terms in proximity File name matching File name matching Select the type of query that will be performed with the words A megadott szavakkal végzett keresés típusának kiválasztása Number of additional words that may be interspersed with the chosen ones A keresett szavak között található további szavak megengedett száma In field In field No field Nincs mező Any Bármely szó All Mind None semmi Phrase Részmondat Proximity Távolság File name Fájlnév Snippets Snippets Érdemi részek X X Find: Keresés: Next Következő Prev Előző SnippetsW Search Keresés <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Sajnos a megadott határok között nincs pontos egyezés. Talán túl nagy a dokumentum, és a feldolgozó elakadt...</p> Sort By Relevance Sort By Relevance Sort By Page Sort By Page Snippets Window Snippets Window Find Keresés Find (alt) Find (alt) Find Next Find Next Find Previous Find Previous Hide Hide Find next Find next Find previous Find previous Close window Close window SortForm Date Dátum Mime type Mime type SortFormBase Sort Criteria Sort Criteria Sort the Sort the most relevant results by: most relevant results by: Descending Descending Close Bezárás Apply Apply SpecIdxW Special Indexing Egyedi indexelés Do not retry previously failed files. A korábban sikertelenül indexelt fájlok kihagyása Else only modified or failed files will be processed. Egyébként csak a módosult vagy korábban sikertelenül indexelt fájlok lesznek feldolgozva Erase selected files data before indexing. A kijelölt fájlok tárolt adatainak törlése indexelés előtt Directory to recursively index Directory to recursively index Browse Tallózás Start directory (else use regular topdirs): Kezdő könyvtár (üresen a rendes kezdő könyvtár): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Az összes fájl feldolgozásához üresen kell hagyni. Szóközökkel elválasztva több shell típusú minta is megadható.<br>A szóközt tartalmazó mintákat kettős idézőjellel kell védeni.<br>Csak kezdő könyvtár megadásával együtt használható. Selection patterns: Kijelölés mintával: Top indexed entity Az indexelendő kezdő könyvtár Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). A könyvtár, melyet rekurzívan indexelni kell.<br>A beállítófájlban megadott kezdő könyvtáron (topdir) belül kell lennie. Retry previously failed files. Retry previously failed files. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Szóvizsgáló &Expand &Listázás Alt+E Alt+E &Close &Bezárás Alt+C Alt+C Term Szó No db info. Nincs információ az adatbázisról. Doc. / Tot. Dok. / Össz. Match Egyéb beállítások Case Kis-és nagybetű Accents Ékezetek SpellW Wildcards Helyettesítő karakterek Regexp Reguláris kifejezés Spelling/Phonetic Írásmód/fonetika Aspell init failed. Aspell not installed? Az aspell indítása nem sikerült. Telepítve van? Aspell expansion error. Aspell toldalékolási hiba. Stem expansion Szótő és toldalékok error retrieving stemming languages hiba a szótőképzés nyelvének felismerésekor No expansion found Nincsenek toldalékok. Term Szó Doc. / Tot. Dok. / Össz. Index: %1 documents, average length %2 terms Index: %1 documents, average length %2 terms Index: %1 documents, average length %2 terms.%3 results Index: %1 dokumentum, átlagosan %2 szó. %3 találat. %1 results %1 találat List was truncated alphabetically, some frequent Ez egy rövidített, betűrend szerinti lista, gyakori terms may be missing. Try using a longer root. szavak hiányozhatnak. Javallott hosszabb szógyök megadása. Show index statistics Indexstatisztika Number of documents A dokumentumok száma Average terms per document A szavak átlagos száma dokumentumonként Smallest document length Smallest document length Longest document length Longest document length Database directory size Az adatbázis mérete MIME types: MIME típusok: Item Megnevezés Value Érték Smallest document length (terms) A szavak száma a legrövidebb dokumentumban Longest document length (terms) A szavak száma a leghosszabb dokumentumban Results from last indexing: A legutóbbi indexelés eredménye: Documents created/updated létrehozott/frissített dokumentum Files tested vizsgált fájl Unindexed files nem indexelt fájl List files which could not be indexed (slow) List files which could not be indexed (slow) Spell expansion error. Spell expansion error. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index A kijelölt könyvtár nem tartalmaz Xapian indexet. This is the main/local index! Ez a fő-/helyi index! The selected directory is already in the index list A kijelölt könyvtár már szerepel az indexben. Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) error retrieving stemming languages hiba a szótőképzés nyelvének felismerésekor Choose Tallózás Result list paragraph format (erase all to reset to default) A találati lista bekezdésformátuma (törléssel visszaáll az alapértelmezettre) Result list header (default is empty) A találati lista fejléce (alapértelmezetten üres) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) A Recoll beállításainak vagy a Xapian indexnek a könyvtára (pl.: /home/felhasznalo/.recoll vagy /home/felhasznalo/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read A kijelölt könyvtárban egy olvashatatlan Recoll beállítás található. At most one index should be selected Csak egy indexet lehet kijelölni. Cant add index with different case/diacritics stripping option Eltérő kis-és nagybetű-, ill. ékezetkezelésű index nem adható hozzá. Default QtWebkit font Alapértelmezett QtWebkit betűkészlet Any term Bármely szó All terms Minden szó File name Fájlnév Query language Keresőnyelv Value from previous program exit A legutóbb használt Context Context Description Description Shortcut Shortcut Default Default Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Felhasználói felület Number of entries in a result page A találatok száma laponként Result list font A találati lista betűkészlete Helvetica-10 Helvetica-10 Opens a dialog to select the result list font A találati lista betűkészletének kiválasztása Reset Alaphelyzet Resets the result list font to the system default A találati lista betűkészletének rendszerbeli alapértelmezésére állítása Auto-start simple search on whitespace entry. Automatikus keresés szóköz hatására Start with advanced search dialog open. Az összetett keresés ablaka is legyen nyitva induláskor Start with sort dialog open. Start with sort dialog open. Search parameters Keresési beállítások Stemming language A szótőképzés nyelve Dynamically build abstracts Dinamikus kivonatolás Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Próbáljon-e tartalmi kivonatot készíteni a keresőszavak alapján a találati lista elemeihez? Nagy dokumentumok esetén lassú lehet. Replace abstracts from documents A kivonat cseréje Do we synthetize an abstract even if the document seemed to have one? Kivonatoljon akkor is, ha a dokumentum már rendelkezik ezzel? Synthetic abstract size (characters) A kivonat mérete (karakter) Synthetic abstract context words Az kivonat környező szavainak száma External Indexes Külső indexek Add index Index hozzáadása Select the xapiandb directory for the index you want to add, then click Add Index Select the xapiandb directory for the index you want to add, then click Add Index Browse Tallózás &OK &OK Apply changes A változtatások alkalmazása &Cancel &Mégsem Discard changes A változtatások elvetése Result paragraph<br>format string Result paragraph<br>format string Automatically add phrase to simple searches Az egyszerű keresés automatikus bővítése részmondattal A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Ha például a keresőkifejezés a [rolling stones] (két szó), akkor helyettesítődik a [rolling OR stones OR (rolling PHRASE 2 stones)] kifejezéssel. Így előbbre kerülnek azok a találatok, meylek a keresett szavakat pontosan úgy tartalmazzák, ahogyan meg lettek adva. User preferences Beállítások Use desktop preferences to choose document editor. Use desktop preferences to choose document editor. External indexes External indexes Toggle selected A kijelölt váltása Activate All Mindet bekapcsol Deactivate All Mindet kikapcsol Remove selected A kijelölt törlése Remove from list. This has no effect on the disk index. Törlés a listából. Az index a lemezről nem törlődik. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Remember sort activation state. A rendezési állapot mentése Maximum text size highlighted for preview (megabytes) Az előnézeti kiemelés korlátja (megabyte) Texts over this size will not be highlighted in preview (too slow). Ezen méret felett az előnézetben nem alkalmaz kiemelést (túl lassú) Highlight color for query terms A keresőszavak kiemelésének színe Prefer Html to plain text for preview. Az előnézetben HTML egyszerű szöveg helyett If checked, results with the same content under different names will only be shown once. A különböző nevű, de azonos tartalmú találatokból csak egy jelenjen meg Hide duplicate results. Többszörös találatok elrejtése Choose editor applications A társítások beállítása Display category filter as toolbar instead of button panel (needs restart). Display category filter as toolbar instead of button panel (needs restart). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Szavak listája, melyek keresőszóként megadva automatikusan ext:xxx keresőnyelvi kifejezéssé alakíttatnak Query language magic file name suffixes. Keresőnyelvi mágikus fájlnévkiterjesztések Enable Bekapcsolás ViewAction Changing actions with different current values Changing actions with different current values Mime type Mime type Command Parancs MIME type MIME típus Desktop Default Asztali alapértelmezés Changing entries with different current values A cserélendő mezők értéke eltér egymástól. ViewActionBase File type File type Action Action Select one or several file types, then click Change Action to modify the program used to open them Select one or several file types, then click Change Action to modify the program used to open them Change Action Change Action Close Bezárás Native Viewers Dokumentumtípusok megjelenítői Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Egy vagy több MIME típus kijelölése után az alsó keretben állítható be az adott típusokhoz elvárt művelet. Use Desktop preferences by default Az asztali alapértelmezés alkalmazása Select one or several file types, then use the controls in the frame below to change how they are processed Kijelölhető egy vagy több elérési út is Exception to Desktop preferences Eltérés az asztali beállításoktól Action (empty -> recoll default) Művelet (üres -> Recoll alapértelmezés) Apply to current selection Alkalmazás a kijelöltekre Recoll action: Recoll művelet: current value jelenlegi érték Select same Azonosak kijelölése <b>New Values:</b> <b>Új érték:</b> Webcache Webcache editor Webes gyorstár szerkesztése Search regexp Keresés reguláris kifejezéssel TextLabel WebcacheEdit Copy URL URL másolása Unknown indexer state. Can't edit webcache file. Az indexelő állapota ismeretlen. A webes gyorstár nem szerkeszthető. Indexer is running. Can't edit webcache file. Az indexelő fut. A webes gyorstár nem szerkeszthető. Delete selection A kijelöltek törlése Webcache was modified, you will need to run the indexer after closing this window. A webes gyorstár módosult. Ezen ablak bezárása után indítani kell az indexelőt. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url URL Date Dátum Size URL URL WinSchedToolW Error Hiba Configuration not initialized Configuration not initialized <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> Command already started Command already started Recoll Batch indexing Recoll Batch indexing Start Windows Task Scheduler tool Start Windows Task Scheduler tool Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Steal Beagle indexing queue Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Web cache directory name Web cache directory name The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Max. size for the web cache (MB) Max. size for the web cache (MB) Entries will be recycled once the size is reached Entries will be recycled once the size is reached Web page store directory name A weblapokat tároló könyvtár neve The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. A látogatott weblapok másolatát tároló könyvtár neve.<br>Relatív elérési út a beállításokat tároló könyvtárhoz képest értendő. Max. size for the web store (MB) A webes tároló max. mérete (MB) Process the WEB history queue A webes előzmények feldolgozása Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) A Firefoxszal látogatott oldalak indexelése<br>(a Firefox Recoll kiegészítőjét is telepíteni kell) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). A méret elérésekor a legkorábbi bejegyzések törlődnek.<br>Csak a növelésnek van haszna, mivel csökkentéskor a már létező fájl nem lesz kisebb (csak egy része állandóan kihasználatlan marad). confgui::ConfIndexW Can't write configuration file A beállítófájl írása sikertelen Recoll - Index Settings: Recoll - Index Settings: confgui::ConfParamFNW Browse Tallózás Choose Tallózás confgui::ConfParamSLW + + - - Add entry Add entry Delete selected entries Delete selected entries ~ ~ Edit selected entries Edit selected entries confgui::ConfSearchPanelW Automatic diacritics sensitivity Automatikus ékezetérzékenység <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Automatikusan különbözőnek tekinti az ékezetes betűket az ékezet nélküli párjuktól, ha tartalmaz ékezetes betűt a kifejezés (az unac_except_trans kivételével). Egyébként a keresőnyelv <i>D</i> módosítójával érhető el ugyanez. Automatic character case sensitivity Kis-és nagybetűk automatikus megkülönböztetése <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Automatikusan különbözőnek tekinti a kis-és nagybetűket, ha az első karakter kivételével bárhol tartalmaz nagybetűt a kifejezés. Egyébként a keresőnyelv <i>C</i> módosítójával érhető el ugyanez. Maximum term expansion count A toldalékok maximális száma <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Egy szó toldalékainak maximális száma (pl. helyettesítő karakterek használatakor). Az alapértelmezett 10 000 elfogadható érték, és elkerülhető vele a felhasználói felület időleges válaszképtelensége is. Maximum Xapian clauses count A Xapian feltételek maximális száma <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. Egy Xapian kereséshez felhasználható elemi feltételek maximális száma. Néha a szavak toldalékolása szorzó hatású, ami túlzott memóriahasználathoz vezethet. Az alapértelmezett 100 000 a legtöbb esetben elegendő, de nem is támaszt különleges igényeket a hardverrel szemben. confgui::ConfSubPanelW Global Minden könyvtárra vonatkozik Max. compressed file size (KB) A tömörített fájlok max. mérete (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. A tömörített fájlok indexbe kerülésének határértéke. -1 esetén nincs korlát. 0 esetén soha nem történik kicsomagolás. Max. text file size (MB) Szövegfájl max. mérete (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. A szövegfájlok indexbe kerülésének határértéke. -1 esetén nincs korlát. Az óriásira nőtt naplófájlok feldolgozása kerülhető el így. Text file page size (KB) Szövegfájl lapmérete (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Ha be van állítva (nem egyenlő -1), akkor a szövegfájlok indexelése ilyen méretű darabokban történik. Ez segítséget nyújt a nagyon nagy méretű szövegfájlokban (pl. naplófájlok) való kereséshez. Max. filter exec. time (S) A szűrő max. futási ideje (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. A túl hosszú ideig futó külső szűrők leállítása Néha előfordul (pl. postscript esetén), hogy a szűrő végtelen ciklusba kerül. -1 esetén nincs korlát. Only mime types MIME típusok An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Az indexelendő MIME típusok listája.<br>Csak ezek a típusok kerülnek az indexbe. Rendesen üres és inaktív. Exclude mime types Kizárt MIME típusok Mime types not to be indexed Ezek a MIME típusok kimaradnak az indexelésből Max. filter exec. time (s) Max. filter exec. time (s) confgui::ConfTopPanelW Top directories Kezdő könyvtárak The list of directories where recursive indexing starts. Default: your home. A megadott könyvtárak rekurzív indexelése. Alapértelmezett értéke a saját könyvtár. Skipped paths Kizárt elérési utak These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Az indexelő által mellőzendő könyvtárak nevei.<br>Használhatók a helyettesítő karakterek. Csak az indexelő hatókörébe eső elérési utakat lehet megadni (pl.: ha a kezdő könyvtár a „/home/felhasznalo” és a „/home” egy link a „/usr/home”-ra, akkor helyes elérési út a „/home/felhasznalo/tmp*”, de nem az a „/usr/home/felhasznalo/tmp*”). Stemming languages A szótőképzés nyelve The languages for which stemming expansion<br>dictionaries will be built. Ezen nyelvekhez készüljön szótövező és -toldalékoló szótár Log file name A naplófájl neve The file where the messages will be written.<br>Use 'stderr' for terminal output Az üzenetek kiírásának a helye.<br>A „stderr” a terminálra küldi az üzeneteket. Log verbosity level A naplózás szintje This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Az üzenetek mennyiségének szabályozása,<br>a hibaüzenetekre szorítkozótól a részletes hibakeresésig. Index flush megabytes interval Indexírási intervallum (MB) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Az az adatmennyiség, melyet két lemezre írás között az indexelő feldolgoz.<br>Segíthet kézben tartani a memóriafoglalást. Alapértelmezett: 10MB Max disk occupation (%) Max. lemezhasználat (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Százalékos lemezfoglalás, melyen túllépve az indexelő nem működik tovább (megelőzendő az összes szabad hely elfoglalását).<br>0 esetén nincs korlát (alapértelmezett). No aspell usage Az aspell mellőzése Aspell language Az aspell nyelve The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Database directory name Az adatbázis könyvtárneve The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Use system's 'file' command Use system's 'file' command Use the system's 'file' command if internal<br>mime type identification fails. Use the system's 'file' command if internal<br>mime type identification fails. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. A szóvizsgálóban az aspell használatának mellőzése a hasonló szavak keresésekor.<br>Hasznos, ha az aspell nincs telepítve vagy nem működik. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Az aspell szótár nyelve. pl. „en” vagy „hu”...<br>Ha nincs megadva, akkor az NLS környezet alapján lesz beállítva, ez általában megfelelő. A rendszerre telepített nyelveket az „aspell config” parancs kiadása után a „data-dir” könyvtárban található .dat fájlokból lehet megtudni. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Az indexet tartalmazó könyvtár neve.<br>Relatív elérési út a beállítási könyvtárhoz képest értendő. Alapértelmezett: „xapiandb”. Unac exceptions Unac kivételek <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Az unac alapértelmezetten eltávolít minden ékezetet és szétbontja a ligatúrákat. Az itt megadott kivételekkel lehetőség van adott karakterek esetén tiltani a műveletet, ha a használt nyelv ezt szükségessé teszi. Ezen kívül előírhatók további felbontandó karakterek is. Az egyes elemeket egymástól szóközzel kell elválasztani. Egy elem első karaktere az eredetit, a további karakterek a várt eredményt határozzák meg. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Max disk occupation (%, 0 means no limit) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. uiPrefsDialogBase User preferences Beállítások User interface Felhasználói felület Number of entries in a result page A találatok száma laponként If checked, results with the same content under different names will only be shown once. A különböző nevű, de azonos tartalmú találatokból csak egy jelenjen meg Hide duplicate results. Többszörös találatok elrejtése Highlight color for query terms A keresőszavak kiemelésének színe Result list font A találati lista betűkészlete Opens a dialog to select the result list font A találati lista betűkészletének kiválasztása Helvetica-10 Helvetica-10 Resets the result list font to the system default A találati lista betűkészletének rendszerbeli alapértelmezésére állítása Reset Alaphelyzet Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Result paragraph<br>format string Result paragraph<br>format string Texts over this size will not be highlighted in preview (too slow). Ezen méret felett az előnézetben nem alkalmaz kiemelést (túl lassú) Maximum text size highlighted for preview (megabytes) Az előnézeti kiemelés korlátja (megabyte) Use desktop preferences to choose document editor. Use desktop preferences to choose document editor. Choose editor applications A társítások beállítása Display category filter as toolbar instead of button panel (needs restart). Display category filter as toolbar instead of button panel (needs restart). Auto-start simple search on whitespace entry. Automatikus keresés szóköz hatására Start with advanced search dialog open. Az összetett keresés ablaka is legyen nyitva induláskor Start with sort dialog open. Start with sort dialog open. Remember sort activation state. A rendezési állapot mentése Prefer Html to plain text for preview. Az előnézetben HTML egyszerű szöveg helyett Search parameters Keresési beállítások Stemming language A szótőképzés nyelve A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Ha például a keresőkifejezés a [rolling stones] (két szó), akkor helyettesítődik a [rolling OR stones OR (rolling PHRASE 2 stones)] kifejezéssel. Így előbbre kerülnek azok a találatok, meylek a keresett szavakat pontosan úgy tartalmazzák, ahogyan meg lettek adva. Automatically add phrase to simple searches Az egyszerű keresés automatikus bővítése részmondattal Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Próbáljon-e tartalmi kivonatot készíteni a keresőszavak alapján a találati lista elemeihez? Nagy dokumentumok esetén lassú lehet. Dynamically build abstracts Dinamikus kivonatolás Do we synthetize an abstract even if the document seemed to have one? Kivonatoljon akkor is, ha a dokumentum már rendelkezik ezzel? Replace abstracts from documents A kivonat cseréje Synthetic abstract size (characters) A kivonat mérete (karakter) Synthetic abstract context words Az kivonat környező szavainak száma The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Szavak listája, melyek keresőszóként megadva automatikusan ext:xxx keresőnyelvi kifejezéssé alakíttatnak Query language magic file name suffixes. Keresőnyelvi mágikus fájlnévkiterjesztések Enable Bekapcsolás External Indexes Külső indexek Toggle selected A kijelölt váltása Activate All Mindet bekapcsol Deactivate All Mindet kikapcsol Remove from list. This has no effect on the disk index. Törlés a listából. Az index a lemezről nem törlődik. Remove selected A kijelölt törlése Click to add another index directory to the list Click to add another index directory to the list Add index Index hozzáadása Apply changes A változtatások alkalmazása &OK &OK Discard changes A változtatások elvetése &Cancel &Mégsem Abstract snippet separator A kivonat elemeinek elválasztója Use <PRE> tags instead of <BR>to display plain text as html. Use <PRE> tags instead of <BR>to display plain text as html. Lines in PRE text are not folded. Using BR loses indentation. Lines in PRE text are not folded. Using BR loses indentation. Style sheet Stíluslap Opens a dialog to select the style sheet file A megjelenés stílusát leíró fájl kiválasztása Choose Tallózás Resets the style sheet to default A stílus visszaállítása az alapértelmezettre Lines in PRE text are not folded. Using BR loses some indentation. Lines in PRE text are not folded. Using BR loses some indentation. Use <PRE> tags instead of <BR>to display plain text as html in preview. Use <PRE> tags instead of <BR>to display plain text as html in preview. Result List Találati lista Edit result paragraph format string A találatok bekezdésformátuma Edit result page html header insert A találatok lapjának fejlécformátuma Date format (strftime(3)) Dátumformátum (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Egy olyan gyakorisági határérték, mely felett az adott szavak kihagyandók a részmondatokból. Részmondatkereséskor a gyakori szavak a teljesítménybeli problémák fő okai. A kihagyott szavak lazítják a részek közti kapcsolatot és gyengítik az automatikus részmondat hatásfokát. Az alapértelmezett érték 2 (százalék). Autophrase term frequency threshold percentage Az automatikus részmondatok százalékos gyakorisági határértéke Plain text to HTML line style Az egyszerű szövegből alkotott HTML sor stílusa Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. A PRE tagok közti sorok nincsenek törve. BR tag estén a behúzások elveszhetnek. A PRE+wrap valószínűleg a legjobb választás. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + wrap Exceptions Exceptions Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Disable Qt autocompletion in search entry. A Qt automatikus kiegészítésének tiltása a keresőmezőben Search as you type. Keresés minden leütéskor Paths translations Elérési út átalakítása Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. További index felvétele a listára. Egy Recoll beállítási könyvtárat vagy egy Xapian indexkönyvtárat kell megadni. Snippets window CSS file CSS az <i>Érdemi részek</i> ablakhoz Opens a dialog to select the Snippets window CSS style sheet file Az <i>Érdemi részek</i> ablak tartalmának stílusát leíró fájl kiválasztása Resets the Snippets window style Az <i>Érdemi részek</i> ablak stílusának alaphelyzetbe állítása Decide if document filters are shown as radio buttons, toolbar combobox, or menu. A szűrők megjeleníthetők rádiógombokkal, legördülő listában az eszköztáron vagy menüben Document filter choice style: A szűrőválasztó stílusa: Buttons Panel Rádiógombok Toolbar Combobox Legördülő lista Menu Menü Show system tray icon. Ikon az értesítési területen Close to tray instead of exiting. Bezárás az értesítési területre kilépés helyett Start with simple search mode Az egyszerű keresés módja induláskor Show warning when opening temporary file. Ideiglenes fájlok megnyitásakor figyelmeztetés User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Az <i>Érdemi részek</i> ablak tartalmára alkalmazandó stílus.<br>A találati lista fejléce az <i>Érdemi részek</i> ablakban is megjelenik. Synonyms file Szinonímafájl Highlight CSS style for query terms Highlight CSS style for query terms Recoll - User Preferences Recoll - User Preferences Set path translations for the selected index or for the main one if no selection exists. Set path translations for the selected index or for the main one if no selection exists. Activate links in preview. Activate links in preview. Make links inside the preview window clickable, and start an external browser when they are clicked. Make links inside the preview window clickable, and start an external browser when they are clicked. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Start search on completer popup activation. Start search on completer popup activation. Maximum number of snippets displayed in the snippets window Maximum number of snippets displayed in the snippets window Sort snippets by page number (default: by weight). Sort snippets by page number (default: by weight). Suppress all beeps. Suppress all beeps. Application Qt style sheet Application Qt style sheet Limit the size of the search history. Use 0 to disable, -1 for unlimited. Limit the size of the search history. Use 0 to disable, -1 for unlimited. Maximum size of search history (0: disable, -1: unlimited): Maximum size of search history (0: disable, -1: unlimited): Generate desktop notifications. Generate desktop notifications. Misc Misc Work around QTBUG-78923 by inserting space before anchor text Work around QTBUG-78923 by inserting space before anchor text Display a Snippets link even if the document has no pages (needs restart). Display a Snippets link even if the document has no pages (needs restart). Maximum text size highlighted for preview (kilobytes) Maximum text size highlighted for preview (kilobytes) Start with simple search mode: Az egyszerű keresés módja induláskor: Hide toolbars. Hide toolbars. Hide status bar. Hide status bar. Hide Clear and Search buttons. Hide Clear and Search buttons. Hide menu bar (show button instead). Hide menu bar (show button instead). Hide simple search type (show in menu only). Hide simple search type (show in menu only). Shortcuts Shortcuts Hide result table header. Hide result table header. Show result table row headers. Show result table row headers. Reset shortcuts defaults Reset shortcuts defaults Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Use F1 to access the manual Use F1 to access the manual Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Simple search type Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Dark mode Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Result Table Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_cs.ts0000644000175000017500000071667414444307651014265 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Všechny výrazy Any clause Některý z výrazů texts Texty spreadsheets Tabulky presentations Představení media Multimedia messages Zprávy other Jiné Bad multiplier suffix in size filter Špatná přípona násobitele ve filtru velikosti text Text spreadsheet Tabulky presentation Představení message Zpráva Advanced Search Pokročilé hledání History Next Další historie History Prev Předchozí historie Load next stored search Načíst další uložené vyhledávání Load previous stored search Načíst předchozí uložené vyhledávání AdvSearchBase Advanced search Pokročilé hledání Restrict file types Omezit souborových typů Save as default Uložit jako výchozí Searched file types Hledané souborové typy All ----> Vše ----> Sel -----> Výběr -----> <----- Sel <----- Výběr <----- All <----- Vše Ignored file types Přehlížené souborové typy Enter top directory for search Zadejte základní adresář pro hledání Browse Procházet Restrict results to files in subtree: Omezit výsledky na soubory v následujícím podadresáři: Start Search Spustit hledání Search for <br>documents<br>satisfying: Hledat <br>dokumenty<br>, které splňují následující hlediska: Delete clause Smazat poslední výraz Add clause Přidat nový výraz Check this to enable filtering on file types Zaškrtněte pro zapnutí filtrování podle souborových typů By categories Podle skupin Check this to use file categories instead of raw mime types Zaškrtněte pro používání skupin souborů místo MIME typů Close Zavřít All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Všechna pole napravo, která nejsou prázdná, budou spojována spojeními AND (volba "Všechny výrazy") nebo OR (volba "Některý z výrazů"). <br>Typy polí "Jakékoli" "Vše" a "Žádné" mohou přijmout směs jednoduchých slov, a věty uzavřené dvojitými uvozovkami.<br>Pole bez dat jsou přehlížena. Invert Obrátit Minimum size. You can use k/K,m/M,g/G as multipliers Nejmenší velikost: Můžete použít k/K,m/M,g/G jako násobitele Min. Size Nejmenší velikost Maximum size. You can use k/K,m/M,g/G as multipliers Největší velikost: Můžete použít k/K,m/M,g/G jako násobitele Max. Size Největší velikost Select Vybrat Filter Filtrovat From Od To Do Check this to enable filtering on dates Zaškrtněte pro zapnutí filtrování podle dat Filter dates Filtrovat data Find Najít Check this to enable filtering on sizes Zaškrtněte pro zapnutí filtrování podle velikostí Filter sizes Filtrovat velikosti Filter birth dates ConfIndexW Can't write configuration file Nelze zapsat soubor s nastavením Global parameters Celkové parametry Local parameters Místní parametry Search parameters Parametry hledání Top directories Počáteční adresáře The list of directories where recursive indexing starts. Default: your home. Seznam adresářů, ve kterých začíná rejstříkování včetně podsložek. Výchozí: adresář Home. Skipped paths Přeskočené cesty These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Názvy názvy cest adresářů, které se nebudou rejstříkovat.<br>Může obsahovat zástupné symboly (žolíky). Musí odpovídat cestám, které rejstříkovač vidí (např. pokud v počátečních adresářích stojí '/home/me' a '/home' je ve skutečnosti odkazem na '/usr/home', potom by byl správným zápisem skippedPath '/home/me/tmp*' a ne '/usr/home/me/tmp*') Stemming languages Jazyky s kmeny slov The languages for which stemming expansion<br>dictionaries will be built. Jazyky, pro které se vytvoří <br>adresáře rozšíření kmenů slov. Log file name Název pro soubor se zápisem The file where the messages will be written.<br>Use 'stderr' for terminal output Soubor, do kterého se zapíše výstupní zpráva.<br>Pro výstupy na terminál použijte 'stderr' Log verbosity level Úroveň podrobnosti zápisu This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Tato hodnota upravuje množství zpráv,<br>od pouze chyb až po velké množství dat zajímavých pro ladění. Index flush megabytes interval Interval v megabytech pro vymazání rejstříku This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Tato hodnota upravuje množství dat, která jsou rejstříkována mezi spláchnutími na disk.<br>Pomáhá to řídit použití paměti rejstříkovače. Výchozí je 10 MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Toto je procentní podíl využívání disku - celkové využití disku, ne velikost rejstříku , kdy rejstříkování selže a zastaví se (kvůli vyhnutí se zaplnění vašeho disku).<br>Výchozí hodnota 0 odstraní všechna omezení, znamená žádné omezení. No aspell usage Nepoužívat aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Zakáže používání aspellu pro vytvoření přibližné podoby pravopisu v nástroji průzkumníka výrazů.<br> Užitečné, pokud aspell není přítomen anebo nepracuje. Aspell language Jazyk aspellu The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Jazyk pro adresář aspellu. Mělo by to vypadat jako 'en' nebo 'fr' nebo 'cs'...<br>Pokud není tato hodnota nastavena, použije se pro její vypočítání prostředí NLS, což obvykle pracuje. Pro získání představy o tom, co je ve vašem systému nainstalováno, napište 'aspell config' a hledejte soubory .dat v adresáři 'data-dir'. Database directory name Název adresáře s databází The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Název pro adresář, v němž se má ukládat rejstřík.<br>Neabsolutní cesta je vzata relativně k adresáři s nastavením. Výchozí je 'xapiandb'. Unac exceptions Výjimky unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Toto jsou výjimky pro mechanismus unac, který ve výchozím nastavení odstraňuje všechny diakritické znaky a nahrazuje je kanonickými obdobami. Toto odstraňování akcentů můžete (v závislosti na vaší řeči) pro některé znaky potlačit a zadat dodatečná nahrazení, např. pro ligatury. V každém mezerou odděleném záznamu je první znak zdrojovým (výchozím) a zbytek je nahrazení. Process the WEB history queue Zpracovat řadu historie WEBu Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Povolí rejstříkování Firefoxem navštívených stránek.<br>(také je potřeba, abyste nainstalovali přídavný modul Recollu pro Firefox) Web page store directory name Název adresáře pro ukládání internetové stránky The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Název pro adresář, kam se mají ukládat kopie navštívených internetových stránek.<br>Neabsolutní cesta je vzata relativně k adresáři s nastavením. Max. size for the web store (MB) Největší velikost pro ukládání internetových stránek (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Záznamy budou po dosažení velikosti vráceny do původního stavu.<br>Skutečně dává smysl jen zvětšení velikosti, protože zmenšení hodnoty neoseká stávající soubor (na konci jen plýtvání místem). Automatic diacritics sensitivity Automaticky rozlišovat diakritická znaménka <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Zapnout automaticky rozlišování diakritických znamének, když hledaný pojem obsahuje znaky a akcenty (ne v unac_except_trans). Jinak pro musíte použít jazyk dotazu a modifikátor <i>D</i>. Automatic character case sensitivity Automaticky rozlišovat velká a malá písmena <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Zapnout automaticky rozlišování velkých a malých písmen, když záznam obsahuje velká písmena (mimo na prvním místě). Jinak pro musíte použít jazyk dotazu a modifikátor <i>C</i>. Maximum term expansion count Největší počet rozšíření výrazu <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Největší počet rozšíření pro jeden výraz (např. při použití žolíků). Standardní výchozí hodnota 10 000 je rozumná a zabrání tomu, aby se hledaný pojem jevil jako zamrzlý, zatímco je procházen seznam pojmů. Maximum Xapian clauses count Největší počet výrazů Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Největší počet základních výrazů, které přidáme do jednoho dotazu Xapian. V některých případech se mohou výsledky rozšíření výrazu vynásobit, a my se chceme vyvarovat nadbytečné spotřebě paměti. Standardní výchozí hodnota 100 000 by měla ve většině případů naprosto postačovat a hodit se k typickému současnému sestavení zařízení (hardware). The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The language for which stemming expanzionaries will be build.<br>See Xapian stemmer documentation for possible values. Např. czech english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Jazyk pro aspell slovník. Hodnoty jsou dvoupísmenné jazykové kódy, např. 'cs', 'fr' . .<br>Pokud tato hodnota není nastavena, bude k výpočtu použita NLS prostředí, které obvykle funguje. Chcete-li získat představu o tom, co je instalováno na vašem systému, napište 'aspell config' a hledejte . u souborů v adresáři 'data-dir'. Indexer log file name Název souboru protokolu indexeru If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Je-li prázdné, bude použita výše uvedená hodnota názvu souboru protokolu. Může být užitečné mít samostatný záznam pro diagnostické účely, protože společný log bude vymazán při<br>spuštění GUI. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Procento plného limitu disku, na kterém přestaneme indexovat<br>Např. 90% se zastaví na 90% plné, 0 nebo 100 znamená bez limitu) Web history Historie webu Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Pouze typy MIME An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Vybraný seznam rejstříkovaných typů MIME.<br>Nic jiného se nebude rejstříkovat. Obyčejně je seznam prázdný a nečinný Exclude mime types Vyloučené typy MIME Mime types not to be indexed Typy MIME, které se nemají rejstříkovat Max. compressed file size (KB) Největší velikost zabaleného souboru (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Tato hodnota nastavuje práh, za kterým nebudou zabalené soubory zpracovávány. Nastavte na -1 pro žádné omezení, na 0 pro vůbec žádné rozbalování. Max. text file size (MB) Největší velikost textového souboru (KB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Tato hodnota nastavuje práh, za kterým nebudou textové soubory zpracovávány. Nastavte na -1 pro žádné omezení. Je to kvůli vyloučení obřích souborů se zápisem z rejstříkování. Text file page size (KB) Velikost stránky textového souboru (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Pokud je nastavena tato hodnota (nerovná se -1), textové soubory budou pro rejstříkování rozděleny na kousky o této velikosti. To pomůže při prohledávání velmi velkých textových souborů (např. souborů se zápisem). Max. filter exec. time (s) Max. doba trvání filtru (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Vnější filtry pracující déle než po tak dlouhou dobu budou přerušeny. Je to pro ten zřídkavý případ (např. postscript), kdy by dokument mohl zapříčinit vejití filtru do smyčky. Nastavte na -1 pro žádné omezení. Global Celkové CronToolW Cron Dialog Dialog Cron <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> dávkový rejstříkovací rozvrh (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Každé pole může obsahovat zástupný symbol (*), jednoduchou číselnou hodnotu, čárkou oddělené seznamy (1,3,5) a rozsahy (1-7). Obecněji, pole se budou používat <span style=" font-style:italic;">jak je</span> uvnitř souboru crontab, a lze použít úplnou stavbu crontab, podívejte se na crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Například, zadání <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Dny, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> a <span style=" font-family:'Courier New,courier';">15</span> v <span style=" font-style:italic;">Minuty</span> spustí rejstříkování (recollindex) každý den v 12:15 dopoledne a 7:15 odpoledne</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Rozvrh s velmi častým spuštěním je pravděpodobně méně účinný než je rejstříkování ve skutečném čase.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Dny v týdnu (* nebo 0-7, 0 nebo 7 je neděle) Hours (* or 0-23) Hodiny (* nebo 0-23) Minutes (0-59) Minuty (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Klepněte na <span style=" font-style:italic;">Zakázat</span> pro zastavení automatického dávkového rejstříkování, <span style=" font-style:italic;">Povolit</span> pro jeho zapnutí, <span style=" font-style:italic;">Zrušit</span>, aby vše zůstalo beze změny.</p></body></html> Enable Povolit Disable Zakázat It seems that manually edited entries exist for recollindex, cannot edit crontab Zdá se, že pro recollindex existují ručně upravené záznamy, nelze upravit crontab Error installing cron entry. Bad syntax in fields ? Chyba při instalaci záznamu cron. Špatná skladba v polích? EditDialog Dialog Dialog EditTrans Source path Cesta ke zdroji Local path Místní cesta Config error Chyba v nastavení Original path Původní cesta EditTransBase Path Translations Překlady cest Setting path translations for Nastavení překladů cest pro Select one or several file types, then use the controls in the frame below to change how they are processed Vyberte jeden nebo více datových typů a použijte ovládací prvky v rámečku níže pro změnu způsobu, jakým jsou zpracovány Add Přidat Delete Smazat Cancel Zrušit Save Uložit FirstIdxDialog First indexing setup První nastavení rejstříkování <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Zdá se, že rejstřík pro toto nastavení neexistuje.</span><br /><br />Pokud chcete pouze zrejstříkovat svůj domovský adresář sadou rozumných výchozích nastavení, stiskněte tlačítko <span style=" font-style:italic;">Spustit rejstříkování nyní</span>. Podrobnosti budete moci upravit později. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud chcete mít větší dohled, použijte následující odkazy pro upravení nastavení rejstříkování a rozvrhu.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">K těmto nástrojům lze přistupovat později v nabídce <span style=" font-style:italic;">Nastavení</span>.</p></body></html> Indexing configuration Nastavení rejstříkování This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Toto vám umožní nastavit adresáře, které chcete rejstříkovat, a další parametry, jako jsou cesty pro vyloučené soubory, výchozí znakové sady atd. Indexing schedule Rozvrh rejstříkování This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Toto vám umožní zvolit mezi dávkovým rejstříkováním a rejstříkováním ve skutečném čase, a nastavit automatický rozvrh pro dávkové rejstříkování (za použití cronu). Start indexing now Spustit rejstříkování nyní FragButs %1 not found. %1 nenalezen. %1: %2 %1: %2 Fragment Buttons Fragment tlačítka Query Fragments Kousky hledání IdxSchedW Index scheduling setup Nastavení rozvrhu rejstříkování <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> rejstříkování může běžet nepřetržitě, soubory se rejstříkují při jejich změně, nebo běžet v samostatných intervalech. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Četba příručky vám může pomoci při rozhodování se mezi těmito přístupy (stiskněte F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Tento nástroj vám může pomoci s nastavením rozvrhu pro automatizaci běhů dávkového rejstříkování, nebo spustit rejstříkování ve skutečném čase, když se přihlásíte (nebo obojí, což zřídkakdy dává smysl). </p></body></html> Cron scheduling Rozvrh cron The tool will let you decide at what time indexing should run and will install a crontab entry. Nástroj vám umožní rozhodnout se, kdy má rejstříkování běžet, a nainstaluje záznam crontab. Real time indexing start up Spuštění rejstříkování ve skutečném čase Decide if real time indexing will be started when you log in (only for the default index). Rozhodněte, zda se rejstříkování ve skutečném čase spustí, když se přihlásíte (pouze pro výchozí rejstřík). ListDialog Dialog Dialog GroupBox Seskupovací okénko Main No db directory in configuration Nenastaven žádný databázový adresář Could not open database in Nepodařilo se otevřít databázi v . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Klepněte na tlačítko Zrušit pro úpravu souboru s nastavením, předtím než se začne s rejstříkováním nebo na OK pro započetí s rejstříkováním. Configuration problem (dynconf Konfigurationsproblem (dynconf) "history" file is damaged or un(read)writeable, please check or remove it: Soubor "history" je poškozen nebo nezapisovatelný/nečitelný. Prověřte jej, prosím, anebo jej odstraňte: "history" file is damaged, please check or remove it: Soubor "history" je poškozen. Prověřte jej, prosím, anebo jej odstraňte: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Hledat: &Next &Další &Previous &Předchozí Match &Case Dbát na &psaní velkých a malých písmen Clear Vyprázdnit Creating preview text Vytváří se náhledový text Loading preview text into editor Náhledový text se nahrává do editoru Cannot create temporary directory Nelze vytvořit dočasný adresář Cancel Zrušit Close Tab Zavřít kartu Missing helper program: Chybí program s nápovědou: Can't turn doc into internal representation for Chyba při rejstříkování dokumentu Cannot create temporary directory: Nelze vytvořit dočasný adresář: Error while loading file Chyba při nahrávání souboru Form Formulář Tab 1 Tab 1 Open Otevřít Canceled Zrušeno Error loading the document: file missing. Chyba při načítání dokumentu: soubor chybí. Error loading the document: no permission. Chyba při načítání dokumentu: žádné oprávnění. Error loading: backend not configured. Chyba při načítání: backend není nakonfigurován. Error loading the document: other handler error<br>Maybe the application is locking the file ? Chyba při načítání dokumentu: jiná chyba handleru<br>Možná aplikace uzamčuje soubor? Error loading the document: other handler error. Chyba při načítání dokumentu: jiná chyba obsluhy. <br>Attempting to display from stored text. <br>Pokus o zobrazení z uloženého textu. Could not fetch stored text Nelze načíst uložený text Previous result document Dokument s předchozím výsledkem Next result document Dokument s dalším výsledkem Preview Window Náhled okna Close Window Zavřít okno Next doc in tab Další doc v záložce Previous doc in tab Předchozí doc v záložce Close tab Zavřít kartu Print tab Print tab Close preview window Zavřít náhled Show next result Zobrazit další výsledek Show previous result Zobrazit předchozí výsledek Print Tisk PreviewTextEdit Show fields Ukázat pole Show main text Ukázat hlavní text Print Tisk Print Current Preview Vytisknout nynější náhled Show image Ukázat obrázek Select All Vybrat vše Copy Kopírovat Save document to file Uložit dokument do souboru Fold lines Zalomit řádky Preserve indentation Zachovat odsazení Open document Otevřít dokument Reload as Plain Text Reload as HTML QObject Global parameters Celkové parametry Local parameters Místní parametry <b>Customised subtrees <b>Vlastní podstromy The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Seznam podadresářů v rejstříkované hierarchii <br>kde některé parametry je potřeba nově vymezit. Výchozí: prázdný. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Parametry, které následují, jsou nastaveny buď na nejvyšší úrovni, pokud nic<br>, nebo pokud je v seznamu výše vybrán prázdný řádek, nebo pro vybraný podadresář.<br>Adresáře můžete přidat anebo odebrat klepnutím na tlačítka +/-. Skipped names Přeskočené názvy These are patterns for file or directory names which should not be indexed. Toto jsou vzory pro názvy souborů nebo adresářů, které se nemají rejstříkovat. Default character set Výchozí znaková sada This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Toto je znaková sada, která se používá pro čtení souborů, které svou znakovou sadu vnitřně neurčují, např.. soubory s textem.<br>Výchozí hodnota je prázdná a používá se hodnota prostředí NLS. Follow symbolic links Sledovat symbolické odkazy Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Během rejstříkování sledovat symbolické odkazy. Výchozí nastavení je ne kvůli vyvarovaní se dvojitého rejstříkování Index all file names Rejstříkovat všechny souborové názvy Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Rejstříkovat všechny názvy souborů, jejichž obsah nelze určit nebo zpracovat (žádný nebo nepodporovaný MIME typ). Výchozí hodnota je ano Beagle web history Internetová historie Beagle Search parameters Parametry hledání Web history Historie webu Default<br>character set Výchozí<br>znaková sada Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Toto je znaková sada, která se používá pro čtení souborů, které svou znakovou sadu vnitřně neurčují, např.. soubory s textem.<br>Výchozí hodnota je prázdná a používá se hodnota prostředí NLS. Ignored endings Přehlížená zakončení These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Toto jsou konce názvu souboru pro soubory, které budou indexovány pouze podle obsahu (žádný pokus o identifikaci typu MIME, žádné dekomprese, žádná indexace obsahu. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Toto jsou zakončení souborů pro soubory, které se budou rejstříkovat výhradně podle svého názvu (žádné určování typu MIME, žádné rozbalování, žádné rejstříkování obsahu). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>Parametry následující jsou nastaveny buď na nejvyšší úrovni, pokud je ve výše uvedeném listboxu nebo pro vybraný podadresář vybrán žádný nebo prázdný řádek. Složky můžete přidat nebo odebrat kliknutím na tlačítka +/. QWidget Create or choose save directory Vytvořit nebo vybrat ukládací adresář Choose exactly one directory Vybrat přesně jeden adresář Could not read directory: Nepodařilo se číst z adresáře: Unexpected file name collision, cancelling. Neočekávaný střet v souborovém názvu. Ruší se. Cannot extract document: Nelze vytáhnout dokument: &Preview &Náhled &Open &Otevřít Open With Otevřít s Run Script Spustit skript Copy &File Name Kopírovat název &souboru Copy &URL Kopírovat adresu (&URL) &Write to File &Zapsat do souboru Save selection to files Uložit výběr do souborů Preview P&arent document/folder Náhled na &rodičovský dokument/složku &Open Parent document/folder &Otevřít rodičovský dokument/složku Find &similar documents Najít &podobné dokumenty Open &Snippets window Otevřít okno s úr&yvky Show subdocuments / attachments Ukázat podřízené dokumenty/přílohy &Open Parent document &Otevřít nadřazený dokument &Open Parent Folder &Otevřít nadřazenou složku Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Neukazovat znovu. RTIToolW Real time indexing automatic start Automatické spuštění rejstříkování ve skutečném čase <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> rejstříkování může být nastaveno tak, aby běželo jako démon. Soubory jsou aktualizovány při jejich změně, ve skutečném čase. Získáte tak vždy nejnovější rejstřík, ale prostředky systému se při tom používají nepřetržitě.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Spustit rejstříkovacího démona s mým sezením pracovní plochy. Also start indexing daemon right now. Spustit rejstříkovacího démona ihned. Replacing: Nahrazení: Replacing file Nahrazení souboru Can't create: Nelze vytvořit: Warning Varování Could not execute recollindex Nepodařilo se spustit recollindex Deleting: Mazání: Deleting file Smazání souboru Removing autostart Odstranění automatického spuštění Autostart file deleted. Kill current process too ? Soubor automatického spuštění smazán. Zabít i současný proces? RclCompleterModel Hits RclMain About Recoll O programu Recoll Executing: [ Provádí se: [ Cannot retrieve document info from database Žádné informace o dokumentu v databázi Warning Varování Can't create preview window Nelze vytvořit náhledové okno Query results Výsledky hledání Document history Historie dokumentu History data Historická data Indexing in progress: Rejstříkuje se: Files Soubory Purge Vyčistit Stemdb Kmeny slov Closing Zavření Unknown Neznámý This search is not active any more Toto hledání už není činné Can't start query: Nelze spustit hledání: Bad viewer command line for %1: [%2] Please check the mimeconf file Chybový příkaz pro prohlížeč pro %1: [%2] Prověřte soubor mimeconf Cannot extract document or create temporary file Nelze vytáhnout dokument nebo vytvořit dočasný soubor (no stemming) Źádné rozšíření kmene slova (all languages) Všechny jazyky error retrieving stemming languages Chyba při vyhledání jazyků s kmeny slov Update &Index Obnovit &rejstřík Indexing interrupted Rejstříkování přerušeno Stop &Indexing Zastavit &rejstříkování All Vše media Multimedia message Zpráva other Jiné presentation Představení spreadsheet Tabulky text Text sorted Tříděno filtered Filtrováno External applications/commands needed and not found for indexing your file types: Pro rejstříkování vašich MIME typů jsou potřeba vnější programy/příkazy, které ale nebyly nalezeny: No helpers found missing Nenalezeny žádné pomocné programy Missing helper programs Chybí pomocné programy Save file dialog Uložit dialog souboru Choose a file name to save under Vyberte název souboru k uložení Document category filter Filtr pro skupinu dokumentu No external viewer configured for mime type [ Žádný vnější prohlížeč nebyl nastaven pro MIME typ [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Prohlížeč stanovený v MIME zobrazení pro %1: %2 nenalezen. Chcete spustit dialog s nastavením? Can't access file: Nelze přistoupit k souboru: Can't uncompress file: Nelze rozbalit soubor: Save file Uložit soubor Result count (est.) Počet výsledků (odhad) Query details Podrobnosti o hledání Could not open external index. Db not open. Check external indexes list. Nepodařilo se otevřít vnější rejstřík. Databáze neotevřena. Prověřte seznam vnějších rejstříků. No results found Nenalezeny žádné výsledky None Žádný Updating Obnova Done Hotovo Monitor Dohled Indexing failed Rejstříkování se nezdařilo The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Nynější rejstříkovací proces nebyl spuštěn z tohoto rozhraní. Klepněte na OK pro jeho zabití, nebo na Zrušit, aby byl ponechán sám Erasing index Smazání rejstříku Reset the index and start from scratch ? Nastavit rejstřík znovu a začít od nuly? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Hledání běží.<br>Kvůli omezením rejstříkovací knihovny<br>zrušení ukončí program Error Chyba Index not open Rejstřík neotevřen Index query error Chyba při hledání v rejstříku Indexed Mime Types Rejstříkované mime typy Content has been indexed for these MIME types: Obsah byl indexován pro tyto typy MIME: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Rejstřík není pro tento soubor nejnovější. Odmítá se riziko, že by byl ukázán špatný záznam. Klepněte na OK pro obnovení rejstříku pro tento soubor, pak, až bude rejstříkování hotovo, spusťte dotaz znovu. Jinak klepněte na Zrušit. Can't update index: indexer running Nelze obnovit rejstřík: běží rejstříkovač Indexed MIME Types Rejstříkované MIME typy Bad viewer command line for %1: [%2] Please check the mimeview file Chybový příkaz pro prohlížeč pro %1: [%2] Prověřte soubor mimeconf Viewer command line for %1 specifies both file and parent file value: unsupported Příkaz pro prohlížeč pro %1 stanovuje jak hodnotu souboru tak hodnotu rodičovského souboru: nepodporováno Cannot find parent document Nelze najít rodičovský dokument Indexing did not run yet Rejstříkování ještě neběželo External applications/commands needed for your file types and not found, as stored by the last indexing pass in Pro vaše souborové typy jsou potřeba vnější programy/příkazy, které ale nebyly nalezeny, jak byly uloženy při posledním rejstříkování v Index not up to date for this file. Refusing to risk showing the wrong entry. Rejstřík není pro tento soubor nejnovější. Ukázání nesprávného záznamu bylo zamítnuto. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Klepněte na tlačítko pro aktualizaci rejstříku pro tento soubor, potom dotaz, až bude rejstříkování hotovo, spusťte znovu. Jinak klepněte na Zrušit. Indexer running so things should improve when it's done Rejstříkovač běží, takže věci by se po dokončení rejstříkování měly zlepšit Sub-documents and attachments Podřízené dokumenty a přílohy Document filter Filtr dokumentu Index not up to date for this file. Refusing to risk showing the wrong entry. Rejstřík není pro tento soubor nejnovější. Ukázání nesprávného záznamu bylo zamítnuto. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Klepněte na tlačítko pro aktualizaci rejstříku pro tento soubor, potom hledání, až bude rejstříkování hotovo, spusťte znovu. The indexer is running so things should improve when it's done. Rejstříkovač běží, takže věci by se po dokončení rejstříkování měly zlepšit. The document belongs to an external indexwhich I can't update. Dokument je součástí vnějšího rejstříku, který nelze aktualizovat. Click Cancel to return to the list. Click Ignore to show the preview anyway. Klepněte na tlačítko Zrušit pro návrat do seznamu. Klepněte na tlačítko Přehlížet, aby byl přesto ukázán náhled. Duplicate documents Zdvojené dokumenty These Urls ( | ipath) share the same content: Tyto adresy ( | ipath) sdílejí totožný obsah: Bad desktop app spec for %1: [%2] Please check the desktop file Chybná specifikace aplikace pro %1: [%2] Prověřte soubor pracovní plochy Bad paths Špatné cesty Bad paths in configuration file: Špatné cesty v souboru s nastavením: Selection patterns need topdir Výběrové vzory potřebují počáteční adresář Selection patterns can only be used with a start directory Výběrové vzory lze použít jen s počátečním adresářem No search Žádné hledání No preserved previous search Žádné zachované předchozí hledání Choose file to save Vybrat soubor k uložení Saved Queries (*.rclq) Uložené dotazy (*.rclq) Write failed Nepodařilo se zapsat Could not write to file Nepodařilo se zapsat do souboru Read failed Nepodařilo se přečíst Could not open file: Nepodařilo se otevřít soubor: Load error Chyba při nahrávání Could not load saved query Nepodařilo se nahrát uložené hledání Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Otevírá se dočasná kopie. Úpravy budou ztraceny, pokud je neuložíte<br/>do trvalého umístění. Do not show this warning next time (use GUI preferences to restore). Neukazovat toto varování příště (použít nastavení uživatelského rozhraní pro obnovení). Disabled because the real time indexer was not compiled in. Zakázáno protože rejstříkovač ve skutečném čase nebyl sestaven. This configuration tool only works for the main index. Tento nastavovací nástroj pracuje jen pro hlavní rejstřík. The current indexing process was not started from this interface, can't kill it Nynější rejstříkovací proces nebyl spuštěn z tohoto rozhraní. Nelze jej ukončit The document belongs to an external index which I can't update. Dokument je součástí vnějšího rejstříku, který nelze aktualizovat. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Klepněte na tlačítko Zrušit pro návrat do seznamu. <br>Klepněte na tlačítko Přehlížet pro ukázání náhledu tak jako tak (zapamatovat si pro toto sezení). Index scheduling Rozvržení rejstříkování Sorry, not available under Windows for now, use the File menu entries to update the index Promiňte. Není nyní dostupné pod OS Windows. Použijte položek v nabídce Soubor k aktualizaci rejstříku Can't set synonyms file (parse error?) Nelze nastavit soubor se slovy majícími stejný význam (synonyma). Chyba při zpracování? Index locked Rejstřík uzamknut Unknown indexer state. Can't access webcache file. Neznámý stav rejstříkovače. Nelze přistupovat k souboru s internetovou vyrovnávací pamětí. Indexer is running. Can't access webcache file. Rejstříkovač běží. Nelze přistupovat k souboru s internetovou vyrovnávací pamětí. with additional message: s dodatečnou zprávou: Non-fatal indexing message: Nekritická zpráva o rejstříkování: Types list empty: maybe wait for indexing to progress? Píše seznam prázdný: Možná počkat na pokračování rejstříkování? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Příkaz pro prohlížeč pro %1 stanovuje rodičovský soubor, ale adresa (URL) je http[s]: nepodporováno Tools Nástroje Results Výsledky (%d documents/%d files/%d errors/%d total files) (%d dokumentů/%d souborů/%d chyby/%d souborů) (%d documents/%d files/%d errors) (%d dokumentů/%d souborů /%d chyb) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Prázdné nebo neexistující cesty v konfiguračním souboru. Klepnutím na tlačítko Ok přesto začnete indexovat (chybějící data nebudou vymazána z indexu): Indexing done Indexování dokončeno Can't update index: internal error Může't aktualizovat index: interní chyba Index not up to date for this file.<br> Index není pro tento soubor aktuální.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Také se zdá, že poslední aktualizace indexu souboru selhala.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Klepnutím na tlačítko Ok se pokusíte aktualizovat index pro tento soubor. Po dokončení indexování budete muset dotaz znovu spustit.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Klepnutím na tlačítko Zrušit se vrátíte do seznamu.<br>Klepnutím na tlačítko Ignorovat zobrazení náhledu (a pamatovat pro tuto relaci). Existuje riziko, že se zobrazí chybná položka.<br/> documents dokumenty document dokument files soubory file soubor errors chyby error chyba total files) celkem souborů) No information: initial indexing not yet performed. Žádné informace: počáteční indexování ještě nebylo provedeno. Batch scheduling Hromadné plánování The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Nástroj vám umožní rozhodnout, v jakém čase by se indexace měla spustit. Používá plánovač úkolů Windows. Confirm Potvrdit Erasing simple and advanced search history lists, please click Ok to confirm Mazání jednoduchých a pokročilých seznamů historie vyhledávání, prosím klikněte na tlačítko OK pro potvrzení Could not open/create file Nelze otevřít/vytvořit soubor F&ilter &Prolínač Could not start recollindex (temp file error) Nelze spustit recollindex (dočasná chyba souboru) Could not read: Nelze přečíst: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Toto nahradí aktuální obsah řetězce seznamu výsledků a názvu souboru GUI qss. Pokračovat? You will need to run a query to complete the display change. Pro dokončení změny zobrazení budete muset spustit dotaz. Simple search type Jednoduchý typ vyhledávání Any term Jakýkoli výraz All terms Všechny výrazy File name Název souboru Query language Jazyk hledání Stemming language Jazyk s kmeny slov Main Window Hlavní okno Focus to Search Zaměřit se na hledání Focus to Search, alt. Zaměřit se na hledání, také. Clear Search Vymazat hledání Focus to Result Table Zaměřit se na tabulku výsledků Clear search Vymazat hledání Move keyboard focus to search entry Přesunout zaměření klávesnice do položky hledání Move keyboard focus to search, alt. Přesuňte zaměření klávesnice pro vyhledávání. Toggle tabular display Přepnout zobrazení tabulky Move keyboard focus to table Přesunout zaostření klávesnice do tabulky Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Předchozí strana Next page Další strana &File &Soubor E&xit &Ukončit &Tools &Nástroje &Help &Nápověda &Preferences &Nastavení Search tools Nástroje pro hledání Result list Seznam s výsledky &About Recoll &O programu Recoll Document &History &Historie dokumentu Document History Historie dokumentu &Advanced Search &Pokročilé hledání Advanced/complex Search Pokročilé/Složené hledání &Sort parameters Parametry &třídění Sort parameters Parametry třídění Next page of results Další strana s výsledky Previous page of results Předchozí strana s výsledky &Query configuration Nastavení &hledání &User manual &Uživatelská příručka Recoll Přepočítat Ctrl+Q Ctrl + Q Update &index Obnovit &rejstřík Term &explorer Průzkumník &výrazů Term explorer tool Nástroj průzkumníka výrazů External index dialog Dialog pro vnější rejstříkování &Erase document history &Vymazat historii dokumentu First page První strana Go to first page of results Jít na první stranu s výsledky &Indexing configuration Nastavení &rejstříkování All Vše &Show missing helpers &Ukázat chybějící pomocné programy PgDown O stranu dolů (PgDown) Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Domů, Ctrl+S, Ctrl+Q, Ctrl+S PgUp O stranu nahoru (PgUp) &Full Screen &Celá obrazovka F11 F11 Full Screen Na celou obrazovku &Erase search history &Vymazat historii hledání sortByDateAsc Třídit podle data vzestupně Sort by dates from oldest to newest Roztřídit podle data od nejstaršího po nejnovější sortByDateDesc Třídit podle data sestupně Sort by dates from newest to oldest Roztřídit podle data od nejnovějšího po nejstarší Show Query Details Ukázat podrobnosti hledání Show results as table Ukázat výsledky jako tabulku &Rebuild index &Sestavit rejstřík znovu &Show indexed types &Ukázat rejstříkované typy Shift+PgUp Shift+PgUp &Indexing schedule Rozvrh &rejstříkování E&xternal index dialog Dialog pro &vnější rejstříkování &Index configuration Nastavení &rejstříku &GUI configuration Nastavení uživatelského roz&hraní &Results &Výsledky Sort by date, oldest first Roztřídit podle data, nejprve nejstarší Sort by date, newest first Roztřídit podle data, nejprve nejnovější Show as table Ukázat jako tabulku Show results in a spreadsheet-like table Ukázat výsledky v tabulce na způsob sešitu s listy v tabulkovém kalkulátoru Save as CSV (spreadsheet) file Uložit jako soubor CSV (tabulkový dokument) Saves the result into a file which you can load in a spreadsheet Uložit výsledek do souboru, jejž můžete nahrát jako sešit s listy v tabulkovém kalkulátoru Next Page Další strana Previous Page Předchozí strana First Page První strana Query Fragments Kousky hledání With failed files retrying S novým pokusem o zpracování selhavších souborů Next update will retry previously failed files Nová aktualizace rejstříku se pokusí znovu zpracovat nyní nezpracované soubory Save last query Uložit poslední hledání Load saved query Nahrát uložené hledání Special Indexing Zvláštní rejstříkování Indexing with special options Rejstříkování se zvláštními volbami Indexing &schedule &Rozvrh rejstříkování Enable synonyms Povolit slova mající stejný význam &View &Pohled Missing &helpers Chybějící &pomocné programy Indexed &MIME types Rejstříkované &MIME typy Index &statistics &Statistika rejstříku Webcache Editor Editor internetové vyrovnávací paměti Trigger incremental pass Spustit přírůstkové procházení E&xport simple search history &Exportovat jednoduchou historii vyhledávání Use default dark mode Použít výchozí tmavý režim Dark mode Tmavý režim &Query &Dotaz Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Filtrovat data Assisted complex search Filter birth dates RclTrayIcon Restore Obnovit Quit Ukončit RecollModel Abstract Výtah Author Autor Document size Velikost dokumentu Document date Datum dokumentu File size Velikost souboru File name Název souboru File date Datum souboru Ipath Ipath Keywords Klíčová slova Mime type Mime typ Original character set Původní znaková sada Relevancy rating Hodnocení závažnosti Title Název URL Adresa (URL) Mtime Mtime Date Datum Date and time Datum a čas Ipath Cesta MIME type Typ MIME Can't sort by inverse relevance Může'seřadit podle obráceného významu ResList Result list Výsledky Unavailable document Nedostupný dokument Previous Předchozí Next Další <p><b>No results found</b><br> <p><b>Nebyly nalezeny žádné výsledky</b><br> &Preview &Náhled Copy &URL Kopírovat adresu (&URL) Find &similar documents Najít &podobné dokumenty Query details Podrobnosti o hledání (show query) (ukázat hledání) Copy &File Name Kopírovat název &souboru filtered Filtrováno sorted Tříděno Document history Historie dokumentu Preview Náhled Open Otevřít <p><i>Alternate spellings (accents suppressed): </i> <p><i>Náhradní pravopis (přízvuky potlačeny): </i> &Write to File &Zapsat do souboru Preview P&arent document/folder Náhled na &rodičovský dokument/složku &Open Parent document/folder &Otevřít rodičovský dokument/složku &Open &Otevřít Documents Dokumenty out of at least mimo alespoň for pro <p><i>Alternate spellings: </i> <p><i>Náhradní pravopis: </i> Open &Snippets window Otevřít okno s úr&yvky Duplicate documents Zdvojené dokumenty These Urls ( | ipath) share the same content: Tyto adresy ( | ipath) sdílejí totožný obsah: Result count (est.) Počet výsledků (odhad) Snippets Úryvky This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort Nastavit třídění &znovu &Delete column &Smazat sloupec Add " Přidat " " column " sloupec Save table to CSV file Uložit tabulku jako soubor CSV Can't open/create file: Nelze otevřít/vytvořit soubor: &Preview &Náhled &Open &Otevřít Copy &File Name Kopírovat název &souboru Copy &URL Kopírovat adresu (&URL) &Write to File &Zapsat do souboru Find &similar documents Najít &podobné dokumenty Preview P&arent document/folder Náhled na &rodičovský dokument/složku &Open Parent document/folder &Otevřít rodičovský dokument/složku &Save as CSV &Uložit jako CSV Add "%1" column Přidat sloupec "%1" Result Table Tabulka výsledků Open Otevřít Open and Quit Otevřít a ukončit Preview Náhled Show Snippets Zobrazit snippety Open current result document Otevřít aktuální dokument výsledku Open current result and quit Otevřít aktuální výsledek a ukončit Show snippets Zobrazit textové bloky Show header Zobrazit záhlaví Show vertical header Zobrazit vertikální záhlaví Copy current result text to clipboard Zkopírovat aktuální text výsledku do schránky Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Náhled &Open &Otevřít Copy &File Name Kopírovat název &souboru Copy &URL Kopírovat adresu (&URL) &Write to File &Zapsat do souboru Find &similar documents Najít &podobné dokumenty Preview P&arent document/folder Náhled na &rodičovský dokument/složku &Open Parent document/folder &Otevřít rodičovský dokument/složku ResultPopup &Preview &Náhled &Open &Otevřít Copy &File Name Kopírovat název &souboru Copy &URL Kopírovat adresu (&URL) &Write to File &Zapsat do souboru Save selection to files Uložit výběr do souborů Preview P&arent document/folder Náhled na &rodičovský dokument/složku &Open Parent document/folder &Otevřít rodičovský dokument/složku Find &similar documents Najít &podobné dokumenty Open &Snippets window Otevřít okno s úr&yvky Show subdocuments / attachments Ukázat podřízené dokumenty/přílohy Open With Otevřít s Run Script Spustit skript SSearch Any term Jakýkoli výraz All terms Všechny výrazy File name Název souboru Completions Doplnění Select an item: Vyberte položku: Too many completions Příliš mnoho doplnění Query language Jazyk hledání Bad query string Špatný řetězec hledání Out of memory Není dostupná žádná další paměť Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Zadejte výraz jazyka hledání. Seznam:<br> <i>term1 term2</i> : 'term1' a 'term2' do kteréhokoli pole.<br> <i>field:term1</i> : 'term1' do pole 'field'.<br> Obvyklé názvy polí/synonyma:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudopole: dir, mime/format, type/rclcat, date.<br> Příklady intervalů dvou dat: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> Nepovoleny žádné závorky.<br> <i>"term1 term2"</i> : větný úsek (musí se objevit přesně). Možné modifikátory:<br> <i>"term1 term2"p</i> : neuspořádané hledání podle blízkosti s výchozí vzdáleností.<br> Použijte odkaz <b>Ukázat hledání</b>, když máte o výsledku pochybnost, a podívejte se do příručky (&lt;F1>) na další podrobnosti. Enter file name wildcard expression. Zadejte žolíkový výraz (zástupný symbol) pro název souboru. Enter search terms here. Type ESC SPC for completions of current term. Zde zadejte hledané výrazy. Stiskněte ESC SPC pro doplnění současného výrazu. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Zadejte výraz jazyka hledání. Seznam:<br> <i>term1 term2</i> : 'term1' a 'term2' do kteréhokoli pole.<br> <i>field:term1</i> : 'term1' do pole 'field'.<br> Obvyklé názvy polí/synonyma:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudopole: dir, mime/format, type/rclcat, date.<br> Příklady intervalů dvou dat: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> Můžete použít kulaté závorky, aby byly věci zřetelnější.<br> <i>"term1 term2"</i> : větný úsek (musí se objevit přesně). Možné modifikátory:<br> <i>"term1 term2"p</i> : neuspořádané hledání podle blízkosti s výchozí vzdáleností.<br> Použijte odkaz <b>Ukázat hledání</b>, když máte o výsledku pochybnost, a podívejte se do příručky (&lt;F1>) na další podrobnosti. Stemming languages for stored query: Jazyky s kmeny slov pro uložená hledání: differ from current preferences (kept) liší se od nynějšího nastavení (ponecháno) Auto suffixes for stored query: Automatické přípony pro uložená hledání: External indexes for stored query: Vnější rejstříky pro uložená hledání: Autophrase is set but it was unset for stored query Automatické tvoření slovních obratů je nastaveno, ale bylo zrušeno pro uložené hledání Autophrase is unset but it was set for stored query Automatické tvoření slovních obratů je zrušeno, ale bylo nastaveno pro uložené hledání Enter search terms here. Zde zadejte hledané výrazy. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; hranice: 1px pevná černá; border-collapse: collapse; zhroucení hranic: zhroucení; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Jazyk dotazu. Kliknutím <b>Zobrazit dotaz</b>.&nbsp; You should really look at the manual (F1)</p> Měli byste se opravdu podívat na manuál (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Co</th><th>Příklady</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>A</td><td>jeden druhý&nbsp;&nbsp;&nbsp;one AND dva&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Nebo</td><td>jeden NEBO dva&nbsp;&nbsp;&nbsp;one || dva</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Komplexní boolean. NEBO má prioritu, použijte závorky&nbsp; where needed</td><td>(one AND two) OR three</td></tr> v případě potřeby</td><td>(jeden A dva) NEBO tři</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Ne</td><td>období</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Fráze</td><td>"pride a dotčení"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Neuspořádané prox. (výchozí slack=10)</td><td>"poškodí&nbsp;hrdost"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Žádné kmenové rozšíření: kapitalizovat</td><td>Floor</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>specifické pro pole</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>A uvnitř pole (bez pořadí)</td><td>autor:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>NEBO uvnitř pole</td><td>autor:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Názvy polí</td><td>název/předmět/titulek&nbsp;&nbsp;autora/od<br>příjemce/do&nbsp;&nbsp;název souboru&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Cesta k filtru adresáře</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>filtr typu MIME</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Datové intervaly</td><td>datum:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> Datum:2018&nbsp;&nbsp;datum:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Může't otevřít index Could not restore external indexes for stored query:<br> Externí indexy pro uložený dotaz nelze obnovit:<br> ??? ??? Using current preferences. Použití aktuálních preferencí. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Smazat Ctrl+S Ctrl + S Erase search entry Smazat hledaný záznam Search Hledat Start query Spustit hledání Enter search terms here. Type ESC SPC for completions of current term. Zde zadejte hledané výrazy. Stiskněte ESC SPC pro doplnění současného výrazu. Choose search type. Vyberte typ hledání. Show query history Zobrazit historii dotazů Enter search terms here. Zde zadejte hledané výrazy. Main menu Hlavní menu SearchClauseW SearchClauseW Vyhledávací ClauseW Any of these jakýkoli z těchto All of these Všechny tyto None of these Žádný z těchto This phrase Tato slova Terms in proximity Podobné výrazy File name matching Odpovídající názvy souborů Select the type of query that will be performed with the words Vyberte druh hledání, se kterým se slova budou hledat Number of additional words that may be interspersed with the chosen ones Počet slov, která se smějí nacházet mezi hledanými In field V poli No field Žádné pole Any Jakýkoliv All Vše None Žádný Phrase Tato slova Proximity Podobné výrazy File name Název souboru Snippets Snippets Úryvky X X Find: Hledat: Next Další Prev Předchozí SnippetsW Search Hledat <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>V rámci omezení hledání nebyla bohužel nalezena žádná shoda. Pravděpodobně je dokument velice velký a vyvíječ úryvků se v něm ztratil (nebo skončil ve škarpě)...</p> Sort By Relevance Seřadit podle důležitosti Sort By Page Seřadit podle stránky Snippets Window Okno textových bloků Find Najít Find (alt) Najít (alt) Find Next Najít další Find Previous Najít předchozí Hide Skrýt Find next Najít další Find previous Najít předchozí Close window Zavřít okno SortForm Date Datum Mime type Mime Type SortFormBase Sort Criteria Sortierkriterium Sort the Zeige die most relevant results by: relevantesten Ergebnisse sortiert nach: Descending Absteigend Close Schließen Apply Übernehmen SpecIdxW Special Indexing Zvláštní rejstříkování Do not retry previously failed files. Nezkoušet znovu soubory, které předtím selhaly. Else only modified or failed files will be processed. Jinak jen změněné nebo selhavší soubory budou zpracovány. Erase selected files data before indexing. Vymazat před rejstříkováním data vybraných souborů. Directory to recursively index Adresář pro rekurzivní index Browse Procházet Start directory (else use regular topdirs): Začáteční adresář (jinak použít počáteční adresáře): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Ponechat prázdné pro vybrání všech souborů. Můžete použít více vzorů oddělených mezerami.<br>Vzory s vloženými mezerami musejí být uzavřeny ve dvojitých uvozovkách.<br>Lze použít, jen když je nastaven začáteční cíl. Selection patterns: Výběrové vzory: Top indexed entity Předmět rejstříkovaný od spuštění Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Adresář k rejstříkování včetně podadresářů. Musí být uvnitř rejstříkované oblasti<br>, jak je stanovena v souboru s nastavením (počáteční adresáře). Retry previously failed files. Opakovat dříve neúspěšné soubory. Start directory. Must be part of the indexed tree. We use topdirs if empty. Počáteční adresář. Musí být součástí indexovaného stromu. Pokud je prázdné, použijeme topdiry. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Počáteční adresář. Musí být součástí indexovaného stromu. Pokud je prázdné, použijte celou indexovanou oblast. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Průzkumník výrazů &Expand &Rozbalit Alt+E Alt + E &Close &Zavřít Alt+C Alt+C Term Ausdruck No db info. Žádné informace o databázi. Doc. / Tot. Dok. / Tot. Match Shoda Case Rozlišování velkých a malých písmen Accents Přízvuky SpellW Wildcards Zástupné symboly Regexp Regulární výraz Spelling/Phonetic Pravopis/Hláskosloví Aspell init failed. Aspell not installed? Chyba při spuštění Aspellu. Aspell není nainstalován? Aspell expansion error. Chyba rozšíření Aspell. Stem expansion Rozšíření kmene slova error retrieving stemming languages Chyba při vyhledání jazyka s kmeny slov No expansion found Nenalezeno žádné rozšíření Term Výraz Doc. / Tot. Dok. / Tot. Index: %1 documents, average length %2 terms Rejstřík: %1 dokumentů, průměrná délka %2 výrazy(ů) Index: %1 documents, average length %2 terms.%3 results Rejstřík: %1 dokumentů, průměrná délka %2 výrazy(ů). %3 výsledky(ů) %1 results %1 výsledky(ů) List was truncated alphabetically, some frequent Seznam byl zkrácen abecedně, některé četné terms may be missing. Try using a longer root. pojmy mohou chybět. Zkuste použít delší kořen. Show index statistics Ukázat statistiku rejstříku Number of documents Počet dokumentů Average terms per document Průměrný počet výrazů na dokument Smallest document length Délka nejmenšího dokumentu Longest document length Délka nejdelšího dokumentu Database directory size Velikost adresáře s databází MIME types: Typy MIME: Item Položka Value Hodnota Smallest document length (terms) Nejmenší délka dokumentu (mez) Longest document length (terms) Největší délka dokumentu (mez) Results from last indexing: Výsledky posledního rejstříkování: Documents created/updated Dokumenty vytvořené nebo aktualizované Files tested Vyzkoušené soubory Unindexed files Nezrejstříkované soubory List files which could not be indexed (slow) Vypsat soubory, které se nepodařilo zrejstříkovat (pomalé) Spell expansion error. Chyba v pravopisných návrzích. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Zdá se, že vybraný adresář není rejstříkem Xapian Index This is the main/local index! Toto je hlavní/místní rejstřík! The selected directory is already in the index list Vybraný adresář je již částí rejstříkového seznamu Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Vyberte adresář s rejstříkem Xapian Indexverzeichnis (např.: /home/benutzer/.recoll/xapiandb) error retrieving stemming languages Chyba při vyhledání jazyka s kmeny slov Choose Vybrat Result list paragraph format (erase all to reset to default) Formát odstavce seznamu s výsledky (vymazat všechny pro znovunastavení na výchozí) Result list header (default is empty) Záhlaví seznamu s výsledky (výchozí je prázdné) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Vyberte adresář s nastavením pro Recoll nebo rejstříkový adresář Xapianu (např.: /home/me/.recoll or /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Vybraný adresář vypadá jako adresář s nastavením pro Recoll, ale nastavení se nepodařilo přečíst At most one index should be selected Je potřeba vybrat alespoň jeden rejstřík Cant add index with different case/diacritics stripping option Nelze přidat rejstřík s odlišnou volbou pro velikost písma/diakritiku Default QtWebkit font Výchozí písmo QtWebkit Any term Jakýkoli výraz All terms Všechny výrazy File name Název souboru Query language Jazyk hledání Value from previous program exit Hodnota obdržená z posledního ukončení programu Context Kontext Description L 343, 22.12.2009, s. 1). Shortcut Zkratka Default Výchozí Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Benutzeroberfläche Number of entries in a result page Anzahl der Ergebnisse pro Seite Result list font Schriftart für Ergebnisliste Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Öffnet einen Dialog zur Auswahl der Schriftart für die Ergebnisliste Reset Nastavit znovu Resets the result list font to the system default Setzt die Schriftart für die Ergebnisliste zurück auf den Standardwert Auto-start simple search on whitespace entry. Automatisch eine einfache Suche starten, wenn ein Worttrenner im Sucheingabefeld eingegeben wird. Start with advanced search dialog open. Nach dem Start automatisch den Dialog für die erweiterte Suche öffnen. Start with sort dialog open. Nach dem Start automatisch den Sortierdialog öffnen. Search parameters Suchparameter Stemming language Stemming Sprache Dynamically build abstracts Zusammenfassungen dynamisch erzeugen Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Festlegung ob Zusammenfassungen für Ergebnisse im Kontext der Suchparameter erzeugt werden (kann bei großen Dokumenten langsam sein). Replace abstracts from documents Ersetzen der Zusammenfassungen in den Dokumenten Do we synthetize an abstract even if the document seemed to have one? Festlegung ob eine Zusammenfassung auch dann erzeugt wird, wenn das Dokument schon eine Zusammenfassung enthält Synthetic abstract size (characters) Länge der erzeugten Zusammenfassung (Zeichen) Synthetic abstract context words Anzahl der Kontextworte in der Zusammenfassung External Indexes externe Indizes Add index Index hinzufügen Select the xapiandb directory for the index you want to add, then click Add Index Wählen Sie das xapiandb-Verzeichnis des zuzufügenden Indizes und klicken Sie auf Index hinzufügen Browse Auswahl &OK &OK Apply changes Änderungen übernehmen &Cancel &Abbrechen Discard changes Änderungen verwerfen Result paragraph<br>format string Formatstring für Ergebnisse Automatically add phrase to simple searches Automatisches Zufügen von Sätzen zu einfachen Suchen A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Eine Suche nach [Jürgen Klinsmann] wird geändert nach [Jürgen OR Klinsmann OR (Jürgen PHRASE 2 Klinsmann)]. Dadurch sollten Ergebnisse, die exakte Übereinstimmungen der Suchworte enthalten, stärker gewichtet werden. User preferences Benutzereinstellungen Use desktop preferences to choose document editor. Die Einstellung des Dokumenteneditors erfolgt in den Desktopvoreinstellungen. External indexes Externe Indizes Toggle selected Auswahl umkehren Activate All Alle Auswählen Deactivate All Alle Abwählen Remove selected Ausgewählte entfernen Remove from list. This has no effect on the disk index. Aus der Liste entfernen. Dies hat keinen Einfluss auf den gespeicherten Index. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Stanovuje formát pro každý odstavec seznamu s výsledky. Použijte qt nahrazení formátu html a printf:<br>%A přehled<br> %D datum<br> %I název obrázku ikony<br> %K klíčová slova (jsou-li)<br> %L odkazy na náhled a úpravy<br> %M mime typ<br> %N počet výsledků<br> %R procento významnosti<br> %S informace o velikosti<br> %T název<br> %U adresa (URL)<br> Remember sort activation state. Speichern, ob Sortieren aktiviert ist. Maximum text size highlighted for preview (megabytes) Největší velikost textu zvýrazněného pro náhled (megabyty) Texts over this size will not be highlighted in preview (too slow). Texty nad tuto velikost nebudou v náhledu zvýrazňovány (příliš pomalé). Highlight color for query terms Zvýraznit barvu výrazů hledání Prefer Html to plain text for preview. Upřednostňovat pro náhled HTML před prostým textem If checked, results with the same content under different names will only be shown once. Je-li zaškrtnuto, budou výsledky se stejným obsahem pod jinými názvy ukázány jen jednou. Hide duplicate results. Skrýt zdvojené výsledky. Choose editor applications Vybrat programy editorů Display category filter as toolbar instead of button panel (needs restart). Zobrazit skupinový filtr jako nástrojový pruh místo tlačítkového panelu (potřebuje spustit program znovu). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Slova v seznamu budou v záznamu jazyka hledání automaticky obrácena na věty ext:xxx. Query language magic file name suffixes. Kouzelné přípony souborového názvu jazyka hledání Enable Povolit ViewAction Changing actions with different current values Mění se činnosti s odlišnými nynějšími hodnotami Mime type Mime typ Command Příkaz MIME type Typ MIME Desktop Default Výchozí plocha Changing entries with different current values Mění se záznamy s odlišnými nynějšími hodnotami ViewActionBase File type Dateityp Action Aktion Select one or several file types, then click Change Action to modify the program used to open them Vyberte jeden nebo více datových typů a klepněte na "Změnit činnost" pro změnu programu přiřazeného k jejich otevření Change Action Změnit činnost Close Zavřít Native Viewers Prohlížeče Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Vyberte jeden nebo několik MIME typů a potom klepněte na "Změnit činnost"<br>Taktéž můžete tento dialog zavřít a zaškrtnout "Použít nastavení pracovní plochy"<br>v hlavním panelu, aby se tento seznam přehlížel a pracovní plocha se použila jako výchozí. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Vyberte jeden nebo více MIME typů a použijte ovládací prvky v rámečku s tlačítky pro změnu způsobu, jakým jsou zpracovány. Use Desktop preferences by default Použít nastavení pracovní plochy jako výchozí Select one or several file types, then use the controls in the frame below to change how they are processed Vyberte jeden nebo více datových typů a použijte ovládací prvky v rámečku níže pro změnu způsobu, jakým jsou zpracovány Exception to Desktop preferences Výjimka pro nastavení pracovní plochy Action (empty -> recoll default) Činnost (prázdné -> výchozí pro Recoll) Apply to current selection Použít na nynější výběr Recoll action: Aktion current value aktuální hodnota Select same Vybrat stejný <b>New Values:</b> <b>Nové hodnoty:</b> Webcache Webcache editor Editor internetové vyrovnávací paměti Search regexp Hledat regulární výraz TextLabel WebcacheEdit Copy URL Kopírovat adresu (URL) Unknown indexer state. Can't edit webcache file. Neznámý stav rejstříkovače. Nelze upravovat soubor s internetovou vyrovnávací pamětí. Indexer is running. Can't edit webcache file. Rejstříkovač běží. Nelze upravovat soubor s internetovou vyrovnávací pamětí. Delete selection Smazat výběr Webcache was modified, you will need to run the indexer after closing this window. Internetová vyrovnávací paměť byla změněna. Po zavření tohoto okna budete muset spustit rejstříkovač. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url URL Date Datum Size URL Adresa (URL) WinSchedToolW Error Chyba Configuration not initialized Konfigurace nebyla inicializována <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexování dávkového plánování</h3><p>K tomu používáme standardní plánovač úkolů pro Windows. Program bude spuštěn, když klepnete na tlačítko níže.</p><p>Můžete použít celé rozhraní (<i>Vytvořit úkol</i> v menu vpravo), nebo průvodce zjednodušeným <i>, vytvořit základní úkol</i> . V obou případech Kopírovat/Vložit cestu dávkového souboru uvedenou níže jako <i>Akce</i> , která má být provedena.</p> Command already started Příkaz byl již spuštěn Recoll Batch indexing Přepočítat indexování dávky Start Windows Task Scheduler tool Spustit Windows Task Scheduler nástroj Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Ukrást rejstříkovací řadu Beagle Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle NESMǏ běžet. Povolí zpracování řady Beagle pro rejstříkování internetové historie Firefoxu.<br>(také byste měl nainstalovat přídavný modul Beagle pro Firefox) Web cache directory name Název adresáře webové keše The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Název adresáře, ve kterém má být keš uložena pro navštívené webové stránky.<br>Neabsolutní cesta je vzhledem k adresáři konfigurace. Max. size for the web cache (MB) Max. velikost pro webovou keš (MB) Entries will be recycled once the size is reached Záznamy budou opětně použity, jakmile bude velikost dosažena Web page store directory name Název adresáře pro ukládání internetové stránky The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Název pro adresář, kam se mají ukládat kopie navštívených internetových stránek.<br>Neabsolutní cesta je vzata relativně k adresáři s nastavením. Max. size for the web store (MB) Největší velikost pro ukládání internetových stránek (MB) Process the WEB history queue Zpracovat řadu historie WEBu Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Povolí rejstříkování Firefoxem navštívených stránek.<br>(také je potřeba, abyste nainstalovali přídavný modul Recollu pro Firefox) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Záznamy budou po dosažení velikosti vráceny do původního stavu.<br>Skutečně dává smysl jen zvětšení velikosti, protože zmenšení hodnoty neoseká stávající soubor (na konci jen plýtvání místem). confgui::ConfIndexW Can't write configuration file Nelze zapsat soubor s nastavením Recoll - Index Settings: Přepočítat - Nastavení indexu: confgui::ConfParamFNW Browse Procházet Choose Vybrat confgui::ConfParamSLW + + - - Add entry Přidat položku Delete selected entries Odstranit vybrané položky ~ ~ Edit selected entries Upravit vybrané položky confgui::ConfSearchPanelW Automatic diacritics sensitivity Automaticky rozlišovat diakritická znaménka <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Zapnout automaticky rozlišování diakritických znamének, když hledaný pojem obsahuje znaky a akcenty (ne v unac_except_trans). Jinak pro musíte použít jazyk dotazu a modifikátor <i>D</i>. Automatic character case sensitivity Automaticky rozlišovat velká a malá písmena <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Zapnout automaticky rozlišování velkých a malých písmen, když záznam obsahuje velká písmena (mimo na prvním místě). Jinak pro musíte použít jazyk dotazu a modifikátor <i>C</i>. Maximum term expansion count Největší počet rozšíření výrazu <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Největší počet rozšíření pro jeden výraz (např. při použití žolíků). Standardní výchozí hodnota 10 000 je rozumná a zabrání tomu, aby se hledaný pojem jevil jako zamrzlý, zatímco je procházen seznam pojmů. Maximum Xapian clauses count Největší počet výrazů Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Největší počet základních výrazů, které přidáme do jednoho dotazu Xapian. V některých případech se mohou výsledky rozšíření výrazu vynásobit, a my se chceme vyvarovat nadbytečné spotřebě paměti. Standardní výchozí hodnota 100 000 by měla ve většině případů naprosto postačovat a hodit se k typickému současnému sestavení zařízení (hardware). confgui::ConfSubPanelW Global Celkové Max. compressed file size (KB) Největší velikost zabaleného souboru (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Tato hodnota nastavuje práh, za kterým nebudou zabalené soubory zpracovávány. Nastavte na -1 pro žádné omezení, na 0 pro vůbec žádné rozbalování. Max. text file size (MB) Největší velikost textového souboru (KB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Tato hodnota nastavuje práh, za kterým nebudou textové soubory zpracovávány. Nastavte na -1 pro žádné omezení. Je to kvůli vyloučení obřích souborů se zápisem z rejstříkování. Text file page size (KB) Velikost stránky textového souboru (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Pokud je nastavena tato hodnota (nerovná se -1), textové soubory budou pro rejstříkování rozděleny na kousky o této velikosti. To pomůže při prohledávání velmi velkých textových souborů (např. souborů se zápisem). Max. filter exec. time (S) Největší čas na provedení filtru (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Vnější filtry pracující déle než po tak dlouho budou přerušeny. Je to pro ten zřídkavý případ (např. postscript), kdy by dokument mohl zapříčinit filtr loopSet na -1 pro žádné omezení. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Vnější filtry pracující déle než po tak dlouhou dobu budou přerušeny. Je to pro ten zřídkavý případ (např. postscript), kdy by dokument mohl zapříčinit vejití filtru do smyčky. Nastavte na -1 pro žádné omezení. Only mime types Pouze typy MIME An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Vybraný seznam rejstříkovaných typů MIME.<br>Nic jiného se nebude rejstříkovat. Obyčejně je seznam prázdný a nečinný Exclude mime types Vyloučené typy MIME Mime types not to be indexed Typy MIME, které se nemají rejstříkovat Max. filter exec. time (s) Max. doba trvání filtru (s) confgui::ConfTopPanelW Top directories Počáteční adresáře The list of directories where recursive indexing starts. Default: your home. Seznam adresářů, ve kterých začíná rejstříkování včetně podsložek. Výchozí: adresář Home. Skipped paths Přeskočené cesty These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Názvy adresářů, které se nebudou rejstříkovat.<br>Může obsahovat zástupné symboly (žolíky). Musí odpovídat cestám, které rejstříkovač vidí (např. pokud v počátečních adresářích stojí '/home/me' a '/home' je ve skutečnosti odkazem na '/usr/home', potom by byl správným zápisem skippedPath '/home/me/tmp*' a ne '/usr/home/me/tmp*') Stemming languages Jazyky s kmeny slov The languages for which stemming expansion<br>dictionaries will be built. Jazyky, pro které se vytvoří <br>adresáře rozšíření kmenů slov. Log file name Název pro soubor se zápisem The file where the messages will be written.<br>Use 'stderr' for terminal output Soubor, do kterého se zapíše výstupní zpráva.<br>Pro výstupy na terminál použijte 'stderr' Log verbosity level Úroveň podrobnosti zápisu This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Tato hodnota upravuje množství zpráv,<br>od pouze chyb až po velké množství dat zajímavých pro ladění. Index flush megabytes interval Interval v megabytech pro vymazání rejstříku This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Tato hodnota upravuje množství dat, která jsou rejstříkována mezi spláchnutími na disk.<br>Pomáhá to řídit použití paměti rejstříkovače. Výchozí je 10 MB Max disk occupation (%) Největší obsazení disku (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Jde o procentní podíl využívání disku, kdy rejstříkování selže a zastaví se (kvůli vyhnutí se zaplnění vašeho disku).<br>0 znamená žádné omezení (tato hodnota je nastavena jako výchozí). No aspell usage Nepoužívat aspell Aspell language Jazyk aspellu The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Jazyk pro adresář aspellu. Mělo by to vypadat jako 'en' nebo 'fr' nebo 'cs'...<br>Pokud není tato hodnota nastavena, použije se pro její vypočítání prostředí NLS, což obvykle pracuje. Pro získání představy o tom, co je ve vašem systému nainstalováno, napište 'aspell config' a hledejte soubory .dat v adresáři 'data-dir'. Database directory name Název adresáře s databází The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Název pro adresář, v němž se má ukládat rejstřík.<br>Neabsolutní cesta je vzata relativně k adresáři s nastavením. Výchozí je 'xapiandb'. Use system's 'file' command Použít příkaz 'file' Use the system's 'file' command if internal<br>mime type identification fails. Použít příkaz 'file', když vnitřní<br>rozpoznání MIME typu selže. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Zakáže používání aspellu pro vytvoření přibližné podoby pravopisu v nástroji průzkumníka výrazů.<br> Užitečné, pokud aspell není přítomen anebo nepracuje. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Jazyk pro adresář aspellu. Mělo by to vypadat jako 'en' nebo 'fr' nebo 'cs'...<br>Pokud není tato hodnota nastavena, použije se pro její vypočítání prostředí NLS, což obvykle pracuje. Pro získání představy o tom, co je ve vašem systému nainstalováno, napište 'aspell config' a hledejte soubory .dat v adresáři 'data-dir'. The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Název pro adresář, v němž se má ukládat rejstřík.<br>Neabsolutní cesta je vzata relativně k adresáři s nastavením. Výchozí je 'xapiandb'. Unac exceptions Výjimky unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Toto jsou výjimky pro mechanismus unac, který ve výchozím nastavení odstraňuje všechny diakritické znaky a nahrazuje je kanonickými obdobami. Toto odstraňování akcentů můžete (v závislosti na vaší řeči) pro některé znaky potlačit a zadat dodatečná nahrazení, např. pro ligatury. V každém mezerou odděleném záznamu je první znak zdrojovým (výchozím) a zbytek je nahrazení. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Názvy názvy cest adresářů, které se nebudou rejstříkovat.<br>Může obsahovat zástupné symboly (žolíky). Musí odpovídat cestám, které rejstříkovač vidí (např. pokud v počátečních adresářích stojí '/home/me' a '/home' je ve skutečnosti odkazem na '/usr/home', potom by byl správným zápisem skippedPath '/home/me/tmp*' a ne '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Největší obsazení disku (%, 0 znamená bez omezení) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Toto je procentní podíl využívání disku - celkové využití disku, ne velikost rejstříku , kdy rejstříkování selže a zastaví se (kvůli vyhnutí se zaplnění vašeho disku).<br>Výchozí hodnota 0 odstraní všechna omezení, znamená žádné omezení. uiPrefsDialogBase User preferences Uživatelská nastavení User interface Uživatelské rozhraní Number of entries in a result page Počet výsledků na stranu If checked, results with the same content under different names will only be shown once. Je-li zaškrtnuto, budou výsledky se stejným obsahem pod jinými názvy ukázány jen jednou. Hide duplicate results. Skrýt zdvojené výsledky. Highlight color for query terms Zvýraznit barvu výrazů hledání Result list font Písmo pro seznam s výsledky Opens a dialog to select the result list font Otevře dialog pro výběr písma seznamu výsledků Helvetica-10 Helvetica-10 Resets the result list font to the system default Nastaví písmo pro seznam s výsledky znovu na výchozí hodnotu Reset Nastavit znovu Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Stanovuje formát pro každý odstavec seznamu s výsledky. Použijte qt nahrazení formátu html a printf:<br>%A přehled<br> %D datum<br> %I název obrázku ikony<br> %K klíčová slova (jsou-li)<br> %L odkazy na náhled a úpravy<br> %M mime typ<br> %N počet výsledků<br> %R procento významnosti<br> %S informace o velikosti<br> %T název<br> %U adresa (URL)<br> Result paragraph<br>format string Řetězec formátu<br>pro výsledky Texts over this size will not be highlighted in preview (too slow). Texty nad tuto velikost nebudou v náhledu zvýrazňovány (příliš pomalé). Maximum text size highlighted for preview (megabytes) Největší velikost textu zvýrazněného pro náhled (megabyty) Use desktop preferences to choose document editor. Použít nastavení pracovní plochy pro výběr editoru pro dokumenty. Choose editor applications Vybrat programy editorů Display category filter as toolbar instead of button panel (needs restart). Zobrazit skupinový filtr jako nástrojový pruh místo tlačítkového panelu (potřebuje spustit program znovu). Auto-start simple search on whitespace entry. Automaticky spustit jednoduché hledání, když je do zadávacího pole pro hledání zadáno prázdné místo (mezera). Start with advanced search dialog open. Po spuštění automaticky otevřít dialog pro rozšířené hledání Start with sort dialog open. Nach dem Start automatisch den Sortierdialog öffnen. Remember sort activation state. Zapamatovat si stav zapnutí hledání Prefer Html to plain text for preview. Upřednostňovat pro náhled HTML před prostým textem Search parameters Parametry hledání Stemming language Jazyk s kmeny slov A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Hledání [Ježíš Kristus] se změní na [Ježíš OR Kristus OR (Ježíš PHRASE 2 Kristus)]. Tímto by měly být silněji zváženy výsledky, které obsahují přesné shody s hledaným slovem. Automatically add phrase to simple searches Automatické přidání vět do jednoduchého hledání Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Stanovení, zda se má vytvořit přehled pro výsledky v souvislosti s parametrem hledání. U velkých dokumentů může být pomalé. Dynamically build abstracts Vytvářet přehledy dynamicky Do we synthetize an abstract even if the document seemed to have one? Stanovení, zda se má vytvořit přehled i tehdy, když dokument již nějaký přehled obsahuje. Replace abstracts from documents Nahradit přehledy v dokumentech Synthetic abstract size (characters) Délka vytvořeného přehledu (počet znaků) Synthetic abstract context words Počet souvisejících slov v přehledu The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Slova v seznamu budou v záznamu jazyka hledání automaticky obrácena na věty ext:xxx. Query language magic file name suffixes. Kouzelné přípony souborového názvu jazyka hledání Enable Povolit External Indexes Vnější rejstříky Toggle selected Přepnout vybrané Activate All Zapnout vše Deactivate All Vypnout vše Remove from list. This has no effect on the disk index. Odstranit ze seznamu. Nemá to žádný účinek na uložený rejstřík. Remove selected Odstranit vybrané Click to add another index directory to the list Klepnout pro přidání dalšího rejstříkového adresáře do seznamu Add index Přidat rejstřík Apply changes Použít změny &OK &OK Discard changes Zahodit změny &Cancel Z&rušit Abstract snippet separator Oddělovač úryvků Use <PRE> tags instead of <BR>to display plain text as html. Použít značky <PRE> namísto <BR> pro zobrazení prostého textu jako HTML. Lines in PRE text are not folded. Using BR loses indentation. Řádky v textu PRE nejsou složeny. Použití BR povede ke ztrátě odsazení. Style sheet Stylový list Opens a dialog to select the style sheet file Otevře dialog pro výběr souboru se stylovým listem Choose Vybrat Resets the style sheet to default Nastaví stylový list znovu na výchozí Lines in PRE text are not folded. Using BR loses some indentation. Řádky v textu PRE nejsou složeny. Použití BR povede ke ztrátě odsazení. Use <PRE> tags instead of <BR>to display plain text as html in preview. Použít značky <PRE> namísto <BR> pro zobrazení prostého textu formátovaného v náhledu jako HTML. Result List Seznam s výsledky Edit result paragraph format string Upravit řetězec formátu pro výsledky Edit result page html header insert Upravit záhlaví html na straně s výsledky Date format (strftime(3)) Formát data (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Práh četnosti (procento), od kterého se výrazy nepoužívají. Slovní obraty obsahující příliš četné výrazy způsobují výkonnostní potíže. Přeskočené výrazy zvětšují vzdálenost slovního obratu a zmenšují účinnost funkce automatického hledání slovního obratu. Výchozí hodnota je 2 (procenta). Autophrase term frequency threshold percentage Četnost výskytu výrazu (procento) pro automatické tvoření slovních obratů Plain text to HTML line style Prostý text do stylu řádku HTML Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Řádky v PRE textu nejsou zalomeny. Při použití BR dojde ke ztrátě některých zalomení. Možná je to, co chcete styl PRE + zalomení. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + zalomení Exceptions Výjimky Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. MIME typy, jež nemají být předány xdg-open, dokonce i když je nastaveno "Použít nastavení plochy".<br>Užitečné pro předání čísla strany a hledaného řetězce, např. Evince. Disable Qt autocompletion in search entry. Zakázat automatické doplňování Qt při zadávání v poli pro hledání. Search as you type. Hledat při psaní. Paths translations Překlady cest Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Klepnout pro přidání dalšího rejstříkového adresáře do seznamu. Můžete vybrat buď adresář s nastavením pro Recoll nebo rejstřík Xapian. Snippets window CSS file Soubor CSS okna s úryvky Opens a dialog to select the Snippets window CSS style sheet file Otevře dialog pro výběr souboru CSS se stylovým listem okna s úryvky Resets the Snippets window style Nastaví znovu styl okna s úryvky Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Rozhodnout, zda se dokumentové filtry ukazují jako kulatá tlačítka, rozbalovací seznamy v nástrojovém pruhu, nebo jako nabídka. Document filter choice style: Styl výběru filtrů dokumentů: Buttons Panel Panel s tlačítky Toolbar Combobox Rozbalovací seznam v nástrojovém panelu Menu Nabídka Show system tray icon. Ukázat ikonu v oznamovací oblasti panelu. Close to tray instead of exiting. Zavřít do oznamovací oblasti panelu namísto ukončení. Start with simple search mode Spustit v jednoduchém vyhledávacím režimu Show warning when opening temporary file. Ukázat varování při otevírání dočasného souboru. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Uživatelský styl k použití v okně s úryvky.<br>Poznámka: Záhlaví stránky s výsledky je zahrnuto i v záhlaví okna s úryvky. Synonyms file Soubor se slovy majícími stejný význam Highlight CSS style for query terms Zvýraznit styl CSS výrazů hledání Recoll - User Preferences Recoll - Uživatelská nastavení Set path translations for the selected index or for the main one if no selection exists. Nastavit překlady cest pro vybraný rejstřík nebo pro hlavní, pokud žádný vybrán není. Activate links in preview. Zapnout odkazy v náhledu. Make links inside the preview window clickable, and start an external browser when they are clicked. Udělat odkazy uvnitř náhledového okna klepnutelnými a spustit vnější prohlížeč, když je na ně klepnuto. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Výrazy dotazu zvýrazňující výsledky. <br>Možná zkuste něco jako "color:red;pozadí:yellow" pro něco živějšího, než je výchozí plán... Start search on completer popup activation. Spustit hledání při aktivaci kompletního vyskakovacího okna. Maximum number of snippets displayed in the snippets window Maximální počet textových bloků zobrazených v okně textových bloků Sort snippets by page number (default: by weight). Seřadit úryvky podle čísla stránky (výchozí: podle hmotnosti). Suppress all beeps. Potlačit všechny pípnutí. Application Qt style sheet Panel ve stylu aplikace Qt Limit the size of the search history. Use 0 to disable, -1 for unlimited. Omezit velikost historie vyhledávání. Použijte 0 pro vypnutí, -1 pro neomezené. Maximum size of search history (0: disable, -1: unlimited): Maximální velikost historie vyhledávání (0: zakázáno, -1: neomezeně): Generate desktop notifications. Generovat oznámení na ploše Misc Různé Work around QTBUG-78923 by inserting space before anchor text Pracujte kolem QTBUG-78923 vložením místa před zakotvením textu Display a Snippets link even if the document has no pages (needs restart). Zobrazit odkaz Snippets i v případě, že dokument nemá žádné stránky (vyžaduje restart). Maximum text size highlighted for preview (kilobytes) Maximální velikost textu zvýrazněného pro náhled (v kilobajtech) Start with simple search mode: Spustit v jednoduchém vyhledávacím režimu: Hide toolbars. Skrýt lišty nástrojů. Hide status bar. Skrýt stavový řádek. Hide Clear and Search buttons. Skrýt tlačítka "Vyčistit" a "Vyhledat". Hide menu bar (show button instead). Skrýt lištu nabídek (místo toho zobrazit tlačítko). Hide simple search type (show in menu only). Skrýt jednoduchý typ vyhledávání (pouze v nabídce). Shortcuts Zkratky Hide result table header. Skrýt záhlaví tabulky výsledků. Show result table row headers. Zobrazit záhlaví tabulky výsledků. Reset shortcuts defaults Obnovit výchozí nastavení zástupců Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Zakažte Ctrl+[0-9]/[a-z] zkratky pro skok do řádků tabulky. Use F1 to access the manual Použijte F1 pro přístup k manuálu Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Jednoduchý typ vyhledávání Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Tmavý režim Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Tabulka výsledků Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_uk.ts0000644000175000017500000100736614444307651014267 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Всі поля Any clause Будь-яке поле texts тексти spreadsheets таблиці presentations презентації media мультимедіа messages повідомлення other інше Bad multiplier suffix in size filter Помилковий мультиплікатор у фільтрі text текст spreadsheet таблиці presentation презентації message повідомлення Advanced Search Складний пошук History Next Історія Далі History Prev Історія Load next stored search Завантажити наступний збережений пошук Load previous stored search Завантажити збережений пошук AdvSearchBase Advanced search Складний пошук Restrict file types Обмежити типи файлів Save as default Зберегти як типові Searched file types Бажані All ----> Всі -----> Sel -----> Виб -----> <----- Sel <----- Виб <----- All <----- Всі Ignored file types Ігноровані Enter top directory for search Шукати тільки у каталозі Browse Перегляд Restrict results to files in subtree: Обмежити пошук по файлах з піддерева: Start Search Шукати Search for <br>documents<br>satisfying: Шукати<br>документи,</br>що задовільняють: Delete clause Прибрати поле Add clause Додати поле Check this to enable filtering on file types Використовувати фільтрацію по типах файлів By categories По категоріях Check this to use file categories instead of raw mime types Використовувати категорії замість типів MIME Close Закрити All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Всі непусті поля буде об'єднано за допомогою АБО ("усі слова") або ТА ("будь-які слова").<br>Поля типу "будь-які слова", "усі слова" та "без цих слів" приймають суміш слів та фраз у подвійних лапках.<br>Поля без даних не беруться до уваги. Invert Invert Minimum size. You can use k/K,m/M,g/G as multipliers Мінімальний розмір. Ви можете використовувати k/K,m/M,g/G як множники Min. Size Min. Size Maximum size. You can use k/K,m/M,g/G as multipliers Максимальний розмір. Ви можете використовувати k/K,m/M,g/G як множники Max. Size Макс. розмір Select Вибрати Filter Фільтр From Від To На Check this to enable filtering on dates Позначте цей пункт, щоб увімкнути фільтрацію дат Filter dates Фільтрувати дати Find Знайти Check this to enable filtering on sizes Позначте цей прапорець, щоб увімкнути фільтрування за розмірами Filter sizes Розмір фільтру Filter birth dates ConfIndexW Can't write configuration file Неможливо записати файл конфіґурації Global parameters Загальні параметри Local parameters Місцеві параметри Search parameters Параметри пошуку Top directories Верхні теки The list of directories where recursive indexing starts. Default: your home. Список тек, з яких починається рекурсивне індексування. Типово: домашня тека. Skipped paths Пропускати шляхи These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Це шляхи каталогів, в яких не буде входити індексація.<br>Елементи контуру, які можуть містити шаблони. Записи повинні відповідати шляхам індексатор (наприклад, якщо верхні каталоги включають '/home/me' і '/будинок' насправді посилання на '/usr/home'правильний запис у пропущеному шляху буде '/home/me/tmp*', не '/usr/home/me/tmp*') Stemming languages Мови зі словоформами The languages for which stemming expansion<br>dictionaries will be built. Мови, для яких буде побудовано<br>словники розкриття словоформ. Log file name Файл журналу The file where the messages will be written.<br>Use 'stderr' for terminal output Файл, куди підуть повідомлення.<br>'stderr' для терміналу Log verbosity level Докладність журналу This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Встановити обсяг повідомлень,<br>від помилок до даних зневадження. Index flush megabytes interval Інтервал скидання індексу (Мб) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Скільки даних буде проіндексовано між скиданнями індексу на диск.<br>Допомагає контролювати використання пам'яті індексатором. Типово: 10Мб This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Це відсоток використання диску - загальний обсяг диску, а не розмір індексу - при якому індексація зазнає невдачі та зупинки.<br>Стандартне значення 0 видаляє будь-які межі. No aspell usage Не використовувати aspell Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Вимикає використання aspell для генерації наближень у написання в навіґаторі термінів.<br>Корисне, коли aspell відсутній або зламаний. Aspell language Мова aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Мова аспельного словника. Це має виглядати на 'en' або 'fr' . .<br>Якщо це значення не встановлено, то NLS середовище буде використане для обчислення, що зазвичай працює. Щоб отримати уявлення про те, що було встановлено на вашій системі, введіть 'аюту' і подивіться на . у папці 'data-dir' Database directory name Тека бази даних The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Ім'я каталогу, де зберігати індекс<br>каталог, не є абсолютний шлях відноситься до каталогу конфігурації. За замовчуванням 'xapiandb'. Unac exceptions Юнацькі винятки <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Це винятки з механізму unac, який, за замовчуванням, видаляє всі діакритики, і виконує канонічну декомпозицію. Ви можете перевизначити неакцентовані на деякі символи, в залежності від вашої мови, і вказати додаткові декомпозиції. . для лігатур. При відрізах першого символу - це вихідний, а решта - переклад. Process the WEB history queue Обробити чергу історії WEB Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Вмикає індексацію відвіданих сторінок.<br>(вам також потрібно встановити плагін для Firefox) Web page store directory name Ім'я каталогу веб-сторінок магазину The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Ім'я каталогу, де зберігати відвідані веб-сторінки.<br>Шлях не абсолютний шлях відноситься до каталогу налаштувань. Max. size for the web store (MB) Макс. розмір для веб-магазину (МБ) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Записів буде перероблено після досягнення розміру.<br>Збільшення розміру дійсно має сенс, бо зменшення значення не призведе до скорочення існуючого файлу (лише відходів в кінці). Automatic diacritics sensitivity Автоматична чутливість діакритики <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Автоматично викликати чутливість до діакриту, якщо пошуковий термін має акцентовані символи (не в unac_except_trans). Можливо, вам потрібно використовувати мову запитів і модифікатор <i>D</i> щоб вказати чутливість до діакритики. Automatic character case sensitivity Автоматична чутливість регістру символів <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Автоматично чутливість до регістру ієрогліфів, якщо запис має символи у верхньому регістрі, окрім першої позиції. Також вам потрібно використовувати мову запиту і модифікатор <i>C</i> щоб вказати чутливість до символів . Maximum term expansion count Максимальний термін розширення <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Максимальна кількість розширення для одного строку (наприклад, при використанні шаблонів). За замовчуванням 10 000 є розумним і буде уникати запитів, які з'являються замороженими, тоді як двигун рухається в цьому списку. Maximum Xapian clauses count Максимальна кількість заяв Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Максимальна кількість елементарних застережень, які ми додаємо до одного Xapian запиту. У деяких випадках результатом строкового розширення може бути багатократне і ми хочемо уникати використання надмірної пам'яті. Значення за замовчуванням 100 000 має бути достатньо високим у більшості випадків і сумісним з поточними типовими конфігураціями обладнання. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... Будуть зібрані мови, для яких будуть обрані словники розширень.<br>Подивіться на документацію Xapian stemmer з можливих значень. Наприклад, english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Мова аспек-словника. Значення - 2-літерні коди мови, наприклад 'en', 'fr' . .<br>Якщо це значення не встановлено, то NLS середовище буде використане для обчислення, що зазвичай працює. Щоб отримати уявлення про те, що було встановлено на вашій системі, введіть 'аюту' і подивіться на . у папці 'data-dir' Indexer log file name Ім'я файла журналу індексування If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Якщо пусто, буде використано ім'я файлу журналу. Можливо, корисно мати окремий журнал для діагностичних цілей, оскільки загальний журнал буде стертий при запуску<br>інтерфейсу. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Відсоток повного порогу на диску, за якого ми припиняємо індексувати<br>Наприклад: 90% до повного заряду, 0 або 100 означає відсутність обмежень) Web history Веб-історія Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Лише типи МІМЕ An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Виключний список індексованих mime-типів.<br>Додатково нічого не буде індексовано. Зазвичай, порожній і неактивний Exclude mime types Виключити типи МІМЕ Mime types not to be indexed Типи MIME не індексуються Max. compressed file size (KB) Межа розміру стиснених файлів (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Це значення встановлює поріг розміру стиснених файлів, більші за нього не буде опрацьовано. -1 вимикає ліміт, 0 вимикає декомпресію. Max. text file size (MB) Макс. розмір текстового файлу (МБ) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Це значення встановлює поріг, після якого текстові файли не будуть оброблятися. Встановіть в -1 для відсутності ліміту. Це за винятком файлів monster журналів з індексу. Text file page size (KB) Розмір текстової сторінки (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Якщо це значення встановлено (не дорівнює -1), текстові файли будуть розділені у чанках такого розміру для індексування. Це допоможе шукати дуже великі текстові файли (тобто файли журналу). Max. filter exec. time (s) Макс. виконання фільтрів. Час (ів) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Зовнішні фільтри працюють довше, ніж це буде перервано. Це для рідкісного випадку (i: postscript) де документ може спричинити фільтр для циклу. Встановіть -1 для відсутності ліміту. Global Глобальні CronToolW Cron Dialog Cron Dialog <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Дні тижня (* або 0-7, 0 або 7 - неділя) Hours (* or 0-23) Годин (* або 0-23) Minutes (0-59) Хвилин (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-/W3C/DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> , li { white-space: попередня обритя; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Клікніть <span style=" font-style:italic;">Вимкніть</span> щоб зупинити автоматичне парне індексацію, <span style=" font-style:italic;">Увімкнено</span> , щоб задіяти, <span style=" font-style:italic;">Скасуйте</span> нічого не змінити.</p></body></html> Enable Увімкнено Disable Вимкнено It seems that manually edited entries exist for recollindex, cannot edit crontab Здається, що відредаговані вручну записи існують для recollindex, не можна редагувати crontab Error installing cron entry. Bad syntax in fields ? Помилка встановлення планувальника cron. Невдалий синтаксис у поля? EditDialog Dialog Діалог EditTrans Source path Шлях до вихідного коду Local path Локальний шлях Config error Помилка конфігурації Original path Початковий шлях EditTransBase Path Translations Переклади шляхів Setting path translations for Налаштування перекладів шляху для Select one or several file types, then use the controls in the frame below to change how they are processed Виберіть один або кілька типів файлів, а потім використовуйте параметри в кадрі нижче, щоб змінити їх обробку Add Додати Delete Видалити Cancel Відмінити Save Зберегти FirstIdxDialog First indexing setup Перше налаштування індексування <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-/W3C/DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> , li { white-space: попередня обритя; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">, Схоже, індекс цієї конфігурації не існує.</span><br /><br />, Якщо ви просто хочете індексувати домашній каталог за допомогою заданого значення за замовчуванням, натисніть <span style=" font-style:italic;">Почати індексацію</span> . Ви зможете згодом налаштувати подробиці. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Якщо потрібно більше контролю, змініть значення наступних посилань для налаштування конфігурації індексування та розкладу.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ці інструменти можна отримати пізніше <span style=" font-style:italic;">Налаштування</span> меню.</p></body></html> Indexing configuration Конфігурація індексування This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Це дозволить вам налаштувати каталоги, які ви хочете індексувати або інші параметри, такі як виключені шляхи до файлів або імена, набір символів за замовчуванням і т. д. Indexing schedule Розклад індексування This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Це дозволить вам вибирати між пакетною і індексацією в реальному часі, і налаштувати автоматичний розклад для пакетної індексації (за допомогою cron). Start indexing now Почати індексацію зараз FragButs %1 not found. %1 не знайдено. %1: %2 %1: %2 Fragment Buttons Кнопки фрагменту Query Fragments Фрагменти запитів IdxSchedW Index scheduling setup Налаштування планування індексів <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-/W3C/DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> , li { white-space: попередня обритя; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing може працювати безповоротно, індексування файлів під час їх змін, чи запуску з перервних інтервалів. </p> ,<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Читання ручної інструкції може допомогти вам вирішити ці підходи (натисніть F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Цей інструмент допоможе вам налаштувати розклад для автоматизації виконання батчів індексації, або почати реально індексацію часу, коли ви увійдете (або і те, й інше, що рідко має сенс). </p></body></html> Cron scheduling Планування Cron The tool will let you decide at what time indexing should run and will install a crontab entry. Інструмент дозволить вам вирішити, в який час індексації слід запустити і буде встановити запис на crontab Real time indexing start up Запуск індексування в реальному часі Decide if real time indexing will be started when you log in (only for the default index). Оберіть, чи буде розпочато індексацію часу при вході (лише для індексу за замовчуванням). ListDialog Dialog Діалог GroupBox Групак Main No db directory in configuration В конфігурації немає каталогу БД Could not open database in Не можу відкрити базу даних в . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Натисніть Відміна, якщо бажаєте відредагувати конфіґурацію до початку індексування, чи OK для продовження. Configuration problem (dynconf Проблема конфігурації (dynconf "history" file is damaged or un(read)writeable, please check or remove it: "історія" файл пошкоджений або недоступний для запису, будь ласка, перевірте чи видаліть його: "history" file is damaged, please check or remove it: "історія" файл пошкоджений, будь ласка, перевірте або видаліть його: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Шукати: &Next &Наступне &Previous &Попереднє Match &Case &Чутливість до реєстру Clear Стерти Creating preview text Створюю текст для перегляду Loading preview text into editor Завантажую текст перегляду в редактор Cannot create temporary directory Не можу створити тимчасову теку Cancel Відмінити Close Tab Закрити вкладку Missing helper program: Не знайдено допоміжну програму: Can't turn doc into internal representation for Неможливо перетворити документ на внутрішнє представлення для Cannot create temporary directory: Не вдалося створити тимчасову теку: Error while loading file Помилка при завантаженні файлу Form Форма Tab 1 Tab 1 Open Відкрити Canceled Скасовано Error loading the document: file missing. Помилка завантаження документа: відсутній файл. Error loading the document: no permission. Помилка при завантаженні документа: немає дозволу. Error loading: backend not configured. Помилка завантаження: backend не налаштований. Error loading the document: other handler error<br>Maybe the application is locking the file ? Помилка при завантаженні документу: інша помилка обробника<br>Можливо, програма блокує файл ? Error loading the document: other handler error. Помилка при завантаженні документа: іншої помилки обробника. <br>Attempting to display from stored text. <br>Спроба відобразити із збереженого тексту. Could not fetch stored text Не вдалося отримати збережений текст Previous result document Попередній документ результату Next result document Наступний документ результату Preview Window Вікно попереднього перегляду Close Window Закрити вікно Next doc in tab Наступний доктор у вкладці Previous doc in tab Попередній доктор у вкладці Close tab Закрити вкладку Print tab Print tab Close preview window Закрити вікно попереднього перегляду Show next result Показати наступний результат Show previous result Показати попередній результат Print Друк PreviewTextEdit Show fields Показувати поля Show main text Показувати основний текст Print Друк Print Current Preview Надрукувати поточний перегляд Show image Показати зображення Select All Виділити все Copy Копія Save document to file Зберегти документ у файл Fold lines Складки рядків Preserve indentation Зберегти відступ Open document Відкритий документ Reload as Plain Text Reload as HTML QObject Global parameters Загальні параметри Local parameters Місцеві параметри <b>Customised subtrees <b>Піддерева з налаштуваннями The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Список тек у індексованій ієрархії,<br>для яких деякі параметри потрібно змінити. Типово: пустий. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>Нижченаведені параметри змінюються або на верхньому рівні, якщо<br>не вибрано нічого або пустий рядок, або для вибраної теки.<br>Ви можете додати або прибрати теки кнопками +/-. Skipped names Пропускати назви These are patterns for file or directory names which should not be indexed. Шаблони назв файлів або тек, які не буде індексовано. Default character set Типове кодування This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Кодування, яке буде застосовано при читанні файлів, які не вказують таке особливо (наприклад, чисто текстових файлів).<br>Типово невказане, тоді використовується значення з оточення (локалі). Follow symbolic links Розкривати символічні посилання Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Ходити по симлінках при індексації. Типово "ні" для уникнення дублікатів Index all file names Індексувати всі назви файлів Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Індексувати також назви файлів, вміст яких не може бути впізнано чи оброблено (невідомий або непідтримуваний тип MIME). Типово "так" Beagle web history Бігле веб-історія Search parameters Параметри пошуку Web history Веб-історія Default<br>character set За замовчуванням<br>кодувати Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Символ, що використовується для читання файлів, які не визначають налаштування символу внутрішньо, наприклад цілком текстові файли.<br>Значення за замовчуванням порожнє, і значення для використання NLS. Ignored endings Ігноровані закінчення These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Це кінець назви файлу для файлів, які будуть індексуватися лише контентом (без спроби ідентифікації типу MIME, без декомпресії, без індексації вмісту. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Це кінець назви файлу для файлів, які будуть індексуватися лише по імені (без спроби ідентифікації типу MIME, без декомпресії, без індексації вмісту). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>Параметри, які випливають, встановлені на верхньому рівні, якщо в переліку вище не вибрано нічого або порожній рядок, або для вибраного підкаталогу. Ви можете додати або видалити каталоги, натиснувши кнопки +/-. QWidget Create or choose save directory Створити або вибрати теку для збереження Choose exactly one directory Виберіть лише один каталог Could not read directory: Не вдалося прочитати каталог: Unexpected file name collision, cancelling. Неочікуване зіткнення з іменем файлу, скасування Cannot extract document: Не вдалося витягти документ: &Preview &Переглянути &Open &Відкрити Open With Відкрити за допомогою Run Script Запуск сценарію Copy &File Name Копіювати &назву файла Copy &URL Копіювати &URL &Write to File &Написати у файл Save selection to files Зберегти виділене для файлів Preview P&arent document/folder Попередній перегляд &зовнішньої документа/теки &Open Parent document/folder &Відкрити батьківську документ/теку Find &similar documents Знайти &схожі документи Open &Snippets window Відкрити &вікно сніпетів Show subdocuments / attachments Показати піддокументи / вкладення &Open Parent document &Відкрити батьківський документ &Open Parent Folder &Відкрити батьківську теку Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Не показувати знову. RTIToolW Real time indexing automatic start Автоматичний старт індексування реальному часі <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-/W3C/DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict. td"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> , li { white-space: попередня обритя; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">рецептів</span> індексації може бути настроєно виконуватись як демон, оновлення індексу як зміни файлів в реальному часі. Ви отримуєте постійний формат часу, але постійні використання системних ресурсів.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Запустити індексацію служби з моєю сесією на робочому столі. Also start indexing daemon right now. Також почати індексацію служби просто зараз. Replacing: Замінник: Replacing file Заміна файлу Can't create: Може't створити: Warning Попередження Could not execute recollindex Не вдалося виконати recollindex Deleting: Видалення: Deleting file Видалення файлу Removing autostart Видалення автозапуску Autostart file deleted. Kill current process too ? Автозапуск файлу видалено. Зупинити поточний процес? RclCompleterModel Hits RclMain About Recoll Про Recoll Executing: [ Виконую: [ Cannot retrieve document info from database Не можу здобути документ з бази даних Warning Попередження Can't create preview window Не можу створити вікно перегляду Query results Результати запиту Document history Історія документів History data Дані історії Indexing in progress: Індексується: Files Файли Purge Очистити Stemdb База коренів Closing Закриваю Unknown Невідомо This search is not active any more Цей пошук вже неактивний Can't start query: Неможливо почати запит: Bad viewer command line for %1: [%2] Please check the mimeconf file Невірний командний рядок для переглядача %1: [%2] Перевірте файл mimeconf Cannot extract document or create temporary file Неможливо здобути документ чи створити тимчасовий файл (no stemming) (без словоформ) (all languages) (всі мови) error retrieving stemming languages помилка здобування списку мов Update &Index Поновити &індекс Indexing interrupted Індексування перервано Stop &Indexing Пе&рервати індексування All всі media медіа message повідомлення other інше presentation презентації spreadsheet таблиці text текст sorted сортоване filtered фільтроване External applications/commands needed and not found for indexing your file types: Відсутні зовнішні додатки/команди, що потрібні для індексування ваших документів: No helpers found missing Всі додаткові програми наявні Missing helper programs Відсутні додаткові програми Save file dialog Зберегти файл Choose a file name to save under Оберіть ім'я файла для збереження Document category filter Фільтр категорії документів No external viewer configured for mime type [ Немає налаштованого зовнішнього переглядача для типу mime [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Переглядач, вказаний в mimeview для %1: %2 не знайдено. Ви хочете почати діалогове вікно налаштувань? Can't access file: Може'файл доступу: Can't uncompress file: Може't нестиснутий файл: Save file Зберегти файл Result count (est.) Підсумкова кількість (майже) Query details Деталі запиту Could not open external index. Db not open. Check external indexes list. Не вдалося відкрити зовнішній індекс. Db не відкрито. Перевірте список зовнішніх індексів. No results found Нічого не знайдено None Без ефекту Updating Оновлення Done Виконано Monitor Монітор Indexing failed Індексацію не вдалося The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Поточний процес індексування не був запущений з цього інтерфейсу. Натисніть ОК, щоб вбити його в будь-якому разі, або Скасувати, щоб залишити його одним Erasing index Стирання індексу Reset the index and start from scratch ? Скинути індекс і почати з нуля? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Триває запит.<br>В результаті обмежень індексації бібліотека<br>скасовується вихід з програми Error Помилка Index not open Індекс не відкритий Index query error Помилка запиту індекса Indexed Mime Types Індексовані типи MIME Content has been indexed for these MIME types: Вміст було індексовано для цих типів MIME: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Індекс не оновлений до дати цього файлу. Відмова переймається тим, що ризикує показати неправильний запис. Натисніть Ок, щоб оновити індекс для цього файлу, а потім перезапустіть запит після завершення індексації. Ще раз, скасувати. Can't update index: indexer running Може't оновлення індекс: запускається індексатор Indexed MIME Types Індексовані MIME типи Bad viewer command line for %1: [%2] Please check the mimeview file Помилковий командний рядок переглядача для %1: [%2] Будь ласка, перевірте файл mimeview Viewer command line for %1 specifies both file and parent file value: unsupported Перегляд командного рядка %1 вказує як файл так і значення батьківського файлу: не підтримується Cannot find parent document Не вдалося знайти батьківський документ Indexing did not run yet Індексування ще не запущено External applications/commands needed for your file types and not found, as stored by the last indexing pass in Зовнішні програми / команди, необхідні для типів файлів і не знайдені, як збережені при останньому переданні індексації Index not up to date for this file. Refusing to risk showing the wrong entry. Індекс не оновлений до дати цього файлу. Відмова переймається тим, що ризикує показати неправильний запис. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Натисніть Ок, щоб оновити індекс для цього файлу, а потім перезапустіть запит після завершення індексації. Ще раз, скасувати. Indexer running so things should improve when it's done Індексатор працював, щоб все мало покращити коли's виконано Sub-documents and attachments Піддокументи та вкладення Document filter Фільтр документа Index not up to date for this file. Refusing to risk showing the wrong entry. Індекс не оновлений до дати цього файлу. Відмова переймається тим, що ризикує показати неправильний запис. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Натисніть Ок, щоб оновити індекс для цього файлу, тоді вам потрібно буде перезапустити запит після завершення індексації. The indexer is running so things should improve when it's done. Індекатор працює, щоб усе мало бути кращим коли він's завершено. The document belongs to an external indexwhich I can't update. Документ належить до зовнішнього індексу, який я можу'оновити . Click Cancel to return to the list. Click Ignore to show the preview anyway. Натисніть "Скасувати", щоб повернутися до списку. Натисніть "Ігнорувати для перегляду" все одно показати попередній перегляд. Duplicate documents Дублювати документи These Urls ( | ipath) share the same content: Ці адреси (| ipath) діляться тим самим змістом: Bad desktop app spec for %1: [%2] Please check the desktop file Помилкова специфікація додатка для %1: [%2] Будь ласка, перевірте файл стільниці Bad paths Помилкові шляхи Bad paths in configuration file: Помилкові шляхи у файлі конфігурації: Selection patterns need topdir Шаблони відбору потребують топіка Selection patterns can only be used with a start directory Шаблони виділення можуть використовуватися тільки в початковому каталозі No search Не знайдено No preserved previous search Немає збережених попереднього пошуку Choose file to save Виберіть файл для збереження Saved Queries (*.rclq) Збережені запити (*.rclq) Write failed Помилка запису Could not write to file Не вдалося записати файл Read failed Помилка читання Could not open file: Не вдалося відкрити файл: Load error Помилка завантаження Could not load saved query Не вдалося завантажити збережений запит Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Do not show this warning next time (use GUI preferences to restore). Не показувати це попередження наступного разу (використовуйте налаштування GUI для відновлення). Disabled because the real time indexer was not compiled in. Вимкнено, оскільки індексування в реальному часі не було скомпільовано. This configuration tool only works for the main index. Цей інструмент конфігурації працює лише для головного індексу. The current indexing process was not started from this interface, can't kill it Поточний процес індексування не був запущений з цього інтерфейсу, може't вбити його The document belongs to an external index which I can't update. Документ належить до зовнішнього індексу, який я можу'оновити . Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Натисніть "Скасувати", щоб повернутися до списку. <br>Натисніть "Ігнорувати для того, щоб показати попередній перегляд (та запам'ятати для цієї сесії). Index scheduling Планування індексів Sorry, not available under Windows for now, use the File menu entries to update the index Вибачте, поки що не доступне під Windows, використовуйте записи в меню "Файл" для оновлення індексу Can't set synonyms file (parse error?) Чи може'встановити синонім файл (помилка аналізування?) Index locked Індекс заблокований Unknown indexer state. Can't access webcache file. Невідомий стан індексатора. Може'доступ до файлу webcache Indexer is running. Can't access webcache file. Indexer запущено. Може'доступ до файлу webcache файлу. with additional message: з додатковим повідомленням: Non-fatal indexing message: Нефатальне повідомлення індексації: Types list empty: maybe wait for indexing to progress? Список типів порожній: можливо чекати завершення індексації? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Програма перегляду командного рядка %1 визначає батьківський файл, але URL-адреса - http[s]: не підтримується Tools Інструменти Results Результати (%d documents/%d files/%d errors/%d total files) (%d документів/%d файлів /%d помилок /%d загалом файлів) (%d documents/%d files/%d errors) (%d документів/%d файлів /%d помилок) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Порожній або не існує шляхів у файлі конфігурації. Натисніть Ok, щоб почати індексацію (дані про відсутні не будуть видалені з індексу): Indexing done Індексацію завершено Can't update index: internal error Може't оновлення індекс: внутрішня помилка Index not up to date for this file.<br> Індекс не оновлений до дати цього файлу.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Також, здається, що останнє індексне оновлення файлу провалилося</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Натисніть Ок, щоб спробувати оновити індекс для цього файлу. Вам потрібно буде знову запустити запит після завершення індексації<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Натисніть "Скасувати", щоб повернутися до списку.<br>Натисніть "Ігнорувати для того, щоб показати попередній перегляд (та запам'ятати для цієї сесії). Існує ризик показу неправильного запису.<br/> documents документи document документ files файли file файл errors помилки error помилка total files) всього файлів) No information: initial indexing not yet performed. Немає інформації: початкове індексування ще не виконано. Batch scheduling Пакетне планування The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Інструмент дозволить вам вирішити, в який час при індексуванні має запускатися. Він використовує планувальник завдань Windows завдань. Confirm Підтвердити Erasing simple and advanced search history lists, please click Ok to confirm Стирання простих і просунутих списків історії пошуку, будь ласка, натисніть Ок, щоб підтвердити Could not open/create file Не вдалося відкрити/створити файл F&ilter Запов&ітувач Could not start recollindex (temp file error) Не вдалося запустити recollindex (помилка тимчасового файлу) Could not read: Не вдалося прочитати: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Це замінить поточний вміст заголовка списку результатів і імені файлу GUI qss. Продовжити? You will need to run a query to complete the display change. Вам потрібно буде запустити запит для завершення зміни дисплея. Simple search type Простий тип пошуку Any term Будь-яке слово All terms Усі слова File name Ім'я файлу Query language Мова запиту Stemming language Мова словоформ Main Window Головне вікно Focus to Search Перейти до пошуку Focus to Search, alt. Перейти до пошуку, самі. Clear Search Очистити пошук Focus to Result Table Фокус до результатів Clear search Очистити пошук Move keyboard focus to search entry Пересунути клавіатуру на пошуковий запис Move keyboard focus to search, alt. Перемістіть клавіатуру до пошуку, зовсім. Toggle tabular display Переключити табуляційний екран Move keyboard focus to table Перемістити фокус клавіатури до таблиці Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Попередня сторінка Next page Наступна сторінка &File &Файл E&xit &Вихід &Tools &Інструменти &Help &Довідка &Preferences &Налаштування Search tools Інструменти пошуку Result list Список результатів &About Recoll Про &Recoll Document &History &Історія документів Document History Історія документів &Advanced Search &Складний пошук Advanced/complex Search Складний (поглиблений) пошук &Sort parameters &Параметри сортування Sort parameters Параметри сортування Next page of results Наступна сторінка результатів Previous page of results Попередня сторінка результатів &Query configuration Конфіґурація &запиту &User manual &Довідник користувача Recoll Запах Ctrl+Q Ctrl+Q Update &index &Поновити індекс Term &explorer &Навіґатор термінів Term explorer tool Інструмент для вивчання термінів External index dialog Діалог зовнішнього індексу &Erase document history &Очистити історію документів First page Перша сторінка Go to first page of results Перейти до першої сторінки результатів &Indexing configuration &Конфіґурація індексування All всі &Show missing helpers Відсутні програми PgDown ПДР Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp Пігап &Full Screen &Весь екран F11 Ф11 Full Screen На весь екран &Erase search history &Стерти історію пошуку sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Сортувати за датою від найстаріших до найновіших sortByDateDesc сортби-Датедек Sort by dates from newest to oldest Сортувати за датою від найновіших до старіших Show Query Details Показувати подробиці запиту Show results as table Показати результати як таблицю &Rebuild index &Перебудувати індекс &Show indexed types &Показати індексовані типи Shift+PgUp Shift+PgUp &Indexing schedule &Розклад індексування E&xternal index dialog Д&одальне вікно вічного індексу &Index configuration &Індексувати конфігурацію &GUI configuration &Налаштування інтерфейсу &Results &Результати Sort by date, oldest first Впорядкувати по даті, спочатку старіші Sort by date, newest first Впорядкувати за датою, спочатку новіше Show as table Показати як таблицю Show results in a spreadsheet-like table Показати результати в таблиці електронної таблиці Save as CSV (spreadsheet) file Зберегти як CSV (електронна таблиця) файл Saves the result into a file which you can load in a spreadsheet Зберігає результат у файл, який можна завантажити в таблицю Next Page Наступна сторінка Previous Page Попередня сторінка First Page Перша сторінка Query Fragments Фрагменти запитів With failed files retrying З помилковою спробою файлів повторна спроба Next update will retry previously failed files Наступне оновлення буде повторюватися раніше невдалі файли Save last query Зберегти останній запит Load saved query Завантажити збережений запит Special Indexing Спеціальний індекс Indexing with special options Індексування з спеціальними опціями Indexing &schedule індексування &розкладу Enable synonyms Увімкнути синоніми &View &Вигляд Missing &helpers Відсутні &помічники Indexed &MIME types Інде&овані типи MIME Index &statistics З&качати статистику Webcache Editor Редактор Webcache Trigger incremental pass Тригер інкрементний прохід E&xport simple search history Експорт простої історії пошуку Use default dark mode Використовувати стандартний темний режим Dark mode Темний режим &Query &Запит Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Фільтрувати дати Assisted complex search Filter birth dates RclTrayIcon Restore Відновити Quit Вийти RecollModel Abstract Абстракції Author Автор Document size Розмір документа Document date Дата документа File size Розмір файлу File name Ім'я файлу File date Дата файлу Ipath Іпатія Keywords Ключові слова Mime type Тип MIME Original character set Початковий набір символів Relevancy rating Рейтинг релевантності Title Найменування URL Адреса Mtime Мтім Date Дата Date and time Дата та час Ipath Іпатія MIME type MIME-тип Can't sort by inverse relevance Може'сортування по інверсній релевантності ResList Result list Список результатів Unavailable document Документ недосяжний Previous Попередня Next Наступна <p><b>No results found</b><br> <p><b>Не знайдено</b><br> &Preview &Переглянути Copy &URL Копіювати &URL Find &similar documents Знайти &схожі документи Query details Деталі запиту (show query) (показати запит) Copy &File Name Копіювати &назву файла filtered фільтроване sorted сортоване Document history Історія документів Preview Перегляд Open Відкрити <p><i>Alternate spellings (accents suppressed): </i> <p><i>Альтернативні орфографії (акценти придушено): </i> &Write to File &Написати у файл Preview P&arent document/folder Попередній перегляд &зовнішньої документа/теки &Open Parent document/folder &Відкрити батьківську документ/теку &Open &Відкрити Documents Документи out of at least з принаймні for по <p><i>Alternate spellings: </i> <p><i>Альтернативні орфографії: </i> Open &Snippets window Відкрити &вікно сніпетів Duplicate documents Дублювати документи These Urls ( | ipath) share the same content: Ці адреси (| ipath) діляться тим самим змістом: Result count (est.) Підсумкова кількість (майже) Snippets Сніпети This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Сортування за замовчуванням &Delete column &Видалити стовпець Add " Додати " " column " стовпці Save table to CSV file Зберегти таблицю в CSV файл Can't open/create file: Може'відкрити або створити файл: &Preview &Переглянути &Open &Відкрити Copy &File Name Копіювати &назву файла Copy &URL Копіювати &URL &Write to File &Написати у файл Find &similar documents Знайти &схожі документи Preview P&arent document/folder Попередній перегляд &зовнішньої документа/теки &Open Parent document/folder &Відкрити батьківську документ/теку &Save as CSV &Зберегти як CSV Add "%1" column Додати "%1" стовпець Result Table Таблиця результатів Open Відкрити Open and Quit Відкрити і вийти Preview Перегляд Show Snippets Показати сніпети Open current result document Відкрити поточний документ результату Open current result and quit Відкрити поточний результат і вийти Show snippets Показати сніпети Show header Відображати заголовок Show vertical header Показати вертикальний заголовок Copy current result text to clipboard Скопіювати поточний текст результату в буфер обміну Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Переглянути &Open &Відкрити Copy &File Name Копіювати &назву файла Copy &URL Копіювати &URL &Write to File &Написати у файл Find &similar documents Знайти &схожі документи Preview P&arent document/folder Попередній перегляд &зовнішньої документа/теки &Open Parent document/folder &Відкрити батьківську документ/теку ResultPopup &Preview &Переглянути &Open &Відкрити Copy &File Name Копіювати &назву файла Copy &URL Копіювати &URL &Write to File &Написати у файл Save selection to files Зберегти виділене для файлів Preview P&arent document/folder Попередній перегляд &зовнішньої документа/теки &Open Parent document/folder &Відкрити батьківську документ/теку Find &similar documents Знайти &схожі документи Open &Snippets window Відкрити &вікно сніпетів Show subdocuments / attachments Показати піддокументи / вкладення Open With Відкрити за допомогою Run Script Запуск сценарію SSearch Any term Будь-яке слово All terms Усі слова File name Ім'я файлу Completions Доповнення Select an item: Оберіть: Too many completions Занадто багато доповнень Query language Мова запиту Bad query string Невірний рядок запиту Out of memory Недостатньо пам'яті Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Введіть вираз мови запиту. Шкіра аркуш:<br> <i>термін2</i> : 'term1' і 'term2' у будь-якому полі.<br> <i>поле:term1</i> : 'term1' в полі 'поле'.<br> Стандартних Полів імена/синоніми:<br> title/subject/caption, author/from, recipient/to, ename, ext.<br> псевдополя: бруд, mime/format, type/rclcat, дата.<br> проміжок дати: 2009-03-05-20 2009-03-03-03-03-03-03-03-01/01/P2M.<br> <i>терміни 2 АБО вираз2</i> : вираз1 І (терміни 2 АБО умова).<br> Не допускаються справжні дужки.<br> <i>"термін1 термін2"</i> : фраза (має відбуватися точно). Можливі модифікатори:<br> <i>"термін2"р</i> : невпорядкований пошук близькості з відстанню за замовчуванням.<br> Використайте <b>Показати запит</b> , коли виникає сумніви щодо результату й дивіться вручну (&л; 1>) для детальнішої інформації. Enter file name wildcard expression. Введіть шаблон назви файлу. Enter search terms here. Type ESC SPC for completions of current term. Введіть пошукові слова. Можна використовувати Esc-пробіл для доповнення. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Введіть вираз мови запиту. Шкіра аркуш:<br> <i>термін2</i> : 'term1' і 'term2' у будь-якому полі.<br> <i>поле:term1</i> : 'term1' в полі 'поле'.<br> Стандартних Полів імена/синоніми:<br> title/subject/caption, author/from, recipient/to, ename, ext.<br> псевдополя: бруди, мим/формат, тип/rclcat, дата.<br> проміжок дати: 2009-03-01/2009-05-20 2009-03-03-03-03-01/01/P2M.<br> <i>терміни 2 АБО вираз2</i> : вираз1 І (терміни 2 АБО умова).<br> Ви можете використовувати дужки, щоб зробити це зрозумілішим.<br> <i>"термін1 термін2"</i> : фраза (має відбуватися точно). Можливі модифікатори:<br> <i>"термін2"р</i> : невпорядкований пошук близькості з відстанню за замовчуванням.<br> Використайте <b>Показати запит</b> , коли виникає сумніви щодо результату й дивіться вручну (&л; 1>) для детальнішої інформації. Stemming languages for stored query: Видалення мов для збереженого запиту: differ from current preferences (kept) відрізняти від поточних параметрів (зберегти) Auto suffixes for stored query: Автоматично суфікси для збереженого запиту: External indexes for stored query: Зовнішні індекси для збереженого запиту: Autophrase is set but it was unset for stored query Встановити автофразу, але вона була не встановлена для збереженого запиту Autophrase is unset but it was set for stored query Автофраза не встановлена, але була встановлена для збереженого запиту Enter search terms here. Введіть тут умови пошуку. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; рамка: solid black; border-collapse: collapse; обвал кордону: обвал; } } th,td { th,td { text-align: center; text-align: center; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Чектор мови запиту. У сумніві: натисніть <b>Показати запит</b>.&nbsp; You should really look at the manual (F1)</p> Вам дійсно потрібно переглянути інструкцію (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Що</th><th>Приклади</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>І</td><td>одна дві&nbsp;&nbsp;&nbsp;1 і дві&nbsp;&nbsp;&nbsp;1 і два</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Або</td><td>один АБО два&nbsp;&nbsp;&nbsp;один || два</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Complex boolean. АБО має пріоритет, використовуйте дужки&nbsp; where needed</td><td>(one AND two) OR three</td></tr> де потрібно</td><td>(один І два) АБО три</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Не</td><td>-термін</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Фраза</td><td>"гордість і упередження"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Невпорядкований проксі. (стандартна слабкість=10)</td><td>"упередження&nbsp;гордість"р</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Без розширення стей: великі</td><td>Поверх</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Польовий специфічний</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>І всередині поля (немає замовлення)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>АБО всередині поля</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Фільтр каталогів</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>фільтр MIME типу</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Інтервал між датами</td><td>дата:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> дата:2018&nbsp;&nbsp;дата:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Може't відкритий індекс Could not restore external indexes for stored query:<br> Не вдалося відновити зовнішні індекси для збереженого запиту:<br> ??? ??? Using current preferences. Використання поточних уподобань. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Стерти Ctrl+S Ctrl+S Erase search entry Стерти вміст рядка запита Search Знайти Start query Почати запит Enter search terms here. Type ESC SPC for completions of current term. Введіть пошукові слова. Можна використовувати Esc-пробіл для доповнення. Choose search type. Оберіть тип пошуку. Show query history Показати історію запитів Enter search terms here. Введіть тут умови пошуку. Main menu Головне меню SearchClauseW SearchClauseW Пошуку CluseW Any of these будь-які слова All of these усі слова None of these без цих слів This phrase фраза Terms in proximity слова поблизу File name matching назва файлу Select the type of query that will be performed with the words Виберіть тип запиту, який буде зроблено по цих словах Number of additional words that may be interspersed with the chosen ones Кількість додаткових слів, що можуть бути між обраними In field В полі No field Без поля Any Будь-яке All всі None Без ефекту Phrase Фраза Proximity Наближення File name Ім'я файлу Snippets Snippets Сніпети X Ікс Find: Знайти: Next Наступна Prev Попередня SnippetsW Search Знайти <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Вибачте, в межах обмежень не було знайдено точного збігу. Можливо документ дуже великий, а генератор сніпетів загубив у лабіринті...</p> Sort By Relevance Сортувати за рівнем Sort By Page Сортувати за сторінкою Snippets Window Вікно сніпетів Find Знайти Find (alt) Знайти (альт) Find Next Знайти наступне Find Previous Знайти попереднє Hide Приховати Find next Знайти наступний Find previous Знайти попереднє Close window Закрити вікно SortForm Date Дата Mime type Тип MIME SortFormBase Sort Criteria Критерії сортування Sort the Сортувати most relevant results by: кращих результатів за: Descending спаданням Close Закрити Apply Застосувати SpecIdxW Special Indexing Спеціальний індекс Do not retry previously failed files. Не намагайтеся повторити невдалі файли. Else only modified or failed files will be processed. Можливо оброблятимуться тільки змінені або невдалі файли. Erase selected files data before indexing. Видаляти дані обраних файлів перед індексуванням. Directory to recursively index Каталог з рекурсивним індексом Browse Перегляд Start directory (else use regular topdirs): Початковий каталог (інакше використовувати регулярні вершини): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Залиште порожнім, щоб вибрати всі файли. Ви можете використовувати кілька шаблонів, розділених пробілами.<br>Шаблон з вбудованими пробілами слід цитувати з подвійними лапками.<br>Можна використовувати лише якщо встановлено початкову ціль. Selection patterns: Шаблони виділення: Top indexed entity Верхній індексований об'єкт Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Директорія для рекурсивного індексу. Це має бути всередині звичайної індексованої області<br> як визначено в файлі конфігурації (пов’язано з текою). Retry previously failed files. Повторіть спробу невдалих файлів. Start directory. Must be part of the indexed tree. We use topdirs if empty. Початкова директорія. Повинно бути частиною індексованого дерева. Якщо порожні. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Запустити каталог. Якщо порожня, обов'язково має бути частиною індексованого дерева. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Навіґатор термінів &Expand &Розкрити Alt+E Alt+клавiша переходу &Close &Закрити Alt+C Alt+C Term Слово No db info. Немає інформації про db. Doc. / Tot. Документ. / Вт. Match Матч Case Звернення Accents Акценти SpellW Wildcards Шаблони Regexp Регвираз Spelling/Phonetic Напис/звучання Aspell init failed. Aspell not installed? Не вдалося запустити aspell. Воно взагалі встановлене? Aspell expansion error. Помилка розкриття aspell. Stem expansion Розкриття словоформ error retrieving stemming languages помилка здобування списку мов No expansion found Розкриття не знайдене Term Слово Doc. / Tot. Документ. / Вт. Index: %1 documents, average length %2 terms Індекс: %1 документів, середня довжина %2 термінів Index: %1 documents, average length %2 terms.%3 results Index: %1 документів, середня довжина %2 умови.%3 результатів %1 results %1 результатів List was truncated alphabetically, some frequent Список промальовувався по алфавіту, деякі часті terms may be missing. Try using a longer root. термінів можуть бути відсутні. Спробуйте використати більш довгий root. Show index statistics Показати статистику індексів Number of documents Кількість документів Average terms per document Середнє умови на документ Smallest document length Найменша довжина документа Longest document length Найдовша довжина документа Database directory size Розмір теки в базі даних MIME types: Типи MIME: Item Елемент Value Цінність Smallest document length (terms) Найменша довжина документа (терміни) Longest document length (terms) Довга довжина документа (терміни) Results from last indexing: Результати з останнього індексування: Documents created/updated Документи створено/оновлено Files tested Файлів перевірено Unindexed files Неіндексовані файли List files which could not be indexed (slow) Список файлів, які не вдалося індексувати (повільно) Spell expansion error. Помилка розширення правопису. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Обрана тека не схожа на індекс Xapian This is the main/local index! Це основний/локальний індекс! The selected directory is already in the index list Обрана тека вже у списку індексів Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Оберіть теку із індексом Xapian (наприклад, /home/приятель/.recoll/xapiandb) error retrieving stemming languages помилка здобування списку мов Choose Перегляд Result list paragraph format (erase all to reset to default) Формат списку результатів (видалити всі параметри, щоб скинути до типових) Result list header (default is empty) Заголовок списку результатів (типово порожній) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Оберіть директорію конфігурації або теку індексу xapian (наприклад, /home/me/.recoll або /home/me/.recoll/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Вибраний каталог виглядає як каталог конфігурації Recoll, але конфігурація не може бути прочитана At most one index should be selected Потрібно вибрати не більше одного індексу Cant add index with different case/diacritics stripping option Заборона додавання індексу з різними варіантами/діакритичними знаками Default QtWebkit font Default QtWebkit font Any term Будь-яке слово All terms Усі слова File name Ім'я файлу Query language Мова запиту Value from previous program exit Значення попереднього виходу з програми Context Контекст Description Опис Shortcut Ярлик Default Типово Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Інтерфейс Number of entries in a result page Кількість результатів на сторінку Result list font Шрифт списку результатів Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Відкриває діалог вибору шрифту списку результатів Reset Скинути Resets the result list font to the system default Повертає шрифт у типовий системний Auto-start simple search on whitespace entry. Починати простий пошук при введенні пробілу. Start with advanced search dialog open. Відкривати діалог складного пошуку при старті. Start with sort dialog open. Відкривати діалог сортування при старті. Search parameters Параметри пошуку Stemming language Мова словоформ Dynamically build abstracts Динамічно будувати конспекти Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Чи намагатися будувати конспекти для результатів пошуку, використовуючі контекст знайдених слів? Може працювати повільно для великих документів. Replace abstracts from documents Заміняти наявні у документах конспекти Do we synthetize an abstract even if the document seemed to have one? Чи робити новий конспект, навіть якщо якийсь вже є в документі? Synthetic abstract size (characters) Розмір синтетичного конспекту (у символах) Synthetic abstract context words Контекстних слів у конспекті External Indexes Зовнішні індекси Add index Додати індекс Select the xapiandb directory for the index you want to add, then click Add Index Оберіть потрібну теку із індексом Xapian та натисніть "Додати індекс" Browse Перегляд &OK &ОК Apply changes Застосувати зміни &Cancel &Відміна Discard changes Відмінити зміни Result paragraph<br>format string Рядок форматування<br>блоку результатів Automatically add phrase to simple searches Автоматично додавати фразу до простих пошуків A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Пошук [rolling stones] (2 слова) буде змінено на [rolling or stones or (rolling phrase 2 stones)]. Це може підняти результати, в яких пошукові слова зустрічаються саме в такій послідовності, як в запиті. User preferences Вподобання Use desktop preferences to choose document editor. Використовувати налаштування десктопу щодо редактору документів. External indexes Зовнішні індекси Toggle selected Переключити вибране Activate All Включити все Deactivate All Виключити все Remove selected Видалити вибране Remove from list. This has no effect on the disk index. Видалити зі списку. Не впливає на дисковий індекс. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Визначає формат для кожного блоку списку результатів. Використовуйте html-формат qt та схожі на printf заміни:<br>%A анотація<br> %D дата<br> %I назва піктограми<br> %K ключові слова (якщо є)<br> %L посилання перегляду та редагування<br> %M тип MIME<br> %N кількість результатів<br> %R релевантність<br> %S розмір<br> %T назва<br> %U URL<br> Remember sort activation state. Запам'ятати стан сортування. Maximum text size highlighted for preview (megabytes) Максимальний розмір тексту із підсвічуванням (Мб) Texts over this size will not be highlighted in preview (too slow). Тексти із розміром, більшим за вказаний, не буде підсвічено у попередньому перегляді (повільно). Highlight color for query terms Колір виділення ключових слів Prefer Html to plain text for preview. Віддавати перевагу HTML над текстом для перегляду. If checked, results with the same content under different names will only be shown once. Якщо увімкнене, результати с таким самим змістом та різними назвами буде показано не більше одного разу. Hide duplicate results. Ховати дублікати Choose editor applications Оберіть редактори Display category filter as toolbar instead of button panel (needs restart). Відображати фільтр категорій як панель інструментів замість панелі кнопок (потребує перезавантаження). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Слова в списку будуть автоматично перетворені на ext:xxx тверджень при введенні в мову запиту. Query language magic file name suffixes. Абонентський код назв файлів магічного типу. Enable Увімкнено ViewAction Changing actions with different current values Зміна дій із різними поточними значеннями Mime type Тип MIME Command Команда MIME type MIME-тип Desktop Default Типово для робочого стола Changing entries with different current values Зміна записів з різними поточними значеннями ViewActionBase File type Тип файлу Action Дія Select one or several file types, then click Change Action to modify the program used to open them Оберіть один або декілька типів файлів, потім натисніть "Змінити дію", щоб змінити програму для них Change Action Змінити дію Close Закрити Native Viewers Рідні переглядачі Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Виберіть один або кілька типів mime, потім натисніть "Змініть дію"<br>Ви також можете закрити це діалогове вікно і перевірити "Налаштування робочого столу"<br>на головній панелі, щоб проігнорувати цей список і використати налаштування робочого столу. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Виберіть один або кілька МІМЕ типів за допомогою елементів керування у нижній кадрі щоб змінити спосіб їх обробки. Use Desktop preferences by default Використовувати налаштування робочого столу за замовчуванням Select one or several file types, then use the controls in the frame below to change how they are processed Виберіть один або кілька типів файлів, а потім використовуйте параметри в кадрі нижче, щоб змінити їх обробку Exception to Desktop preferences Виключення до налаштувань робочого столу Action (empty -> recoll default) Дія (порожня -> Скинути за замовчуванням) Apply to current selection Застосувати до поточного виділення Recoll action: Дія при відновленні: current value поточне значення Select same Виберіть те ж саме <b>New Values:</b> <b>Нові значення:</b> Webcache Webcache editor Редактор Webcache Search regexp Шукати регулярний вираз TextLabel WebcacheEdit Copy URL Копіювати посилання Unknown indexer state. Can't edit webcache file. Невідомий стан індексатора. Може'редагувати файл веб-кешу. Indexer is running. Can't edit webcache file. Indexer запущений. Може'редагувати файл webcache Delete selection Видалити вибране Webcache was modified, you will need to run the indexer after closing this window. Веб-кеш було змінено, вам потрібно буде запустити індексатор після закриття цього вікна. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url URL-адреса Date Дата Size URL Адреса WinSchedToolW Error Помилка Configuration not initialized Конфігурацію не ініціалізовано <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> Command already started Команда вже розпочата Recoll Batch indexing Повторна індексація батчів Start Windows Task Scheduler tool Запустити інструмент планувальника завдань Windows Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Черга індексування крадіжки Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle MUST не запущено. Дозволяє обробляти черги звірів для індексування історії Firefox.<br>(Вам також слід встановити плагін для Firefox Beagle) Web cache directory name Ім'я каталогу веб-кешу The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Ім'я каталогу, де зберігати кеш для відвідуваних веб-сторінок.<br>Шлях шлях виконується відносно каталогу налаштувань. Max. size for the web cache (MB) Макс. розмір для веб-кешу (МБ) Entries will be recycled once the size is reached Записи будуть перероблені після досягнення розміру Web page store directory name Ім'я каталогу веб-сторінок магазину The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Ім'я каталогу, де зберігати відвідані веб-сторінки.<br>Шлях не абсолютний шлях відноситься до каталогу налаштувань. Max. size for the web store (MB) Макс. розмір для веб-магазину (МБ) Process the WEB history queue Обробити чергу історії WEB Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Вмикає індексацію відвіданих сторінок.<br>(вам також потрібно встановити плагін для Firefox) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Записів буде перероблено після досягнення розміру.<br>Збільшення розміру дійсно має сенс, бо зменшення значення не призведе до скорочення існуючого файлу (лише відходів в кінці). confgui::ConfIndexW Can't write configuration file Неможливо записати файл конфіґурації Recoll - Index Settings: Recoll - Параметри індексу: confgui::ConfParamFNW Browse Перегляд Choose Перегляд confgui::ConfParamSLW + + - - Add entry Додати запис Delete selected entries Видалити виділені записи ~ ~ Edit selected entries Редагувати вибрані записи confgui::ConfSearchPanelW Automatic diacritics sensitivity Автоматична чутливість діакритики <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Автоматично викликати чутливість до діакриту, якщо пошуковий термін має акцентовані символи (не в unac_except_trans). Можливо, вам потрібно використовувати мову запитів і модифікатор <i>D</i> щоб вказати чутливість до діакритики. Automatic character case sensitivity Автоматична чутливість регістру символів <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Автоматично чутливість до регістру ієрогліфів, якщо запис має символи у верхньому регістрі, окрім першої позиції. Також вам потрібно використовувати мову запиту і модифікатор <i>C</i> щоб вказати чутливість до символів . Maximum term expansion count Максимальний термін розширення <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Максимальна кількість розширення для одного строку (наприклад, при використанні шаблонів). За замовчуванням 10 000 є розумним і буде уникати запитів, які з'являються замороженими, тоді як двигун рухається в цьому списку. Maximum Xapian clauses count Максимальна кількість заяв Xapian <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Максимальна кількість елементарних застережень, які ми додаємо до одного Xapian запиту. У деяких випадках результатом строкового розширення може бути багатократне і ми хочемо уникати використання надмірної пам'яті. Значення за замовчуванням 100 000 має бути достатньо високим у більшості випадків і сумісним з поточними типовими конфігураціями обладнання. confgui::ConfSubPanelW Global Глобальні Max. compressed file size (KB) Межа розміру стиснених файлів (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Це значення встановлює поріг розміру стиснених файлів, більші за нього не буде опрацьовано. -1 вимикає ліміт, 0 вимикає декомпресію. Max. text file size (MB) Макс. розмір текстового файлу (МБ) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Це значення встановлює поріг, після якого текстові файли не будуть оброблятися. Встановіть в -1 для відсутності ліміту. Це за винятком файлів monster журналів з індексу. Text file page size (KB) Розмір текстової сторінки (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Якщо це значення встановлено (не дорівнює -1), текстові файли будуть розділені у чанках такого розміру для індексування. Це допоможе шукати дуже великі текстові файли (тобто файли журналу). Max. filter exec. time (S) Макс. виконання фільтру. час (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Зовнішні фільтри працюють довше, ніж це буде перервано. Це для рідкісного випадку (i: postscript) де документ може спричинити фільтр до циклу -1 без обмежень. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Зовнішні фільтри працюють довше, ніж це буде перервано. Це для рідкісного випадку (i: postscript) де документ може спричинити фільтр для циклу. Встановіть -1 для відсутності ліміту. Only mime types Лише типи МІМЕ An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Виключний список індексованих mime-типів.<br>Додатково нічого не буде індексовано. Зазвичай, порожній і неактивний Exclude mime types Виключити типи МІМЕ Mime types not to be indexed Типи MIME не індексуються Max. filter exec. time (s) Макс. виконання фільтрів. Час (ів) confgui::ConfTopPanelW Top directories Верхні теки The list of directories where recursive indexing starts. Default: your home. Список тек, з яких починається рекурсивне індексування. Типово: домашня тека. Skipped paths Пропускати шляхи These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Це назви тек, у які індексування не потрапить.<br> Може містити шаблони. Має співпадати із шляхами, що бачить індексатор (наприклад, якщо topdirs містить '/home/me' та '/home' є посиланням на '/usr/home', то вірний запис буде '/home/me/tmp*', а не '/usr/home/me/tmp*') Stemming languages Мови зі словоформами The languages for which stemming expansion<br>dictionaries will be built. Мови, для яких буде побудовано<br>словники розкриття словоформ. Log file name Файл журналу The file where the messages will be written.<br>Use 'stderr' for terminal output Файл, куди підуть повідомлення.<br>'stderr' для терміналу Log verbosity level Докладність журналу This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Встановити обсяг повідомлень,<br>від помилок до даних зневадження. Index flush megabytes interval Інтервал скидання індексу (Мб) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Скільки даних буде проіндексовано між скиданнями індексу на диск.<br>Допомагає контролювати використання пам'яті індексатором. Типово: 10Мб Max disk occupation (%) Максимальне використання диску (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Відсоток зайнятого диску, коли індексування буде зупинено (щоб уникнути заповнення доступного простору).<br>Типово: 0 (без ліміту). No aspell usage Не використовувати aspell Aspell language Мова aspell The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Мова словника aspell. Має виглядати як 'en' або 'uk'...<br>Якщо не встановлене, буде використане оточення (локаль), що зазвичай робить. Щоб з'ясувати, що маємо на системі, наберіть 'aspell config' та перегляньте файли .dat у теці 'data-dir'. Database directory name Тека бази даних The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Назва теки, де міститься індекс<br>Відносний шлях буде трактовано відносно теки конфіґурації. Типово: 'xapiandb'. Use system's 'file' command Використовувати системну 'file' Use the system's 'file' command if internal<br>mime type identification fails. Використовувати команду 'file' з системи, коли внутрішнє<br>визначення типу MIME дає збій. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Вимикає використання aspell для генерації наближень у написання в навіґаторі термінів.<br>Корисне, коли aspell відсутній або зламаний. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Мова аспельного словника. Це має виглядати на 'en' або 'fr' . .<br>Якщо це значення не встановлено, то NLS середовище буде використане для обчислення, що зазвичай працює. Щоб отримати уявлення про те, що було встановлено на вашій системі, введіть 'аюту' і подивіться на . у папці 'data-dir' The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Ім'я каталогу, де зберігати індекс<br>каталог, не є абсолютний шлях відноситься до каталогу конфігурації. За замовчуванням 'xapiandb'. Unac exceptions Юнацькі винятки <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. <p>Це винятки з механізму unac, який, за замовчуванням, видаляє всі діакритики, і виконує канонічну декомпозицію. Ви можете перевизначити неакцентовані на деякі символи, в залежності від вашої мови, і вказати додаткові декомпозиції. . для лігатур. При відрізах першого символу - це вихідний, а решта - переклад. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Це шляхи каталогів, в яких не буде входити індексація.<br>Елементи контуру, які можуть містити шаблони. Записи повинні відповідати шляхам індексатор (наприклад, якщо верхні каталоги включають '/home/me' і '/будинок' насправді посилання на '/usr/home'правильний запис у пропущеному шляху буде '/home/me/tmp*', не '/usr/home/me/tmp*') Max disk occupation (%, 0 means no limit) Максимальна зайнятість диску (%, 0 - без обмежень) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Це відсоток використання диску - загальний обсяг диску, а не розмір індексу - при якому індексація зазнає невдачі та зупинки.<br>Стандартне значення 0 видаляє будь-які межі. uiPrefsDialogBase User preferences Вподобання User interface Інтерфейс Number of entries in a result page Кількість результатів на сторінку If checked, results with the same content under different names will only be shown once. Якщо увімкнене, результати с таким самим змістом та різними назвами буде показано не більше одного разу. Hide duplicate results. Ховати дублікати Highlight color for query terms Колір виділення ключових слів Result list font Шрифт списку результатів Opens a dialog to select the result list font Відкриває діалог вибору шрифту списку результатів Helvetica-10 Helvetica-10 Resets the result list font to the system default Повертає шрифт у типовий системний Reset Скинути Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Визначає формат для кожного блоку списку результатів. Використовуйте html-формат qt та схожі на printf заміни:<br>%A анотація<br> %D дата<br> %I назва піктограми<br> %K ключові слова (якщо є)<br> %L посилання перегляду та редагування<br> %M тип MIME<br> %N кількість результатів<br> %R релевантність<br> %S розмір<br> %T назва<br> %U URL<br> Result paragraph<br>format string Рядок форматування<br>блоку результатів Texts over this size will not be highlighted in preview (too slow). Тексти із розміром, більшим за вказаний, не буде підсвічено у попередньому перегляді (повільно). Maximum text size highlighted for preview (megabytes) Максимальний розмір тексту із підсвічуванням (Мб) Use desktop preferences to choose document editor. Використовувати налаштування десктопу щодо редактору документів. Choose editor applications Оберіть редактори Display category filter as toolbar instead of button panel (needs restart). Відображати фільтр категорій як панель інструментів замість панелі кнопок (потребує перезавантаження). Auto-start simple search on whitespace entry. Починати простий пошук при введенні пробілу. Start with advanced search dialog open. Відкривати діалог складного пошуку при старті. Start with sort dialog open. Відкривати діалог сортування при старті. Remember sort activation state. Запам'ятати стан сортування. Prefer Html to plain text for preview. Віддавати перевагу HTML над текстом для перегляду. Search parameters Параметри пошуку Stemming language Мова словоформ A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Пошук [rolling stones] (2 слова) буде змінено на [rolling or stones or (rolling phrase 2 stones)]. Це може підняти результати, в яких пошукові слова зустрічаються саме в такій послідовності, як в запиті. Automatically add phrase to simple searches Автоматично додавати фразу до простих пошуків Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Чи намагатися будувати конспекти для результатів пошуку, використовуючі контекст знайдених слів? Може працювати повільно для великих документів. Dynamically build abstracts Динамічно будувати конспекти Do we synthetize an abstract even if the document seemed to have one? Чи робити новий конспект, навіть якщо якийсь вже є в документі? Replace abstracts from documents Заміняти наявні у документах конспекти Synthetic abstract size (characters) Розмір синтетичного конспекту (у символах) Synthetic abstract context words Контекстних слів у конспекті The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Слова в списку будуть автоматично перетворені на ext:xxx тверджень при введенні в мову запиту. Query language magic file name suffixes. Абонентський код назв файлів магічного типу. Enable Увімкнено External Indexes Зовнішні індекси Toggle selected Переключити вибране Activate All Включити все Deactivate All Виключити все Remove from list. This has no effect on the disk index. Видалити зі списку. Не впливає на дисковий індекс. Remove selected Видалити вибране Click to add another index directory to the list Натисніть, щоб додати до списку іншу теку індексу Add index Додати індекс Apply changes Застосувати зміни &OK &ОК Discard changes Відмінити зміни &Cancel &Відміна Abstract snippet separator Розділювач фрагментів Use <PRE> tags instead of <BR>to display plain text as html. Використовуйте теги <PRE> замість <BR>для відображення простого тексту в якості html. Lines in PRE text are not folded. Using BR loses indentation. Рядки в тексті PRE не складені. Використання BR втрачає відступи. Style sheet Таблиця стилів Opens a dialog to select the style sheet file Відкриває діалог для вибору файлу таблиці стилів Choose Перегляд Resets the style sheet to default Скинути таблицю стилів на типовий Lines in PRE text are not folded. Using BR loses some indentation. Рядки в тексті PRE не складені. Використання BR втрачає деякі відступи. Use <PRE> tags instead of <BR>to display plain text as html in preview. Використовуйте <PRE> теги замість <BR>для відображення простого тексту в якості html у попередньому перегляді. Result List Підсумки Edit result paragraph format string Редагувати результат рядка формату абзац Edit result page html header insert Редагувати заголовок HTML шаблону результату сторінки Date format (strftime(3)) Date format (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Відсоток частоти порогу, протягом якого ми не використовуємо терміни в автофразі. Частотні терміни є досить складною проблемою з фраз. Пропущені терміни, збільшують фразу "пошкодження" і зменшують ефективність автофрази-паролю Значення, за замовчуванням 2 (percent). Autophrase term frequency threshold percentage Відсоток частоти автофрази, Plain text to HTML line style Простий текст до стилю HTML рядка Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Рядки в тексті PRE не складені. Використання BR втрачає деякі відступи. PRE + стиль обрухту може бути тим, що ви хочете. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + загортання Exceptions Винятки Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Типи Mime, які не слід передавати у xdg-відкриття, навіть коли "Використовувати налаштування робочого стола" встановлено.<br> Корисно для того, щоб передати номер сторінки та пошукові параметри до, наприклад evince. Disable Qt autocompletion in search entry. Вимкнути автодоповнення Qt в пошуковому записі. Search as you type. Пошук по вашому типу. Paths translations Переклади шляхів Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Натисніть, щоб додати іншу теку для індексу до списку. Ви можете вибрати теку з конфігурацією Recoll чи індексом Xapa. Snippets window CSS file Сніпети віконного файлу CSS Opens a dialog to select the Snippets window CSS style sheet file Відкриває діалогове вікно для вибору файлу таблиці стилів CSS Resets the Snippets window style Скинути стиль вікна сніпетів Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Оберіть, чи відображаються фільтри документів як радіо, панель інструментів combobox або меню. Document filter choice style: Стиль фільтру документа: Buttons Panel Панель кнопок Toolbar Combobox Комбокс з інструментами Menu Меню Show system tray icon. Показувати піктограму на панелі завдань. Close to tray instead of exiting. Закривати до трею замість виходу. Start with simple search mode Почніть з простого пошуку Show warning when opening temporary file. Показувати застереження при відкритті тимчасового файлу. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Стиль користувача, який буде застосовуватися до вікна сніпетів.<br> Примітка: заголовок сторінки результату також включений у заголовок вікна сніпетів. Synonyms file Синонім файлу Highlight CSS style for query terms Виділяти стиль CSS для умов запиту Recoll - User Preferences Recoll - Налаштування користувача Set path translations for the selected index or for the main one if no selection exists. Встановити переклад шляхів для вибраного індексу або головного, якщо не існує вибору. Activate links in preview. Активувати посилання в режимі попереднього перегляду. Make links inside the preview window clickable, and start an external browser when they are clicked. При натисканні на вікно попереднього перегляду, відбуваються посилання для запуску зовнішнього браузера. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Умови запиту виділені в результаті. <br>Може, спробуйте щось на зразок "color:red;background:жовтий" для чогось більше, ніж за замовчуванням синій... Start search on completer popup activation. Почати пошук по завершенню спливаючого вікна активації. Maximum number of snippets displayed in the snippets window Максимальна кількість сніпетів, які відображаються у вікні сніпетів Sort snippets by page number (default: by weight). Сортування сніпетів за номером сторінки (за замовчуванням: за вагою). Suppress all beeps. Придушити всіх гусень. Application Qt style sheet Таблиця стилів додатку Limit the size of the search history. Use 0 to disable, -1 for unlimited. Обмежити розмір історії пошуку. Використовуйте 0, щоб вимкнути, -1 для необмежених. Maximum size of search history (0: disable, -1: unlimited): Максимальний розмір історії пошуку (0: вимкнути, -1: необмежено): Generate desktop notifications. Згенерувати сповіщення на робочому столі. Misc Інше Work around QTBUG-78923 by inserting space before anchor text Робота над QTBUG-78923 шляхом вставлення простору перед якорем тексту Display a Snippets link even if the document has no pages (needs restart). Відображати посилання на сніпети, навіть якщо в документі немає сторінок (потребує перезапуску). Maximum text size highlighted for preview (kilobytes) Максимальний розмір тексту виділений для попереднього перегляду (кілобайт) Start with simple search mode: Почніть з простого пошуку: Hide toolbars. Приховати панелі інструментів. Hide status bar. Сховати рядок стану. Hide Clear and Search buttons. Приховати кнопки очищення та пошуку. Hide menu bar (show button instead). Приховати панель меню (замість цього показувати кнопку). Hide simple search type (show in menu only). Приховати тип простого пошуку (показувати лише в меню). Shortcuts Гарячі клавіші Hide result table header. Сховати заголовок таблиці результатів. Show result table row headers. Показати заголовки рядків результату. Reset shortcuts defaults Скинути налаштування ярликів Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Вимкнути комбінації клавіш Ctrl+[0-9]/[a-z] для перемикання до рядків таблиці. Use F1 to access the manual Для ручного доступу до F1 скористайтеся функцією Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Простий тип пошуку Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Темний режим Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Таблиця результатів Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_ru.ts0000644000175000017500000072101014506533140014253 00000000000000 ActSearchDLG Menu search Меню поиска AdvSearch All clauses всем условиям Any clause любому условию text текст texts тексты spreadsheet таблица spreadsheets таблицы presentation презентация media медиа message сообщение other прочее Advanced Search Сложный поиск Load next stored search Загрузить следующий сохранённый запрос Load previous stored search Загрузить предыдущий сохранённый запрос Bad multiplier suffix in size filter Неверный множитель в фильтре размера AdvSearchBase Advanced search Сложный поиск Find Найти All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Все заполненные поля справа будут объединены логическим И («Все условия») или ИЛИ («Любое условие»). <br>В полях типа «Любые», «Все» или «Без» допустимы сочетания простых слов и фразы, заключённые в двойные кавычки.<br>Пустые поля игнорируются. Search for <br>documents<br>satisfying: Искать <br>документы,<br>удовлетворяющие: Delete clause Удалить условие Add clause Добавить условие Filter Фильтр Check this to enable filtering on dates Включить фильтрование по дате Filter dates Фильтровать по дате From Из To В Filter birth dates Фильтровать по дате рождения Check this to enable filtering on sizes Включить фильтрование по размеру Filter sizes Фильтровать по размеру Minimum size. You can use k/K,m/M,g/G as multipliers Минимальный размер. Допускается использование множителей к/К, м/М, г/Г Min. Size Минимум Maximum size. You can use k/K,m/M,g/G as multipliers Максимальный размер. Допускается использование множителей к/К, м/М, г/Г Max. Size Максимум Check this to enable filtering on file types Фильтровать по типам файлов Restrict file types Ограничить типы файлов Check this to use file categories instead of raw mime types Использовать категории, а не типы MIME By categories По категориям Save as default Сделать параметром по умолчанию Searched file types Искать среди All ----> Все ----> Sel -----> Выделенные ----> <----- Sel <----- Выделенные <----- All <----- Все Ignored file types Игнорируемые типы файлов Enter top directory for search Указать имя каталога верхнего уровня для поиска Browse Обзор Restrict results to files in subtree: Ограничить результаты поиска файлами в подкаталоге: Invert Обратить Start Search Начать поиск Close Закрыть ConfIndexW Can't write configuration file Невозможно записать файл конфигурации Global parameters Общие параметры Local parameters Локальные параметры Web history История в веб Search parameters Параметры поиска Top directories Каталоги верхнего уровня The list of directories where recursive indexing starts. Default: your home. Список каталогов, где начинается рекурсивное индексирование. По умолчанию: домашний каталог. Skipped paths Пропущенные пути These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Полный путь к директории, которая не будет затрагиваться при индексировании. <br>Может содержать маски. Записи должны совпадать с путями, которые видит индексатор (например, если topdirs включает «/home/me», а «/home» на самом деле ведёт к «/usr/home», правильной записью skippedPath будет «/home/me/tmp*», а не «/usr/home/me/tmp*») Stemming languages Языки со словоформами The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... Языки, для которых будут составлены словари словоформ.<br>Доступные значения описаны в документации к Xapian (например, english, french, russian...) Log file name Имя файла журнала The file where the messages will be written.<br>Use 'stderr' for terminal output Файл, куда будут записываться сообщения.<br>Используйте 'stderr' для вывода в терминал Log verbosity level Уровень подробности журнала This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Это значение определяет подробность сообщений,<br>от ошибок до отладочных данных. Indexer log file name Имя файла журнала индексатора If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Если поле пустое, будет использовано имя файла выще. Отдельный файл с журналом для отладки может быть полезен, т.к. общий журнал<br>при старте интерфейса будет затёрт. Index flush megabytes interval Интервал сброса данных индекса (МБ) This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Это значение определяет количество данных, индексируеммых между сбросами на диск.<br>Помогает контролировать использование памяти индексатором. Значение по умолчанию: 10МБ Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Степень заполнения диска в процентах, при которой прекратится индексирование<br>Например, 90% для останова на 90% заполнения; 0 или 100 снимает ограничение Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) Степень заполнения диска в процентах, при которой прекратится индексирование<br>Например, 90% для останова на 90% заполнения; 0 или 100 снимает ограничение This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Процент занятого пространства на диске (в целом, не только индексом), при котором индексирование завершится ошибкой и прекратится.<br>По умолчанию значение 0 снимает ограничение. No aspell usage Не использовать aspell (by default, aspell suggests mispellings when a query has no results). (по умолчанию aspell подсказывает об опечатках, когда запрос не даёт результатов) Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Отключает использование aspell для создания вариантов написания в обозревателе терминов.<br> Полезно, если aspell отсутствует или не работает. Aspell language Язык aspell The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Язык словаря aspell в виде двухбуквенного кода, например, 'en', 'fr', 'ru'...<br>Если значение не установлено, будет предпринята попытка вывести его из локали, что обычно срабатывает. Чтобы посмотреть, что установлено на вашей системе, наберите 'aspell config' и поищите .dat-файлы в каталоге, указанном как 'data-dir'. Database directory name Каталог базы данных The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Имя каталога, в котором хранится индекс<br>Путь указывается относительно каталога конфигурации и не является абсолютным. По умолчанию: «xapiandb». Unac exceptions Исключения unac <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Это исключения для механизма unac, который по умолчанию отбрасывает все диакритические знаки и проводит каноническую декомпозицию. Можно переопределить механизм удаления надстрочных знаков для отдельных символов или добавить правила декомпозиции (например, для лигатур). В каждой отделённой запятой записи первый символ является исходным, а остальные — его интерпретации. Process the Web history queue Обработка очереди истории веб-поиска Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Включает индексацию посещённых страниц в Firefox.<br>(требуется установка плагина Recoll) Web page store directory name Имя каталога с сохранёнными веб-страницами The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Имя каталога хранения просмотренных веб-страниц.<br>Путь указывается относительно каталога конфигурации и не является абсолютным. Max. size for the web store (MB) Предельный размер веб-хранилища (МБ) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Записи будут удалены при достижении максимального размера хранилища.<br>Целесообразно только увеличивать размер, так как уменьшение значения не повлечёт усечение существующего файла (лишь перестанет использовать его хвост). Page recycle interval Интервал обновления страницы <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. <p>По умолчанию в кэше хранится только один экземпляр ссылки. Это можно изменить в настройках, где указывается значение длительности хранения нескольких экземпляров ('день', 'неделя', 'месяц', 'год'). Учтите, что увеличение интервала не сотрёт уже существующие записи. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 На заметку: старые страницы будут стёрты, чтобы освободилось место для новых, когда будет достигнут предельный объём. Текущий размер: %1 Automatic diacritics sensitivity Автоматический учёт диакритических знаков <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. <p>Автоматически включает учёт диакритических знаков, если строка поиска содержит диакритические знаки (кроме unac_except_trans). В противном случае используйте язык запросов и модификатор <i>D</i> для учёта диакритических знаков. Automatic character case sensitivity Автоматический учёт регистра <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Автоматически включает учёт регистра, если строка поиска содержит заглавные буквы (кроме первой буквы). В противном случае используйте язык запросов и модификатор <i>C</i> учёта регистра. Maximum term expansion count Предельное число однокоренных слов <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. <p>Предельное число однокоренных слов для одного слова (например, при использовании масок). Значение по умолчанию в 10 000 является разумным и поможет избежать ситуаций, когда запрос кажется зависшим при переборе списка слов. Maximum Xapian clauses count Предельное число Xapian-предложений <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. <p>Предельное число элементарных условий, добавляемых к запросу Xapian. В некоторых случаях результат поиска однокоренных слов может быть избыточным и занять слишком большой объём памяти. Значение по умолчанию в 100 000 достаточно для большинства случаев и подходит для современных аппаратных конфигураций. ConfSubPanelW Only mime types Только MIME-типы An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Исчерпывающий перечень индексируемых типов MIME.<br>Другие типы индексироваться не будут. Обычно пуст и неактивен Exclude mime types Исключить MIME-типы Mime types not to be indexed Типы MIME, индексирование которых проводиться не будет Max. compressed file size (KB) Предельный размер сжатого файла (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Это значение устанавливает предельный размер сжатых файлов, которые будут обрабатываться. Значение -1 снимает ограничение, 0 отключает распаковку. Max. text file size (MB) Предельный размер текстового файла (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Это значение устанавливает предельный размер текстовых файлов, которые будут обрабатываться. Значение -1 снимает ограничение. Рекомендуется использовать для исключения файлов журнала большого размера из процесса индексирования. Text file page size (KB) Pазмер страницы текстового файла (КБ) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Если это значение установлено (т.е. не равно -1), то при индексировании текстовые файлы разбиваются на блоки соответствующего размера. Данный параметр полезен при выполнении поиска в очень больших текстовых файлах (например, файлах журналов). Max. filter exec. time (s) Пред. время работы фильтра (с) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Работа внешних фильтров, длящаяся дольше указанного времени, будет прервана. Применяется для редких случаев (например, с фильтром postscript), когда возникает зацикливание фильтра при обработке какого-то документа. Установите значение -1, чтобы снять ограничение. Global Общее CronToolW Cron Dialog Настройка заданий Cron <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Индексирование <span style=" font-weight:600;">Recoll</span> по расписанию (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Каждое поле может содержать маску (*), единичное числовое значение, разделённый запятыми список (1,3,5) или диапазон чисел (1-7). Эти поля будут использованы <span style=" font-style:italic;">как есть</span> в файле crontab, также можно указать необходимые параметры в самом файле, см. crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Например, если ввести знак <span style=" font-family:'Courier New,courier';">*</span> в поле <span style=" font-style:italic;">«Дни недели»</span>, <span style=" font-family:'Courier New,courier';">12,19</span> — в поле <span style=" font-style:italic;">«Часы»</span> и <span style=" font-family:'Courier New,courier';">15</span> — в поле <span style=" font-style:italic;">«Минуты»</span>, индексирование будет производиться ежедневно в 12:15 и 19:15.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Расписание с очень частыми запусками может оказаться менее эффективным, чем индексирование в реальном времени.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Дни недели (* или 0-7, 0 или 7 — воскресенье) Hours (* or 0-23) Часы (* или 0-23) Minutes (0-59) Minutes (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Для остановки автоматического идексирования по расписанию нажмите <span style=" font-style:italic;">«Выключить»</span>, для запуска — <span style=" font-style:italic;">«Включить»</span>, для отмены внесённых изменений — <span style=" font-style:italic;">«Отмена»</span>.</p></body></html> Enable Включить Disable Выключить It seems that manually edited entries exist for recollindex, cannot edit crontab Похоже, что для recollindex есть вручную исправленные записи, редактирование crontab невозможно Error installing cron entry. Bad syntax in fields ? Ошибка установки записи cron. Неверный синтаксис полей? EditDialog Dialog Диалог EditTrans Source path Исходный путь Local path Локальный путь Config error Ошибки конфигурации Original path Изначальный путь EditTransBase Path Translations Корректировка путей Setting path translations for Задать корректировку для Select one or several file types, then use the controls in the frame below to change how they are processed Выберите типы файлов и используйте кнопки управления ниже, чтобы изменить порядок обработки файлов Add Добавить Delete Удалить Cancel Отмена Save Сохранить FirstIdxDialog First indexing setup Настройка первого индексирования <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Похоже, что индекс для этой конфигурации не существует.</span><br /><br />Для индексирования только домашнего каталога с набором умолчаний нажмите кнопку <span style=" font-style:italic;">«Запустить индексирование»</span>. Детальную настройку можно будет провести позже. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Если нужно больше контроля, воспользуйтесь приведёнными ниже ссылками для настройки параметров и расписания индексирования.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Перейти к этим инструментам позднее можно через меню <span style=" font-style:italic;">«Настройка»</span>.</p></body></html> Indexing configuration Настройка индексирования This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Здесь можно указать, какие каталоги требуется индексировать, а также настроить такие параметры как исключение путей или имён файлов, используемые по умолчанию кодировки и т.д. Indexing schedule Расписание индексирования This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Здесь можно выбрать режим индексирования: по расписанию или в реальном времени, а также настроить расписание автоиндексирования (с использованием cron). Start indexing now Запустить индексирование FragButs %1 not found. %1 не найден. %1: %2 %1: %2 Query Fragments Фрагменты запроса IdxSchedW Index scheduling setup Настройка расписания индексирования <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Индексирование <span style=" font-weight:600;">Recoll</span> может работать постоянно, индексируя изменяющиеся файлы, или запускаться через определённые промежутки времени. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Рекомендуется ознакомиться с руководством пользователя программы, чтобы выбрать наиболее подходящий режим работы (нажмите F1 для вызова справки). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Этот инструмент позволяет выбрать, будет ли индексирование производиться по расписанию или в реальном времени при входе в систему (или оба варианта сразу, что вряд ли имеет смысл). </p></body></html> Cron scheduling Расписание запуска The tool will let you decide at what time indexing should run and will install a crontab entry. Этот инструмент позволяет выбрать, в какое время запускать индексирование, а также сделать запись в crontab. Real time indexing start up Запуск индексирования в реальном времени Decide if real time indexing will be started when you log in (only for the default index). Здесь можно выбрать, нужно ли начинать индексирование в реальном времени при входе в систему (только для индекса по умолчанию). ListDialog Dialog Диалог GroupBox GroupBox Main "history" file is damaged, please check or remove it: Файл истории повреждён, проверьте или удалите его: No db directory in configuration Каталог базы не задан в конфигурации Needs "Show system tray icon" to be set in preferences! Необходимо включить параметр настройки «Отображать значок в трее»! Preview Form Форма Tab 1 1-я вкладка &Search for: &Искать: &Next &Следующий &Previous &Предыдущий Clear Очистить Match &Case &С учётом регистра Previous result document Предыдущий документ с результатами Next result document Следующий документ с результатами Open Открыть Preview Window Окно предпросмотра Close preview window Закрыть окно предпросмотра Show next result Показать следующий результат Show previous result Показать предыдущий результат Close tab Закрыть вкладку Print Печать Error loading the document: file missing. Ошибка загрузки документа: файл отсутствует. Error loading the document: no permission. Ошибка загрузки документа: нет разрешения. Error loading: backend not configured. Ошибка загрузки: бэкенд не настроен. Error loading the document: other handler error<br>Maybe the application is locking the file ? загрузки документа: ошибка другого обработчика<br>Может, приложение заблокировало файл? Error loading the document: other handler error. Ошибка загрузки документа: ошибка другого обработчика. <br>Attempting to display from stored text. <br>Попытка отобразить из сохранённого текста. Missing helper program: Отсутствует обработчики: Can't turn doc into internal representation for Невозможно сконвертировать документ во внутреннее представление для Canceled Отменено Cancel Отмена Could not fetch stored text Не удалось получить сохранённый текст Creating preview text Создание текста для просмотра Loading preview text into editor Загрузка текста в редактор PreviewTextEdit Show fields Показать поля Show image Показать изображение Show main text Показать основной текст Reload as Plain Text Перезагрузить как простой текст Reload as HTML Перезагрузить как HTML Select All Выделить всё Copy Копировать Print Печать Fold lines Линия сгиба Preserve indentation Сохранить отступы Save document to file Сохранить документ в файл Open document Открыть документ Print Current Preview Печать текущего вида QObject <b>Customised subtrees <b>Пользовательские подкаталоги The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Список подкаталогов индексируемого дерева,<br>к которым должны применяться особые параметры. По умолчанию: пусто. <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i> Skipped names Пропускать These are patterns for file or directory names which should not be indexed. Шаблоны имён файлов или каталогов, имена которых не следует индексировать. Ignored endings Игнорируемые окончания These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Окончания имён файлов, индексируемых только по имени (без попытки определить типы MIME, разжатия либо индексации содержимого). Default<br>character set Кодировка по умолчанию Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Кодировка, которая будет использована при чтении файлов, в которых кодировка не указана явно; например, простых текстовых файлов.<br>Значение по умолчанию не установлено и берётся из параметров системы (локали). Follow symbolic links Открывать символические ссылки Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Открывать символические ссылки при индексировании. По умолчанию действие не выполняется во избежание дублированного индексирования Index all file names Индексировать все имена файлов Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Индексировать имена файлов, содержимое которых невозможно определить или обработать (неизвестный или неподдерживаемый тип MIME). По умолчанию включено QWidget Create or choose save directory Создать или выбрать каталог сохранения Choose exactly one directory Выберите только один каталог Could not read directory: Невозможно прочитать каталог: Unexpected file name collision, cancelling. Неожиданный конфликт имён файлов, отмена действия. Cannot extract document: Невозможно извлечь документ: &Preview &Просмотр &Open О&ткрыть Open With Открыть с помощью Run Script Запустить выполнение сценария Copy &File Path Копировать &путь файла Copy &URL Копировать &URL Copy File Name Копировать &имя файла Copy Text Копировать текст &Write to File &Записать в файл Save selection to files Сохранить выделение в файлы Preview P&arent document/folder &Просмотр родительского документа/каталога &Open Parent document &Открыть родительский документ &Open Parent Folder &Открыть родительский каталог Find &similar documents Найти &похожие документы Open &Snippets window Открыть окно &выдержек Show subdocuments / attachments Показать вложенные документы QxtConfirmationMessage Do not show again. Больше не показывать. RTIToolW Real time indexing automatic start Автозапуск индексирования в реальном времени <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Индексирование при помощи <span style=" font-weight:600;">Recoll</span> может быть настроено как сервис, обновляющий индекс одновременно с изменением файлов, то есть в реальном времени. При этом постоянное обновление индекса будет происходить за счёт непрерывного использования системных ресурсов.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Запускать службу индексирования одновременно с сеансом рабочего стола. Also start indexing daemon right now. Также запустить прямо сейчас службу индексирования. Replacing: Замена: Replacing file Замена файла Can't create: Невозможно создать: Warning Предупреждение Could not execute recollindex Не удалось запустить recollindex Deleting: Удаление: Deleting file Удаление файла Removing autostart Отмена автозапуска Autostart file deleted. Kill current process too ? Файл автозапуска удалён. Прервать текущий процесс? RclCompleterModel Hits (количество нажатий) RclMain Indexing in progress: Идёт индексирование: None Нет Updating Обновление Flushing Заполнение Purge Очистка Stemdb Корнебаза Closing Закрытие Done Готово Monitor Монитор Unknown неизвестно documents документы document документ files файлы file файл errors ошибки error ошибка total files) всего файлов) Indexing interrupted Индексирование прервано Indexing failed Не удалось выполнить индексирование with additional message: с дополнительным сообщением: Non-fatal indexing message: Сообщение о некритичной ошибке индексирования: Stop &Indexing О&становить индексирование Index locked Индекс заблокирован Update &Index Обновить &индекс Indexing done Индексация завершена Bad paths Неверные пути Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Пустые или несуществующие пути в файле конфигурации. Нажмите Ok, если всё равно хотите запустить процесс индексирования (отсутствующие данные не будут убраны из файла индекса): Erasing index Стирание индекса Reset the index and start from scratch ? Сбросить индекс и начать заново? Selection patterns need topdir Для шаблонов отбора требуется topdir Selection patterns can only be used with a start directory Шаблоны отбора могут быть использованы только с начальным каталогом Warning Предупреждение Can't update index: indexer running Невозможно обновить индекс: индексатор уже запущен Can't update index: internal error Невозможно обновить индекс: внутренняя ошибка Simple search type Тип простого поиска Any term Любое слово All terms Все слова File name Имя файла Query language Язык запроса Stemming language Язык словоформ (no stemming) (без словоформ) (all languages) (все языки) error retrieving stemming languages ошибка получения списка языков словоформ Can't access file: Невозможно получить доступ к файлу: Index not up to date for this file.<br> Индекс для этого файла не обновлён.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Кстати, похоже, последняя попытка обновления для этого файла также не удалась.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Щёлкните по кнопке Ok, чтобы попытаться обновить индекс для этого файла. По окончании индексирования нужно будет повторить запрос.<br> The indexer is running so things should improve when it's done. Индексация выполняется, по завершении должно стать лучше. The document belongs to an external index which I can't update. Документ относится к внешнему индексу, который невозможно обновить. Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> >Нажмите «Отмена» для возврата к списку. <br>Нажмите «Игнорировать», чтобы открыть просмотр (и запомнить выбор для данного сеанса). Can't create preview window Невозможно создать окно просмотра This search is not active anymore Этот поиск больше не активен This search is not active any more Этот поиск больше не активен Cannot retrieve document info from database Невозможно извлечь сведения о документе из базы No search Результаты поиска отсутствуют No preserved previous search Отсутствуют сохранённые результаты предыдущего поиска Choose file to save Выбор файла для сохранения Saved Queries (*.rclq) Сохраненные запросы (*.rclq) Write failed Не удалось записать Could not write to file Не удалось выполнить запись в файл Read failed Ошибка записи Could not open file: Не удалось открыть файл: Load error Ошибка загрузки Could not load saved query Не удалось загрузить сохранённый запрос Filter directories Фильтровать каталоги Bad desktop app spec for %1: [%2] Please check the desktop file Неверная спецификация для %1: [%2] Проверьте файл .desktop No external viewer configured for mime type [ Не настроена внешняя программа для просмотра MIME-типа [ Bad viewer command line for %1: [%2] Please check the mimeview file Ошибка командной строки программы просмотра %1: [%2] Проверьте файл mimeview The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Программа просмотра, указанная в mimeview для %1: %2, не найдена. Открыть диалог настройки? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported В командной строке программы просмотра %1 указан родительский файл, а в URL — сетевой протокол http[s]: не поддерживается Viewer command line for %1 specifies parent file but URL is not file:// : unsupported В командной строке программы просмотра %1 указан родительский файл, а в URL — не file:// : не поддерживается Viewer command line for %1 specifies both file and parent file value: unsupported В командной строке программы просмотра %1 указан как сам файл, так и родительский файл: не поддерживается Cannot find parent document Невозможно найти родительский документ Cannot extract document or create temporary file Невозможно извлечь документ или создать временный файл Can't uncompress file: Невозможно распаковать файл: Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Открывается временная копия. Изменения будут утеряны, если их не сохранить<br/>особо. Do not show this warning next time (use GUI preferences to restore). Больше не показывать (для восстановления используйте настройки интерфейса). Executing: [ Выполняется: [ Unknown indexer state. Can't access webcache file. Неизвестный статус индексатора. Невозможно получить доступ к файлу веб-кэша. Indexer is running. Can't access webcache file. Идёт индексирование. Невозможно получить доступ к файлу веб-кэша. Batch scheduling Планирование The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Инструмент для настройки времени запуска индексации. Использует планировщик задач Windows. Disabled because the real time indexer was not compiled in. Отключено, так как не был вкомпилирован индексатор данных в реальном времени. This configuration tool only works for the main index. Данный инструмент настройки применим только к основному индексу. About Recoll О программе No information: initial indexing not yet performed. Нет данных: первичная индексация ещё не проведена. External applications/commands needed for your file types and not found, as stored by the last indexing pass in Внешние приложения/команды, требуемые для индексирования файлов, не найдены, как указано в результатах последнего индексирования в No helpers found missing Все обработчики доступны Missing helper programs Отсутствующие обработчики Error Ошибка Index query error Ошибка запроса индекса Indexed MIME Types Проиндексированные MIME-типы Content has been indexed for these MIME types: Было проиндексировано содержимое для следующих типов MIME: Types list empty: maybe wait for indexing to progress? Список типов пуст: может, обождать доиндексирования? Duplicates Дубликаты Main Window Главное окно Clear search Очистить поиск Move keyboard focus to search entry Перенести фокус ввода на строку поиска Move keyboard focus to search, alt. Перенести фокус ввода на строку поиска, также Toggle tabular display Переключить табличное отображение Show menu search dialog Показать диалоговое окно поиска Move keyboard focus to table Перенести фокус клавиатуры на таблицу Tools Инструменты Results Результаты All Всё media медиа message сообщение other прочее presentation презентация spreadsheet таблица text текст sorted сортированное filtered отфильтрованное Document filter Фильтр документов F&ilter Ф&ильтр Main index open error: Ошибка открытия основного индекса: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. Индекс может быть повреждён. Попробуйте запустить xapian-check или пересоздать индекс? Could not open external index. Db not open. Check external indexes list. Не удалось открыть внешний индекс. База не открыта. Проверьте список внешних индексов. Can't set synonyms file (parse error?) Невозможно установить файл синонимов (ошибка разбора?) Query results Результаты запроса Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Идёт обработка запроса.<br>Из-за ограничений библиотеки<br>отмена действия приведёт к закрытию приложения Result count (est.) Кол-во результатов (прим.) No results found Поиск не дал результатов Save file Сохранить файл Sub-documents and attachments Вложенные документы History data Данные истории Document history История документов Confirm Подтвердить Erasing simple and advanced search history lists, please click Ok to confirm Стираю историю простого и сложного поиска, нажмите Ok для подтверждения Could not open/create file Не удалось открыть/создать файл RclMainBase Recoll Recoll Query Language Filters Фильтры языка запросов Filter dates Фильтровать по дате Filter birth dates Фильтровать по дате рождения E&xit В&ыход Ctrl+Q Ctrl+Q Update &index Обновить индекс Trigger incremental pass Запустить пошаговый проход Start real time indexer Запустить индексацию в фоне &Rebuild index Пересоздать индекс &Erase document history &Стереть историю документов &Erase search history &Стереть историю поиска E&xport simple search history Э&кспортировать историю простого поиска Missing &helpers Недостающие &обработчики Indexed &MIME types Индексированные &MIME типы &About Recoll О программе &User manual &Руководство пользователя Document &History История &документов Document History История документов &Advanced Search Сложный поиск Assisted complex search Сложный поиск с поддержкой &Sort parameters &Параметры сортировки Sort parameters Параметры сортировки Term &explorer Обозреватель &терминов Term explorer tool Инструмент обзора терминов Next page Следующая страница Next page of results Следующая страница результатов PgDown PgDown First page Первая страница Go to first page of results Перейти к первой странице результатов Shift+PgUp Shift+PgUp Previous page Предыдущая страница Previous page of results Предыдущая страница результатов PgUp PgUp &Index configuration Настройка &индекса Indexing &schedule &Расписание индексирования &GUI configuration Настройка и&нтерфейса E&xternal index dialog Настройка &внешнего индекса External index dialog Настройка внешнего индекса Enable synonyms Учитывать синонимы &Full Screen Во весь &экран Full Screen Во весь &экран F11 F11 Increase results text font size Увеличить размер шрифта результатов Increase Font Size Увеличить размер шрифта Decrease results text font size Уменьшить размер шрифта результатов Decrease Font Size Уменьшить размер шрифта Sort by date, oldest first Сортировать по дате, старые вначале Sort by dates from oldest to newest Сортировать по дате от старым к новым Sort by date, newest first Сортировать по дате, новые вначале Sort by dates from newest to oldest Сортировать по дате от новых к старым Show Query Details Показать сведения о запросе Show as table Показать в виде таблицы Show results in a spreadsheet-like table Показать результаты в виде таблицы Save as CSV (spreadsheet) file Сохранить как CSV-файл Saves the result into a file which you can load in a spreadsheet Сохранить результат в файл, который можно загрузить в электронную таблицу Next Page Следующая страницы Previous Page Предыдущая страница First Page Первая страница Query Fragments Фрагменты запроса With failed files retrying С повторной обработкой файлов с ошибками Next update will retry previously failed files При следующем обновлении будут повторно обработаны файлы с ошибками Save last query Сохранить последний запрос Load saved query Загрузить последний запрос Special Indexing Специальное индексирование Indexing with special options Индексирование с особыми параметрами Index &statistics &Статистика индекса Webcache Editor Редактор веб-кэша &File &Файл &View &Вид &Tools &Инструменты &Preferences &Настройки &Help &Справка &Results &Результаты &Query &Запрос RclTrayIcon Restore Восстановить Quit Выйти RecollModel Abstract Содержимое Author Автор Document size Размер документа Document date Дата документа File size Размер файла File name Имя файла File date Дата файла Ipath I-путь Keywords Ключевые слова MIME type MIME-типы Original character set Исходная кодировка Relevancy rating Соответствие Title Заголовок URL URL Date Дата Date and time Дата и время Can't sort by inverse relevance Нельзя отсортировать в обратном соответствии ResList <p><b>No results found</b><br> <p><b>Поиск не дал результатов</b><br> Documents Документы out of at least из по меньшей мере for для Previous Предыдущий Next Следующий Unavailable document Документ недоступен Preview Просмотр Open Открыть Snippets Выдержки (show query) (показать запрос) <p><i>Alternate spellings (accents suppressed): </i> <p><i>Варианты написания (без диакритических знаков): </i> <p><i>Alternate spellings: </i> <p><i>Варианты написания: </i> This spelling guess was added to the search: Это совпадение произношения было добавлено в поиск: These spelling guesses were added to the search: Эти совпадения произношений были добавлены в поиск: Document history История Result list Список результатов Result count (est.) Кол-во результатов (прим.) Query details Подробности запроса ResTable Use Shift+click to display the text instead. Использовать Shift+щелчок мыши, чтобы показать текст. Result Table Таблица результатов Open current result document Открыть документ с текущими результатами Open current result and quit Открыть текущие результаты и выйти Preview Просмотр Show snippets Показать выдержки Show header Показать заголовок Show vertical header Показать вертикальный заголовок Copy current result text to clipboard Сохранить текст текущих результатов в буфер обмена Copy result text and quit Скопировать текст результатов и выйти Save table to CSV file Сохранить таблицу в CSV-файл Can't open/create file: Невозможно открыть/создать файл: %1 bytes copied to clipboard %1 байт скопировано в буфер обмена &Reset sort &Сбросить сортировку &Save as CSV &Сохранить как CSV &Delete column Удалить столбец Add "%1" column Добавить столбец "%1" SSearch Any term Любое слово All terms Все слова File name Имя файла Query language Язык запроса Simple search Простой поиск History История <html><head><style> table, th, td { border: 1px solid black; border-collapse: collapse; } th,td { text-align: center; </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Шпаргалка по языку запросов. Неясно - щёлкните <b>Показать запрос</b>.&nbsp; You should really look at the manual (F1)</p> И впрямь стоит заглянуть в справку (F1)</p> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>What</th><th>Примеры</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>И</td><td>раз два&nbsp;&nbsp;&nbsp;раз AND два&nbsp;&nbsp;&nbsp;раз && два</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Или</td><td>раз OR два&nbsp;&nbsp;&nbsp;раз || два</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Сложное булево. OR в приоритете, используйте скобки&nbsp; where needed</td><td>(one AND two) OR three</td></tr> при надобности</td><td>(раз AND два) OR три</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Не</td><td>-слово</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Фраза</td><td>"гордыня и предубеждение"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Упорядоченная близость (допуск=1)</td><td>"гордыня предубеждение"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Неупорядоченная близость (допуск=1)</td><td>"предубеждение гордыня"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Неупор. близ. (штат.допуск=10)</td><td>"предубеждение&nbsp;гордыня"p</td></tr> <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <p>Шпаргалка по языку запросов. Неясно — щёлкните <b>Показать сведения о запросе</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> <tr><td>Заглавные буквы для подавления словоформ</td><td>Слово</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>По полям</td><td>author:остен&nbsp;&nbsp;title:предубеждение</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>AND внутри поля (как угодно)</td><td>author:джейн,остен</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>OR внутри поля</td><td>author:остен/бронте</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Имена полей</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Фильтр путей</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Фильтр MIME-типов</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Промежуток времени</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Размер</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> Enter file name wildcard expression. Укажите маску имени файла. Enter search terms here. Укажите искомые слова. Bad query string Неверное значение запроса. Out of memory Память исчерпана Can't open index Не могу открыть индекс Stemming languages for stored query: Языки словоформ для сохранённого запроса: differ from current preferences (kept) отличаются от текущих параметров (сохранено) Auto suffixes for stored query: Автоматически подставляемые суффиксы для сохранённого запроса: Could not restore external indexes for stored query:<br> Не удалось восстановить внешние индексы для значений отбора:<br> ??? ??? Using current preferences. Используются текущие настройки. Autophrase is set but it was unset for stored query Автофраза задана, но для сохранённого запроса сброшена Autophrase is unset but it was set for stored query Автофраза не задана, но для сохранённого запроса задана SSearchBase SSearchBase SSearchBase Erase search entry Стереть содержимое поиска Clear Очистить Start query Начать запрос Search Поиск Choose search type. Выбрать тип поиска. Show query history Показать историю запросов Main menu Главное меню SearchClauseW Any Любые All Все None Нет Phrase Фраза Proximity Близость File name Имя файла No field Поле отсутствует Select the type of query that will be performed with the words Выберите, какой тип запроса по словам будет произведён Number of additional words that may be interspersed with the chosen ones Количество возможных других слов между выбранными Snippets Snippets Выдержки Find: Найти: Next След. Prev Пред. SnippetsW Search Поиск Snippets Window Выдержки Find Найти Find (alt) Найти (также) Find next Найти след. Find previous Найти пред. Close window Закрыть окно поиска Sort By Relevance Сортировать по соответствию Sort By Page Сортировать по странице <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>К сожалению, точные совпадения с заданными параметрами не найдены. Возможно, документ слишком большой и выдерживалка не выдержала...</p> SpecIdxW Special Indexing Специальное индексирование Retry previously failed files. Обработать файлы с ошибками повторно. Else only modified or failed files will be processed. Или будут обрабатываться только изменённые файлы или файлы с ошибками. Erase selected files data before indexing. Стирать сведения по выбранным файлам перед индексированием. Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Каталог для рекурсивного индексирования. Должен находиться внутри обычной индексируемой области,<br> как указано в файле настройки (topdirs). Browse Обзор Start directory. Must be part of the indexed tree. Use full indexed area if empty. Начальный каталог. Должен быть частью индексируемого дерева каталогов. Использовать весь индекс, если каталог пуст. Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Оставьте поле пустым для выбора всех файлов. Можно использовать несколько шаблонов через пробел.<br>Шаблоны, включающие в себя пробел, должны быть взяты в двойные кавычки.<br>Можно использовать только если задан начальный каталог. Selection patterns: Шаблоны отбора: Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Файл с выводом диагностических сообщений. Будет пересоздан с дальнейшей записью диагностики индексирования (причин пропуска файлов). Top indexed entity Верхняя индексируемая сущность Diagnostics file Файл журнала SpellBase Term Explorer Обозреватель терминов Match Учитывать Case регистр Accents диакритические знаки &Expand &Однокоренные слова Alt+E Alt+E &Close &Закрыть Alt+C Alt+C No db info. Нет информации о базе. SpellW Wildcards маски Regexp Регулярные выражения Stem expansion Однокоренные слова Spelling/Phonetic Написание/произношение Show index statistics Показать статистику индекса List files which could not be indexed (slow) Показать непроиндексировавшиеся файлы (небыстро) error retrieving stemming languages ошибка получения списка языков словоформ Index: %1 documents, average length %2 terms.%3 results Индекс: %1 документ(ов), средняя длина %2 слов(о). %3 результат(ов) Spell expansion error. Ошибка поиска однокоренных слов. Spell expansion error. Ошибка поиска однокоренных слов. %1 results %1 результат(ов) No expansion found Однокоренных слов не найдено List was truncated alphabetically, some frequent Список сокращён по алфавиту, некоторые часто повторяющиеся terms may be missing. Try using a longer root. слова могут отсутствовать. Попробуйте более длинный корень. Number of documents Число документов Average terms per document Слов на документ (в среднем) Smallest document length (terms) Наименьшая длина документа (слов) Longest document length (terms) Наибольшая длина документа (слов) Results from last indexing: Результаты последнего индексирования: Documents created/updated Создано/обновлено документов Files tested Проверено файлов Unindexed files Непроиндексированных файлов Database directory size Размер каталога базы данных MIME types: Типы MIME: Item Элемент Value Значение Term Термин Doc. / Tot. Док. / Всего UIPrefsDialog Any term Любой All terms Все File name Имя файла Query language Язык запросов Value from previous program exit Значение из предыдущего запуска программы Choose Выбрать error retrieving stemming languages ошибка получения списка языков словоформ Context Контекст Description Описание Shortcut Сочетание клавиш Default По умолчанию Default QtWebkit font Шрифт QtWebkit по умолчанию Result list paragraph format (erase all to reset to default) Формат абзаца в списке результатов (очистите для сброса к умолчанию) Result list header (default is empty) Заголовок списка результатов (по умолчанию пуст) Choose QSS File Выбрать файл QSS At most one index should be selected Следует выбрать не больше одного индекса Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Выберите каталог настроек recoll или каталог индекса xapian (например: /home/me/.recoll или /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Выбранный каталог выглядит как каталог с настройками Recoll, но настройки не выходит прочитать The selected directory does not appear to be a Xapian index Выбранный каталог непохож на индекс Xapian Can't add index with different case/diacritics stripping option. Невозможно добавить индекс с другими настройками учёта регистра и диакритических знаков. Cant add index with different case/diacritics stripping option Невозможно добавить индекс с другими настройками учёта регистра и диакритических знаков This is the main/local index! Этот индекс является главным/локальным! The selected directory is already in the index list Этот каталог уже указан в списке индексов ViewAction Desktop Default Взять из окружения MIME type Тип MIME Command Команда Changing entries with different current values Изменение записей с различными текущими значениями ViewActionBase Native Viewers Встроенные просмотрщики Select one or several mime types then use the controls in the bottom frame to change how they are processed. Выберите MIME-типы и используйте кнопки в рамке ниже, чтобы изменить характер их обработки. Use Desktop preferences by default Использовать настройки окружения по умолчанию Select one or several file types, then use the controls in the frame below to change how they are processed Выберите типы файлов и используйте кнопки, расположенные в рамке ниже, чтобы изменить характер их обработки Recoll action: Действие Recoll: current value текущее значение Select same Выделить такие же <b>New Values:</b> <b>Новые значение:</b> Exception to Desktop preferences Исключения из настроек окружения Action (empty -> recoll default) Действие (пусто -> по умолчанию) Apply to current selection Применить к выделению Close Закрыть Webcache Webcache editor Редактор веб-кэша TextLabel Текстовая подпись Search regexp Поиск по регулярному выражению WebcacheEdit Maximum size %1 (Index config.). Current size %2. Write position %3. Предельный размер %1 (конф. индекса). Текущий размер %2. Позиция записи %3. Copy URL Копировать URL Save to File Сохранить в файл Unknown indexer state. Can't edit webcache file. Неизвестный статус индексатора. Невозможно редактировать файл веб-кэша. Indexer is running. Can't edit webcache file. Индексатор запущен. Нельзя редактировать файл веб-кэша. Delete selection Удалить выделенные File creation failed: Создание файла не удалось: Webcache was modified, you will need to run the indexer after closing this window. Содержимое веб-кэша былыо изменено, после закрытия этого окна необходимо запустить индексирование. WebcacheModel MIME MIME Date Дата Size Размер URL URL Url Ссылка WinSchedToolW Recoll Batch indexing Пакетное индексирование Recoll Start Windows Task Scheduler tool Запустить планировщик задач Windows Error Ошибка Configuration not initialized Конфигурация не инициализирована Could not create batch file Не удалось создать пакетный файл <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Планирование пакетного индексирования Recoll</h3><p>Для этого используется стандартный планировщик задач Windows. Программа будет запущена после нажатия расположенной ниже кнопки.</p><p>Можно использовать либо полный интерфейс (<i>Создать задачу</i> в меню справа), либо упрощённый мастер <i>Создать простую задачу</i>. В обоих случаях следует Копировать/Вставить приведённый ниже путь к пакетному файлу как <i>Действие</i> для выполнения.</p> Command already started Команда уже запущена confgui::ConfParamFNW Choose Выбрать confgui::ConfParamSLW + + Add entry Добавить запись - - Delete selected entries Удалить выделенные записи ~ ~ Edit selected entries Изменить выделенные записи uiPrefsDialogBase Recoll - User Preferences Recoll — настройки пользователя User interface Интерфейс пользователя Choose editor applications Выбор приложений-редакторов Start with simple search mode: Начать с режима простого поиска: Limit the size of the search history. Use 0 to disable, -1 for unlimited. Ограничения размера истории поиска. 0: отключить, -1: без ограничений. Maximum size of search history (0: disable, -1: unlimited): Предельный размер истории поиска (0: отключить, -1: без ограничений): Start with advanced search dialog open. Открывать диалог сложного поиска при запуске. Remember sort activation state. Запомнить порядок сортировки результатов. Depth of side filter directory tree Глубина бокового дерева каталогов Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Стиль отображения фильтров: в виде селекторов, поля со списком на панели инструментов или меню. Document filter choice style: Стиль отображения фильтров: Buttons Panel Панель кнопок Toolbar Combobox Поле со списком Menu Меню Hide some user interface elements. Скрыть некоторые элементы интерфейса Hide: Скрыть: Toolbars Поля со списками Status bar Панель состояния Show button instead. Показывать кнопки. Menu bar Панель меню Show choice in menu only. Отображать выбор только в меню. Simple search type Тип простого поиска Clear/Search buttons Кнопки Очистить/Поиск Show system tray icon. Отображать значок в трее Close to tray instead of exiting. Сворачивать окно вместо закрытия. Generate desktop notifications. Отображать уведомления. Suppress all beeps. Отключить звук. Show warning when opening temporary file. Отображать предупреждение при открытии временного файла. Disable Qt autocompletion in search entry. Отключить автодополнение Qt в строке поиска. Start search on completer popup activation. Начинать поиск при активации всплывающего окна автодополнения. Maximum number of history entries in completer list Предельное число записей журнала в списке автодополнения Number of history entries in completer: Число записей журнала в автодополнении: Displays the total number of occurences of the term in the index Показывает общее количество вхождений термина в индексе Show hit counts in completer popup. Показывать счётчик нажатий во всплывающем окне автодополнения. Texts over this size will not be highlighted in preview (too slow). Текст большего размера не будет подсвечен при просмотре (слишком медленно). Maximum text size highlighted for preview (kilobytes) Предельный размер текста, выбранного для просмотра (в килобайтах) Prefer Html to plain text for preview. Предпочитать для просмотра HTML простому тексту. Prefer HTML to plain text for preview. Предпочитать для просмотра HTML простому тексту. Make links inside the preview window clickable, and start an external browser when they are clicked. Сделать ссылки внутри окна просмотра активными и запускать браузер при щелчке по ссылке. Activate links in preview. Активировать ссылки в режиме просмотра. Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Строки в тексте PRE нескладные. При использовании BR теряются некоторые отступы. Возможно, PRE + Wrap подойдёт больше. Plain text to HTML line style Стиль отображения строк HTML в простом тексте <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + wrap Highlight CSS style for query terms CSS-стиль подсветки слов запроса Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Подсветка терминов в результатах запроса. <br>Можно попробовать что-то более выделяющееся вроде "color:red;background:yellow" вместо цвета по умолчанию... Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Коэффициент масштабирования интерфейса. Полезно, если не устраивает значение по умолчанию. Display scale (default 1.0): Размер отображения (по умолчанию 1.0): Application Qt style sheet Внешний вид приложения Resets the style sheet to default Сбросить на вид по умолчению None (default) Нет (по умолчанию) Uses the default dark mode style sheet Использовать тёмную тему по умолчанию Dark mode Тёмная тема Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Откроется диалоговое окно для выбора файла стиля оформления.<br> Для примера откройте /usr/share/recoll/examples/recoll[-dark].qss Choose QSS File Выбрать файл QSS Shortcuts Сочетания клавиш Use F1 to access the manual Для вызова справки нажмите F1 Reset shortcuts defaults Сбросить настройки сочетаний клавиш Result List Список результатов Number of entries in a result page Число записей в списке результатов Result list font Шрифт списка результатов Opens a dialog to select the result list font Открыть диалоговое окно выбора шрифта списка результатов Helvetica-10 Helvetica-10 Resets the result list font to the system default Сбросить шрифт списка результатов на системный Reset Сброс Edit result paragraph format string Редактировать строку форматирования параграфа результатов Edit result page html header insert Редактировать вставку html-заголовка страницы результатов Date format (strftime(3)) Формат даты (strftime(3)) Abstract snippet separator Условный разделитель выдержек User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Пользовательский стиль оформления для окна просмотра выдержек.<br> На заметку: вставка заголовка страницы результатов также включена в заголовок окна выдержек. Snippets window CSS file Файл оформления CSS окна просмотра выдержек Opens a dialog to select the Snippets window CSS style sheet file Откроется окно выбора файла оформления CSS окна просмотра выдержек Choose Выбрать Resets the Snippets window style Сбросить стиль оформления окна просмотра выдержек Maximum number of snippets displayed in the snippets window Предельное число выдержек, показываемых в окне просмотра Sort snippets by page number (default: by weight). Сортировать по номеру страницы (по умолчанию: по объёму) Display a Snippets link even if the document has no pages (needs restart). Показать ссылку на выдержки, даже если в документе нет страниц (нужен перезапуск). Result Table Таблица результатов Hide result table header. Скрывать шапку таблицы результатов Show result table row headers. Показывать шапки рядов таблицы результатов Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. Отключить сочетания клавиш Ctrl+[0-9]/Shift+[a-z] для перехода по рядам таблицы. To display document text instead of metadata in result table detail area, use: Для отображения в таблице результатов текста документа вместо метаданных используйте: left mouse click Щелчок левой кнопкой мыши Shift+click Shift+щелчок Do not display metadata when hovering over rows. Не показывать метаданные при наведении курсора на строки. Search parameters Параметры поиска If checked, results with the same content under different names will only be shown once. Показывать результаты с тем же содержанием под разными именами не более одного раза Hide duplicate results. Скрывать дубликаты. Stemming language Язык словоформ A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Поиск [rolling stones] (два слова) будет изменён на [rolling OR stones OR (rolling phrase 2 stones)]. Automatically add phrase to simple searches Автоматически добавлять фразу при простом поиске Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Порог частоты в процентах, выше которого слова в автофразе не используются. Часто появляющиеся слова представляют основную проблему обработки фраз. Пропуск слов ослабляет фразу и уменьшает эффективность функции автофразы. Значение по умолчанию: 2 (процента). Autophrase term frequency threshold percentage Процент порогового значения частоты автофраз Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Создавать описания для результатов поиска с использованием контекста слов запроса? Процесс может оказаться медленным для больших документов. Dynamically build abstracts Составлять описания на лету Do we synthetize an abstract even if the document seemed to have one? Создавать описание, даже когда оно вроде есть для данного документа? Replace abstracts from documents Заменять описания из документов Synthetic abstract size (characters) Размер создаваемого описания (символов) Synthetic abstract context words Число слов контекста в описании The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Список слов, которые будут автоматически преобразованы в расширение файла вида ext:xxx в запросе. Query language magic file name suffixes. Распознавание типа файлов при помощи файла сигнатур (magic file). Enable Включить Add common spelling approximations for rare terms. Добавлять наиболее подходящие варианты написания для редко встречающихся терминов. Automatic spelling approximation. Автоподбор написания. Max spelling distance Предельное расстояние между написаниями Synonyms file Файл с синонимами External Indexes Индексы однокоренных слов Toggle selected Переключить выделенные Activate All Включить всё Deactivate All Выключить всё Set path translations for the selected index or for the main one if no selection exists. Задать корректировку путей для выбранного или главного индекса, если ничего не выбрано. Paths translations Корректировка путей Remove from list. This has no effect on the disk index. Удалить из списка. Индекс на диске без изменений. Remove selected Удалить выделенное Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Щёлкните, чтобы добавить другой каталог индекса в список. Можно выбрать каталог конфигурации Recoll или индекс Xapian. Add index Добавить индекс Misc Разное The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Ошибка, приводящая к отображению странных символов в подсвеченных словах на тамильском. Обход заключается в том, чтобы вставить дополнительный символ пробела. Work around Tamil QTBUG-78923 by inserting space before anchor text Обходить ошибку QTBUG-78923 в записях на тамильском, вставляя пробел перед якорным текстом. Apply changes Принять изменения &OK &ОК Discard changes Отменить изменения &Cancel Отменить recoll-1.36.1/qtgui/i18n/recoll_sv.ts0000644000175000017500000070063314444307651014274 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Alla satser Any clause Valfria satser texts texter spreadsheets kalkylblad presentations presentationer media media messages meddelanden other annat Bad multiplier suffix in size filter Dålig multiplikatorsuffix i storleksfilter text text spreadsheet kalkylblad presentation presentation message meddelande Advanced Search Avancerat sök History Next Historik Nästa History Prev Historik Föregående Load next stored search Ladda nästa lagrade sökning Load previous stored search Ladda tidigare lagrad sökning AdvSearchBase Advanced search Avancerad sökning Restrict file types Begränsa filtyper Save as default Spara som standard Searched file types Sökta filtyper All ----> Alla ----> Sel -----> Valda -----> <----- Sel <----- Valda <----- All <----- Alla Ignored file types Undantagna filtyper Enter top directory for search Ange den översta mappen för sökning Browse Bläddra Restrict results to files in subtree: Begränsa resultaten till filer i underträdet: Start Search Sök Search for <br>documents<br>satisfying: Sök efter <br>dokument<br>tillfredsställande: Delete clause Ta bort sats Add clause Lägg till sats Check this to enable filtering on file types Markera detta för att filtrera efter filtyper By categories Efter kategorier Check this to use file categories instead of raw mime types Markera det här om du vill använda filkategorier i stället för raw mime-typer Close Stäng All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Alla icke tomma fält till höger kombineras med konjunktionerna OCH (Alternativet "Alla satser") eller ELLER (Alternativet "Valfria satser").<br>Fälttyperna "Alla" "Valfri" och "Ingen", kan acceptera en blandning av enkla ord och fraser, omgivna av citationstecken.<br>Fält utan data undantas. Invert Invertera Minimum size. You can use k/K,m/M,g/G as multipliers Minsta storlek. Du kan använda k/K,m/M och g/G som multiplikatorer Min. Size Min. storlek Maximum size. You can use k/K,m/M,g/G as multipliers Största storlek. Du kan använda k/K,m/M och g/G som multiplikatorer Max. Size Max. storlek Select Välj Filter Filtrera From Från To Till Check this to enable filtering on dates Markera detta för sökning efter datum Filter dates Filtrera efter datum Find Hitta Check this to enable filtering on sizes Markera detta för filtrering efter storlekar Filter sizes Filtrera efter storlek Filter birth dates ConfIndexW Can't write configuration file Kan inte skriva inställningsfil Global parameters Övergripande parametrar Local parameters Lokala parametrar Search parameters Sökparametrar Top directories Toppmappar The list of directories where recursive indexing starts. Default: your home. Listan över mappar där rekursiv indexering börjar. Standard är din hemkatalog. Skipped paths Undantagna sökvägar These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Det här är sökvägsnamn till kataloger som inte kommer att indexeras.<br>Sökvägar kan innehålla jokertecken. Posterna måste matcha de sökvägar som indexeraren ser (exempel: Om överordnade kataloger inkluderar "/home/me" och "/home" egentligen är en länk till "/usr/home", skulle en korrekt undantagen sökväg vara "/home/me/tmp*", inte "/usr/home/me/tmp*") Stemming languages Igenkända språk The languages for which stemming expansion<br>dictionaries will be built. Språken som hindrar expansion<br>ordböcker kommer att byggas. Log file name Loggfilsnamn The file where the messages will be written.<br>Use 'stderr' for terminal output Filen där meddelandena kommer att skrivas.<br>Använd "stderr" för utdata i terminal. Log verbosity level Informationsnivå i loggen This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Detta värde justerar mängden meddelanden,<br>från endast felmeddelanden till en mängd felsökningsdata. Index flush megabytes interval Indexdumpningsintervall i megabyte This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Detta värde justera mängden data som indexeras mellan dumpningar till disk.<br>Detta hjälper till att kontrollera indexerarens minnesanvändning. Standard är 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Det här är diskanvändningen i procent - Sammanlagd diskanvändning, inte den storlek där indexering kommer att misslyckas och stoppas.<br>Standardvärdet 0, tar bort all begränsning. No aspell usage Ingen Aspell-användning Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Inaktiverar användning av Aspell för att generera stavningsnärmevärde i termutforskarverktyget. Aspell language Aspell-språk The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Språket för aspell ordbok. Detta bör se ut 'sv' eller 'fr' . .<br>Om detta värde inte är inställt, kommer NLS-miljön att användas för att beräkna det, vilket vanligtvis fungerar. För att få en uppfattning om vad som installeras på ditt system, skriv 'aspell config' och leta efter . vid filer i katalogen 'data-dir' Database directory name Databasens mappnamn The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Namnet på en mapp där index lagras.<br>En icke-absolut sökväg tas i förhållande till konfigurationskatalogen. Standard är "xapiandb". Unac exceptions Unac-undantag <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Dessa är undantag från unac-mekanismen som, som standard, tar bort alla diakritiska tecken, och utför vägledande nedbrytning. Du kan åsidosätta vissa ointressanta tecken, beroende på ditt språk, och ange ytterligare nedbrytningar, t.ex. för ligaturer. I varje blankstegsseparerad post är det första tecknet källan och resten är översättningen. Process the WEB history queue Bearbeta webbhistorikkön Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Aktiverar indexering av sidor besökta med Firefox.<br>(Du behöver installera Recoll-tillägget i Firefox). Web page store directory name Mappnamn för webbsidelagring The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Namnet på en mapp där kopiorna av besökta webbsidor skall lagras.<br>En icke-absolut sökväg tas i förhållande till konfigurationskatalogen. Max. size for the web store (MB) Max storlek för webblagring (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Poster kommer att återvinnas när storleken är uppnådd. <br>Bara ökad storlek är meningsfull, eftersom minskat värde inte kommer att trunkera en befintlig fil (bara slösa med utrymmet). Automatic diacritics sensitivity Automatisk känslighet för diakritiska tecken <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Utlöser automatisk känslighet för diakritiska tecken om söktermen har accenttecken (inte i unac_except_trans). Annars behöver du använda frågespråket och <i>D</i>-modifieraren för att ange diakritiska teckens känslighet. Automatic character case sensitivity Atomatisk skiftlägeskänslighet <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Utlöser automatiskt skiftlägeskänslighet om posten har versaltecken i någon annan än den första positionen. Annars behöver du använda frågespråket och <i>C</i>-modifieraren för att ange skiftlägeskänslighet. Maximum term expansion count Max antal termutvidgningar <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Max antal utvidgningar för en enskild term (t.ex vid användning av jokertecken). Standardvärdet 10 000 är rimligt och kommer att undvika förfrågningar som verkar frysta, medan motorn går igenom termlistan. Maximum Xapian clauses count Max antal Xapian-satser <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. Max antal elementärsatser vi lägger till i en enda Xapian-sökning. I vissa fall kan resultatet av termutvidgning vara multiplikativ, och vi vill undvika att använda överdrivet mycket minne. Standardvärdet 100 000 bör i de flesta fall, vara både tillräckligt högt och kompatibelt med aktuella typiska maskinvarukonfigurationer. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... De språk för vilka igenkänningsordböcker kommer att byggas.<br>Se Xapians dokumentation för möjliga värden. T.ex. english, swedish, german ... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Språket för Aspell-ordboken. Värdena är 2-bokstavs språkkoder, t.ex."en", "sv" ...<br>Om detta värde inte anges kommer NLS-miljön att användas för att beräkna det, vilket vanligtvis fungerar. Skriv "aspell config" och leta efter .dat filer inuti "data-dir"-mappen, för att få en uppfattning om vad som är installerat på ditt system. Indexer log file name Loggfilsnamn för indexeraren If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Om det lämnas tomt, kommer ovanstående värde för loggfilsnamn att användas. Det kan vara användbart att ha en separat logg för diagnostiska ändamål eftersom den gemensamma loggen kommer att raderas när GUI startas. Disk full threshold percentage at which we stop indexing<br>E.g. 90% to stop at 90% full, 0 or 100 means no limit) Tröskelvärde för full disk, i procent, då vi slutar indexera<br>T.ex 90% för att stoppa vid 90% full disk, (0 eller 100 betyder ingen begränsning). Web history Webbhistorik Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types Endast mime-typer An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive En exklusiv lista över indexerade mime-typer.<br>Inget annat kommer att indexeras. Normalt tom och inaktiv. Exclude mime types Undanta mime-typer Mime types not to be indexed Mime-typer som inte skall indexeras Max. compressed file size (KB) Max komprimerad filstorlek (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Det här värdet anger ett tröskelvärde bortom vilket komprimerade filer inte kommer att bearbetas. Ange -1 för ingen begränsning, 0 för ingen dekomprimering någonsin. Max. text file size (MB) Max textfilsstorlek (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Det här värdet anger ett tröskelvärde bortom vilket textfiler inte kommer att behandlas. Ange -1 för ingen begränsning. Detta är för att utesluta monsterloggfiler från indexet. Text file page size (KB) Sidstorlek för textfiler (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Om detta värde anges (inte lika med -1), kommer textfiler att delas upp i bitar av denna storlek, för indexering. Detta kommer att underlätta vid genomsökning av mycket stora textfiler (t.ex. loggfiler). Max. filter exec. time (s) Maxtid för filterexekvering. (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Externa filter som arbetar längre än detta kommer att avbrytas. Detta är för det sällsynta fallet (t.ex. postscript) där ett dokument kan orsaka att ett filter loopar. Ange -1 för ingen begränsning. Global Övergripande CronToolW Cron Dialog Cron-dialog <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batchindexeringsschema (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Varje fält kan innehålla ett jokertecken (*), ett enkelt numeriskt värde, kommaavgränsade listor (1,3,5) och intervall (1-7). Mer allmänt kommer fälten att användas <span style=" font-style:italic;">som de är</span> inuti crontab-filen, och den fullständiga crontab-syntaxen kan användas, se crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />Anges t.ex. <span style=" font-family:'Courier New,courier';">*</span> i <span style=" font-style:italic;">dagar, </span><span style=" font-family:'Courier New,courier';">12,19</span> i <span style=" font-style:italic;">timmar</span> och <span style=" font-family:'Courier New,courier';">15</span> i <span style=" font-style:italic;">minuter</span> kommer recollindex att startas varje dag klockan 12:15 och 19:15</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ett schema med mycket frekventa aktiveringar är förmodligen mindre effektivt än realtidsindexering.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Dagar i veckan (* eller 0-7, 0 eller 7 är Söndag) Hours (* or 0-23) Timmar (* eller 0-23) Minutes (0-59) Minuter (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Klicka <span style=" font-style:italic;">Inaktivera</span> för att stoppa automatisk batchindexering, <span style=" font-style:italic;">Aktivera</span> för att starta den, <span style=" font-style:italic;">Avbryt</span> för att inte ändra något.</p></body></html> Enable Aktivera Disable Inaktivera It seems that manually edited entries exist for recollindex, cannot edit crontab Det verkar finnas manuellt redigerade poster för recollindex, kan inte redigera crontab. Error installing cron entry. Bad syntax in fields ? Fel vid installation av cron-post. Dålig syntax i något fält ? EditDialog Dialog Dialog EditTrans Source path Källsökväg Local path Lokal sökväg Config error Inställningsfel Original path Ursprunglig sökväg EditTransBase Path Translations Sökvägsöversättningar Setting path translations for Anger sökvägsöversättningar för Select one or several file types, then use the controls in the frame below to change how they are processed Välj en eller flera filtyper, använd sedan kontrollerna i ramen nedan, för att ändra hur de bearbetas Add Lägg till Delete Ta bort Cancel Avbryt Save Spara FirstIdxDialog First indexing setup Första indexering <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Det verkar inte finnas något index för den här konfigurationen.</span><br /><br />Om du bara vill indexera din hemkatalog med en uppsättning rimliga standardvärden, trycker du på knappen <span style=" font-style:italic;">Börja indexera nu</span>. Du kommer att kunna justera detaljerna senare. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Om du vill ha mer kontroll använder du följande länkar för att justera indexeringskonfiguration och schema.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Dessa verktyg kan nås senare från <span style=" font-style:italic;">Inställningar</span> i menyn.</p></body></html> Indexing configuration Indexeringskonfiguration This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Detta kommer att låta dig justera vilka kataloger du vill indexera och andra parametrar, som uteslutna filsökvägar eller namn, standardteckenuppsättningar, etc. Indexing schedule Indexeringsschema This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Detta låter dig välja mellan batch och realtidsindexering, och ställa in ett automatiskt schema för batch-indexering (med hjälp av cron). Start indexing now Börja indexera nu FragButs %1 not found. %1 hittades inte. %1: %2 %1: %2 Fragment Buttons Fragmentknappar Query Fragments Sökfragment IdxSchedW Index scheduling setup Inställning av indexeringsschema <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexering kan köras permanent, indexera filer efter hand som de ändras, eller köras i diskreta intervall. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Manualen kan hjälpa dig att välja mellan dessa metoder (tryck F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Det här verktyget kan hjälpa dig att ställa in ett schema för att automatisera batchindexkörningar eller starta indexering i realtid när du loggar in (eller både och, vilket sällan är vettigt). </p></body></html> Cron scheduling Cron-schema The tool will let you decide at what time indexing should run and will install a crontab entry. Verktyget låter dig bestämma vid vilken tid indexering ska köras och installerar en crontab-post. Real time indexing start up Starta indexering i realtid Decide if real time indexing will be started when you log in (only for the default index). Avgör om indexering i realtid ska startas när du loggar in (endast för standardindex). ListDialog Dialog Dialog GroupBox Gruppruta Main No db directory in configuration Ingen databasmapp i inställningarna Could not open database in Kunde inte öppna databasen i . Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed. . Klicka på Avbryt om du vill redigera konfigurationsfilen innan indexering startar, eller Ok för att låta den fortsätta. Configuration problem (dynconf Konfigurationsproblem (dynconf "history" file is damaged or un(read)writeable, please check or remove it: "historik" filen är skadad eller oskrivbar, kontrollera eller ta bort den: "history" file is damaged, please check or remove it: Historikfilen är skadad. Kontrollera eller ta bort den: Needs "Show system tray icon" to be set in preferences! Preview &Search for: &Sök efter: &Next &Nästa &Previous &Föregående Match &Case Skift&lägeskänsligt Clear Rensa Creating preview text Skapar förhandsgranskningstext Loading preview text into editor Läser in förhandsgranskningstext i redigeraren Cannot create temporary directory Kan inte skapa temporär katalog Cancel Avbryt Close Tab Stäng flik Missing helper program: Saknade hjälpprogram: Can't turn doc into internal representation for Kan inte göra doc till intern representation för Cannot create temporary directory: Kan inte skapa temporär katalog: Error while loading file Fel vid laddning av fil Form Formulär Tab 1 Flik 1 Open Öppna Canceled Avbruten Error loading the document: file missing. Kunde inte läsa in dokumentet: Filen saknas. Error loading the document: no permission. Kunde inte läsa in dokumentet: Behörighet saknas. Error loading: backend not configured. Kunde inte läsa in: Serversidan inte konfigurerad Error loading the document: other handler error<br>Maybe the application is locking the file ? Kunde inte läsa in dokumentet: Annat hanterarefel. <br>Programmet kanske låser filen? Error loading the document: other handler error. Kunde inte läsa in dokumentet: Annat hanterarefel. <br>Attempting to display from stored text. <br>Försöker visa från lagrad text. Could not fetch stored text Kunde inte hämta lagrad text Previous result document Föregående resultatdokument Next result document Nästa resultatdokument Preview Window Förhandsgranska fönster Close Window Stäng fönster Next doc in tab Nästa dokument i fliken Previous doc in tab Föregående dokument i fliken Close tab Stäng flik Print tab Print tab Close preview window Stäng förhandsgranskningsfönster Show next result Visa nästa resultat Show previous result Visa föregående resultat Print Skriv ut PreviewTextEdit Show fields Visa fält Show main text Visa huvudtext Print Skriv ut Print Current Preview Skriv ut aktuell förhandsgranskning Show image Visa bild Select All Markera alla Copy Kopiera Save document to file Spara dokument som fil Fold lines Viklinjer Preserve indentation Bevara indrag Open document Öppna dokument Reload as Plain Text Reload as HTML QObject Global parameters Övergripande parametrar Local parameters Lokala parametrar <b>Customised subtrees <b>Anpassade underträd The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Listan över undermappar i den indexerade hierarkin <br>där vissa parametrar behöver omdefinieras. Standard: tom. <i>The parameters that follow are set either at the top level, if nothing<br>or an empty line is selected in the listbox above, or for the selected subdirectory.<br>You can add or remove directories by clicking the +/- buttons. <i>De parametrar som följer är inställda antingen på toppnivå, om inget<br>eller en tom rad är markerad i listrutan ovan, eller för den valda underkatalogen.<br>Du kan lägga till eller ta bort kataloger genom att klicka på +/- knapparna. Skipped names Undantagna namn These are patterns for file or directory names which should not be indexed. Detta är mallar för fil- eller mappnamn som inte skall indexeras. Default character set Standard teckenuppsättning This is the character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Detta är teckenuppsättningen som används för att läsa filer som inte identifierar teckenuppsättningen internt, till exempel rena textfiler.<br>Standardvärdet är tomt och värdet från NLS-miljöerna används. Follow symbolic links Följ symboliska länkar Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Följ symboliska länkar vid indexering. Standard är nej, för att undvika dubblettindexering. Index all file names Indexera alla filnamn Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Indexera namnen på filer som innehållet inte kan identifieras eller bearbetas för (ingen eller ej stödd mime-typ). Standard: sant. Beagle web history Beagle webbhistorik Search parameters Sökparametrar Web history Webbhistorik Default<br>character set Standard<br>teckenuppsättning Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Teckenuppsättning som används för läsning av filer som inte identifierar teckenuppsättningen internt, till exempel rena textfiler.<br>Standardvärdet är tom, och värdet från NLS-miljön används. Ignored endings Ignorerade ändelser These are file name endings for files which will be indexed by content only (no MIME type identification attempt, no decompression, no content indexing. Dessa är filnamnslut för filer som endast kommer att indexeras av innehåll (inga MIME-typ-identifieringsförsök, ingen dekompression, ingen innehållsindexering. These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). Dessa är filnamnsändelser för filer som endast kommer att indexeras efter namn. (ingen MIME-typidentifiering, ingen dekompression, ingen innehållsindexering). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. <i>De parametrar som följer anges antingen på toppnivå, om ingenting eller en tom rad är markerad i listboxen ovan, eller för den markerade undermappen. Du kan lägga till eller ta bort mappar genom att klicka på +/-. QWidget Create or choose save directory Skapa eller välj mapp att spara i Choose exactly one directory Välj exakt en mapp Could not read directory: Kunde inte läsa mappen: Unexpected file name collision, cancelling. Oväntad filnamnskrock, avbryter. Cannot extract document: Kan inte extrahera dokument: &Preview &Förhandsgranska &Open &Öppna Open With Öppna med Run Script Kör skript Copy &File Name Kopiera &filnamn Copy &URL Kopiera &URL &Write to File &Skriv till fil Save selection to files Spara markerat till filer Preview P&arent document/folder Förhandsgranska &överordnat dokument/mapp &Open Parent document/folder &Öppna överordnat dokument/mapp Find &similar documents Hitta &liknande dokument Open &Snippets window Öppna &textavsnittsfönster Show subdocuments / attachments Visa dokument/bilagor &Open Parent document &Öppna överordnat dokument &Open Parent Folder Ö&ppna överordnad mapp Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. Visa inte igen. RTIToolW Real time indexing automatic start Automatisk start av realtidsindexering <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexering kan ställas in för att köras som en tjänst, uppdatera indexet efterhand som filer ändras, i realtid. Du får ett alltid aktuellt index, men systemresurser används permanent.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Starta indexeringstjänsten med min skrivbordssession. Also start indexing daemon right now. Starta också indexertingstjänsten nu direkt. Replacing: Ersätter: Replacing file Ersätter fil Can't create: Kan inte skapa: Warning Varning Could not execute recollindex Kunde inte köra recollindex Deleting: Tar bort: Deleting file Tar bort fil Removing autostart Tar bort autostart Autostart file deleted. Kill current process too ? Autostartfilen borttagen. Vill du döda aktuell process också? RclCompleterModel Hits RclMain About Recoll Om Recoll Executing: [ Kör: [ Cannot retrieve document info from database Kan inte hämta dokumentinformation från databasen Warning Varning Can't create preview window Kan inte skapa förhandsgranskningsfönster Query results Sökresultat Document history Dokumenthistorik History data Historikdata Indexing in progress: Indexering pågår: Files Filer Purge Rensa Stemdb Stemdb Closing Stänger Unknown Okänd This search is not active any more Sökningen är inte aktiv längre Can't start query: Kan't starta frågan: Bad viewer command line for %1: [%2] Please check the mimeconf file Felaktig kommandorad för %1: [%2] Kontrollera filen mimeconf Cannot extract document or create temporary file Kan inte extrahera dokument eller skapa temporär fil (no stemming) (ingen igenkänning) (all languages) (alla språk) error retrieving stemming languages kunde inte hämta igänkännda språk Update &Index Uppdatera &index Indexing interrupted Indexering avbruten Stop &Indexing Stoppa i&ndexering All Alla media media message meddelande other annat presentation presentation spreadsheet kalkylblad text text sorted sorterat filtered filtrerat External applications/commands needed and not found for indexing your file types: Externa program/kommandon behövs och hittades inte för att indexera dina filtyper: No helpers found missing Inga saknade hjälpare hittades Missing helper programs Saknade hjälpprogram Save file dialog Spara fildialog Choose a file name to save under Välj ett filnamn att spara under Document category filter Dokument kategori filter No external viewer configured for mime type [ Ingen extern visare konfigurerad för mime-typ [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Det visningsläge som anges i mimeview för %1: %2 hittas inte. Vill du starta inställningsdialogen? Can't access file: Kan inte komma åt fil: Can't uncompress file: Kan inte extrahera fil: Save file Spara fil Result count (est.) Antal träffar (uppsk.) Query details Sökdetaljer Could not open external index. Db not open. Check external indexes list. Kunde inte öppna externt index. Databasen inte öppen. Kolla extern indexlista. No results found Inga träffar None Inget Updating Uppdaterar Done Klar Monitor Övervaka Indexing failed Indexering misslyckades The current indexing process was not started from this interface. Click Ok to kill it anyway, or Cancel to leave it alone Den aktuella indexeringsprocessen startades inte från det här gränssnittet. Klicka på OK för att stoppa den ändå, eller Avbryt för att lämna den kvar. Erasing index Raderar index Reset the index and start from scratch ? Vill du återställa index och börja om från start? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Sökning pågår.<br>På grund av begränsningar i indexeringsbiblioteket,<br>kommer avbryt att avsluta programmet. Error Fel Index not open Index inte öppet Index query error Index-sökfel Indexed Mime Types Indexerade Mime-typer Content has been indexed for these MIME types: Innehåll har indexerats för dessa MIME-typer: Index not up to date for this file. Refusing to risk showing the wrong entry. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Index inte uppdaterat för denna fil. Vägran att riskera att visa fel post. Klicka på OK för att uppdatera indexet för den här filen och kör sedan om frågan när indexering är klar. Can't update index: indexer running Kan inte uppdatera index: Indexering körs. Indexed MIME Types Indexerade MIME-typer Bad viewer command line for %1: [%2] Please check the mimeview file Felaktigt visarkommando för %1: [%2] Kolla mimewiew-filen. Viewer command line for %1 specifies both file and parent file value: unsupported Visarkommandot för %1 specificerar både fil- och överordnat filvärde: Stöds ej. Cannot find parent document Kan inte hitta överordnat dokument Indexing did not run yet Indexeringen kördes inte ännu External applications/commands needed for your file types and not found, as stored by the last indexing pass in Externa program/kommandon som behövs för dina filtyper och inte hittas, som lagrats av det senaste indexeringspasset i Index not up to date for this file. Refusing to risk showing the wrong entry. Index inte uppdaterat för denna fil. Vägran att riskera att visa fel post. Click Ok to update the index for this file, then re-run the query when indexing is done. Else, Cancel. Klicka på OK för att uppdatera indexet för den här filen och kör sedan om frågan när indexering är klar. Indexer running so things should improve when it's done Indexeraren kör så saker bör förbättras när det's gjort Sub-documents and attachments Underdokument och bilagor Document filter Dokumentfilter Index not up to date for this file. Refusing to risk showing the wrong entry. Index inte uppdaterat för denna fil. Vägran att riskera att visa fel post. Click Ok to update the index for this file, then you will need to re-run the query when indexing is done. Klicka OK för att uppdatera indexet för denna fil, då måste du köra om frågan när indexering är klar. The indexer is running so things should improve when it's done. Indexeraren körs så saker och ting bör förbättras när det är klart. The document belongs to an external indexwhich I can't update. Dokumentet tillhör ett externt index som jag kan't uppdatera. Click Cancel to return to the list. Click Ignore to show the preview anyway. Klicka på Avbryt för att återvända till listan. Klicka på Ignorera för att visa förhandsgranskningen ändå. Duplicate documents Dubblettdokument These Urls ( | ipath) share the same content: Dessa URL:er ( | ipath) delar samma innehåll: Bad desktop app spec for %1: [%2] Please check the desktop file Felaktig programspec. för %1: [%2] Kolla desktop-filen. Bad paths Felagtiga sökvägar Bad paths in configuration file: Dåliga sökvägar i konfigurationsfilen: Selection patterns need topdir Markeringsmall behöver toppkatalog Selection patterns can only be used with a start directory Markeringsmallar kan bara användas med en startmapp No search Inget sök No preserved previous search Inga bevarade tidigare sökningar Choose file to save Välj fil att spara Saved Queries (*.rclq) Sparade förfrågningar (*.rclq) Write failed Skrivning misslyckades Could not write to file Kunde inte skriva till fil Read failed Läsning misslyckades Could not open file: Kunde inte öppna filen: Load error Inläsningsfel Could not load saved query Kunde inte läsa in sparad fråga Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Öppnar en tillfällig kopia. Redigeringar kommer att gå förlorade om du inte sparar<br/> till en permanent plats. Do not show this warning next time (use GUI preferences to restore). Visa inte denna varning nästa gång (använd GUI-inställningar för att återställa). Disabled because the real time indexer was not compiled in. Inaktiverad eftersom realtidsindexeraren inte kompilerats in. This configuration tool only works for the main index. Det här konfigurationsverktyget fungerar bara för huvudindexet. The current indexing process was not started from this interface, can't kill it Den aktuella indexeringsprocessen startades inte från detta gränssnitt, kan't döda det The document belongs to an external index which I can't update. Dokumentet tillhör ett externt index som jag inte kan uppdatera. Click Cancel to return to the list. <br>Click Ignore to show the preview anyway (and remember for this session). Klicka på Avbryt för att återvända till listan. <br>Klicka på Ignorera för att visa förhandsgranskningen ändå (och kom ihåg för denna session). Index scheduling Index schemaläggning Sorry, not available under Windows for now, use the File menu entries to update the index Tyvärr, inte tillgänglig under Windows för tillfället, använd menyposterna för att uppdatera indexet Can't set synonyms file (parse error?) Kan inte hämta synonymfil (tolkningsfel) Index locked Index låst Unknown indexer state. Can't access webcache file. Okänt indexeringsstatus. Kan inte komma åt Webcache-filen. Indexer is running. Can't access webcache file. Indexering körs. Kan inte komma åt Webcache-filen. with additional message: med tilläggsmeddelande: Non-fatal indexing message: Ofarligt indexeringsmeddelande: Types list empty: maybe wait for indexing to progress? Typlistan är tom: Kanske vänta på att indexering skall fortgå? Viewer command line for %1 specifies parent file but URL is http[s]: unsupported Visarkommando för %1 anger överordnad fil men URL är http[s]: Stöds ej. Tools Verktyg Results Resultat (%d documents/%d files/%d errors/%d total files) (%d dokument/%d filer/%d fel/%d filer totalt) (%d documents/%d files/%d errors) (%d dokument/%d filer/%d fel) Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Tomma eller icke existerande sökvägar i konfigurationsfilen. Klicka på OK för att börja indexera ändå (frånvarande data kommer inte att rensas bort från index): Indexing done Indexering klar Can't update index: internal error Kan inte uppdatera index: Internt fel. Index not up to date for this file.<br> Index inte uppdaterat för denna fil.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> <em>Det verkar också som om senaste indexuppdatering för filen misslyckades.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Klicka OK för att försöka uppdatera index för den här filen. Du kommer att behöva köra sökningen igen när indexeringen är klar. <br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> Klicka Avbryt för att gå tillbaka till listan. <br>Klicka Ignorera för att visa förhandsgranskningen ändå (och komma ihåg för den här sessionen). Det finns risk för att fel post visas.<br/> documents dokument document dokument files filer file fil errors fel error fel total files) antal filer) No information: initial indexing not yet performed. Ingen information: Inledande indexering ännu inte utförd. Batch scheduling Batchschemaläggning The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Verktyget låter dig bestämma vid vilken tid indexering ska köras. Det använder Windows schemaläggare. Confirm Bekräfta Erasing simple and advanced search history lists, please click Ok to confirm Raderar enkel och avancerad sökhistorik, klicka OK för att bekräfta. Could not open/create file Kunde inte öppna/skapa fil F&ilter &Filter Could not start recollindex (temp file error) Kunde inte starta recollindex (tempfilsfel) Could not read: Kunde inte läsa: This will replace the current contents of the result list header string and GUI qss file name. Continue ? Detta kommer att ersätta det aktuella innehållet i rubriksträngen för resultatlistan och GUI qss-filnamn. Vill du fortsätta? You will need to run a query to complete the display change. Du behöver köra en sökning för att slutföra visningsändringen. Simple search type Enkel söktyp Any term Valfri term All terms Alla termer File name Filnamn Query language Frågespråk Stemming language Igenkänt språk Main Window Huvudfönster Focus to Search Fokusera på sökning Focus to Search, alt. Fokusera på sökning, alt. Clear Search Rensa sökning Focus to Result Table Fokusera på resultattabellen Clear search Rensa sökning Move keyboard focus to search entry Flytta tangentbordsfokus för att söka post Move keyboard focus to search, alt. Flytta tangentbordsfokus för att söka, alt. Toggle tabular display Växla tabulär visning Move keyboard focus to table Flytta tangentbordsfokus till tabell Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Previous page Föregående sida Next page Nästa sida &File &Arkiv E&xit A&vsluta &Tools &Verktyg &Help &Hjälp &Preferences &Inställningar Search tools Sök verktyg Result list Resultatlista &About Recoll &Om Recoll Document &History Dokument&historik Document History Dokumenthistorik &Advanced Search &Avancerat sök Advanced/complex Search Avancerat/Komplext sök &Sort parameters &Sorteringsparametrar Sort parameters Sorteringsparametrar Next page of results Nästa resultatsida Previous page of results Föregående resultatsida &Query configuration &Frågekonfiguration &User manual &Användarmanual Recoll Återställa Ctrl+Q Ctrl+Q Update &index Uppdatera &index Term &explorer Term&utforskare Term explorer tool Termutforskningsverktyg External index dialog Extern indexdialog &Erase document history &Radera dokumenthistorik First page Första sidan Go to first page of results Gå till första resultatsidan &Indexing configuration &Indexeringskonfiguration All Alla &Show missing helpers &Visa saknade hjälpare PgDown PgDown Shift+Home, Ctrl+S, Ctrl+Q, Ctrl+S Skift+Home, Ctrl+S, Ctrl+Q, Ctrl+S PgUp PgUp &Full Screen &Helskärm F11 F11 Full Screen Helskärmsläge &Erase search history &Radera sökhistorik sortByDateAsc sortByDateAsc Sort by dates from oldest to newest Sortera efter datum från äldst till nyast sortByDateDesc sortByDateDesc Sort by dates from newest to oldest Sortera efter datum från nyast till äldst Show Query Details Visa sökdetaljer Show results as table Visa resultat som tabell &Rebuild index &Bygg om index &Show indexed types &Visa indexerade typer Shift+PgUp Skift+PgUp &Indexing schedule &Indexeringsschema E&xternal index dialog E&xtern indexdialog &Index configuration &Indexkonfiguration &GUI configuration &GUI-konfiguration &Results &Resultat Sort by date, oldest first Sortera efter datum, äldst först Sort by date, newest first Sortera efter datum, nyast först Show as table Visa som tabell Show results in a spreadsheet-like table Visa resultat i en kalkylbladsliknande tabell Save as CSV (spreadsheet) file Spara som CSV-fil (kalkylblad) Saves the result into a file which you can load in a spreadsheet Spara resultatet i en fil som kan läsas in i ett kalkylblad Next Page Nästa sida Previous Page Föregående sida First Page Första sidan Query Fragments Sökfragment With failed files retrying Med misslyckade filers återförsök Next update will retry previously failed files Nästa uppdatering försöker igen med tidigare misslyckade filer Save last query Spara senaste sökningen Load saved query Läs in sparad sökning Special Indexing Specialindexering Indexing with special options Indexering med specialalternativ Indexing &schedule Indexerings&schema Enable synonyms Aktivera synonymer &View &Visa Missing &helpers Saknade &hjälpare Indexed &MIME types Indexerade &MIME-typer Index &statistics Index&statistik Webcache Editor Webbcache-redigerare Trigger incremental pass Utlös inkrementellt pass E&xport simple search history E&xportera enkel sökhistorik Use default dark mode Använd standardmörkt läge Dark mode Mörkt läge &Query &Fråga Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Filtrera efter datum Assisted complex search Filter birth dates RclTrayIcon Restore Återställ Quit Avsluta RecollModel Abstract Abstrakt Author Skapare Document size Dokumentstorlek Document date Dokumentdatum File size Filstorlek File name Filnamn File date Fildatum Ipath Iväg Keywords Nyckelord Mime type Mime-typ Original character set Ursprunglig teckenuppsättning Relevancy rating Relevansbetyg Title Titel URL URL Mtime Mtime Date Datum Date and time Datum och tid Ipath Iväg MIME type MIME-typ Can't sort by inverse relevance Kan inte sortera efter omvänd relevans ResList Result list Resultatlista Unavailable document Icke tillgängligt dokument Previous Föregående Next Nästa <p><b>No results found</b><br> <p><b>Inga träffar</b><br> &Preview &Förhandsgranska Copy &URL Kopiera &URL Find &similar documents Hitta &liknande dokument Query details Sökdetaljer (show query) (visa sökning) Copy &File Name Kopiera &filnamn filtered filtrerat sorted sorterat Document history Dokumenthistorik Preview Förhandsgranska Open Öppna <p><i>Alternate spellings (accents suppressed): </i> <p><i>Alternativa stavningar (accenter undantagna): </i> &Write to File &Skriv till fil Preview P&arent document/folder Förhandsgranska &överordnat dokument/mapp &Open Parent document/folder &Öppna överordnat dokument/mapp &Open &Öppna Documents Dokument out of at least ur minst for för <p><i>Alternate spellings: </i> <p><i>Alternativa stavningar: </i> Open &Snippets window Öppna &textavsnittsfönster Duplicate documents Dubblettdokument These Urls ( | ipath) share the same content: Dessa URL:er ( | ipath) delar samma innehåll: Result count (est.) Antal träffar (uppsk.) Snippets Utdrag This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Återställ sortering &Delete column &Ta bort kolumn Add " Lägg till " " column " kolumn Save table to CSV file Spara tabell till CSV-fil Can't open/create file: Kan inte öppna/skapa fil: &Preview &Förhandsgranska &Open &Öppna Copy &File Name Kopiera &filnamn Copy &URL Kopiera &URL &Write to File &Skriv till fil Find &similar documents Hitta &liknande dokument Preview P&arent document/folder Förhandsgranska &överordnat dokument/mapp &Open Parent document/folder &Öppna överordnat dokument/mapp &Save as CSV &Spara som CSV Add "%1" column Lägg till kolumn "%1" Result Table Resultattabell Open Öppna Open and Quit Öppna och avsluta Preview Förhandsgranska Show Snippets Visa textmoduler Open current result document Öppna aktuellt resultatdokument Open current result and quit Öppna nuvarande resultat och avsluta Show snippets Visa textmoduler Show header Visa sidhuvud Show vertical header Visa vertikalt sidhuvud Copy current result text to clipboard Kopiera nuvarande resultattext till urklipp Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit ResTableDetailArea &Preview &Förhandsgranska &Open &Öppna Copy &File Name Kopiera &filnamn Copy &URL Kopiera &URL &Write to File &Skriv till fil Find &similar documents Hitta &liknande dokument Preview P&arent document/folder Förhandsgranska &överordnat dokument/mapp &Open Parent document/folder &Öppna överordnat dokument/mapp ResultPopup &Preview &Förhandsgranska &Open &Öppna Copy &File Name Kopiera &filnamn Copy &URL Kopiera &URL &Write to File &Skriv till fil Save selection to files Spara markerat till filer Preview P&arent document/folder Förhandsgranska &överordnat dokument/mapp &Open Parent document/folder &Öppna överordnat dokument/mapp Find &similar documents Hitta &liknande dokument Open &Snippets window Öppna &textavsnittsfönster Show subdocuments / attachments Visa dokument/bilagor Open With Öppna med Run Script Kör skript SSearch Any term Valfri term All terms Alla termer File name Filnamn Completions Slutföranden Select an item: Välj ett objekt: Too many completions För många slutföranden Query language Frågespråk Bad query string Felaktig söksträng Out of memory Slut på minne Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> No actual parentheses allowed.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Ange uttryck för frågans språk. Cheat sheet:<br> <i>term1 term2</i> : 'term1' och 'term2' i alla fält.<br> <i>fält:term1</i> : 'term1' i fält 'fält'.<br> Standardfältnamn/synonymer:<br> titel/ämne/bildtext, författare/från, mottagare/till, filnamn, ext.<br> Pseudofält: dir, mime/format, type/rclcat, datum.<br> Två datumintervall exemplar: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 ELLER term3</i> : term1 OCH (term2 ELLER term3).<br> Inga faktiska parenteser tillåtna.<br> <i>"term1 term2"</i> : phrase (måste ske exakt). Möjliga modifierare:<br> <i>"term1 term2"p</i> : obeställd närhetssökning med standardavstånd.<br> Använd <b>Visa fråga</b> länk när du är osäker på resultatet och se manuell (&lt; 1>) för mer detaljer. Enter file name wildcard expression. Ange filnamn jokertecken uttryck. Enter search terms here. Type ESC SPC for completions of current term. Skriv in sökord här. Skriv ESC SPC för färdigställanden av nuvarande term. Enter query language expression. Cheat sheet:<br> <i>term1 term2</i> : 'term1' and 'term2' in any field.<br> <i>field:term1</i> : 'term1' in field 'field'.<br> Standard field names/synonyms:<br> title/subject/caption, author/from, recipient/to, filename, ext.<br> Pseudo-fields: dir, mime/format, type/rclcat, date, size.<br> Two date interval exemples: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 OR term3</i> : term1 AND (term2 OR term3).<br> You can use parentheses to make things clearer.<br> <i>"term1 term2"</i> : phrase (must occur exactly). Possible modifiers:<br> <i>"term1 term2"p</i> : unordered proximity search with default distance.<br> Use <b>Show Query</b> link when in doubt about result and see manual (&lt;F1>) for more detail. Ange uttryck för frågans språk. Cheat sheet:<br> <i>term1 term2</i> : 'term1' och 'term2' i alla fält.<br> <i>fält:term1</i> : 'term1' i fält 'fält'.<br> Standardfältnamn/synonymer:<br> titel/ämne/bildtext, författare/från, mottagare/till, filnamn, ext.<br> Pseudofält: dir, mime/format, type/rclcat, datum, storlek.<br> Två datumintervall exemplar: 2009-03-01/2009-05-20 2009-03-01/P2M.<br> <i>term1 term2 ELLER term3</i> : term1 OCH (term2 ELLER term3).<br> Du kan använda parenteser för att göra saker tydligare.<br> <i>"term1 term2"</i> : phrase (måste ske exakt). Möjliga modifierare:<br> <i>"term1 term2"p</i> : obeställd närhetssökning med standardavstånd.<br> Använd <b>Visa fråga</b> länk när du är osäker på resultatet och se manuell (&lt; 1>) för mer detaljer. Stemming languages for stored query: Igenkända språk för lagrad sökning: differ from current preferences (kept) skiljer sig från aktuella inställningar (hålls) Auto suffixes for stored query: Autosuffix för lagrad sökning: External indexes for stored query: Externa index för lagrad fråga: Autophrase is set but it was unset for stored query Autofras är inställt men det var undantaget för lagrad sökning Autophrase is unset but it was set for stored query Autofras är undantaget men den angavs för lagrad förfrågan Enter search terms here. Ange söktermer här. <html><head><style> <html><head><style> table, th, td { table, th, td { border: 1px solid black; kant: 1 px fast svart; border-collapse: collapse; gräns-kollaps: kollaps; } } th,td { th,td { text-align: center; textjustering: centrera; </style></head><body> </style></head><body> <p>Query language cheat-sheet. In doubt: click <b>Show Query</b>.&nbsp; <p>Query-språkets lathund. Vid tvivel: Klicka <b>Visa Query</b>.&nbsp; You should really look at the manual (F1)</p> Du borde verkligen kolla manualen (F1)</p> <table border='1' cellspacing='0'> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><th>Vilka</th><th>exempel</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Och</td><td>en två&nbsp;&nbsp;&nbsp;en OCH två&nbsp;&nbsp;&nbsp;en && två</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Eller</td><td>en ELLER två&nbsp;&nbsp;&nbsp;en <unk> två</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; <tr><td>Komplex boolean. ELLER har prioritet, använd parenteser&nbsp; where needed</td><td>(one AND two) OR three</td></tr> där det behövs</td><td>(en och två) ELLER tre</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Inte</td><td>sikt</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Fras</td><td>"stolthet och fördomar"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Obeställd prox. (standard slack=10)</td><td>"fördomar&nbsp;stolthet"p</td></tr> <tr><td>No stem expansion: capitalize</td><td>Floor</td></tr> <tr><td>Ingen stamexpansion: kapitalisera</td><td>Golv</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>Fältspecifik</td><td>författare:austen&nbsp;&nbsp;titel:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OCH inuti fältet (ingen ordning)</td><td>författare: jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>ELLER inuti fältet</td><td>författare:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Fältnamn</td><td>titel/ämne/bildtext&nbsp;&nbsp;författare/från<br>mottagare/till&nbsp;&nbsp;filnamn&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>Katalogsökvägfilter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>MIME-typfilter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> <tr><td>Datumintervall</td><td>datum:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> datum:2018&nbsp;&nbsp;datum:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> </table></body></html> Can't open index Kan inte öppna index Could not restore external indexes for stored query:<br> Kunde inte återställa externt index för lagrad sökning:<br> ??? ??? Using current preferences. Använder aktuella inställningar. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase SSearchBase Clear Rensa Ctrl+S Ctrl+S Erase search entry Radera sökpost Search Sök Start query Starta sök Enter search terms here. Type ESC SPC for completions of current term. Skriv in sökord här. Skriv ESC SPC för färdigställanden av nuvarande term. Choose search type. Välj söktyp Show query history Visa sökhistorik Enter search terms here. Ange söktermer här. Main menu Huvudmeny SearchClauseW SearchClauseW SökClauseW Any of these Någon av dessa All of these Alla dessa None of these Inget av dessa This phrase Denna fras Terms in proximity Villkor i närhet File name matching Filnamn som matchar Select the type of query that will be performed with the words Välj vilken typ av sökning som skall utföras med orden Number of additional words that may be interspersed with the chosen ones Antal ytterligare ord som kan varvas med de valda In field I fält No field Inga fält Any Valfritt All Alla None Ingen Phrase Fras Proximity Närheten File name Filnamn Snippets Snippets Textsnuttar X X Find: Sök: Next Nästa Prev Föregående SnippetsW Search Sök <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> <p>Ingen exakt träff hittades inom gränsen. Dokumentet är troligen väldigt stort och textsnuttgeneratorn gick vilse i en labyrint...</p> Sort By Relevance Sortera efter relevans Sort By Page Sortera efter sida Snippets Window Textmoduler Fönster Find Hitta Find (alt) Hitta (alt) Find Next Hitta nästa Find Previous Hitta föregående Hide Dölj Find next Sök nästa Find previous Sök föregående Close window Stäng fönster SortForm Date Datum Mime type Mime-typ SortFormBase Sort Criteria Sortera kriterier Sort the Sortera den most relevant results by: mest relevanta resultat genom att: Descending Fallande Close Stäng Apply Tillämpa SpecIdxW Special Indexing Specialindexering Do not retry previously failed files. Försök inte igen tidigare misslyckade filer. Else only modified or failed files will be processed. Annars kommer endast ändrade eller misslyckade filer att bearbetas. Erase selected files data before indexing. Radera valda filers data före indexering Directory to recursively index Katalog att rekursivt indexera Browse Bläddra Start directory (else use regular topdirs): Startkatalog (använd annars vanliga topdirs): Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Lämnas tom för att markera alla filer. Du kan använda flera blankstegsseparerade skaltypsmallar.<br>Mallar innehållande blanksteg bör infattas med sitationstecken (").<br>Kan bara användas om startmapp har angetts. Selection patterns: Markeringsmallar: Top indexed entity Toppindexerad enhet Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Mapp att indexera rekursivt. Detta måste finnas innanför det vanliga indexerade området<br> som definieras i konfigurationsfilen (överordnade mappar). Retry previously failed files. Försök igen med tidigare misslyckade filer. Start directory. Must be part of the indexed tree. We use topdirs if empty. Start katalog. Måste vara en del av det indexerade trädet. Vi använder topdirs om det är tomt. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Startmapp. Måste vara en del av det indexerade trädet. Använder hela det indexerade området om den lämnas tom. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer Termutforskare &Expand &Expandera Alt+E Alt+E &Close &Stäng Alt+C Alt+C Term Termin No db info. Ingen databasinfo. Doc. / Tot. Dok. / Tot. Match Matcha Case Skiftläge Accents Accenter SpellW Wildcards Jokertecken Regexp Regexp Spelling/Phonetic Stavning/Fonetik Aspell init failed. Aspell not installed? Aspell init misslyckades. Aspell inte installerad? Aspell expansion error. Aspell expansionsfel. Stem expansion Stamutvidgning error retrieving stemming languages fel vid hämtning av igenkänningsspråk No expansion found Ingen utvidgning hittades Term Termin Doc. / Tot. Dok. / Tot. Index: %1 documents, average length %2 terms Index: %1 dokument, genomsnittslängd %2 termer Index: %1 documents, average length %2 terms.%3 results Index: %1 dokument, genomsnittlig längd %2 termer.%3 träffar %1 results %1 träffar List was truncated alphabetically, some frequent Listan avkortades alfabetiskt, några frekventa terms may be missing. Try using a longer root. termer kan saknas. försök använda en längre root. Show index statistics Visa indexstatistik Number of documents Antal dokument Average terms per document Genomsnittligt antal termer per dokument Smallest document length Minsta dokumentlängd Longest document length Längsta dokumentlängd Database directory size Databasmappens storlek MIME types: MIME-typer: Item Objekt Value Värde Smallest document length (terms) Kortast dokumentlängd (termer) Longest document length (terms) Längst dokumentlängd (termer) Results from last indexing: Resultat från senaste indexering: Documents created/updated Skapade/Uppdaterade dokument Files tested Testade filer Unindexed files Ej indexerade filer List files which could not be indexed (slow) Lista filer som inte kunde indexeras (långsam) Spell expansion error. Fel vif stavningsutvidgning. Spell expansion error. UIPrefsDialog The selected directory does not appear to be a Xapian index Den valda mappen verkar inte vara ett Xapian-index This is the main/local index! Detta är lokalt huvudindex! The selected directory is already in the index list Den valda mappen finns redan i indexlistan Select xapian index directory (ie: /home/buddy/.recoll/xapiandb) Välj xapian index katalog (dvs: /home/buddy/.recoll/xapiandb) error retrieving stemming languages fel vid hämtning av igenkända språk Choose Välj Result list paragraph format (erase all to reset to default) Styckeformat för resultatlista (radera alla för att återställa till standard) Result list header (default is empty) Rubrik för resultatlista (tomt som standard) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) Välj Recoll konfigurationsmapp eller Xapian indexmapp (t.ex. /home/<användare>/.recoll eller /home/<användare>/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read Den valda mappen ser ut som en Recoll konfigurationsmapp men det gick inte att läsa konfigurationen At most one index should be selected Ett index bör väljas, som mest Cant add index with different case/diacritics stripping option Kan inte lägga till index med olika skiftläge / diakritiska strippningsalternativ Default QtWebkit font Standard QtWebkit-teckensnitt Any term Valfri term All terms Alla termer File name Filnamn Query language Frågespråk Value from previous program exit Värde från föregående programavslut Context Kontext Description Beskrivning Shortcut Genväg Default Standard Choose QSS File Can't add index with different case/diacritics stripping option. UIPrefsDialogBase User interface Användargränssnitt Number of entries in a result page Antal poster i resultatlistan Result list font Teckensnitt i resultatlistan Helvetica-10 Helvetica-10 Opens a dialog to select the result list font Öppnar en dialog för att välja teckensnitt i resultatlistan Reset Återställ Resets the result list font to the system default Återställer teckensnittet till systemstandard Auto-start simple search on whitespace entry. Auto-starta enkel sökning på blanksteg. Start with advanced search dialog open. Starta med "Avancerad sökning" öppnad. Start with sort dialog open. Börja med att öppna sorteringsdialogrutan. Search parameters Sökparametrar Stemming language Igenkänt språk Dynamically build abstracts Bygg abstrakta referat dynamiskt Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Vill du försöka bygga sammandrag för resultatlisteposter genom att använda kontexten för frågetermer? Kan vara långsamt med stora dokument. Replace abstracts from documents Ersätt abstrakter från dokument Do we synthetize an abstract even if the document seemed to have one? Vill du synthetisera en abstrakt även om dokumentet verkade ha en? Synthetic abstract size (characters) Syntetisk abstraktstorlek (tecken) Synthetic abstract context words Syntetiska abstrakta kontextord External Indexes Externa index Add index Lägg till index Select the xapiandb directory for the index you want to add, then click Add Index Välj katalogen xapiandb för det index du vill lägga till, klicka sedan på Lägg till index Browse Bläddra &OK &Ok Apply changes Tillämpa ändringar &Cancel &Avbryt Discard changes Ignorera ändringar Result paragraph<br>format string Resultatstycke<br>formatsträng Automatically add phrase to simple searches Lägg automatiskt till fras, vid enkel sökning A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. En sökning efter [rolling stones] (2 termer) kommer att ändras till [rolling eller stones eller (rolling fras 2 stones)]. Detta bör ge högre företräde till de träffar där söktermerna visas exakt som de anges. User preferences Användarens inställningar Use desktop preferences to choose document editor. Använd skrivbordsinställningar för att välja dokumentredigerare. External indexes Externa index Toggle selected Omvänd markering Activate All Aktivera alla Deactivate All Avaktivera alla Remove selected Ta bort markerat Remove from list. This has no effect on the disk index. Ta bort från listan. Detta har ingen effekt på diskindex. Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definierar formatet för varje resultatlista stycket. Använd qt html-format och utskrifts-liknande ersättningar:<br>%A Abstrakt<br> %D Datum<br> %I Ikonbildnamn<br> %K Nyckelord (om någon)<br> %L Förhandsgranska och redigera länkar<br> %M Mime-typ<br> %N Resultatnummer<br> %R Relevansprocent<br> %S Storleksinformation<br> %T Titel<br> %U Url<br> Remember sort activation state. Kom ihåg status för sorteringsaktivering. Maximum text size highlighted for preview (megabytes) Maximal textstorlek markerad för förhandsgranskning (megabyte) Texts over this size will not be highlighted in preview (too slow). Texter över denna storlek kommer inte att markeras i förhandsgranskningen (för långsamt). Highlight color for query terms Markera färg för frågetermer Prefer Html to plain text for preview. Föredra HTML framför oformaterad text i förhandsgranskning. If checked, results with the same content under different names will only be shown once. Om markerad, visas träffar med samma innehåll under olika namn, endast en gång. Hide duplicate results. Dölj dubbletträffar. Choose editor applications Välj redigeringsprogram Display category filter as toolbar instead of button panel (needs restart). Visa kategorifilter som verktygsfält istället för knapppanel (kräver omstart). The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Orden i listan kommer automatiskt att omvandlas till ext:xxx-satser i frågespråksposten. Query language magic file name suffixes. Frågespråkets magiska filnamnssuffix. Enable Aktivera ViewAction Changing actions with different current values Ändra åtgärder med olika aktuella värden Mime type Mime-typ Command Kommando MIME type MIME-typ Desktop Default Skrivbordsstandard Changing entries with different current values Ändrar poster med olika aktuella värden ViewActionBase File type Typ av fil Action Åtgärd Select one or several file types, then click Change Action to modify the program used to open them Välj en eller flera filtyper, klicka sedan på Ändra åtgärd för att ändra programmet som används för att öppna dem Change Action Ändra åtgärd Close Stäng Native Viewers Inbyggda visare Select one or several mime types then click "Change Action"<br>You can also close this dialog and check "Use desktop preferences"<br>in the main panel to ignore this list and use your desktop defaults. Välj en eller flera mime-typer och klicka sedan på "Ändra åtgärd"<br>Du kan också stänga denna dialogruta och kontrollera "Använd skrivbordsinställningar"<br>i huvudpanelen för att ignorera denna lista och använda dina skrivbordsinställningar. Select one or several mime types then use the controls in the bottom frame to change how they are processed. Välj en eller flera mime-typer, använd sedan kontrollerna i den nedre ramen för att ändra hur de bearbetas. Use Desktop preferences by default Använd skrivbordsinställningar som standard Select one or several file types, then use the controls in the frame below to change how they are processed Välj en eller flera filtyper, använd sedan kontrollerna i ramen nedan för att ändra hur de bearbetas Exception to Desktop preferences Undantag från skrivbordsinställningar Action (empty -> recoll default) Åtgärd (tom -> recoll-standard) Apply to current selection Tillämpa på aktuell markering Recoll action: Recoll-åtgärd: current value aktuellt värde Select same Välj samma <b>New Values:</b> <b>Nya värden:</b> Webcache Webcache editor Webbcache-redigerare Search regexp Sök regexp TextLabel WebcacheEdit Copy URL Kopiera URL Unknown indexer state. Can't edit webcache file. Okänt indexeringsstatus. Kan inte redigera webbcache-filen. Indexer is running. Can't edit webcache file. Indexeraren körs. Kan inte redigera webbcache-filen. Delete selection Ta bort markerat Webcache was modified, you will need to run the indexer after closing this window. Webbcachen har ändrats, du måste indexera efter att detta fönster stängts. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME MIME Url URL Date Datum Size URL URL WinSchedToolW Error Fel Configuration not initialized Konfigurationen är inte initierad <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> <h3>Återställa indexering batch schemaläggning</h3><p>Vi använder standard Windows uppgift schemaläggare för detta. Programmet kommer att startas när du klickar på knappen nedan.</p><p>Du kan använda antingen det fullständiga gränssnittet (<i>Skapa uppgift</i> i menyn till höger) eller den förenklade <i>Skapa grundläggande uppgiften</i> guiden. I båda fallen Kopiera/Klistra in sökvägen för kommandofilen som anges nedan som <i>Åtgärden</i> som ska utföras.</p> Command already started Kommandot är redan startat Recoll Batch indexing Återskapa batch-indexering Start Windows Task Scheduler tool Starta schemaläggningsverktyget för Windows Could not create batch file confgui::ConfBeaglePanelW Steal Beagle indexing queue Stjäla Beagle indexering kö Beagle MUST NOT be running. Enables processing the beagle queue to index Firefox web history.<br>(you should also install the Firefox Beagle plugin) Beagle MÅSTE inte köras. Aktiverar bearbetning av beagle kö för att indexera Firefox webbhistorik.<br>(Du bör också installera Firefox Beagle plugin) Web cache directory name Webb cache katalognamn The name for a directory where to store the cache for visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Namnet på en katalog där cachen lagras för besökta webbsidor.<br>En icke-absolut sökväg tas i förhållande till konfigurationskatalogen. Max. size for the web cache (MB) Max. storlek för webbcache (MB) Entries will be recycled once the size is reached Inlägg kommer att återvinnas när storleken är nådd Web page store directory name Mappnamn för webbsidelagring The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Namnet på en mapp där kopiorna av besökta webbsidor skall lagras.<br>En icke-absolut sökväg tas i förhållande till konfigurationskatalogen. Max. size for the web store (MB) Max storlek för webblagring (MB) Process the WEB history queue Bearbeta webbhistorikkön Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Aktiverar indexering av sidor besökta med Firefox.<br>(Du behöver installera Recoll-tillägget i Firefox). Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Poster kommer att återvinnas när storleken är uppnådd. <br>Bara ökad storlek är meningsfull, eftersom minskat värde inte kommer att trunkera en befintlig fil (bara slösa med utrymmet). confgui::ConfIndexW Can't write configuration file Kan inte skriva inställningsfil Recoll - Index Settings: Återställa - Index inställningar: confgui::ConfParamFNW Browse Bläddra Choose Välj confgui::ConfParamSLW + + - - Add entry Lägg till post Delete selected entries Ta bort markerade poster ~ ~ Edit selected entries Redigera markerade poster confgui::ConfSearchPanelW Automatic diacritics sensitivity Automatisk känslighet för diakritiska tecken <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Utlöser automatisk känslighet för diakritiska tecken om söktermen har accenttecken (inte i unac_except_trans). Annars behöver du använda frågespråket och <i>D</i>-modifieraren för att ange diakritiska teckens känslighet. Automatic character case sensitivity Atomatisk skiftlägeskänslighet <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. <p>Utlöser automatiskt skiftlägeskänslighet om posten har versaltecken i någon annan än den första positionen. Annars behöver du använda frågespråket och <i>C</i>-modifieraren för att ange skiftlägeskänslighet. Maximum term expansion count Max antal termutvidgningar <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Max antal utvidgningar för en enskild term (t.ex vid användning av jokertecken). Standardvärdet 10 000 är rimligt och kommer att undvika förfrågningar som verkar frysta, medan motorn går igenom termlistan. Maximum Xapian clauses count Max antal Xapian-satser <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. Max antal elementärsatser vi lägger till i en enda Xapian-sökning. I vissa fall kan resultatet av termutvidgning vara multiplikativ, och vi vill undvika att använda överdrivet mycket minne. Standardvärdet 100 000 bör i de flesta fall, vara både tillräckligt högt och kompatibelt med aktuella typiska maskinvarukonfigurationer. confgui::ConfSubPanelW Global Övergripande Max. compressed file size (KB) Max komprimerad filstorlek (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Det här värdet anger ett tröskelvärde bortom vilket komprimerade filer inte kommer att bearbetas. Ange -1 för ingen begränsning, 0 för ingen dekomprimering någonsin. Max. text file size (MB) Max textfilsstorlek (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Det här värdet anger ett tröskelvärde bortom vilket textfiler inte kommer att behandlas. Ange -1 för ingen begränsning. Detta är för att utesluta monsterloggfiler från indexet. Text file page size (KB) Sidstorlek för textfiler (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Om detta värde anges (inte lika med -1), kommer textfiler att delas upp i bitar av denna storlek, för indexering. Detta kommer att underlätta vid genomsökning av mycket stora textfiler (t.ex. loggfiler). Max. filter exec. time (S) Max. filtret körtid (S) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loopSet to -1 for no limit. Externa filter som fungerar längre än detta kommer att avbrytas. Detta är för det sällsynta fallet (dvs: postscript) där ett dokument kan orsaka ett filter att loopSet till -1 för ingen gräns. External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Externa filter som arbetar längre än detta kommer att avbrytas. Detta är för det sällsynta fallet (t.ex. postscript) där ett dokument kan orsaka att ett filter loopar. Ange -1 för ingen begränsning. Only mime types Endast mime-typer An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive En exklusiv lista över indexerade mime-typer.<br>Inget annat kommer att indexeras. Normalt tom och inaktiv. Exclude mime types Undanta mime-typer Mime types not to be indexed Mime-typer som inte skall indexeras Max. filter exec. time (s) Maxtid för filterexekvering. (s) confgui::ConfTopPanelW Top directories Toppmappar The list of directories where recursive indexing starts. Default: your home. Listan över mappar där rekursiv indexering börjar. Standard är din hemkatalog. Skipped paths Undantagna sökvägar These are names of directories which indexing will not enter.<br> May contain wildcards. Must match the paths seen by the indexer (ie: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Dessa är namn på kataloger som indexering inte kommer in.<br> Kan innehålla jokertecken. Måste matcha sökvägar som ses av indexeraren (dvs: om topdirs innehåller '/home/me' och '/home' är faktiskt en länk till '/usr/home', en korrekt skippedPath post skulle vara '/home/me/tmp*', inte '/usr/home/me/tmp*') Stemming languages Igenkända språk The languages for which stemming expansion<br>dictionaries will be built. Språken som hindrar expansion<br>ordböcker kommer att byggas. Log file name Loggfilsnamn The file where the messages will be written.<br>Use 'stderr' for terminal output Filen där meddelandena kommer att skrivas.<br>Använd "stderr" för utdata i terminal. Log verbosity level Informationsnivå i loggen This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Detta värde justerar mängden meddelanden,<br>från endast felmeddelanden till en mängd felsökningsdata. Index flush megabytes interval Indexdumpningsintervall i megabyte This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB Detta värde justera mängden data som indexeras mellan dumpningar till disk.<br>Detta hjälper till att kontrollera indexerarens minnesanvändning. Standard är 10MB Max disk occupation (%) Max antal diskockupationer (%) This is the percentage of disk occupation where indexing will fail and stop (to avoid filling up your disk).<br>0 means no limit (this is the default). Detta är den procentandel av diskockupation där indexering kommer att misslyckas och stoppa (för att undvika att fylla din disk).<br>0 betyder ingen gräns (detta är standard). No aspell usage Ingen Aspell-användning Aspell language Aspell-språk The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works.To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Språket för aspell ordbok. Detta bör se ut 'sv' eller 'fr' . .<br>Om detta värde inte är inställt, kommer NLS-miljön att användas för att beräkna det, vilket vanligtvis fungerar. o få en uppfattning om vad som installeras på ditt system, typ 'aspell config' och leta efter . vid filer i katalogen 'data-dir' Database directory name Databasens mappnamn The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Namnet på en katalog där indexet ska lagras<br>En icke-absolut sökväg tas i förhållande till konfigurationskatalogen. Standard är 'xapiandb'. Use system's 'file' command Använd systemet's 'fil' kommando Use the system's 'file' command if internal<br>mime type identification fails. Använd systemet's 'fil' kommandot om intern<br>mime-typ-identifiering misslyckas. Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Inaktiverar användning av Aspell för att generera stavningsnärmevärde i termutforskarverktyget. The language for the aspell dictionary. This should look like 'en' or 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Språket för aspell ordbok. Detta bör se ut 'sv' eller 'fr' . .<br>Om detta värde inte är inställt, kommer NLS-miljön att användas för att beräkna det, vilket vanligtvis fungerar. För att få en uppfattning om vad som installeras på ditt system, skriv 'aspell config' och leta efter . vid filer i katalogen 'data-dir' The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Namnet på en mapp där index lagras.<br>En icke-absolut sökväg tas i förhållande till konfigurationskatalogen. Standard är "xapiandb". Unac exceptions Unac-undantag <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Dessa är undantag från unac-mekanismen som, som standard, tar bort alla diakritiska tecken, och utför vägledande nedbrytning. Du kan åsidosätta vissa ointressanta tecken, beroende på ditt språk, och ange ytterligare nedbrytningar, t.ex. för ligaturer. I varje blankstegsseparerad post är det första tecknet källan och resten är översättningen. These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Det här är sökvägsnamn till kataloger som inte kommer att indexeras.<br>Sökvägar kan innehålla jokertecken. Posterna måste matcha de sökvägar som indexeraren ser (exempel: Om överordnade kataloger inkluderar "/home/me" och "/home" egentligen är en länk till "/usr/home", skulle en korrekt undantagen sökväg vara "/home/me/tmp*", inte "/usr/home/me/tmp*") Max disk occupation (%, 0 means no limit) Max diskockupation (%, 0 betyder ingen gräns) This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. Det här är diskanvändningen i procent - Sammanlagd diskanvändning, inte den storlek där indexering kommer att misslyckas och stoppas.<br>Standardvärdet 0, tar bort all begränsning. uiPrefsDialogBase User preferences Användarens inställningar User interface Användargränssnitt Number of entries in a result page Antal poster i resultatlistan If checked, results with the same content under different names will only be shown once. Om markerad, visas träffar med samma innehåll under olika namn, endast en gång. Hide duplicate results. Dölj dubbletträffar. Highlight color for query terms Markera färg för frågetermer Result list font Teckensnitt i resultatlistan Opens a dialog to select the result list font Öppnar en dialog för att välja teckensnitt i resultatlistan Helvetica-10 Helvetica-10 Resets the result list font to the system default Återställer teckensnittet till systemstandard Reset Återställ Defines the format for each result list paragraph. Use qt html format and printf-like replacements:<br>%A Abstract<br> %D Date<br> %I Icon image name<br> %K Keywords (if any)<br> %L Preview and Edit links<br> %M Mime type<br> %N Result number<br> %R Relevance percentage<br> %S Size information<br> %T Title<br> %U Url<br> Definierar formatet för varje resultatlista stycket. Använd qt html-format och utskrifts-liknande ersättningar:<br>%A Abstrakt<br> %D Datum<br> %I Ikonbildnamn<br> %K Nyckelord (om någon)<br> %L Förhandsgranska och redigera länkar<br> %M Mime-typ<br> %N Resultatnummer<br> %R Relevansprocent<br> %S Storleksinformation<br> %T Titel<br> %U Url<br> Result paragraph<br>format string Resultatstycke<br>formatsträng Texts over this size will not be highlighted in preview (too slow). Texter över denna storlek kommer inte att markeras i förhandsgranskningen (för långsamt). Maximum text size highlighted for preview (megabytes) Maximal textstorlek markerad för förhandsgranskning (megabyte) Use desktop preferences to choose document editor. Använd skrivbordsinställningar för att välja dokumentredigerare. Choose editor applications Välj redigeringsprogram Display category filter as toolbar instead of button panel (needs restart). Visa kategorifilter som verktygsfält istället för knapppanel (kräver omstart). Auto-start simple search on whitespace entry. Auto-starta enkel sökning på blanksteg. Start with advanced search dialog open. Starta med "Avancerad sökning" öppnad. Start with sort dialog open. Börja med att öppna sorteringsdialogrutan. Remember sort activation state. Kom ihåg status för sorteringsaktivering. Prefer Html to plain text for preview. Föredra HTML framför oformaterad text i förhandsgranskning. Search parameters Sökparametrar Stemming language Igenkänt språk A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. En sökning efter [rolling stones] (2 termer) kommer att ändras till [rolling eller stones eller (rolling fras 2 stones)]. Detta bör ge högre företräde till de träffar där söktermerna visas exakt som de anges. Automatically add phrase to simple searches Lägg automatiskt till fras, vid enkel sökning Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Vill du försöka bygga sammandrag för resultatlisteposter genom att använda kontexten för frågetermer? Kan vara långsamt med stora dokument. Dynamically build abstracts Bygg abstrakta referat dynamiskt Do we synthetize an abstract even if the document seemed to have one? Vill du synthetisera en abstrakt även om dokumentet verkade ha en? Replace abstracts from documents Ersätt abstrakter från dokument Synthetic abstract size (characters) Syntetisk abstraktstorlek (tecken) Synthetic abstract context words Syntetiska abstrakta kontextord The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Orden i listan kommer automatiskt att omvandlas till ext:xxx-satser i frågespråksposten. Query language magic file name suffixes. Frågespråkets magiska filnamnssuffix. Enable Aktivera External Indexes Externa index Toggle selected Omvänd markering Activate All Aktivera alla Deactivate All Avaktivera alla Remove from list. This has no effect on the disk index. Ta bort från listan. Detta har ingen effekt på diskindex. Remove selected Ta bort markerat Click to add another index directory to the list Klicka för att lägga till en annan indexkatalog i listan Add index Lägg till index Apply changes Tillämpa ändringar &OK &Ok Discard changes Ignorera ändringar &Cancel &Avbryt Abstract snippet separator Avgränsare för abstrakta kodavsnitt Use <PRE> tags instead of <BR>to display plain text as html. Använd <PRE> taggar istället för <BR>för att visa oformaterad text som html. Lines in PRE text are not folded. Using BR loses indentation. Linjer i PRE text viks inte. Använda BR förlorar indragning. Style sheet Stilmall Opens a dialog to select the style sheet file Öppnar en dialog för att välja formatmallsfil Choose Välj Resets the style sheet to default Återställer formatmallen till standard Lines in PRE text are not folded. Using BR loses some indentation. Linjer i PRE text viks inte. Använda BR förlorar en del indragning. Use <PRE> tags instead of <BR>to display plain text as html in preview. Använd <PRE> taggar istället för <BR>för att visa oformaterad text som html i förhandsgranskningen. Result List Resultatlista Edit result paragraph format string Redigera formatsträngen för resultatstycket Edit result page html header insert Redigera resultatsidans HTML-sidhuvud Date format (strftime(3)) Datumformat (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Tröskelvärde i procent, över vilket vi inte använder termer inuti autofras. Frekventa termer är ett stort prestandaproblem med fraser. Överhoppade termer förstärkar frasens slack och minskar autofraseffektiviteten. Standardvärdet är 2 (procent). Autophrase term frequency threshold percentage Tröskelvärde i procent, för autofrastermfrekvens Plain text to HTML line style Oformaterad text till HTML-stil Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. Rader i PRE-text radbryts inte. Används BR, förloras vissa indrag. PRE + Wrap stil kan vara vad du vill ha. <BR> <BR> <PRE> <PRE> <PRE> + wrap <PRE> + wrap Exceptions Undantag Mime types that should not be passed to xdg-open even when "Use desktop preferences" is set.<br> Useful to pass page number and search string options to, e.g. evince. Mime-typer som inte bör skickas till xdg-open även när "Använd skrivbordsinställningar" är satta.<br> Användbart att skicka sidnummer och söksträngsalternativ till, t.ex. evince. Disable Qt autocompletion in search entry. Inaktivera Qt-autokomplettering i sökposten. Search as you type. Sök som du skriver. Paths translations Sökvägsöversättningar Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Klicka här om du vill lägga till ytterligare en indexkatalog i listan. Du kan välja antingen en Recoll-konfigurationsmapp eller ett Xapian-index. Snippets window CSS file CSS-fil för textavsnittsfönster Opens a dialog to select the Snippets window CSS style sheet file Öppna en dialog för att välja CSS-fil för textavsnittsfönster Resets the Snippets window style Återställ stilen på textavsnittsfönster Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Bestäm om dokumentfilter ska visas som alternativknappar, kombinationsruta i verktygsfältet eller meny. Document filter choice style: Val av dokumentfilterstil: Buttons Panel Knappanel Toolbar Combobox Kombobox i verktygsfältet Menu Meny Show system tray icon. Visa systemfältsikon. Close to tray instead of exiting. Stäng till systemfältet istället för att avsluta. Start with simple search mode Starta i enkelt sökläge Show warning when opening temporary file. Visa varning när tillfällig fil öppnas. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Användarstil att tillämpa på textavsnittsfönstret. <br>Notis: Sidhuvudet för resultatsidan ingår också i textavsnittets fönsterrubrik. Synonyms file Synonymfiler Highlight CSS style for query terms Framhäv CSS-stil för frågetermer Recoll - User Preferences Recoll - Användarinställningar Set path translations for the selected index or for the main one if no selection exists. Ange sökvägsöversättningar för valt index eller för det huvudsakliga om inget markerats. Activate links in preview. Aktivera länkar i förhandsgranskning Make links inside the preview window clickable, and start an external browser when they are clicked. Gör länkar inne i förhandsgranskningsfönstret klickbara, och startar en extern webbläsare. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Frågetermer som framhävs i resultat. <br>Prova något som "color:red;background:yellow" för något mer livfullt än standardblå ... Start search on completer popup activation. Starta sökning vid aktivering av kompletteringspopup. Maximum number of snippets displayed in the snippets window Maximalt antal textavsnitt som visas i textavsnittsfönstret Sort snippets by page number (default: by weight). Sortera textavsnitt efter sidnummer (standard är efter vikt). Suppress all beeps. Undertryck alla pip. Application Qt style sheet Qt-formatmall för programmet Limit the size of the search history. Use 0 to disable, -1 for unlimited. Begränsa sökhistorikens storlek. Använd 0 för att inaktivera, -1 för obegränsat. Maximum size of search history (0: disable, -1: unlimited): Maximal storlek på sökhistoriken (0: inaktiverat, -1: obegränsat): Generate desktop notifications. Generera skrivbordsaviseringar. Misc Diverse Work around QTBUG-78923 by inserting space before anchor text Arbeta runt QTBUG-78923 genom att infoga utrymme före ankartext Display a Snippets link even if the document has no pages (needs restart). Visa en textavsnittslänk även om dokumentet inte har några sidor (omstart krävs). Maximum text size highlighted for preview (kilobytes) Maximal textstorlek som framhävs i förhandsgranskning (kilobyte) Start with simple search mode: Starta i enkelt sökläge: Hide toolbars. Dölj verktygsfält. Hide status bar. Dölj statusfält. Hide Clear and Search buttons. Dölj knapparna Rensa och Sök. Hide menu bar (show button instead). Dölj menyraden (visa knappen istället). Hide simple search type (show in menu only). Dölj enkel söktyp (visa endast i menyn). Shortcuts Genvägar Hide result table header. Dölj resultattabellens rubrik. Show result table row headers. Visa radhuvuden för resultattabellen. Reset shortcuts defaults Återställ standardgenvägar Disable the Ctrl+[0-9]/[a-z] shortcuts for jumping to table rows. Inaktivera Ctrl+[0-9]/[a-z] genvägar för att hoppa till tabellrader. Use F1 to access the manual Använd F1 för att komma åt manualen Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Enkel söktyp Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Mörkt läge Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Resultattabell Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/i18n/recoll_xx.ts0000644000175000017500000037462014444307651014306 00000000000000 ActSearchDLG Menu search AdvSearch All clauses Any clause media other Bad multiplier suffix in size filter text spreadsheet presentation message texts spreadsheets Advanced Search Load next stored search Load previous stored search AdvSearchBase Advanced search Search for <br>documents<br>satisfying: Delete clause Add clause Restrict file types Check this to enable filtering on file types By categories Check this to use file categories instead of raw mime types Save as default Searched file types All ----> Sel -----> <----- Sel <----- All Ignored file types Enter top directory for search Browse Restrict results to files in subtree: Start Search Close All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Invert Minimum size. You can use k/K,m/M,g/G as multipliers Min. Size Maximum size. You can use k/K,m/M,g/G as multipliers Max. Size Filter From To Check this to enable filtering on dates Filter dates Find Check this to enable filtering on sizes Filter sizes Filter birth dates ConfIndexW Can't write configuration file Global parameters Local parameters Search parameters Top directories The list of directories where recursive indexing starts. Default: your home. Skipped paths These are pathnames of directories which indexing will not enter.<br>Path elements may contain wildcards. The entries must match the paths seen by the indexer (e.g.: if topdirs includes '/home/me' and '/home' is actually a link to '/usr/home', a correct skippedPath entry would be '/home/me/tmp*', not '/usr/home/me/tmp*') Stemming languages Log file name The file where the messages will be written.<br>Use 'stderr' for terminal output Log verbosity level This value adjusts the amount of messages,<br>from only errors to a lot of debugging data. Index flush megabytes interval This value adjust the amount of data which is indexed between flushes to disk.<br>This helps control the indexer memory usage. Default 10MB This is the percentage of disk usage - total disk usage, not index size - at which indexing will fail and stop.<br>The default value of 0 removes any limit. No aspell usage Disables use of aspell to generate spelling approximation in the term explorer tool.<br> Useful if aspell is absent or does not work. Aspell language Database directory name The name for a directory where to store the index<br>A non-absolute path is taken relative to the configuration directory. The default is 'xapiandb'. Unac exceptions <p>These are exceptions to the unac mechanism which, by default, removes all diacritics, and performs canonic decomposition. You can override unaccenting for some characters, depending on your language, and specify additional decompositions, e.g. for ligatures. In each space-separated entry, the first character is the source one, and the rest is the translation. Enables indexing Firefox visited pages.<br>(you need also install the Firefox Recoll plugin) Web page store directory name The name for a directory where to store the copies of visited web pages.<br>A non-absolute path is taken relative to the configuration directory. Max. size for the web store (MB) Entries will be recycled once the size is reached.<br>Only increasing the size really makes sense because reducing the value will not truncate an existing file (only waste space at the end). Automatic diacritics sensitivity <p>Automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the <i>D</i> modifier to specify diacritics sensitivity. Automatic character case sensitivity <p>Automatically trigger character case sensitivity if the entry has upper-case characters in any but the first position. Else you need to use the query language and the <i>C</i> modifier to specify character-case sensitivity. Maximum term expansion count <p>Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10 000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. Maximum Xapian clauses count <p>Maximum number of elementary clauses we add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. The languages for which stemming expansion dictionaries will be built.<br>See the Xapian stemmer documentation for possible values. E.g. english, french, german... The language for the aspell dictionary. The values are are 2-letter language codes, e.g. 'en', 'fr' ...<br>If this value is not set, the NLS environment will be used to compute it, which usually works. To get an idea of what is installed on your system, type 'aspell config' and look for .dat files inside the 'data-dir' directory. Indexer log file name If empty, the above log file name value will be used. It may useful to have a separate log for diagnostic purposes because the common log will be erased when<br>the GUI starts up. Web history Process the Web history queue (by default, aspell suggests mispellings when a query has no results). Page recycle interval <p>By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. Note: old pages will be erased to make space for new ones when the maximum size is reached. Current size: %1 Disk full threshold percentage at which we stop indexing<br>(E.g. 90% to stop at 90% full, 0 or 100 means no limit) ConfSubPanelW Only mime types An exclusive list of indexed mime types.<br>Nothing else will be indexed. Normally empty and inactive Exclude mime types Mime types not to be indexed Max. compressed file size (KB) This value sets a threshold beyond which compressedfiles will not be processed. Set to -1 for no limit, to 0 for no decompression ever. Max. text file size (MB) This value sets a threshold beyond which text files will not be processed. Set to -1 for no limit. This is for excluding monster log files from the index. Text file page size (KB) If this value is set (not equal to -1), text files will be split in chunks of this size for indexing. This will help searching very big text files (ie: log files). Max. filter exec. time (s) External filters working longer than this will be aborted. This is for the rare case (ie: postscript) where a document could cause a filter to loop. Set to -1 for no limit. Global CronToolW Cron Dialog <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> Days of week (* or 0-7, 0 or 7 is Sunday) Hours (* or 0-23) Minutes (0-59) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> Enable Disable It seems that manually edited entries exist for recollindex, cannot edit crontab Error installing cron entry. Bad syntax in fields ? EditDialog Dialog EditTrans Source path Local path Config error Original path EditTransBase Path Translations Setting path translations for Select one or several file types, then use the controls in the frame below to change how they are processed Add Delete Cancel Save FirstIdxDialog First indexing setup <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">It appears that the index for this configuration does not exist.</span><br /><br />If you just want to index your home directory with a set of reasonable defaults, press the <span style=" font-style:italic;">Start indexing now</span> button. You will be able to adjust the details later. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you want more control, use the following links to adjust the indexing configuration and schedule.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These tools can be accessed later from the <span style=" font-style:italic;">Preferences</span> menu.</p></body></html> Indexing configuration This will let you adjust the directories you want to index, and other parameters like excluded file paths or names, default character sets, etc. Indexing schedule This will let you chose between batch and real-time indexing, and set up an automatic schedule for batch indexing (using cron). Start indexing now FragButs %1 not found. %1: %2 Query Fragments IdxSchedW Index scheduling setup <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> Cron scheduling The tool will let you decide at what time indexing should run and will install a crontab entry. Real time indexing start up Decide if real time indexing will be started when you log in (only for the default index). ListDialog Dialog GroupBox Main No db directory in configuration "history" file is damaged, please check or remove it: Needs "Show system tray icon" to be set in preferences! Preview Cancel Missing helper program: Can't turn doc into internal representation for Creating preview text Loading preview text into editor &Search for: &Next &Previous Clear Match &Case Form Tab 1 Open Canceled Error loading the document: file missing. Error loading the document: no permission. Error loading: backend not configured. Error loading the document: other handler error<br>Maybe the application is locking the file ? Error loading the document: other handler error. <br>Attempting to display from stored text. Could not fetch stored text Previous result document Next result document Preview Window Close tab Close preview window Show next result Show previous result Print PreviewTextEdit Show fields Show main text Print Print Current Preview Show image Select All Copy Save document to file Fold lines Preserve indentation Open document Reload as Plain Text Reload as HTML QObject <b>Customised subtrees The list of subdirectories in the indexed hierarchy <br>where some parameters need to be redefined. Default: empty. Skipped names These are patterns for file or directory names which should not be indexed. Follow symbolic links Follow symbolic links while indexing. The default is no, to avoid duplicate indexing Index all file names Index the names of files for which the contents cannot be identified or processed (no or unsupported mime type). Default true Default<br>character set Character set used for reading files which do not identify the character set internally, for example pure text files.<br>The default value is empty, and the value from the NLS environnement is used. Ignored endings These are file name endings for files which will be indexed by name only (no MIME type identification attempt, no decompression, no content indexing). <i>The parameters that follow are set either at the top level, if nothing or an empty line is selected in the listbox above, or for the selected subdirectory. You can add or remove directories by clicking the +/- buttons. QWidget Create or choose save directory Choose exactly one directory Could not read directory: Unexpected file name collision, cancelling. Cannot extract document: &Preview &Open Open With Run Script Copy &URL &Write to File Save selection to files Preview P&arent document/folder Find &similar documents Open &Snippets window Show subdocuments / attachments &Open Parent document &Open Parent Folder Copy Text Copy &File Path Copy File Name QxtConfirmationMessage Do not show again. RTIToolW Real time indexing automatic start <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> Start indexing daemon with my desktop session. Also start indexing daemon right now. Replacing: Replacing file Can't create: Warning Could not execute recollindex Deleting: Deleting file Removing autostart Autostart file deleted. Kill current process too ? RclCompleterModel Hits RclMain (no stemming) (all languages) error retrieving stemming languages Indexing in progress: Purge Stemdb Closing Unknown Query results Cannot retrieve document info from database Warning Can't create preview window Cannot extract document or create temporary file Executing: [ About Recoll History data Document history Update &Index Stop &Indexing All media message other presentation spreadsheet text sorted filtered No helpers found missing Missing helper programs No external viewer configured for mime type [ The viewer specified in mimeview for %1: %2 is not found. Do you want to start the preferences dialog ? Can't access file: Can't uncompress file: Save file Result count (est.) Could not open external index. Db not open. Check external indexes list. No results found None Updating Done Monitor Indexing failed Erasing index Reset the index and start from scratch ? Query in progress.<br>Due to limitations of the indexing library,<br>cancelling will exit the program Error Index query error Can't update index: indexer running Indexed MIME Types Bad viewer command line for %1: [%2] Please check the mimeview file Viewer command line for %1 specifies both file and parent file value: unsupported Cannot find parent document External applications/commands needed for your file types and not found, as stored by the last indexing pass in Sub-documents and attachments Document filter The indexer is running so things should improve when it's done. Bad desktop app spec for %1: [%2] Please check the desktop file Indexing interrupted Bad paths Selection patterns need topdir Selection patterns can only be used with a start directory No search No preserved previous search Choose file to save Saved Queries (*.rclq) Write failed Could not write to file Read failed Could not open file: Load error Could not load saved query Disabled because the real time indexer was not compiled in. This configuration tool only works for the main index. Can't set synonyms file (parse error?) The document belongs to an external index which I can't update. Opening a temporary copy. Edits will be lost if you don't save<br/>them to a permanent location. Do not show this warning next time (use GUI preferences to restore). Index locked Unknown indexer state. Can't access webcache file. Indexer is running. Can't access webcache file. with additional message: Non-fatal indexing message: Types list empty: maybe wait for indexing to progress? Tools Results Content has been indexed for these MIME types: Empty or non-existant paths in configuration file. Click Ok to start indexing anyway (absent data will not be purged from the index): Indexing done Can't update index: internal error Index not up to date for this file.<br> <em>Also, it seems that the last index update for the file failed.</em><br/> Click Ok to try to update the index for this file. You will need to run the query again when indexing is done.<br> Click Cancel to return to the list.<br>Click Ignore to show the preview anyway (and remember for this session). There is a risk of showing the wrong entry.<br/> documents document files file errors error total files) No information: initial indexing not yet performed. Batch scheduling The tool will let you decide at what time indexing should run. It uses the Windows task scheduler. Confirm Erasing simple and advanced search history lists, please click Ok to confirm Could not open/create file F&ilter Simple search type Any term All terms File name Query language Stemming language Main Window Clear search Move keyboard focus to search entry Move keyboard focus to search, alt. Toggle tabular display Move keyboard focus to table Flushing Show menu search dialog Duplicates Filter directories Main index open error: . The index may be corrupted. Maybe try to run xapian-check or rebuild the index ?. This search is not active anymore Viewer command line for %1 specifies parent file but URL is not file:// : unsupported RclMainBase Recoll &File &Tools &Preferences &Help E&xit Ctrl+Q Update &index &Erase document history &About Recoll &User manual Document &History Document History &Advanced Search &Sort parameters Sort parameters Term &explorer Term explorer tool Next page Next page of results First page Go to first page of results Previous page Previous page of results External index dialog PgDown PgUp &Full Screen F11 Full Screen &Erase search history Sort by dates from oldest to newest Sort by dates from newest to oldest Show Query Details &Rebuild index Shift+PgUp E&xternal index dialog &Index configuration &GUI configuration &Results Sort by date, oldest first Sort by date, newest first Show as table Show results in a spreadsheet-like table Save as CSV (spreadsheet) file Saves the result into a file which you can load in a spreadsheet Next Page Previous Page First Page Query Fragments With failed files retrying Next update will retry previously failed files Indexing &schedule Enable synonyms Save last query Load saved query Special Indexing Indexing with special options &View Missing &helpers Indexed &MIME types Index &statistics Webcache Editor Trigger incremental pass E&xport simple search history &Query Increase results text font size Increase Font Size Decrease results text font size Decrease Font Size Start real time indexer Query Language Filters Filter dates Assisted complex search Filter birth dates RclTrayIcon Restore Quit RecollModel Abstract Author Document size Document date File size File name File date Keywords Original character set Relevancy rating Title URL Date Date and time Ipath MIME type Can't sort by inverse relevance ResList Result list (show query) Document history <p><b>No results found</b><br> Previous Next Unavailable document Preview Open <p><i>Alternate spellings (accents suppressed): </i> Documents out of at least for <p><i>Alternate spellings: </i> Result count (est.) Query details Snippets This spelling guess was added to the search: These spelling guesses were added to the search: ResTable &Reset sort &Delete column Save table to CSV file Can't open/create file: &Save as CSV Add "%1" column Result Table Preview Open current result document Open current result and quit Show snippets Show header Show vertical header Copy current result text to clipboard Use Shift+click to display the text instead. %1 bytes copied to clipboard Copy result text and quit SSearch Any term All terms File name Query language Bad query string Out of memory Enter file name wildcard expression. Stemming languages for stored query: differ from current preferences (kept) Auto suffixes for stored query: Autophrase is set but it was unset for stored query Autophrase is unset but it was set for stored query Enter search terms here. <html><head><style> table, th, td { border: 1px solid black; border-collapse: collapse; } th,td { text-align: center; </style></head><body> You should really look at the manual (F1)</p> <table border='1' cellspacing='0'> <tr><th>What</th><th>Examples</th> <tr><td>And</td><td>one two&nbsp;&nbsp;&nbsp;one AND two&nbsp;&nbsp;&nbsp;one && two</td></tr> <tr><td>Or</td><td>one OR two&nbsp;&nbsp;&nbsp;one || two</td></tr> <tr><td>Complex boolean. OR has priority, use parentheses&nbsp; where needed</td><td>(one AND two) OR three</td></tr> <tr><td>Not</td><td>-term</td></tr> <tr><td>Phrase</td><td>"pride and prejudice"</td></tr> <tr><td>Ordered proximity (slack=1)</td><td>"pride prejudice"o1</td></tr> <tr><td>Unordered proximity (slack=1)</td><td>"prejudice pride"po1</td></tr> <tr><td>Unordered prox. (default slack=10)</td><td>"prejudice&nbsp;pride"p</td></tr> <tr><td>Field-specific</td><td>author:austen&nbsp;&nbsp;title:prejudice</td></tr> <tr><td>AND inside field (no order)</td><td>author:jane,austen</td></tr> <tr><td>OR inside field</td><td>author:austen/bronte</td></tr> <tr><td>Field names</td><td>title/subject/caption&nbsp;&nbsp;author/from<br>recipient/to&nbsp;&nbsp;filename&nbsp;&nbsp;ext</td></tr> <tr><td>Directory path filter</td><td>dir:/home/me&nbsp;&nbsp;dir:doc</td></tr> <tr><td>MIME type filter</td><td>mime:text/plain mime:video/*</td></tr> <tr><td>Date intervals</td><td>date:2018-01-01/2018-31-12<br> date:2018&nbsp;&nbsp;date:2018-01-01/P12M</td></tr> <tr><td>Size</td><td>size&gt;100k size&lt;1M</td></tr> </table></body></html> Can't open index Could not restore external indexes for stored query:<br> ??? Using current preferences. Simple search History <p>Query language cheat-sheet. In doubt: click <b>Show Query Details</b>.&nbsp; <tr><td>Capitalize to suppress stem expansion</td><td>Floor</td></tr> SSearchBase SSearchBase Clear Erase search entry Search Start query Choose search type. Show query history Main menu SearchClauseW Select the type of query that will be performed with the words Number of additional words that may be interspersed with the chosen ones No field Any All None Phrase Proximity File name Snippets Snippets Find: Next Prev SnippetsW Search <p>Sorry, no exact match was found within limits. Probably the document is very big and the snippets generator got lost in a maze...</p> Sort By Relevance Sort By Page Snippets Window Find Find (alt) Find next Find previous Close window SpecIdxW Special Indexing Else only modified or failed files will be processed. Erase selected files data before indexing. Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Browse Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Selection patterns: Top indexed entity Retry previously failed files. Start directory. Must be part of the indexed tree. Use full indexed area if empty. Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). Diagnostics file SpellBase Term Explorer &Expand Alt+E &Close Alt+C No db info. Match Case Accents SpellW Wildcards Regexp Stem expansion Spelling/Phonetic error retrieving stemming languages No expansion found Term Doc. / Tot. Index: %1 documents, average length %2 terms.%3 results %1 results List was truncated alphabetically, some frequent terms may be missing. Try using a longer root. Show index statistics Number of documents Average terms per document Database directory size MIME types: Item Value Smallest document length (terms) Longest document length (terms) Results from last indexing: Documents created/updated Files tested Unindexed files List files which could not be indexed (slow) Spell expansion error. UIPrefsDialog error retrieving stemming languages The selected directory does not appear to be a Xapian index This is the main/local index! The selected directory is already in the index list Choose Result list paragraph format (erase all to reset to default) Result list header (default is empty) Select recoll config directory or xapian index directory (e.g.: /home/me/.recoll or /home/me/.recoll/xapiandb) The selected directory looks like a Recoll configuration directory but the configuration could not be read At most one index should be selected Default QtWebkit font Any term All terms File name Query language Value from previous program exit Context Description Shortcut Default Choose QSS File Can't add index with different case/diacritics stripping option. ViewAction Command MIME type Desktop Default Changing entries with different current values ViewActionBase Native Viewers Close Select one or several mime types then use the controls in the bottom frame to change how they are processed. Use Desktop preferences by default Select one or several file types, then use the controls in the frame below to change how they are processed Exception to Desktop preferences Action (empty -> recoll default) Apply to current selection Recoll action: current value Select same <b>New Values:</b> Webcache Webcache editor Search regexp TextLabel WebcacheEdit Copy URL Unknown indexer state. Can't edit webcache file. Indexer is running. Can't edit webcache file. Delete selection Webcache was modified, you will need to run the indexer after closing this window. Save to File File creation failed: Maximum size %1 (Index config.). Current size %2. Write position %3. WebcacheModel MIME Date Size URL WinSchedToolW Recoll Batch indexing Start Windows Task Scheduler tool Error Configuration not initialized Could not create batch file <h3>Recoll indexing batch scheduling</h3><p>We use the standard Windows task scheduler for this. The program will be started when you click the button below.</p><p>You can use either the full interface (<i>Create task</i> in the menu on the right), or the simplified <i>Create Basic task</i> wizard. In both cases Copy/Paste the batch file path listed below as the <i>Action</i> to be performed.</p> Command already started confgui::ConfParamFNW Choose confgui::ConfParamSLW + - Add entry Delete selected entries ~ Edit selected entries uiPrefsDialogBase User interface Number of entries in a result page If checked, results with the same content under different names will only be shown once. Hide duplicate results. Result list font Opens a dialog to select the result list font Helvetica-10 Resets the result list font to the system default Reset Texts over this size will not be highlighted in preview (too slow). Choose editor applications Start with advanced search dialog open. Remember sort activation state. Search parameters Stemming language A search for [rolling stones] (2 terms) will be changed to [rolling or stones or (rolling phrase 2 stones)]. This should give higher precedence to the results where the search terms appear exactly as entered. Automatically add phrase to simple searches Do we try to build abstracts for result list entries by using the context of query terms ? May be slow for big documents. Dynamically build abstracts Do we synthetize an abstract even if the document seemed to have one? Replace abstracts from documents Synthetic abstract size (characters) Synthetic abstract context words The words in the list will be automatically turned to ext:xxx clauses in the query language entry. Query language magic file name suffixes. Enable External Indexes Toggle selected Activate All Deactivate All Remove from list. This has no effect on the disk index. Remove selected Add index Apply changes &OK Discard changes &Cancel Abstract snippet separator Choose Resets the style sheet to default Result List Edit result paragraph format string Edit result page html header insert Date format (strftime(3)) Frequency percentage threshold over which we do not use terms inside autophrase. Frequent terms are a major performance issue with phrases. Skipped terms augment the phrase slack, and reduce the autophrase efficiency. The default value is 2 (percent). Autophrase term frequency threshold percentage Plain text to HTML line style Lines in PRE text are not folded. Using BR loses some indentation. PRE + Wrap style may be what you want. <BR> <PRE> <PRE> + wrap Disable Qt autocompletion in search entry. Paths translations Click to add another index directory to the list. You can select either a Recoll configuration directory or a Xapian index. Snippets window CSS file Opens a dialog to select the Snippets window CSS style sheet file Resets the Snippets window style Decide if document filters are shown as radio buttons, toolbar combobox, or menu. Document filter choice style: Buttons Panel Toolbar Combobox Menu Show system tray icon. Close to tray instead of exiting. User style to apply to the snippets window.<br> Note: the result page header insert is also included in the snippets window header. Synonyms file Show warning when opening temporary file. Highlight CSS style for query terms Recoll - User Preferences Set path translations for the selected index or for the main one if no selection exists. Activate links in preview. Make links inside the preview window clickable, and start an external browser when they are clicked. Query terms highlighting in results. <br>Maybe try something like "color:red;background:yellow" for something more lively than the default blue... Start search on completer popup activation. Maximum number of snippets displayed in the snippets window Suppress all beeps. Application Qt style sheet Limit the size of the search history. Use 0 to disable, -1 for unlimited. Maximum size of search history (0: disable, -1: unlimited): Generate desktop notifications. Sort snippets by page number (default: by weight). Misc Display a Snippets link even if the document has no pages (needs restart). Maximum text size highlighted for preview (kilobytes) Start with simple search mode: Shortcuts Hide result table header. Show result table row headers. Reset shortcuts defaults Use F1 to access the manual Hide some user interface elements. Hide: Toolbars Status bar Show button instead. Menu bar Show choice in menu only. Simple search type Clear/Search buttons Disable the Ctrl+[0-9]/Shift+[a-z] shortcuts for jumping to table rows. None (default) Uses the default dark mode style sheet Dark mode Choose QSS File To display document text instead of metadata in result table detail area, use: left mouse click Shift+click Opens a dialog to select the style sheet file.<br>Look at /usr/share/recoll/examples/recoll[-dark].qss for an example. Result Table Do not display metadata when hovering over rows. Work around Tamil QTBUG-78923 by inserting space before anchor text The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. Depth of side filter directory tree Zoom factor for the user interface. Useful if the default is not right for your screen resolution. Display scale (default 1.0): Automatic spelling approximation. Max spelling distance Add common spelling approximations for rare terms. Maximum number of history entries in completer list Number of history entries in completer: Displays the total number of occurences of the term in the index Show hit counts in completer popup. Prefer HTML to plain text for preview. recoll-1.36.1/qtgui/rtitool.h0000644000175000017500000000243514410615043013003 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RTITOOL_W_H_INCLUDED_ #define _RTITOOL_W_H_INCLUDED_ #include "ui_rtitool.h" class QPushButton; class RTIToolW : public QDialog, public Ui::RTIToolW { Q_OBJECT public: RTIToolW(QWidget * parent = 0) : QDialog(parent) { setupUi(this); init(); } public slots: #ifdef _WIN32 void sesclicked(bool) {} void accept() {} private: void init() {} #else void sesclicked(bool); void accept(); private: void init(); #endif }; #endif /* _RTITOOL_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/widgets/0000755000175000017500000000000014521161751012665 500000000000000recoll-1.36.1/qtgui/widgets/listdialog.ui0000644000175000017500000000337214410615043015277 00000000000000 ListDialog 0 0 400 300 Dialog GroupBox Qt::Horizontal QDialogButtonBox::Ok true buttonBox accepted() ListDialog accept() 248 254 157 274 buttonBox rejected() ListDialog reject() 316 260 286 274 recoll-1.36.1/qtgui/widgets/qxtglobal.h0000644000175000017500000001565114410615043014756 00000000000000 /**************************************************************************** ** Copyright (c) 2006 - 2011, the LibQxt project. ** See the Qxt AUTHORS file for a list of authors and copyright holders. ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** * Neither the name of the LibQxt project nor the ** names of its contributors may be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** ** *****************************************************************************/ #ifndef QXTGLOBAL_H #define QXTGLOBAL_H #include #define QXT_VERSION 0x000700 #define QXT_VERSION_STR "0.7.0" //--------------------------global macros------------------------------ #ifndef QXT_NO_MACROS #ifndef _countof #define _countof(x) (sizeof(x)/sizeof(*x)) #endif #endif // QXT_NO_MACROS //--------------------------export macros------------------------------ #define QXT_STATIC #define QXT_DLLEXPORT DO_NOT_USE_THIS_ANYMORE #if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN) # if defined(BUILD_QXT_CORE) # define QXT_CORE_EXPORT Q_DECL_EXPORT # else # define QXT_CORE_EXPORT Q_DECL_IMPORT # endif #else # define QXT_CORE_EXPORT #endif // BUILD_QXT_CORE #if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN) # if defined(BUILD_QXT_GUI) # define QXT_GUI_EXPORT Q_DECL_EXPORT # else # define QXT_GUI_EXPORT Q_DECL_IMPORT # endif #else # define QXT_GUI_EXPORT #endif // BUILD_QXT_GUI #if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN) # if defined(BUILD_QXT_NETWORK) # define QXT_NETWORK_EXPORT Q_DECL_EXPORT # else # define QXT_NETWORK_EXPORT Q_DECL_IMPORT # endif #else # define QXT_NETWORK_EXPORT #endif // BUILD_QXT_NETWORK #if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN) # if defined(BUILD_QXT_SQL) # define QXT_SQL_EXPORT Q_DECL_EXPORT # else # define QXT_SQL_EXPORT Q_DECL_IMPORT # endif #else # define QXT_SQL_EXPORT #endif // BUILD_QXT_SQL #if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN) # if defined(BUILD_QXT_WEB) # define QXT_WEB_EXPORT Q_DECL_EXPORT # else # define QXT_WEB_EXPORT Q_DECL_IMPORT # endif #else # define QXT_WEB_EXPORT #endif // BUILD_QXT_WEB #if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN) # if defined(BUILD_QXT_BERKELEY) # define QXT_BERKELEY_EXPORT Q_DECL_EXPORT # else # define QXT_BERKELEY_EXPORT Q_DECL_IMPORT # endif #else # define QXT_BERKELEY_EXPORT #endif // BUILD_QXT_BERKELEY #if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN) # if defined(BUILD_QXT_ZEROCONF) # define QXT_ZEROCONF_EXPORT Q_DECL_EXPORT # else # define QXT_ZEROCONF_EXPORT Q_DECL_IMPORT # endif #else # define QXT_ZEROCONF_EXPORT #endif // QXT_ZEROCONF_EXPORT #if defined(BUILD_QXT_CORE) || defined(BUILD_QXT_GUI) || defined(BUILD_QXT_SQL) || defined(BUILD_QXT_NETWORK) || defined(BUILD_QXT_WEB) || defined(BUILD_QXT_BERKELEY) || defined(BUILD_QXT_ZEROCONF) # define BUILD_QXT #endif QXT_CORE_EXPORT const char* qxtVersion(); #ifndef QT_BEGIN_NAMESPACE #define QT_BEGIN_NAMESPACE #endif #ifndef QT_END_NAMESPACE #define QT_END_NAMESPACE #endif #ifndef QT_FORWARD_DECLARE_CLASS #define QT_FORWARD_DECLARE_CLASS(Class) class Class; #endif /**************************************************************************** ** This file is derived from code bearing the following notice: ** The sole author of this file, Adam Higerd, has explicitly disclaimed all ** copyright interest and protection for the content within. This file has ** been placed in the public domain according to United States copyright ** statute and case law. In jurisdictions where this public domain dedication ** is not legally recognized, anyone who receives a copy of this file is ** permitted to use, modify, duplicate, and redistribute this file, in whole ** or in part, with no restrictions or conditions. In these jurisdictions, ** this file shall be copyright (C) 2006-2008 by Adam Higerd. ****************************************************************************/ #define QXT_DECLARE_PRIVATE(PUB) friend class PUB##Private; QxtPrivateInterface qxt_d; #define QXT_DECLARE_PUBLIC(PUB) friend class PUB; #define QXT_INIT_PRIVATE(PUB) qxt_d.setPublic(this); #define QXT_D(PUB) PUB##Private& d = qxt_d() #define QXT_P(PUB) PUB& p = qxt_p() template class QxtPrivate { public: virtual ~QxtPrivate() {} inline void QXT_setPublic(PUB* pub) { qxt_p_ptr = pub; } protected: inline PUB& qxt_p() { return *qxt_p_ptr; } inline const PUB& qxt_p() const { return *qxt_p_ptr; } inline PUB* qxt_ptr() { return qxt_p_ptr; } inline const PUB* qxt_ptr() const { return qxt_p_ptr; } private: PUB* qxt_p_ptr; }; template class QxtPrivateInterface { friend class QxtPrivate; public: QxtPrivateInterface() { pvt = new PVT; } ~QxtPrivateInterface() { delete pvt; } inline void setPublic(PUB* pub) { pvt->QXT_setPublic(pub); } inline PVT& operator()() { return *static_cast(pvt); } inline const PVT& operator()() const { return *static_cast(pvt); } inline PVT * operator->() { return static_cast(pvt); } inline const PVT * operator->() const { return static_cast(pvt); } private: QxtPrivateInterface(const QxtPrivateInterface&) { } QxtPrivateInterface& operator=(const QxtPrivateInterface&) { } QxtPrivate* pvt; }; #endif // QXT_GLOBAL recoll-1.36.1/qtgui/widgets/editdialog.ui0000644000175000017500000000271514410615043015251 00000000000000 EditDialog 0 0 614 509 Dialog Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() Dialog accept() 248 254 157 274 buttonBox rejected() Dialog reject() 316 260 286 274 recoll-1.36.1/qtgui/widgets/editdialog.h0000644000175000017500000000202314410615043015053 00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef EDITDIALOG_H #define EDITDIALOG_H #include #include "ui_editdialog.h" class EditDialog : public QDialog, public Ui::EditDialog { Q_OBJECT public: EditDialog(QWidget * parent = 0) : QDialog(parent) { setupUi(this); } }; #endif // EDITDIALOG_H recoll-1.36.1/qtgui/widgets/qxtconfirmationmessage.cpp0000644000175000017500000002473314410615043020107 00000000000000#include "qxtconfirmationmessage.h" /**************************************************************************** * Copyright (c) 2006 - 2011, the LibQxt project. * See the Qxt AUTHORS file for a list of authors and copyright holders. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the LibQxt project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ****************************************************************************/ #include #include #include #include #include #include class QxtConfirmationMessagePrivate : public QxtPrivate { public: QXT_DECLARE_PUBLIC(QxtConfirmationMessage) void init(const QString& message = QString()); QString key() const; int showAgain(); void doNotShowAgain(int result); void reset(); bool remember; QCheckBox* confirm; QString overrideKey; }; void QxtConfirmationMessagePrivate::init(const QString& message) { remember = false; confirm = new QCheckBox(&qxt_p()); if (!message.isNull()) confirm->setText(message); else confirm->setText(QxtConfirmationMessage::tr("Do not show again.")); QGridLayout* grid = qobject_cast(qxt_p().layout()); QDialogButtonBox* buttons = qxt_p().findChild(); if (grid && buttons) { const int idx = grid->indexOf(buttons); int row, column, rowSpan, columnSpan = 0; grid->getItemPosition(idx, &row, &column, &rowSpan, &columnSpan); QLayoutItem* buttonsItem = grid->takeAt(idx); grid->addWidget(confirm, row, column, rowSpan, columnSpan, Qt::AlignLeft | Qt::AlignTop); grid->addItem(buttonsItem, ++row, column, rowSpan, columnSpan); } } QString QxtConfirmationMessagePrivate::key() const { QString value = overrideKey; if (value.isEmpty()) { const QString all = qxt_p().windowTitle() + qxt_p().text() + qxt_p().informativeText(); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) value = QString::number(qChecksum(all.toLocal8Bit())); #else const QByteArray data = all.toLocal8Bit(); value = QString::number(qChecksum(data.constData(), data.length())); #endif } return value; } int QxtConfirmationMessagePrivate::showAgain() { QSettings settings; return settings.value(key(), -1).toInt(); } void QxtConfirmationMessagePrivate::doNotShowAgain(int result) { QSettings settings; settings.setValue(key(), result); } void QxtConfirmationMessagePrivate::reset() { QSettings settings; settings.remove(key()); } /*! \class QxtConfirmationMessage \inmodule QxtWidgets \brief The QxtConfirmationMessage class provides a confirmation message. QxtConfirmationMessage is a confirmation message with checkable \bold {"Do not show again."} option. A checked and accepted confirmation message is no more shown until reseted. Example usage: \code void MainWindow::closeEvent(QCloseEvent* event) { static const QString text(tr("Are you sure you want to quit?")); if (QxtConfirmationMessage::confirm(this, tr("Confirm"), text) == QMessageBox::No) event->ignore(); } \endcode \image qxtconfirmationmessage.png "QxtConfirmationMessage in action." \bold {Note:} QCoreApplication::organizationName and QCoreApplication::applicationName are used for storing settings. In case these properties are empty, \bold "QxtWidgets" and \bold "QxtConfirmationMessage" are used, respectively. */ /*! Constructs a new QxtConfirmationMessage with \a parent. */ QxtConfirmationMessage::QxtConfirmationMessage(QWidget* parent) : QMessageBox(parent) { QXT_INIT_PRIVATE(QxtConfirmationMessage); qxt_d().init(); connect(this, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(onButtonClicked(QAbstractButton*))); } /*! Constructs a new QxtConfirmationMessage with \a icon, \a title, \a text, \a confirmation, \a buttons, \a parent and \a flags. */ QxtConfirmationMessage::QxtConfirmationMessage( QMessageBox::Icon icon, const QString& title, const QString& text, const QString& confirmation, QMessageBox::StandardButtons buttons, QWidget* parent, Qt::WindowFlags flags) : QMessageBox(icon, title, text, buttons, parent, flags) { QXT_INIT_PRIVATE(QxtConfirmationMessage); qxt_d().init(confirmation); connect(this, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(onButtonClicked(QAbstractButton*))); } /*! Destructs the confirmation message. */ QxtConfirmationMessage::~QxtConfirmationMessage() { } /*! Opens an confirmation message box with the specified \a title, \a text and \a confirmation. The standard \a buttons are added to the message box. \a defaultButton specifies the button used when Enter is pressed. \a defaultButton must refer to a button that was given in \a buttons. If \a defaultButton is QMessageBox::NoButton, QMessageBox chooses a suitable default automatically. Returns the identity of the standard button that was clicked. If Esc was pressed instead, the escape button is returned. If \a parent is \c 0, the message box is an application modal dialog box. If \a parent is a widget, the message box is window modal relative to \a parent. */ QMessageBox::StandardButton QxtConfirmationMessage::confirm( QWidget* parent, const QString& title, const QString& text, const QString& confirmation, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { QxtConfirmationMessage msgBox(QMessageBox::NoIcon, title, text, confirmation, QMessageBox::NoButton, parent); QDialogButtonBox* buttonBox = msgBox.findChild(); Q_ASSERT(buttonBox != 0); uint mask = QMessageBox::FirstButton; while (mask <= QMessageBox::LastButton) { uint sb = buttons & mask; mask <<= 1; if (!sb) continue; QPushButton* button = msgBox.addButton((QMessageBox::StandardButton)sb); // Choose the first accept role as the default if (msgBox.defaultButton()) continue; if ((defaultButton == QMessageBox::NoButton && buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) || (defaultButton != QMessageBox::NoButton && sb == uint(defaultButton))) msgBox.setDefaultButton(button); } if (msgBox.exec() == -1) return QMessageBox::Cancel; return msgBox.standardButton(msgBox.clickedButton()); } /*! \property QxtConfirmationMessage::confirmationText \brief the confirmation text The default value is \bold {"Do not show again."} */ QString QxtConfirmationMessage::confirmationText() const { return qxt_d().confirm->text(); } void QxtConfirmationMessage::setConfirmationText(const QString& confirmation) { qxt_d().confirm->setText(confirmation); } /*! \property QxtConfirmationMessage::overrideSettingsKey \brief the override key used for settings When no \bold overrideSettingsKey has been set, the key is calculated with qChecksum() based on title, text and confirmation message. The default value is an empty string. */ QString QxtConfirmationMessage::overrideSettingsKey() const { return qxt_d().overrideKey; } void QxtConfirmationMessage::setOverrideSettingsKey(const QString& key) { qxt_d().overrideKey = key; } /*! \property QxtConfirmationMessage::rememberOnReject \brief whether \bold {"Do not show again."} option is stored even if the message box is rejected (eg. user presses Cancel). The default value is \c false. */ bool QxtConfirmationMessage::rememberOnReject() const { return qxt_d().remember; } void QxtConfirmationMessage::setRememberOnReject(bool remember) { qxt_d().remember = remember; } /*! Shows the confirmation message if necessary. The confirmation message is not shown in case \bold {"Do not show again."} has been checked while the same confirmation message was earlierly accepted. A confirmation message is identified by the combination of title, QMessageBox::text and optional QMessageBox::informativeText. A clicked button with role QDialogButtonBox::AcceptRole or QDialogButtonBox::YesRole is considered as "accepted". \warning This function does not reimplement but shadows QMessageBox::exec(). \sa QWidget::windowTitle, QMessageBox::text, QMessageBox::informativeText */ int QxtConfirmationMessage::exec() { int res = qxt_d().showAgain(); if (res == -1) res = QMessageBox::exec(); return res; } void QxtConfirmationMessage::onButtonClicked(QAbstractButton *button) { QDialogButtonBox* buttons = this->findChild(); Q_ASSERT(buttons != 0); int role = buttons->buttonRole(button); if (qxt_d().confirm->isChecked() && (qxt_d().remember || role != QDialogButtonBox::RejectRole)) { qxt_d().doNotShowAgain(0); } } /*! Resets this instance of QxtConfirmationMessage. A reseted confirmation message is shown again until user checks \bold {"Do not show again."} and accepts the confirmation message. */ void QxtConfirmationMessage::reset() { qxt_d().reset(); } recoll-1.36.1/qtgui/widgets/qxtconfirmationmessage.h0000644000175000017500000000676314410615043017557 00000000000000#ifndef QXTCONFIRMATIONMESSAGE_H /**************************************************************************** * Copyright (c) 2006 - 2011, the LibQxt project. * See the Qxt AUTHORS file for a list of authors and copyright holders. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the LibQxt project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * *****************************************************************************/ #define QXTCONFIRMATIONMESSAGE_H #include #include "qxtglobal.h" class QxtConfirmationMessagePrivate; class QXT_GUI_EXPORT QxtConfirmationMessage : public QMessageBox { Q_OBJECT QXT_DECLARE_PRIVATE(QxtConfirmationMessage) Q_PROPERTY(QString confirmationText READ confirmationText WRITE setConfirmationText) Q_PROPERTY(QString overrideSettingsKey READ overrideSettingsKey WRITE setOverrideSettingsKey) Q_PROPERTY(bool rememberOnReject READ rememberOnReject WRITE setRememberOnReject) public: explicit QxtConfirmationMessage(QWidget* parent = 0); virtual ~QxtConfirmationMessage(); QxtConfirmationMessage( QMessageBox::Icon icon, const QString& title, const QString& text, const QString& confirmation = QString(), QMessageBox::StandardButtons buttons = QMessageBox::NoButton, QWidget* parent = 0, Qt::WindowFlags flags = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); static QMessageBox::StandardButton confirm( QWidget* parent, const QString& title, const QString& text, const QString& confirmation = QString(), QMessageBox::StandardButtons buttons= QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); QString confirmationText() const; void setConfirmationText(const QString& confirmation); QString overrideSettingsKey() const; void setOverrideSettingsKey(const QString& key); bool rememberOnReject() const; void setRememberOnReject(bool remember); public Q_SLOTS: int exec(); void reset(); void onButtonClicked(QAbstractButton *button); }; #endif // QXTCONFIRMATIONMESSAGE_H recoll-1.36.1/qtgui/widgets/listdialog.h0000644000175000017500000000177614410615043015117 00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef LISTDIALOG_H #define LISTDIALOG_H #include "ui_listdialog.h" class ListDialog : public QDialog, public Ui::ListDialog { Q_OBJECT public: ListDialog(QWidget * parent = 0) : QDialog(parent) { setupUi(this); } }; #endif // LISTDIALOG_H recoll-1.36.1/qtgui/winschedtool.cpp0000644000175000017500000000645514427373216014365 00000000000000/* Copyright (C) 2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "winschedtool.h" #include #include #include #include #include #include "recoll.h" #include "smallut.h" #include "rclutil.h" #include "log.h" #include "execmd.h" #include "pathut.h" using namespace std; void WinSchedToolW::init() { if (!theconfig) { QMessageBox::warning(0, tr("Error"), tr("Configuration not initialized")); return; } connect(startPB, SIGNAL(clicked()), this, SLOT(startWinScheduler())); // Use a short path on Windows if possible to avoid issues with // accented characters string confdir = path_shortpath(theconfig->getConfDir()); // path_thisexecpath() returns the directory string recollindex = path_cat(path_thisexecpath(), "recollindex.exe"); LOGDEB("WinSchedTool: recollindex: " << recollindex << endl); string batchfile = path_cat(confdir, "winsched.bat"); LOGDEB("WinSchedTool: batch file " << batchfile << endl); if (!path_exists(batchfile)) { std::fstream fp; if (path_streamopen(batchfile, ios::out|ios::trunc, fp)) { fp << "\"" << recollindex << "\" -c \"" << confdir << "\"\n"; fp.close(); } else { QMessageBox::warning(0, tr("Error"), tr("Could not create batch file")); return; } } QString blurb = tr( "

Recoll indexing batch scheduling

" "

We use the standard Windows task scheduler for this. The program " "will be started when you click the button below.

" "

You can use either the full interface " "(Create task in the menu on the right), or the simplified " "Create Basic task wizard. In both cases Copy/Paste the " "batch file path listed below as the Action to be performed." "

" ); blurb.append("

").append(u8s2qs(batchfile)).append("

"); explainLBL->setText(blurb); explainLBL->setTextInteractionFlags(Qt::TextSelectableByMouse); } void WinSchedToolW::startWinScheduler() { if (m_cmd) { int status; if (m_cmd->maybereap(&status)) { delete m_cmd; } else { QMessageBox::warning(0, "Recoll", tr("Command already started")); return; } } m_cmd = new ExecCmd(); vector lcmd{"c:/windows/system32/taskschd.msc"}; m_cmd->startExec("rclstartw", lcmd, false, false); } recoll-1.36.1/qtgui/restable.h0000644000175000017500000001722014515655616013126 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RESTABLE_H_INCLUDED_ #define _RESTABLE_H_INCLUDED_ #include "autoconfig.h" #include #include #include #include #include #include #include #include "ui_restable.h" #include "docseq.h" class ResTable; typedef std::string (FieldGetter)( const std::string& fldname, const Rcl::Doc& doc); class RecollModel : public QAbstractTableModel { Q_OBJECT public: RecollModel(const QStringList fields, ResTable *tb, QObject *parent = 0); // Reimplemented methods virtual int rowCount (const QModelIndex& = QModelIndex()) const; virtual int columnCount(const QModelIndex& = QModelIndex()) const; virtual QVariant headerData (int col, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual void saveAsCSV(std::fstream& fp); virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); // Specific methods virtual void readDocSource(); virtual void setDocSource(std::shared_ptr nsource); virtual std::shared_ptr getDocSource() {return m_source;} virtual void deleteColumn(int); virtual const std::vector& getFields() {return m_fields;} static const std::map& getAllFields() { return o_displayableFields; } static QString displayableField(const std::string& in); virtual void addColumn(int, const std::string&); // Some column name are aliases/translator for base document field // (ie: date, datetime->mtime). Help deal with this: virtual std::string baseField(const std::string&); // Ignore sort() call because virtual void setIgnoreSort(bool onoff) {m_ignoreSort = onoff;} friend class ResTable; signals: void sortDataChanged(DocSeqSortSpec); private: ResTable *m_table{0}; mutable std::shared_ptr m_source; std::vector m_fields; std::vector m_getters; static std::map o_displayableFields; bool m_ignoreSort; FieldGetter* chooseGetter(const std::string&); HighlightData m_hdata; // Things we cache because we are repeatedly asked for the same. mutable QFont m_cachedfont; mutable int m_reslfntszforcached{-1234}; mutable Rcl::Doc m_cachedoc; mutable int m_rowforcachedoc{-1}; }; class ResTable; // Modified textBrowser for the detail area class ResTableDetailArea : public QTextBrowser { Q_OBJECT public: ResTableDetailArea(ResTable* parent = 0); public slots: virtual void createPopupMenu(const QPoint& pos); virtual void setFont(); virtual void init(); private: ResTable *m_table; }; class ResTablePager; class QUrl; class RclMain; class QShortcut; // This is an intermediary object to help setting up multiple similar // shortcuts with different data (e.g. Ctrl+1, Ctrl+2 etc.). Maybe // there is another way, but this one works. class SCData : public QObject { Q_OBJECT public: SCData(QObject* parent, std::function cb, int row) : QObject(parent), m_cb(cb), m_row(row) {} public slots: virtual void activate() { m_cb(m_row); } private: std::function m_cb; int m_row; }; class ResTable : public QWidget, public Ui::ResTable { Q_OBJECT public: ResTable(QWidget* parent = 0, QStringList fields = QStringList()) : QWidget(parent) { setupUi(this); init(fields); } virtual ~ResTable() {} ResTable(const ResTable&) = delete; ResTable& operator=(const ResTable&) = delete; virtual RecollModel *getModel() {return m_model;} virtual ResTableDetailArea* getDetailArea() {return m_detail;} virtual int getDetailDocNumOrTopRow(); void setRclMain(RclMain *m, bool ismain); void setDefRowHeight(); int fontsize(); public slots: virtual void onTableView_currentChanged(const QModelIndex&); virtual void on_tableView_entered(const QModelIndex& index); virtual void setDocSource(std::shared_ptr nsource); virtual void saveColState(); virtual void resetSource(); virtual void readDocSource(bool resetPos = true); virtual void onSortDataChanged(DocSeqSortSpec); virtual void createPopupMenu(const QPoint& pos); virtual void onClicked(const QModelIndex&); virtual void onDoubleClick(const QModelIndex&); virtual void menuPreview(); virtual void menuSaveToFile(); virtual void menuSaveSelection(); virtual void menuEdit(); virtual void menuEditAndQuit(); virtual void menuOpenWith(QAction *); virtual void menuCopyFN(); virtual void menuCopyPath(); virtual void menuCopyURL(); virtual void menuCopyText(); virtual void menuCopyTextAndQuit(); virtual void menuExpand(); virtual void menuPreviewParent(); virtual void menuOpenParent(); virtual void menuOpenFolder(); virtual void menuShowSnippets(); virtual void menuShowSubDocs(); virtual void createHeaderPopupMenu(const QPoint&); virtual void deleteColumn(); virtual void addColumn(); virtual void resetSort(); // Revert to natural (relevance) order virtual void saveAsCSV(); virtual void linkWasClicked(const QUrl&); virtual void makeRowVisible(int row); virtual void takeFocus(); virtual void onUiPrefsChanged(); virtual void onNewShortcuts(); virtual void setCurrentRowFromKbd(int row); virtual void toggleHeader(); virtual void toggleVHeader(); signals: void docPreviewClicked(int, Rcl::Doc, int); void docSaveToFileClicked(Rcl::Doc); void previewRequested(Rcl::Doc); void editRequested(Rcl::Doc); void openWithRequested(Rcl::Doc, std::string cmd); void headerClicked(); void docExpand(Rcl::Doc); void showSubDocs(Rcl::Doc); void showSnippets(Rcl::Doc); void detailDocChanged(Rcl::Doc, std::shared_ptr); friend class ResTablePager; friend class ResTableDetailArea; protected: bool eventFilter(QObject* obj, QEvent* event); private: void init(QStringList fields); RecollModel *m_model{nullptr}; ResTablePager *m_pager{nullptr}; ResTableDetailArea *m_detail{nullptr}; int m_detaildocnum{-1}; Rcl::Doc m_detaildoc; int m_popcolumn{0}; RclMain *m_rclmain{nullptr}; bool m_ismainres{true}; bool m_rowchangefromkbd{false}; QShortcut *m_opensc{nullptr}; QShortcut *m_openquitsc{nullptr}; QShortcut *m_previewsc{nullptr}; QShortcut *m_showsnipssc{nullptr}; QShortcut *m_showheadersc{nullptr}; QShortcut *m_showvheadersc{nullptr}; QShortcut *m_copycurtextsc{nullptr}; QShortcut *m_copycurtextquitsc{nullptr}; std::vector m_rowlinks; std::vector m_rowsc; }; #endif /* _RESTABLE_H_INCLUDED_ */ recoll-1.36.1/qtgui/rtitool.ui0000644000175000017500000000754514410615043013200 00000000000000 RTIToolW 0 0 423 207 Real time indexing automatic start <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can be set up to run as a daemon, updating the index as files change, in real time. You gain an always up to date index, but system resources are used permanently.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> true Start indexing daemon with my desktop session. Qt::Horizontal 28 20 false 1 0 Also start indexing daemon right now. Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() RTIToolW accept() 204 159 204 90 buttonBox rejected() RTIToolW reject() 204 159 204 90 recoll-1.36.1/qtgui/preview_w.h0000644000175000017500000001513114427373216013326 00000000000000/* Copyright (C) 2006-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _PREVIEW_W_H_INCLUDED_ #define _PREVIEW_W_H_INCLUDED_ #include "autoconfig.h" // Always use a qtextbrowser for now, there is no compelling reason to // switch to webkit here #if 1 || defined(RESLIST_TEXTBROWSER) #define PREVIEW_TEXTBROWSER #endif #include #include #include #include #include #include #ifdef PREVIEW_TEXTBROWSER #include #define PREVIEW_PARENTCLASS QTextBrowser #else #include #define PREVIEW_PARENTCLASS QWebView #endif #include #include "rcldb.h" #include "rclmain_w.h" #include "internfile.h" #include "ui_preview.h" class QTabWidget; class QLabel; class QPushButton; class QCheckBox; class Preview; class PlainToRichQtPreview; class QUrl; class RclMain; class LoadThread; class QTimer; class QEventLoop; class QProgressDialog; class PreviewTextEdit : public PREVIEW_PARENTCLASS { Q_OBJECT public: PreviewTextEdit(QWidget* parent, const char* name, Preview *pv); void moveToAnchor(const QString& name); enum DspType {PTE_DSPTXT, PTE_DSPFLDS, PTE_DSPIMG}; public slots: virtual void displayFields(); virtual void displayText(); virtual void displayImage(); virtual void print(); virtual void createPopupMenu(const QPoint& pos); void onAnchorClicked(const QUrl& url); void reloadAsPlainText(); void reloadAsHTML(); friend class Preview; protected: void mouseDoubleClickEvent(QMouseEvent *); private: Preview *m_preview; std::shared_ptr m_plaintorich; bool m_dspflds; std::string m_url; // filename for this tab std::string m_ipath; // Internal doc path inside file int m_docnum; // Index of doc in db search results. // doc out of internfile (previous fields come from the index) with // main text erased (for space). Rcl::Doc m_fdoc; // The input doc out of the index/query list Rcl::Doc m_dbdoc; // Saved rich (or plain actually) text: the textedit seems to // sometimes (but not always) return its text stripped of tags, so // this is needed (for printing for example) QString m_richtxt; Qt::TextFormat m_format; // Temporary file name (possibly, if displaying image). The // TempFile itself is kept inside main.cpp (because that's where // signal cleanup happens), but we use its name to ask for release // when the tab is closed. std::string m_tmpfilename; QImage m_image; DspType m_curdsp; }; class QShortcut; class Preview : public QDialog, public Ui::Preview { Q_OBJECT public: Preview(RclMain *m, int sid, // Search Id const HighlightData& hdata) // Search terms etc. for highlighting : m_rclmain(m), m_searchId(sid), m_hData(hdata) { setupUi(this); init(); } virtual void closeEvent(QCloseEvent *e); virtual bool eventFilter(QObject *target, QEvent *event); /** * Arrange for the document to be displayed either by exposing the tab * if already loaded, or by creating a new tab and loading it. * @para docnum is used to link back to the result list (to highlight * paragraph when tab exposed etc. */ virtual bool makeDocCurrent(const Rcl::Doc& idoc, int docnum, bool sametab = false); void emitWordSelect(QString); friend class PreviewTextEdit; /** List shortcuts so that the prefs can be edited before any preview is created */ static void listShortcuts(); public slots: // Search stuff virtual void searchTextChanged(const QString& text); virtual void doSearch(const QString& str, bool next, bool reverse, bool wo = false); virtual void nextPressed(); virtual void prevPressed(); // Tabs management virtual void currentChanged(int); virtual void closeCurrentTab(); virtual void closeTab(int index); virtual void emitShowNext(); virtual void emitShowPrev(); virtual void emitSaveDocToFile(); virtual void emitEditRequested(); virtual void togglePlainPre(); virtual void onNewShortcuts(); // Other virtual void zoomIn(); virtual void zoomOut(); signals: void previewClosed(Preview *); void wordSelect(QString); void showNext(Preview *w, int sid, int docnum); void showPrev(Preview *w, int sid, int docnum); void previewExposed(Preview *w, int sid, int docnum); void printCurrentPreviewRequest(); void saveDocToFile(Rcl::Doc); void editRequested(Rcl::Doc); private: RclMain *m_rclmain; // Identifier of search in main window. This is used to check that // we make sense when requesting the next document when browsing // successive search results in a tab. int m_searchId; bool m_dynSearchActive{false}; // Index value the search text comes from. -1 if text was edited int m_searchTextFromIndex{0}; bool m_canBeep{true}; bool m_loading{false}; HighlightData m_hData; bool m_justCreated{true}; // First tab create is different QFont m_font; QShortcut *m_closewinsc{nullptr}; QShortcut *m_nextdocsc{nullptr}; QShortcut *m_prevdocsc{nullptr}; QShortcut *m_closetabsc{nullptr}; QShortcut *m_printtabsc{nullptr}; void init(); virtual void setCurTabProps(const Rcl::Doc& doc, int docnum); virtual PreviewTextEdit *editor(int); virtual PreviewTextEdit *currentEditor(); virtual PreviewTextEdit *addEditorTab(); virtual bool loadDocInCurrentTab(const Rcl::Doc& idoc, int dnm); void displayLoadError( FileInterner::ErrorPossibleCause explain, bool canGetRawText); bool runLoadThread(LoadThread& lthr, QTimer& tT, QEventLoop& loop, QProgressDialog& progress, bool canGetRawText); }; #endif /* _PREVIEW_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/rclzg.cpp0000644000175000017500000000517314410615043012765 00000000000000/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef USE_ZEITGEIST #include "autoconfig.h" #include "rclzg.h" #include "log.h" #include "pathut.h" #include #include #include #include #include #include #include // Can't see no reason why our logger couldn' static QtZeitgeist::Log zglogger; void zg_send_event(ZgSendType, const Rcl::Doc& doc) { static int needinit = 1; if (needinit) { QtZeitgeist::init(); needinit = 0; } // The subject is about the document QtZeitgeist::DataModel::Subject subject; subject.setUri(QString::fromLocal8Bit(doc.url.c_str())); // TODO: refine these subject.setInterpretation(QtZeitgeist::Interpretation::Subject::NFODocument); if (doc.ipath.empty()) subject.setManifestation(QtZeitgeist::Manifestation::Subject::NFOFileDataObject); else subject.setManifestation(QtZeitgeist::Manifestation::Subject::NFOEmbeddedFileDataObject); subject.setOrigin(QString::fromLocal8Bit(path_getfather(doc.url).c_str())); subject.setMimeType(doc.mimetype.c_str()); string titleOrFilename; doc.getmeta(Rcl::Doc::keytt, &titleOrFilename); if (titleOrFilename.empty()) { doc.getmeta(Rcl::Doc::keyfn, &titleOrFilename); } subject.setText(QString::fromUtf8(titleOrFilename.c_str())); QtZeitgeist::DataModel::Event event; event.setTimestamp(QDateTime::currentDateTime()); event.addSubject(subject); event.setInterpretation(QtZeitgeist::Interpretation::Event::ZGAccessEvent); event.setManifestation(QtZeitgeist::Manifestation::Event::ZGUserActivity); event.setActor("app://recoll.desktop"); QtZeitgeist::DataModel::EventList events; events.push_back(event); LOGDEB("zg_send_event, sending for " << (doc.mimetype) << " " << (doc.url) << "\n" ); zglogger.insertEvents(events); } #endif recoll-1.36.1/qtgui/rclmain_w.cpp0000644000175000017500000013360014427373216013627 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "recoll.h" #include "log.h" #include "mimehandler.h" #include "pathut.h" #include "smallut.h" #include "advsearch_w.h" #include "sortseq.h" #include "uiprefs_w.h" #include "guiutils.h" #include "reslist.h" #include "ssearch_w.h" #include "internfile.h" #include "docseqdb.h" #include "docseqhist.h" #include "docseqdocs.h" #include "restable.h" #include "firstidx.h" #include "indexer.h" #include "rclzg.h" #include "snippets_w.h" #include "fragbuts.h" #include "systray.h" #include "rclmain_w.h" #include "rclhelp.h" #include "readfile.h" #include "moc_rclmain_w.cpp" #include "scbase.h" #include "idxmodel.h" #include "cstr.h" using std::string; using std::vector; using std::map; using std::list; QString g_stringAllStem, g_stringNoStem; static const char *settingskey_toolarea="/Recoll/geometry/toolArea"; static const char *settingskey_resarea="/Recoll/geometry/resArea"; static const char *settingskey_sidefilterssize = "/Recoll/geometry/sideFilters"; static Qt::ToolBarArea int2area(int in) { switch (in) { case Qt::LeftToolBarArea: return Qt::LeftToolBarArea; case Qt::RightToolBarArea: return Qt::RightToolBarArea; case Qt::BottomToolBarArea: return Qt::BottomToolBarArea; case Qt::TopToolBarArea: default: return Qt::TopToolBarArea; } } static QString configToTitle() { string confdir = path_getsimple(theconfig->getConfDir()); // Lower-case version. This only works with the ascii part, but // that's ok even if there are non-ascii chars in there, because // we further operate only on ascii substrings. string lconfdir = stringtolower((const string&)confdir); if (!lconfdir.empty() && lconfdir[0] == '.') { lconfdir = lconfdir.substr(1); confdir = confdir.substr(1); } string::size_type pos = lconfdir.find("recoll"); if (pos != string::npos) { lconfdir = lconfdir.substr(0, pos) + lconfdir.substr(pos+6); confdir = confdir.substr(0, pos) + confdir.substr(pos+6); } if (!confdir.empty()) { switch (confdir[0]) { case '.': case '-': case '_': confdir = confdir.substr(1); break; default: break; } } if (confdir.empty()) { confdir = "Recoll"; } else { confdir = string("Recoll - ") + confdir; } return QString::fromUtf8(confdir.c_str()); } void RclMain::init() { setWindowTitle(configToTitle()); buildMenus(); if (getenv("RECOLL_RESULTS_GEOMETRY")) { resultsSetFixedGeometry(); } periodictimer = new QTimer(this); // idxstatus file. Make sure it exists before trying to watch it // (case where we're started on an older index, or if the status // file was deleted since indexing) QString idxfn = path2qs(theconfig->getIdxStatusFile()); QFile qf(idxfn); qf.open(QIODevice::ReadWrite); qf.setPermissions(QFile::ReadOwner|QFile::WriteOwner); qf.close(); m_watcher.addPath(idxfn); setupStatusBar(); setupMenus(); (void)new HelpClient(this); HelpClient::installMap((const char *)this->objectName().toUtf8(), "RCL.SEARCH.GUI.SIMPLE"); // Set the focus to the search terms entry: sSearch->takeFocus(); enbSynAction->setDisabled(prefs.synFile.isEmpty()); enbSynAction->setChecked(prefs.synFileEnable); setupToolbars(); //////// 3 versions of results category filtering: buttons, combobox, menu setupCategoryFiltering(); restable = new ResTable(this); resultsHLayout->insertWidget(1, restable); actionShowResultsAsTable->setChecked(prefs.showResultsAsTable); on_actionShowResultsAsTable_toggled(prefs.showResultsAsTable); onNewShortcuts(); Preview::listShortcuts(); SnippetsW::listShortcuts(); AdvSearch::listShortcuts(); connect(&SCBase::scBase(), SIGNAL(shortcutsChanged()), this, SLOT(onNewShortcuts())); connect(&m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateIdxStatus())); connect(sSearch, SIGNAL(startSearch(std::shared_ptr, bool)), this, SLOT(startSearch(std::shared_ptr, bool))); connect(sSearch, SIGNAL(ssearchTypeChanged(int)), this, SLOT(onSSearchTypeChanged(int))); connect(sSearch, SIGNAL(setDescription(QString)), this, SLOT(onSetDescription(QString))); connect(sSearch, SIGNAL(clearSearch()), this, SLOT(resetSearch())); connect(this, SIGNAL(uiPrefsChanged()), sSearch, SLOT(setPrefs())); connect(preferencesMenu, SIGNAL(triggered(QAction*)), this, SLOT(setStemLang(QAction*))); connect(preferencesMenu, SIGNAL(aboutToShow()), this, SLOT(adjustPrefsMenu())); connect(fileExitAction, SIGNAL(triggered()), this, SLOT(fileExit())); connect(fileToggleIndexingAction, SIGNAL(triggered()), this, SLOT(toggleIndexing())); connect(fileStartMonitorAction, SIGNAL(triggered()), this, SLOT(startMonitor())); connect(fileBumpIndexingAction, SIGNAL(triggered()), this, SLOT(bumpIndexing())); connect(fileRebuildIndexAction, SIGNAL(triggered()), this, SLOT(rebuildIndex())); connect(actionSpecial_Indexing, SIGNAL(triggered()), this, SLOT(showSpecIdx())); connect(fileEraseDocHistoryAction, SIGNAL(triggered()), this, SLOT(eraseDocHistory())); connect(fileEraseSearchHistoryAction, SIGNAL(triggered()), this, SLOT(eraseSearchHistory())); connect(fileExportSSearchHistoryAction, SIGNAL(triggered()), this, SLOT(exportSimpleSearchHistory())); connect(actionSave_last_query, SIGNAL(triggered()), this, SLOT(saveLastQuery())); connect(actionLoad_saved_query, SIGNAL(triggered()), this, SLOT(loadSavedQuery())); connect(actionShow_index_statistics, SIGNAL(triggered()), this, SLOT(showIndexStatistics())); connect(helpAbout_RecollAction, SIGNAL(triggered()), this, SLOT(showAboutDialog())); connect(showMissingHelpers_Action, SIGNAL(triggered()), this, SLOT(showMissingHelpers())); connect(showActiveTypes_Action, SIGNAL(triggered()), this, SLOT(showActiveTypes())); connect(userManualAction, SIGNAL(triggered()), this, SLOT(startManual())); connect(toolsDoc_HistoryAction, SIGNAL(triggered()), this, SLOT(showDocHistory())); connect(toolsAdvanced_SearchAction, SIGNAL(triggered()), this, SLOT(showAdvSearchDialog())); connect(toolsSpellAction, SIGNAL(triggered()), this, SLOT(showSpellDialog())); connect(actionWebcache_Editor, SIGNAL(triggered()), this, SLOT(showWebcacheDialog())); connect(actionQuery_Fragments, SIGNAL(triggered()), this, SLOT(showFragButs())); connect(indexConfigAction, SIGNAL(triggered()), this, SLOT(showIndexConfig())); connect(indexScheduleAction, SIGNAL(triggered()), this, SLOT(showIndexSched())); connect(queryPrefsAction, SIGNAL(triggered()), this, SLOT(showUIPrefs())); connect(extIdxAction, SIGNAL(triggered()), this, SLOT(showExtIdxDialog())); connect(enbSynAction, SIGNAL(toggled(bool)), this, SLOT(setSynEnabled(bool))); connect(toggleFullScreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen())); zoomInAction->setShortcut(QKeySequence::ZoomIn); connect(zoomInAction, SIGNAL(triggered()), this, SLOT(zoomIn())); zoomOutAction->setShortcut(QKeySequence::ZoomOut); connect(zoomOutAction, SIGNAL(triggered()), this, SLOT(zoomOut())); connect(actionShowQueryDetails, SIGNAL(triggered()), reslist, SLOT(showQueryDetails())); connect(periodictimer, SIGNAL(timeout()), this, SLOT(periodic100())); restable->setRclMain(this, true); connect(actionSaveResultsAsCSV, SIGNAL(triggered()), restable, SLOT(saveAsCSV())); connect(this, SIGNAL(docSourceChanged(std::shared_ptr)), restable, SLOT(setDocSource(std::shared_ptr))); connect(this, SIGNAL(searchReset()), restable, SLOT(resetSource())); connect(this, SIGNAL(resultsReady()), restable, SLOT(readDocSource())); connect(this, SIGNAL(sortDataChanged(DocSeqSortSpec)), restable, SLOT(onSortDataChanged(DocSeqSortSpec))); connect(this, SIGNAL(sortDataChanged(DocSeqSortSpec)), this, SLOT(onSortDataChanged(DocSeqSortSpec))); connect(this, SIGNAL(uiPrefsChanged()), restable, SLOT(onUiPrefsChanged())); connect(restable->getModel(), SIGNAL(sortDataChanged(DocSeqSortSpec)), this, SLOT(onExtSortDataChanged(DocSeqSortSpec))); connect(restable, SIGNAL(docPreviewClicked(int, Rcl::Doc, int)), this, SLOT(startPreview(int, Rcl::Doc, int))); connect(restable, SIGNAL(docExpand(Rcl::Doc)), this, SLOT(docExpand(Rcl::Doc))); connect(restable, SIGNAL(showSubDocs(Rcl::Doc)), this, SLOT(showSubDocs(Rcl::Doc))); connect(restable, SIGNAL(openWithRequested(Rcl::Doc, std::string)), this, SLOT(openWith(Rcl::Doc, std::string))); reslist->setRclMain(this, true); connect(this, SIGNAL(docSourceChanged(std::shared_ptr)), reslist, SLOT(setDocSource(std::shared_ptr))); connect(firstPageAction, SIGNAL(triggered()), reslist, SLOT(resultPageFirst())); connect(prevPageAction, SIGNAL(triggered()), reslist, SLOT(resPageUpOrBack())); connect(nextPageAction, SIGNAL(triggered()), reslist, SLOT(resPageDownOrNext())); connect(this, SIGNAL(searchReset()), reslist, SLOT(resetList())); connect(this, SIGNAL(resultsReady()), reslist, SLOT(readDocSource())); connect(this, SIGNAL(uiPrefsChanged()), reslist, SLOT(onUiPrefsChanged())); connect(reslist, SIGNAL(hasResults(int)), this, SLOT(resultCount(int))); connect(reslist, SIGNAL(wordSelect(QString)), sSearch, SLOT(addTerm(QString))); connect(reslist, SIGNAL(wordReplace(const QString&, const QString&)), sSearch, SLOT(onWordReplace(const QString&, const QString&))); connect(reslist, SIGNAL(nextPageAvailable(bool)), this, SLOT(enableNextPage(bool))); connect(reslist, SIGNAL(prevPageAvailable(bool)), this, SLOT(enablePrevPage(bool))); connect(reslist, SIGNAL(docExpand(Rcl::Doc)), this, SLOT(docExpand(Rcl::Doc))); connect(reslist, SIGNAL(showSnippets(Rcl::Doc)), this, SLOT(showSnippets(Rcl::Doc))); connect(reslist, SIGNAL(showSubDocs(Rcl::Doc)), this, SLOT(showSubDocs(Rcl::Doc))); connect(reslist, SIGNAL(docSaveToFileClicked(Rcl::Doc)), this, SLOT(saveDocToFile(Rcl::Doc))); connect(reslist, SIGNAL(editRequested(Rcl::Doc)), this, SLOT(startNativeViewer(Rcl::Doc))); connect(reslist, SIGNAL(openWithRequested(Rcl::Doc, std::string)), this, SLOT(openWith(Rcl::Doc, std::string))); connect(reslist, SIGNAL(docPreviewClicked(int, Rcl::Doc, int)), this, SLOT(startPreview(int, Rcl::Doc, int))); connect(reslist, SIGNAL(previewRequested(Rcl::Doc)), this, SLOT(startPreview(Rcl::Doc))); setFilterCtlStyle(prefs.filterCtlStyle); if (prefs.keepSort && prefs.sortActive) { m_sortspec.field = (const char *)prefs.sortField.toUtf8(); m_sortspec.desc = prefs.sortDesc; emit sortDataChanged(m_sortspec); } QSettings settings; restoreGeometry(settings.value("/Recoll/geometry/maingeom").toByteArray()); QVariant saved = settings.value(settingskey_sidefilterssize); if (saved != QVariant()) { sideFiltersSPLT->restoreState(saved.toByteArray()); } else { QList sizes; sizes << 200 << 600; sideFiltersSPLT->setSizes(sizes); } // We don't want the side filters part of the splitter to change width when the window is // resized sideFiltersSPLT->setStretchFactor(0, 0); sideFiltersSPLT->setStretchFactor(1, 1); populateSideFilters(SFUR_INIT); enableTrayIcon(prefs.showTrayIcon); fileRebuildIndexAction->setEnabled(false); fileToggleIndexingAction->setEnabled(false); fileRetryFailedAction->setEnabled(false); // Start timer on a slow period (used for checking ^C). Will be // speeded up during indexing periodictimer->start(1000); } void RclMain::onSSearchTypeChanged(int typ) { enableSideFilters(typ == SSearch::SST_LANG); } void RclMain::zoomIn() { prefs.reslistfontsize++; emit uiPrefsChanged(); } void RclMain::zoomOut() { if (prefs.reslistfontsize > 1) { prefs.reslistfontsize--; } emit uiPrefsChanged(); } void RclMain::onNewShortcuts() { SCBase& scb = SCBase::scBase(); QKeySequence ks; SETSHORTCUT(sSearch, "main:347", tr("Main Window"), tr("Clear search"), "Ctrl+S", m_clearsearchsc, clearAll); SETSHORTCUT(sSearch, "main:349", tr("Main Window"), tr("Move keyboard focus to search entry"), "Ctrl+L", m_focustosearchsc, takeFocus); SETSHORTCUT(sSearch, "main:352", tr("Main Window"), tr("Move keyboard focus to search, alt."), "Ctrl+Shift+S", m_focustosearcholdsc, takeFocus); // We could set this as an action shortcut, but then, it would not // be editable SETSHORTCUT(this, "main:357", tr("Main Window"), tr("Toggle tabular display"), "Ctrl+T", m_toggletablesc, toggleTable); SETSHORTCUT(this, "main:373", tr("Main Window"), tr("Show menu search dialog"), "Alt+/", m_actionssearchsc, showActionsSearch); ks = scb.get("rclmain:361", tr("Main Window"), tr("Move keyboard focus to table"), "Ctrl+R"); if (!ks.isEmpty()) { delete m_focustotablesc; m_focustotablesc = new QShortcut(ks, this); if (displayingTable) { connect(m_focustotablesc, SIGNAL(activated()), restable, SLOT(takeFocus())); } else { disconnect(m_focustotablesc, SIGNAL(activated()), restable, SLOT(takeFocus())); } } } void RclMain::setupToolbars() { if (nullptr == m_toolsTB) { m_toolsTB = new QToolBar(tr("Tools"), this); m_toolsTB->setObjectName(QString::fromUtf8("m_toolsTB")); m_toolsTB->addAction(toolsAdvanced_SearchAction); m_toolsTB->addAction(toolsDoc_HistoryAction); m_toolsTB->addAction(toolsSpellAction); m_toolsTB->addAction(actionQuery_Fragments); } QSettings settings; int val; if (!prefs.noToolbars) { val = settings.value(settingskey_toolarea).toInt(); this->addToolBar(int2area(val), m_toolsTB); m_toolsTB->show(); } else { m_toolsTB->hide(); } if (nullptr == m_resTB) { m_resTB = new QToolBar(tr("Results"), this); m_resTB->setObjectName(QString::fromUtf8("m_resTB")); } if (!prefs.noToolbars) { val = settings.value(settingskey_resarea).toInt(); this->addToolBar(int2area(val), m_resTB); m_resTB->show(); } else { m_resTB->hide(); } } void RclMain::setupStatusBar() { auto bar = statusBar(); if (prefs.noStatusBar) { bar->hide(); } else { bar->show(); } } void RclMain::setupMenus() { if (prefs.noMenuBar) { MenuBar->hide(); sSearch->menuPB->show(); butmenuSC = new QShortcut(QKeySequence("Alt+m"), this); connect(butmenuSC, SIGNAL(activated()), sSearch->menuPB, SLOT(showMenu())); } else { MenuBar->show(); sSearch->menuPB->hide(); deleteZ(butmenuSC); } } void RclMain::enableTrayIcon(bool on) { on = on && QSystemTrayIcon::isSystemTrayAvailable(); if (on) { if (nullptr == m_trayicon) { m_trayicon = new RclTrayIcon(this, QIcon(QString(":/images/recoll.png"))); } m_trayicon->show(); } else { deleteZ(m_trayicon); } } void RclMain::setupCategoryFiltering() { // This is just to get the common catg strings into the message file static const char* catg_strings[] = { QT_TR_NOOP("All"), QT_TR_NOOP("media"), QT_TR_NOOP("message"), QT_TR_NOOP("other"), QT_TR_NOOP("presentation"), QT_TR_NOOP("spreadsheet"), QT_TR_NOOP("text"), QT_TR_NOOP("sorted"), QT_TR_NOOP("filtered") }; //// Combobox version m_filtCMB = new QComboBox(m_resTB); m_filtCMB->setEditable(false); m_filtCMB->addItem(tr("All")); m_filtCMB->setToolTip(tr("Document filter")); //// Buttons version m_filtFRM = new QFrame(this); m_filtFRM->setObjectName(QString::fromUtf8("m_filtFRM")); QHBoxLayout *bgrphbox = new QHBoxLayout(m_filtFRM); verticalLayout->insertWidget(1, m_filtFRM); QSizePolicy sizePolicy2(QSizePolicy::Preferred, QSizePolicy::Maximum); sizePolicy2.setHorizontalStretch(0); sizePolicy2.setVerticalStretch(0); sizePolicy2.setHeightForWidth(m_filtFRM->sizePolicy().hasHeightForWidth()); m_filtFRM->setSizePolicy(sizePolicy2); m_filtBGRP = new QButtonGroup(m_filtFRM); QRadioButton *allRDB = new QRadioButton(m_filtFRM); allRDB->setObjectName("allRDB"); allRDB->setText(tr("All")); bgrphbox->addWidget(allRDB); int bgrpid = 0; m_filtBGRP->addButton(allRDB, bgrpid++); allRDB->setChecked(true); m_filtFRM->setLayout(bgrphbox); //// Menu version of the document filter control m_filtMN = new QMenu(MenuBar); m_filtMN->setObjectName("m_filtMN"); MenuBar->insertMenu(viewMenu->menuAction(), m_filtMN); buttonTopMenu->insertMenu(viewMenu->menuAction(), m_filtMN); m_filtMN->setTitle(tr("F&ilter")); QActionGroup *fltag = new QActionGroup(this); fltag->setExclusive(true); QAction *act = fltag->addAction(tr("All")); m_filtMN->addAction(act); act->setCheckable(true); act->setData((int)0); //// Go through the categories list and setup menu, buttons and combobox vector cats; theconfig->getGuiFilterNames(cats); m_catgbutvec.push_back(catg_strings[0]); for (const auto& cat : cats) { QRadioButton *but = new QRadioButton(m_filtFRM); QString catgnm = u8s2qs(cat); m_catgbutvec.push_back(cat); // We strip text before the first colon before setting the button name. // This is so that the user can decide the order of buttons by naming // the filter,ie, a:media b:messages etc. QString but_txt = catgnm; int colon = catgnm.indexOf(':'); if (colon != -1) { but_txt = catgnm.right(catgnm.size()-(colon+1)); } but->setText(tr(but_txt.toUtf8())); m_filtCMB->addItem(tr(but_txt.toUtf8())); bgrphbox->addWidget(but); m_filtBGRP->addButton(but, bgrpid++); QAction *act = fltag->addAction(tr(but_txt.toUtf8())); m_filtMN->addAction(act); act->setCheckable(true); act->setData((int)(m_catgbutvec.size()-1)); } m_filtMN->connect(m_filtMN, SIGNAL(triggered(QAction *)), this, SLOT(catgFilter(QAction *))); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) connect(m_filtBGRP, SIGNAL(idClicked(int)), this, SLOT(catgFilter(int))); #else connect(m_filtBGRP, SIGNAL(buttonClicked(int)), this, SLOT(catgFilter(int))); #endif connect(m_filtCMB, SIGNAL(activated(int)), this, SLOT(catgFilter(int))); } void RclMain::setSynEnabled(bool on) { prefs.synFileEnable = on; if (uiprefs) uiprefs->synFileCB->setChecked(prefs.synFileEnable); } void RclMain::resultCount(int n) { actionSortByDateAsc->setEnabled(n>0); actionSortByDateDesc->setEnabled(n>0); } void RclMain::setFilterCtlStyle(int stl) { switch (stl) { case PrefsPack::FCS_MN: setupResTB(false); m_filtFRM->setVisible(false); m_filtMN->menuAction()->setVisible(true); break; case PrefsPack::FCS_CMB: setupResTB(true); m_filtFRM->setVisible(false); m_filtMN->menuAction()->setVisible(false); break; case PrefsPack::FCS_BT: default: setupResTB(false); m_filtFRM->setVisible(true); m_filtMN->menuAction()->setVisible(false); } } // Set up the "results" toolbox, adding the filter combobox or not depending // on config option void RclMain::setupResTB(bool combo) { m_resTB->clear(); m_resTB->addAction(firstPageAction); m_resTB->addAction(prevPageAction); m_resTB->addAction(nextPageAction); m_resTB->addSeparator(); m_resTB->addAction(actionSortByDateAsc); m_resTB->addAction(actionSortByDateDesc); if (combo) { m_resTB->addSeparator(); m_filtCMB->show(); m_resTB->addWidget(m_filtCMB); } else { m_filtCMB->hide(); } m_resTB->addSeparator(); m_resTB->addAction(actionShowResultsAsTable); } // This is called by a timer right after we come up. Try to open // the database and talk to the user if we can't void RclMain::initDbOpen() { bool nodb = false; string reason; bool maindberror; if (!maybeOpenDb(reason, true, &maindberror)) { nodb = true; if (maindberror) { if (theconfig && !path_exists(theconfig->getDbDir())) { FirstIdxDialog fidia(this); connect(fidia.idxconfCLB, SIGNAL(clicked()), this, SLOT(execIndexConfig())); connect(fidia.idxschedCLB, SIGNAL(clicked()), this, SLOT(execIndexSched())); connect(fidia.runidxPB, SIGNAL(clicked()), this, SLOT(rebuildIndex())); fidia.exec(); // Don't open adv search or run cmd line search in this case. return; } else { QMessageBox::warning( 0, "Recoll", tr("Main index open error: ") + u8s2qs(reason) + tr(". The index may be corrupted. Maybe try to run xapian-check or " "rebuild the index ?.")); } } else { QMessageBox::warning(0, "Recoll", tr("Could not open external index. Db not " "open. Check external indexes list.")); } } if (prefs.startWithAdvSearchOpen) showAdvSearchDialog(); // If we have something in the search entry, it comes from a // command line argument if (!nodb && sSearch->hasSearchString()) QTimer::singleShot(0, sSearch, SLOT(startSimpleSearch())); if (!m_urltoview.isEmpty()) viewUrl(); } void RclMain::setStemLang(QAction *id) { LOGDEB("RclMain::setStemLang(" << id << ")\n"); // Check that the menu entry is for a stemming language change // (might also be "show prefs" etc. bool isLangId = false; for (const auto& entry : m_stemLangToId) { if (id == entry.second) isLangId = true; } if (!isLangId) return; // Set the "checked" item state for lang entries for (auto& entry : m_stemLangToId) { entry.second->setChecked(false); } id->setChecked(true); // Retrieve language value (also handle special cases), set prefs, // notify that we changed QString lang; if (id == m_idNoStem) { lang = ""; } else if (id == m_idAllStem) { lang = "ALL"; } else { lang = id->text(); } prefs.queryStemLang = lang; LOGDEB("RclMain::setStemLang(" << id << "): lang [" << qs2utf8s(prefs.queryStemLang) << "]\n"); rwSettings(true); emit stemLangChanged(lang); } // Set the checked stemming language item before showing the prefs menu void RclMain::setStemLang(const QString& lang) { LOGDEB("RclMain::setStemLang(" << qs2utf8s(lang) << ")\n"); QAction *id; if (lang == "") { id = m_idNoStem; } else if (lang == "ALL") { id = m_idAllStem; } else { auto it = m_stemLangToId.find(lang); if (it == m_stemLangToId.end()) return; id = it->second; } for (const auto& entry : m_stemLangToId) { entry.second->setChecked(false); } id->setChecked(true); } // Prefs menu about to show void RclMain::adjustPrefsMenu() { setStemLang(prefs.queryStemLang); } void RclMain::showTrayMessage(const QString& text) { if (m_trayicon && prefs.trayMessages) m_trayicon->showMessage("Recoll", text, QSystemTrayIcon::Information, 2000); } void RclMain::closeEvent(QCloseEvent *ev) { LOGDEB("RclMain::closeEvent\n"); if (isFullScreen()) { prefs.showmode = PrefsPack::SHOW_FULL; } else if (isMaximized()) { prefs.showmode = PrefsPack::SHOW_MAX; } else { prefs.showmode = PrefsPack::SHOW_NORMAL; } ev->ignore(); if (prefs.closeToTray && m_trayicon && m_trayicon->isVisible()) { hide(); return; } fileExit(); } void RclMain::fileExit() { LOGDEB("RclMain: fileExit\n"); // Have to do this both in closeEvent (for close to tray) and fileExit // (^Q, doesnt go through closeEvent) if (isFullScreen()) { prefs.showmode = PrefsPack::SHOW_FULL; } else if (isMaximized()) { prefs.showmode = PrefsPack::SHOW_MAX; } else { prefs.showmode = PrefsPack::SHOW_NORMAL; } if (m_trayicon) { m_trayicon->setVisible(false); } // Don't save geometry if we're currently maximized. At least under X11 // this saves the maximized size. otoh isFullscreen() does not seem needed QSettings settings; if (!isMaximized()) { settings.setValue("/Recoll/geometry/maingeom", saveGeometry()); } if (!prefs.noToolbars) { settings.setValue(settingskey_toolarea, toolBarArea(m_toolsTB)); settings.setValue(settingskey_resarea, toolBarArea(m_resTB)); } restable->saveColState(); settings.setValue(settingskey_sidefilterssize, sideFiltersSPLT->saveState()); if (prefs.ssearchTypSav) { prefs.ssearchTyp = sSearch->searchTypCMB->currentIndex(); } rwSettings(true); deleteAllTempFiles(); qApp->exit(0); } // Start a db query and set the reslist docsource void RclMain::startSearch(std::shared_ptr sdata, bool issimple) { LOGDEB("RclMain::startSearch. Indexing " << (m_idxproc?"on":"off") << " Active " << m_queryActive << "\n"); if (m_queryActive) { LOGDEB("startSearch: already active\n"); return; } m_queryActive = true; restable->setEnabled(false); m_source = std::shared_ptr(); m_searchIsSimple = issimple; // The db may have been closed at the end of indexing string reason; // If indexing is being performed, we reopen the db at each query. if (!maybeOpenDb(reason, m_idxproc != 0)) { QMessageBox::critical(0, "Recoll", u8s2qs(reason)); m_queryActive = false; restable->setEnabled(true); return; } if (prefs.synFileEnable && !prefs.synFile.isEmpty()) { if (!rcldb->setSynGroupsFile(qs2path(prefs.synFile))) { QMessageBox::warning(0, "Recoll", tr("Can't set synonyms file (parse error?)")); return; } } else { rcldb->setSynGroupsFile(""); } Rcl::Query *query = new Rcl::Query(rcldb.get()); query->setCollapseDuplicates(prefs.collapseDuplicates); curPreview = 0; DocSequenceDb *src = new DocSequenceDb(rcldb, std::shared_ptr(query), qs2utf8s(tr("Query results")), sdata); src->setAbstractParams(prefs.queryBuildAbstract, prefs.queryReplaceAbstract); m_source = std::shared_ptr(src); // If this is a file name search sort by mtype so that directories // come first (see the rclquery sort key generator) if (sSearch->searchTypCMB->currentIndex() == SSearch::SST_FNM && m_sortspec.field.empty()) { m_sortspec.field = "mtype"; m_sortspec.desc = false; } m_source->setSortSpec(m_sortspec); setFiltSpec(); emit docSourceChanged(m_source); emit sortDataChanged(m_sortspec); initiateQuery(); } class QueryThread : public QThread { std::shared_ptr m_source; public: QueryThread(std::shared_ptr source) : m_source(source) {} ~QueryThread() {} virtual void run() { cnt = m_source->getResCnt(); } int cnt; }; void RclMain::hideToolTip() { QToolTip::hideText(); } void RclMain::initiateQuery() { if (!m_source) return; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QueryThread qthr(m_source); qthr.start(); QProgressDialog progress(this); progress.setLabelText(tr("Query in progress.
" "Due to limitations of the indexing library,
" "cancelling will exit the program")); progress.setWindowModality(Qt::WindowModal); progress.setRange(0,0); // For some reason setMinimumDuration() does not seem to work with // a busy dialog (range 0,0) Have to call progress.show() inside // the loop. // progress.setMinimumDuration(2000); // Also the multiple processEvents() seem to improve the responsiveness?? for (int i = 0;;i++) { qApp->processEvents(); if (qthr.wait(100)) { break; } if (i == 20) progress.show(); qApp->processEvents(); if (progress.wasCanceled()) { // Just get out of there asap. exit(1); } qApp->processEvents(); } progress.close(); int cnt = qthr.cnt; QString msg; if (cnt > 0) { QString str; msg = tr("Result count (est.)") + ": " + str.setNum(cnt); } else { msg = tr("No results found"); } statusBar()->showMessage(msg, 0); QApplication::restoreOverrideCursor(); m_queryActive = false; restable->setEnabled(true); emit(resultsReady()); } void RclMain::resetSearch() { m_source = std::shared_ptr(); emit searchReset(); } void RclMain::onSortCtlChanged() { if (m_sortspecnochange) return; LOGDEB("RclMain::onSortCtlChanged()\n"); m_sortspec.reset(); if (actionSortByDateAsc->isChecked()) { m_sortspec.field = "mtime"; m_sortspec.desc = false; prefs.sortActive = true; prefs.sortDesc = false; prefs.sortField = "mtime"; } else if (actionSortByDateDesc->isChecked()) { m_sortspec.field = "mtime"; m_sortspec.desc = true; prefs.sortActive = true; prefs.sortDesc = true; prefs.sortField = "mtime"; } else { prefs.sortActive = prefs.sortDesc = false; prefs.sortField = ""; // If this is a file name search sort by mtype so that directories // come first (see the rclquery sort key generator) if (sSearch->searchTypCMB->currentIndex() == SSearch::SST_FNM) { m_sortspec.field = "mtype"; m_sortspec.desc = false; } } if (m_source) m_source->setSortSpec(m_sortspec); emit sortDataChanged(m_sortspec); } void RclMain::onExtSortDataChanged(DocSeqSortSpec spec) { onSortDataChanged(spec); initiateQuery(); } void RclMain::onSortDataChanged(DocSeqSortSpec spec) { LOGDEB("RclMain::onSortDataChanged\n"); m_sortspecnochange = true; if (spec.field.compare("mtime")) { actionSortByDateDesc->setChecked(false); actionSortByDateAsc->setChecked(false); } else { actionSortByDateDesc->setChecked(spec.desc); actionSortByDateAsc->setChecked(!spec.desc); } m_sortspecnochange = false; if (m_source) m_source->setSortSpec(spec); m_sortspec = spec; prefs.sortField = QString::fromUtf8(spec.field.c_str()); prefs.sortDesc = spec.desc; prefs.sortActive = !spec.field.empty(); std::string fld; if (!m_sortspec.field.empty()) { fld = qs2utf8s(RecollModel::displayableField(m_sortspec.field)); } DocSequence::set_translations( qs2utf8s(tr("sorted")) + ": " + fld + (m_sortspec.desc?" ↓":" ↑"), qs2utf8s(tr("filtered"))); } // Needed only because an action is not a widget, so can't be used // with SETSHORTCUT void RclMain::toggleTable() { actionShowResultsAsTable->toggle(); } void RclMain::on_actionShowResultsAsTable_toggled(bool on) { LOGDEB("RclMain::on_actionShowResultsAsTable_toggled(" << on << ")\n"); prefs.showResultsAsTable = on; displayingTable = on; restable->setVisible(on); reslist->setVisible(!on); actionSaveResultsAsCSV->setEnabled(on); if (!on) { int docnum = restable->getDetailDocNumOrTopRow(); if (docnum >= 0) { reslist->resultPageFor(docnum); } if (m_focustotablesc) disconnect(m_focustotablesc, SIGNAL(activated()), restable, SLOT(takeFocus())); sSearch->takeFocus(); } else { int docnum = reslist->pageFirstDocNum(); if (docnum >= 0) { restable->makeRowVisible(docnum); } nextPageAction->setEnabled(false); prevPageAction->setEnabled(false); firstPageAction->setEnabled(false); if (m_focustotablesc) connect(m_focustotablesc, SIGNAL(activated()), restable, SLOT(takeFocus())); // This should not be necessary, but it fixes a display issue with qt 5.12/5.15 // (focal/jammy) at least, where the restable area is not fully painted (but gets updated if // you move the pointer around or resize / move the window, depending). The problem is // slightly random or timing-dependant (not occurring every time). The hide/show does not // seem to hurt performance visibly. restable->hide(); restable->show(); } } void RclMain::on_actionSortByDateAsc_toggled(bool on) { LOGDEB("RclMain::on_actionSortByDateAsc_toggled(" << on << ")\n"); if (on) { if (actionSortByDateDesc->isChecked()) { actionSortByDateDesc->setChecked(false); // Let our buddy work. return; } } onSortCtlChanged(); initiateQuery(); } void RclMain::on_actionSortByDateDesc_toggled(bool on) { LOGDEB("RclMain::on_actionSortByDateDesc_toggled(" << on << ")\n"); if (on) { if (actionSortByDateAsc->isChecked()) { actionSortByDateAsc->setChecked(false); // Let our buddy work. return; } } onSortCtlChanged(); initiateQuery(); } void RclMain::saveDocToFile(Rcl::Doc doc) { QString s = QFileDialog::getSaveFileName( this, tr("Save file"), path2qs(path_home())); string tofile = qs2path(s); TempFile temp; // not used because tofile is set. if (!FileInterner::idocToFile(temp, tofile, theconfig, doc)) { QMessageBox::warning(0, "Recoll", tr("Cannot extract document or create temporary file")); return; } } void RclMain::showSubDocs(Rcl::Doc doc) { LOGDEB("RclMain::showSubDocs\n"); string reason; if (!maybeOpenDb(reason, false)) { QMessageBox::critical(0, "Recoll", QString(reason.c_str())); return; } vector docs; if (!rcldb->getSubDocs(doc, docs)) { QMessageBox::warning(0, "Recoll", QString("Can't get subdocs")); return; } DocSequenceDocs *src = new DocSequenceDocs(rcldb, docs, qs2utf8s(tr("Sub-documents and attachments"))); src->setDescription(qs2utf8s(tr("Sub-documents and attachments"))); std::shared_ptr source(new DocSource(theconfig, std::shared_ptr(src))); ResTable *res = new ResTable(); res->setRclMain(this, false); res->setDocSource(source); res->readDocSource(); res->show(); } // Search for document 'like' the selected one. We ask rcldb/xapian to find // significant terms, and add them to the simple search entry. void RclMain::docExpand(Rcl::Doc doc) { LOGDEB("RclMain::docExpand()\n"); if (!rcldb) return; list terms; terms = m_source->expand(doc); if (terms.empty()) { LOGDEB("RclMain::docExpand: no terms\n"); return; } // Do we keep the original query. I think we'd better not. // rcldb->expand is set to keep the original query terms instead. QString text;// = sSearch->queryText->currentText(); for (list::iterator it = terms.begin(); it != terms.end(); it++) { text += u8s2qs(std::string(" \"") + *it + "\""); } // We need to insert item here, its not auto-done like when the user types // CR sSearch->setSearchString(text); sSearch->setAnyTermMode(); sSearch->startSimpleSearch(); } void RclMain::showDocHistory() { LOGDEB("RclMain::showDocHistory\n"); resetSearch(); curPreview = 0; string reason; if (!maybeOpenDb(reason, false)) { QMessageBox::critical(0, "Recoll", QString(reason.c_str())); return; } // Construct a bogus SearchData structure auto searchdata = std::make_shared(Rcl::SCLT_AND, cstr_null); searchdata->setDescription((const char *)tr("History data").toUtf8()); // If you change the title, also change it in eraseDocHistory() DocSequenceHistory *src = new DocSequenceHistory(rcldb, g_dynconf, qs2utf8s(tr("Document history"))); src->setDescription(qs2utf8s(tr("History data"))); DocSource *source = new DocSource(theconfig, std::shared_ptr(src)); m_source = std::shared_ptr(source); m_source->setSortSpec(m_sortspec); setFiltSpec(); emit docSourceChanged(m_source); emit sortDataChanged(m_sortspec); initiateQuery(); } // Erase all memory of documents viewed void RclMain::eraseDocHistory() { // Clear file storage if (g_dynconf) g_dynconf->eraseAll(docHistSubKey); // Clear possibly displayed history if (reslist->displayingHistory()) { showDocHistory(); } } void RclMain::eraseSearchHistory() { int rep = QMessageBox::warning( 0, tr("Confirm"), tr("Erasing simple and advanced search history lists, please click Ok to confirm"), QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::NoButton); if (rep == QMessageBox::Ok) { prefs.ssearchHistory.clear(); if (sSearch) sSearch->clearAll(); if (g_advshistory) g_advshistory->clear(); } } void RclMain::exportSimpleSearchHistory() { QFileDialog dialog(0, "Saving simple search history"); dialog.setFileMode(QFileDialog::AnyFile); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setViewMode(QFileDialog::List); QFlags flags = QDir::NoDotAndDotDot|QDir::Files; dialog.setFilter(flags); if (dialog.exec() != QDialog::Accepted) { return; } string path = qs2utf8s(dialog.selectedFiles().value(0)); LOGDEB("Chosen path: " << path << "\n"); std::fstream fp; if (!path_streamopen(path, std::ios::out | std::ios::trunc, fp)) { QMessageBox::warning(0, "Recoll", tr("Could not open/create file")); return; } for (int i = 0; i < prefs.ssearchHistory.count(); i++) { fp << qs2utf8s(prefs.ssearchHistory[i]) << "\n"; } fp.close(); } // Called when the uiprefs dialog is ok'd void RclMain::setUIPrefs() { if (!uiprefs) return; LOGDEB("Recollmain::setUIPrefs\n"); populateSideFilters(SFUR_USERCONFIG); ::applyStyleSheet(prefs.qssFile); emit uiPrefsChanged(); enbSynAction->setDisabled(prefs.synFile.isEmpty()); enbSynAction->setChecked(prefs.synFileEnable); } void RclMain::enableNextPage(bool yesno) { if (!displayingTable) nextPageAction->setEnabled(yesno); } void RclMain::enablePrevPage(bool yesno) { if (!displayingTable) { prevPageAction->setEnabled(yesno); firstPageAction->setEnabled(yesno); } } void RclMain::onSetDescription(QString desc) { m_queryDescription = desc; } QString RclMain::getQueryDescription() { if (!m_source) return ""; return m_queryDescription.isEmpty() ? u8s2qs(m_source->getDescription()) : m_queryDescription; } // Set filter, action style void RclMain::catgFilter(QAction *act) { int id = act->data().toInt(); catgFilter(id); } // User pressed a filter button: set filter params in reslist void RclMain::catgFilter(int id) { LOGDEB("RclMain::catgFilter: id " << id << "\n"); if (id < 0 || id >= int(m_catgbutvec.size())) return; switch (prefs.filterCtlStyle) { case PrefsPack::FCS_MN: m_filtCMB->setCurrentIndex(id); m_filtBGRP->buttons()[id]->setChecked(true); break; case PrefsPack::FCS_CMB: m_filtBGRP->buttons()[id]->setChecked(true); m_filtMN->actions()[id]->setChecked(true); break; case PrefsPack::FCS_BT: default: m_filtCMB->setCurrentIndex(id); m_filtMN->actions()[id]->setChecked(true); } m_catgbutvecidx = id; setFiltSpec(); initiateQuery(); } void RclMain::setFiltSpec() { m_filtspec.reset(); if (nullptr == m_source) return; // "Category" buttons if (m_catgbutvecidx != 0) { string catg = m_catgbutvec[m_catgbutvecidx]; string frag; theconfig->getGuiFilter(catg, frag); m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, frag); } // Fragments from the fragbuts buttonbox tool if (fragbuts) { vector frags; fragbuts->getfrags(frags); for (const auto& frag : frags) { m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, frag); } } if (dateFilterCB->isEnabled()) { // The CB is only disabled when we are not in query language mode auto treedirs = idxTreeGetDirs(); if (!treedirs.empty()) { bool first{true}; const std::string prefix{"dir:"}; std::string clause; for (const auto& dir : treedirs) { if (first) { first = false; } else { clause += " OR "; } #ifdef _WIN32 clause += prefix + makeCString(path_slashdrive(dir)); #else clause += prefix + makeCString(dir); #endif } LOGDEB0("Sidefilter dir clause: [" << clause << "]\n"); m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, clause); } if (dateFilterCB->isChecked()) { QString mindate = minDateFilterDTEDT->date().toString("yyyy-MM-dd"); QString maxdate = maxDateFilterDTEDT->date().toString("yyyy-MM-dd"); std::string clause = std::string("date:") + qs2utf8s(mindate) + "/" + qs2utf8s(maxdate); LOGDEB1("RclMain::setFiltSpec: date clause " << clause << "\n"); m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, clause); } } #ifdef EXT4_BIRTH_TIME if (birthDateFilterCB->isEnabled()) { // The CB is only disabled when we are not in query language mode auto treedirs = idxTreeGetDirs(); if (!treedirs.empty()) { bool first{true}; const std::string prefix{"dir:"}; std::string clause; for (const auto& dir : treedirs) { if (first) { first = false; } else { clause += " OR "; } #ifdef _WIN32 clause += prefix + makeCString(path_slashdrive(dir)); #else clause += prefix + makeCString(dir); #endif } LOGDEB0("Sidefilter dir clause: [" << clause << "]\n"); m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, clause); } if (birthDateFilterCB->isChecked()) { QString mindate = minBirthDateFilterDTEDT->date().toString("yyyy-MM-dd"); QString maxdate = maxBirthDateFilterDTEDT->date().toString("yyyy-MM-dd"); std::string clause = std::string("birtime:") + qs2utf8s(mindate) + "/" + qs2utf8s(maxdate); LOGDEB1("RclMain::setFiltSpec: birth date clause " << clause << "\n"); m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, clause); } } #endif m_source->setFiltSpec(m_filtspec); } void RclMain::onFragmentsChanged() { setFiltSpec(); initiateQuery(); } void RclMain::toggleFullScreen() { if (isFullScreen()) showNormal(); else showFullScreen(); } void RclMain::showEvent(QShowEvent *ev) { sSearch->takeFocus(); QMainWindow::showEvent(ev); } void RclMain::resultsSetFixedGeometry() { const char *cp = getenv("RECOLL_RESULTS_GEOMETRY"); if (nullptr == cp) return; std::vector swh; stringToTokens(cp, swh, "xX"); if (swh.size() != 2) { LOGERR("Bad RECOLL_RESULTS_GEOMETRY: " << cp); return; } int w = atoi(swh[0].c_str()); int h = atoi(swh[1].c_str()); if (w <= 0 || h <= 0) { LOGERR("Bad RECOLL_RESULTS_GEOMETRY: " << cp); return; } resultsLayoutWidget->setParent(nullptr); auto scrollArea = new QScrollArea(sideFiltersSPLT); scrollArea->setObjectName(QString::fromUtf8("scrollArea")); scrollArea->setWidgetResizable(true); reslist->setGeometry(QRect(0, 0, w, h)); QSizePolicy sizePolicy2(QSizePolicy::Fixed, QSizePolicy::Fixed); sizePolicy2.setHorizontalStretch(0); sizePolicy2.setVerticalStretch(0); sizePolicy2.setHeightForWidth(reslist->sizePolicy().hasHeightForWidth()); reslist->setSizePolicy(sizePolicy2); reslist->setMinimumSize(QSize(w, h)); reslist->setMaximumSize(QSize(w, h)); scrollArea->setWidget(resultsLayoutWidget); sideFiltersSPLT->replaceWidget(1, scrollArea); } recoll-1.36.1/qtgui/respopup.h0000644000175000017500000000300414410615043013155 00000000000000/* Copyright (C) 2006-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RESPOPUP_H_INCLUDED_ #define _RESPOPUP_H_INCLUDED_ #include "autoconfig.h" class RclMain; namespace ResultPopup { enum Options {showExpand = 0x1, showSubs = 0x2, isMain = 0x3, showSaveOne = 0x4, showSaveSel = 0x8}; extern QMenu *create(QWidget *me, int opts, std::shared_ptr source, Rcl::Doc& doc); extern Rcl::Doc getParent(std::shared_ptr source, Rcl::Doc& doc); extern Rcl::Doc getFolder(Rcl::Doc& doc); extern void copyFN(const Rcl::Doc &doc); extern void copyPath(const Rcl::Doc &doc); extern void copyURL(const Rcl::Doc &doc); extern void copyText(Rcl::Doc &doc, RclMain *rclmain=nullptr); }; #endif /* _RESPOPUP_H_INCLUDED_ */ recoll-1.36.1/qtgui/rclm_saveload.cpp0000644000175000017500000001017714410615043014457 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" /** Saving and restoring named queries */ #include "safesysstat.h" #include #include #include #include "rclmain_w.h" #include "log.h" #include "readfile.h" #include "xmltosd.h" #include "searchdata.h" #include "copyfile.h" #include "pathut.h" using namespace std; using namespace Rcl; static QString prevDir() { QSettings settings; QString prevdir = settings.value("/Recoll/prefs/lastQuerySaveDir").toString(); string defpath = path_cat(theconfig->getConfDir(), "saved_queries"); if (prevdir.isEmpty()) { if (!path_exists(defpath)) { path_makepath(defpath, 0700); } return path2qs(defpath); } else { return prevdir; } } void RclMain::saveLastQuery() { string xml; if (lastSearchSimple()) { xml = sSearch->asXML(); } else { if (g_advshistory) { std::shared_ptr sd; sd = g_advshistory->getnewest(); if (sd) { xml = sd->asXML(); } } } if (xml.empty()) { QMessageBox::information(this, tr("No search"), tr("No preserved previous search")); return; } xml = string("\n") + "\n" + xml + "\n\n"; QFileDialog fileDialog(this, tr("Choose file to save")); fileDialog.setNameFilter(tr("Saved Queries (*.rclq)")); fileDialog.setDefaultSuffix("rclq"); fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setDirectory(prevDir()); if (!fileDialog.exec()) return; QString s = fileDialog.selectedFiles().first(); if (s.isEmpty()) { return; } string tofile(qs2path(s)); // Work around qt 5.9-11 bug (linux at least): defaultSuffix is // not added to saved file name string suff = path_suffix(tofile); if (suff.compare("rclq")) { tofile += ".rclq"; } LOGDEB("RclMain::saveLastQuery: XML: [" << xml << "]\n"); string reason; if (!stringtofile(xml, tofile.c_str(), reason)) { QMessageBox::warning(this, tr("Write failed"), tr("Could not write to file")); } return; } void RclMain::loadSavedQuery() { QString s = QFileDialog::getOpenFileName(this, "Open saved query", prevDir(), tr("Saved Queries (*.rclq)")); if (s.isEmpty()) return; string fromfile(qs2path(s)); string xml, reason; if (!file_to_string(fromfile, xml, &reason)) { QMessageBox::warning(this, tr("Read failed"), tr("Could not open file: ") + QString::fromUtf8(reason.c_str())); return; } // Try to parse as advanced search SearchData std::shared_ptr sd = xmlToSearchData(xml, false); if (sd) { showAdvSearchDialog(); asearchform->fromSearch(sd); return; } LOGDEB("loadSavedQuery: Not advanced search. Parsing as simple search\n"); // Try to parse as Simple Search SSearchDef sdef; if (xmlToSSearch(xml, sdef)) { if (sSearch->fromXML(sdef)) return; } QMessageBox::warning(this, tr("Load error"), tr("Could not load saved query")); } recoll-1.36.1/qtgui/idxsched.ui0000644000175000017500000001045714410615043013273 00000000000000 IdxSchedW 0 0 504 403 Index scheduling setup 0 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> indexing can run permanently, indexing files as they change, or run at discrete intervals. </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reading the manual may help you to decide between these approaches (press F1). </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This tool can help you set up a schedule to automate batch indexing runs, or start real time indexing when you log in (or both, which rarely makes sense). </p></body></html> Qt::AutoText true Cron scheduling false The tool will let you decide at what time indexing should run and will install a crontab entry. Real time indexing start up false Decide if real time indexing will be started when you log in (only for the default index). Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() IdxSchedW accept() 248 254 157 274 buttonBox rejected() IdxSchedW reject() 316 260 286 274 recoll-1.36.1/qtgui/firstidx.h0000644000175000017500000000211214410615043013133 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _FIRSTIDX_H_INCLUDED_ #define _FIRSTIDX_H_INCLUDED_ #include "ui_firstidx.h" class FirstIdxDialog : public QDialog, public Ui::FirstIdxDialog { Q_OBJECT public: FirstIdxDialog(QWidget * parent = 0) : QDialog(parent) { setupUi(this); } }; #endif /* _FIRSTIDX_H_INCLUDED_ */ recoll-1.36.1/qtgui/rclm_sidefilters.cpp0000644000175000017500000001145114427373216015205 00000000000000/* Copyright (C) 2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include "log.h" #include "rclmain_w.h" #include "idxmodel.h" #include "guiutils.h" void RclMain::populateSideFilters(SideFilterUpdateReason reason) { if (m_idxtreemodel && reason == SFUR_USERCONFIG && (m_idxtreemodel->getDepth() == prefs.idxFilterTreeDepth && m_idxtreemodel->getEDbs() == *getCurrentExtraDbs())) { // The filter depths and set of external indexes are the only things which may impact us // after the user has changed the GUI preferences. So no need to lose the current user // selections if these did not change. return; } auto old_idxtreemodel = m_idxtreemodel; m_idxtreemodel = new IdxTreeModel(prefs.idxFilterTreeDepth, *getCurrentExtraDbs(), idxTreeView); m_idxtreemodel->populate(); m_idxtreemodel->setHeaderData(0, Qt::Horizontal, QVariant(tr("Filter directories"))); idxTreeView->setModel(m_idxtreemodel); // Reset the current filter spec, the selection is gone. sideFilterChanged(); if (nullptr != old_idxtreemodel) old_idxtreemodel->deleteLater(); if (reason == SFUR_INIT) { idxTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); idxTreeView->setSelectionMode(QAbstractItemView::MultiSelection); connect(idxTreeView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(sideFilterChanged())); minDateFilterDTEDT->setCalendarPopup(true); maxDateFilterDTEDT->setCalendarPopup(true); minDateFilterDTEDT->setDate(QDate::currentDate()); maxDateFilterDTEDT->setDate(QDate::currentDate()); connect(minDateFilterDTEDT, SIGNAL(dateChanged(const QDate&)), this, SLOT(sideFilterChanged())); connect(maxDateFilterDTEDT, SIGNAL(dateChanged(const QDate&)), this, SLOT(sideFilterChanged())); connect(dateFilterCB, SIGNAL(toggled(bool)), this, SLOT(sideFilterChanged())); connect(dateFilterCB, SIGNAL(toggled(bool)), minDateFilterDTEDT, SLOT(setEnabled(bool))); connect(dateFilterCB, SIGNAL(toggled(bool)), maxDateFilterDTEDT, SLOT(setEnabled(bool))); #ifdef EXT4_BIRTH_TIME birthDateFilterCB->show(); minBirthDateFilterDTEDT->show(); maxBirthDateFilterDTEDT->show(); minBirthDateFilterDTEDT->setCalendarPopup(true); maxBirthDateFilterDTEDT->setCalendarPopup(true); minBirthDateFilterDTEDT->setDate(QDate::currentDate()); maxBirthDateFilterDTEDT->setDate(QDate::currentDate()); connect(minBirthDateFilterDTEDT, SIGNAL(dateChanged(const QDate&)), this, SLOT(sideFilterChanged())); connect(maxBirthDateFilterDTEDT, SIGNAL(dateChanged(const QDate&)), this, SLOT(sideFilterChanged())); connect(birthDateFilterCB, SIGNAL(toggled(bool)), this, SLOT(sideFilterChanged())); connect(birthDateFilterCB, SIGNAL(toggled(bool)), minBirthDateFilterDTEDT, SLOT(setEnabled(bool))); connect(birthDateFilterCB, SIGNAL(toggled(bool)), maxBirthDateFilterDTEDT, SLOT(setEnabled(bool))); #else birthDateFilterCB->hide(); minBirthDateFilterDTEDT->hide(); maxBirthDateFilterDTEDT->hide(); #endif } } void RclMain::sideFilterChanged() { setFiltSpec(); initiateQuery(); } void RclMain::enableSideFilters(bool enable) { idxTreeView->setEnabled(enable); dateFilterCB->setEnabled(enable); minDateFilterDTEDT->setEnabled(enable && dateFilterCB->isChecked()); maxDateFilterDTEDT->setEnabled(enable && dateFilterCB->isChecked()); } void RclMain::clearDirFilter() { idxTreeView->clearSelection(); } std::vector RclMain::idxTreeGetDirs() { std::vector out; auto selmodel = idxTreeView->selectionModel(); if (nullptr != selmodel) { auto selected = selmodel->selectedIndexes(); std::string clause; for (int i = 0; i < selected.size(); i++) { out.push_back(qs2path(selected[i].data(Qt::ToolTipRole).toString())); } } return out; } recoll-1.36.1/qtgui/webcache.h0000644000175000017500000000465014444307651013063 00000000000000/* Copyright (C) 2016-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _WEBCACHE_H_INCLUDED_ #define _WEBCACHE_H_INCLUDED_ #include "autoconfig.h" #include #include #include #include "ui_webcache.h" #include class WebcacheModelInternal; class QCloseEvent; class WebStore; class WebcacheModel : public QAbstractTableModel { Q_OBJECT public: WebcacheModel(QObject *parent = 0); ~WebcacheModel(); WebcacheModel(const WebcacheModel&) = delete; WebcacheModel& operator=(const WebcacheModel&) = delete; // Reimplemented methods virtual int rowCount (const QModelIndex& = QModelIndex()) const; virtual int columnCount(const QModelIndex& = QModelIndex()) const; virtual QVariant headerData (int col, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole ) const; bool deleteIdx(unsigned int idx); std::string getURL(unsigned int idx); std::string getData(unsigned int idx); public slots: void setSearchFilter(const QString&); void reload(); signals: void headerChanged(WebStore *); private: WebcacheModelInternal *m; }; class RclMain; class WebcacheEdit : public QDialog, public Ui::Webcache { Q_OBJECT public: WebcacheEdit(RclMain *parent); public slots: void saveColState(); void createPopupMenu(const QPoint&); void deleteSelected(); void copyURL(); void saveToFile(); void onHeaderChanged(WebStore *); protected: void closeEvent(QCloseEvent *); private: WebcacheModel *m_model; RclMain *m_recoll; bool m_modified; }; #endif /* _WEBCACHE_H_INCLUDED_ */ recoll-1.36.1/qtgui/recoll.pro.in0000644000175000017500000002736314521133076013560 00000000000000# # This only needs editing by the configure script on Unix platforms. It can be readily used on MacOS # and Windows (just rename the file to recoll.pro) # TEMPLATE = app LANGUAGE = C++ TARGET = recoll DEFINES += BUILDING_RECOLL DEFINES += BUILDING_RECOLLGUI QT += xml widgets printsupport CONFIG += qt warn_on thread release lrelease HEADERS += \ actsearch_w.h \ advsearch_w.h \ advshist.h \ confgui/confgui.h \ confgui/confguiindex.h \ firstidx.h \ fragbuts.h \ idxmodel.h \ idxsched.h \ preview_load.h \ preview_plaintorich.h \ preview_w.h \ ptrans_w.h \ rclhelp.h \ rclmain_w.h \ reslist.h \ restable.h \ scbase.h \ searchclause_w.h \ snippets_w.h \ specialindex.h \ spell_w.h \ ssearch_w.h \ systray.h \ uiprefs_w.h \ viewaction_w.h \ webcache.h \ widgets/editdialog.h \ widgets/listdialog.h \ widgets/qxtconfirmationmessage.h SOURCES += \ actsearch_w.cpp \ advsearch_w.cpp \ advshist.cpp \ confgui/confgui.cpp \ confgui/confguiindex.cpp \ fragbuts.cpp \ guiutils.cpp \ idxmodel.cpp \ main.cpp \ multisave.cpp \ preview_load.cpp \ preview_plaintorich.cpp \ preview_w.cpp \ ptrans_w.cpp \ rclhelp.cpp \ rclm_idx.cpp \ rclm_menus.cpp \ rclm_preview.cpp \ rclm_saveload.cpp \ rclm_sidefilters.cpp \ rclm_view.cpp \ rclm_wins.cpp \ rclmain_w.cpp \ rclzg.cpp \ reslist.cpp \ respopup.cpp \ restable.cpp \ scbase.cpp \ searchclause_w.cpp \ snippets_w.cpp \ spell_w.cpp \ ssearch_w.cpp \ systray.cpp \ uiprefs_w.cpp \ viewaction_w.cpp \ webcache.cpp \ widgets/qxtconfirmationmessage.cpp \ xmltosd.cpp FORMS = \ actsearch.ui \ advsearch.ui \ firstidx.ui \ idxsched.ui \ preview.ui \ ptrans.ui \ rclmain.ui \ restable.ui \ snippets.ui \ specialindex.ui \ spell.ui \ ssearchb.ui \ uiprefs.ui \ viewaction.ui \ webcache.ui \ widgets/editdialog.ui \ widgets/listdialog.ui RESOURCES = recoll.qrc TRANSLATIONS = \ i18n/recoll_cs.ts \ i18n/recoll_da.ts \ i18n/recoll_de.ts \ i18n/recoll_el.ts \ i18n/recoll_es.ts \ i18n/recoll_fr.ts \ i18n/recoll_hu.ts \ i18n/recoll_it.ts \ i18n/recoll_ja.ts \ i18n/recoll_ko.ts \ i18n/recoll_lt.ts \ i18n/recoll_nl.ts \ i18n/recoll_pl.ts \ i18n/recoll_ru.ts \ i18n/recoll_sv.ts \ i18n/recoll_tr.ts \ i18n/recoll_uk.ts \ i18n/recoll_xx.ts \ i18n/recoll_zh_CN.ts \ i18n/recoll_zh.ts windows { # QT += webkit webkitwidgets # DEFINES += USING_WEBKIT QT += widgets webenginewidgets DEFINES += USING_WEBENGINE DEFINES += PSAPI_VERSION=1 DEFINES += __WIN32__ DEFINES += UNICODE RC_FILE = recoll.rc QCBUILDLOC = Desktop_Qt_5_15_2_MSVC2019_32bit HEADERS += \ winschedtool.h SOURCES += \ winschedtool.cpp FORMS += \ winschedtool.ui INCLUDEPATH += ../common ../index ../internfile ../query ../unac \ ../utils ../aspell ../rcldb ../qtgui ../xaposix \ confgui widgets contains(QMAKE_CC, gcc){ # MingW QMAKE_CXXFLAGS += -Wno-unused-parameter LIBS += \ C:/recoll/src/windows/build-librecoll-Desktop_Qt_5_8_0_MinGW_32bit-Release/release/librecoll.dll } contains(QMAKE_CC, cl){ # MSVC RECOLLDEPS = ../../../recolldeps/msvc DEFINES += USING_STATIC_LIBICONV SOURCES += ../windows/getopt.cc PRE_TARGETDEPS = \ ../windows/build-librecoll-$$QCBUILDLOC-Release/release/librecoll.lib LIBS += \ -L../windows/build-librecoll-$$QCBUILDLOC-Release/release -llibrecoll \ $$RECOLLDEPS/libxml2/libxml2-2.9.4+dfsg1/win32/bin.msvc/libxml2.lib \ $$RECOLLDEPS/libxslt/libxslt-1.1.29/win32/bin.msvc/libxslt.lib \ -L../windows/build-libxapian-$$QCBUILDLOC-Release/release -llibxapian \ -L$$RECOLLDEPS/build-libiconv-$$QCBUILDLOC-Release/release -llibiconv \ $$RECOLLDEPS/libmagic/src/lib/libmagic.lib \ $$RECOLLDEPS/regex/libregex.lib \ $$RECOLLDEPS/zlib-1.2.11/zdll.lib \ -lrpcrt4 -lws2_32 -luser32 -lshell32 -lshlwapi -lpsapi -lkernel32 } } macx: { QCBUILDLOC=Qt_6_4_2_for_macOS # QT += webkit webkitwidgets # DEFINES += USING_WEBKIT QT += widgets webenginewidgets DEFINES += USING_WEBENGINE QMAKE_APPLE_DEVICE_ARCHS = x86_64 arm64 QMAKE_CXXFLAGS += -pthread -Wno-unused-parameter DEFINES += RECOLL_AS_MAC_BUNDLE HEADERS += \ crontool.h \ rtitool.h SOURCES += \ ../utils/closefrom.cpp \ ../utils/execmd.cpp \ ../utils/netcon.cpp \ ../utils/rclionice.cpp \ crontool.cpp \ rtitool.cpp FORMS += \ crontool.ui \ rtitool.ui INCLUDEPATH += ../common ../index ../internfile ../query ../unac \ ../utils ../aspell ../rcldb ../qtgui ../xaposix \ confgui widgets LIBS += \ ../windows/build-librecoll-$$QCBUILDLOC-Release/liblibrecoll.a \ ../windows/build-libxapian-$$QCBUILDLOC-Release/liblibxapian.a \ -lxslt -lxml2 -liconv -lz ICON = images/recoll.icns APP_DOC.files = \ ../doc/user/usermanual.html \ ../doc/user/docbook-xsl.css APP_DOC.path = Contents/Resources/doc APP_EXAMPLES.files = \ ../sampleconf/fragment-buttons.xml \ ../sampleconf/fields \ ../sampleconf/recoll.conf \ ../sampleconf/mimeconf \ ../sampleconf/mimeview \ ../sampleconf/mimemap \ ../sampleconf/recoll.qss \ ../sampleconf/recoll-dark.qss \ ../sampleconf/recoll-dark.css APP_EXAMPLES.path = Contents/Resources/examples APP_EXAMPLES_MAC.files = \ ../sampleconf/macos/mimeview APP_EXAMPLES_MAC.path = Contents/Resources/examples/macos APP_FILTERS.files = \ ../filters/abiword.xsl \ ../filters/cmdtalk.py \ ../filters/fb2.xsl \ ../filters/gnumeric.xsl \ ../filters/kosplitter.py \ ../filters/msodump.zip \ ../filters/okular-note.xsl \ ../filters/opendoc-body.xsl \ ../filters/opendoc-flat.xsl \ ../filters/opendoc-meta.xsl \ ../filters/openxml-xls-body.xsl \ ../filters/openxml-word-body.xsl \ ../filters/openxml-meta.xsl \ ../filters/ppt-dump.py \ ../filters/rcl7z.py \ ../filters/rclaptosidman \ ../filters/rclaudio.py \ ../filters/rclbasehandler.py \ ../filters/rclbibtex.sh \ ../filters/rclcheckneedretry.sh \ ../filters/rclchm.py \ ../filters/rcldia.py \ ../filters/rcldjvu.py \ ../filters/rcldoc.py \ ../filters/rcldvi \ ../filters/rclepub.py \ ../filters/rclepub1.py \ ../filters/rclexec1.py \ ../filters/rclexecm.py \ ../filters/rclfb2.py \ ../filters/rclgaim \ ../filters/rclgenxslt.py \ ../filters/rclhwp.py \ ../filters/rclics.py \ ../filters/rclimg \ ../filters/rclimg.py \ ../filters/rclinfo.py \ ../filters/rclkar.py \ ../filters/rclkwd \ ../filters/rcllatinclass.py \ ../filters/rcllatinstops.zip \ ../filters/rcllyx \ ../filters/rclman \ ../filters/rclmidi.py \ ../filters/rclocrcache.py \ ../filters/rclocr.py \ ../filters/rclocrabbyy.py \ ../filters/rclocrtesseract.py \ ../filters/rclopxml.py \ ../filters/rclpdf.py \ ../filters/rclppt.py \ ../filters/rclps \ ../filters/rclpst.py \ ../filters/rclpurple \ ../filters/rclpython.py \ ../filters/rclrar.py \ ../filters/rclrtf.py \ ../filters/rclscribus \ ../filters/rclshowinfo \ ../filters/rcltar.py \ ../filters/rcltex \ ../filters/rcltext.py \ ../filters/rcluncomp \ ../filters/rcluncomp.py \ ../filters/rclwar.py \ ../filters/rclxls.py \ ../filters/rclxml.py \ ../filters/rclxmp.py \ ../filters/rclxslt.py \ ../filters/rclzip.py \ ../filters/recoll-we-move-files.py \ ../filters/recollepub.zip \ ../filters/svg.xsl \ ../filters/xls-dump.py \ ../filters/xlsxmltocsv.py \ ../filters/xml.xsl \ ../python/recoll/recoll/conftree.py \ ../python/recoll/recoll/rclconfig.py APP_FILTERS.path = Contents/Resources/filters APP_IMAGES.files = \ images/asearch.png \ images/cancel.png \ images/close.png \ images/clock.png \ images/menu.png \ images/code-block.png \ images/down.png \ images/firstpage.png \ images/history.png \ images/interro.png \ images/nextpage.png \ images/prevpage.png \ images/recoll.icns \ images/recoll.png \ images/sortparms.png \ images/spell.png \ images/table.png \ images/up.png \ mtpics/License_sidux.txt \ mtpics/README \ mtpics/aptosid-book.png \ mtpics/aptosid-manual-copyright.txt \ mtpics/aptosid-manual.png \ mtpics/archive.png \ mtpics/book.png \ mtpics/bookchap.png \ mtpics/document.png \ mtpics/drawing.png \ mtpics/emblem-symbolic-link.png \ mtpics/folder.png \ mtpics/html.png \ mtpics/image.png \ mtpics/message.png \ mtpics/mozilla_doc.png \ mtpics/pdf.png \ mtpics/pidgin.png \ mtpics/postscript.png \ mtpics/presentation.png \ mtpics/sidux-book.png \ mtpics/soffice.png \ mtpics/source.png \ mtpics/sownd.png \ mtpics/spreadsheet.png \ mtpics/text-x-python.png \ mtpics/txt.png \ mtpics/video.png \ mtpics/wordprocessing.png APP_IMAGES.path = Contents/Resources/images QMAKE_BUNDLE_DATA = APP_EXAMPLES APP_EXAMPLES_MAC APP_FILTERS APP_IMAGES APP_DOC } unix:!macx { VPATH = @srcdir@ @QMAKE_ENABLE_WEBKIT@QT += webkitwidgets @QMAKE_ENABLE_WEBKIT@DEFINES += USING_WEBKIT @QMAKE_ENABLE_WEBENGINE@DEFINES += USING_WEBENGINE @QMAKE_ENABLE_WEBENGINE@QT += webenginewidgets @QMAKE_ENABLE_ZEITGEIST@QT += dbus @QMAKE_ENABLE_ZEITGEIST@QMAKE_CXXFLAGS += -DUSE_ZEITGEIST @QMAKE_ENABLE_GUIDEBUG@CONFIG += force_debug_info contains(DEFINE_BTIME, 1) { DEFINES += EXT4_BIRTH_TIME } HEADERS += crontool.h \ rtitool.h SOURCES += crontool.cpp \ rtitool.cpp FORMS += crontool.ui \ rtitool.ui UI_DIR = .ui MOC_DIR = .moc OBJECTS_DIR = .obj LIBS += -L../.libs -lrecoll # You will need LD_PRELOAD=/path/to/libasan.xx because -lasan need to be # first in libs, so can't use LIBS += # QMAKE_CXXFLAGS += -fsanitize=address -fno-omit-frame-pointer # Note: libdir may be substituted with sthing like $(exec_prefix)/lib # at this point and will go as such in the Makefile. Expansion will be # completed at make time. @PUBLIC_LIB_FALSE@LIBS += -Wl,-rpath=@libdir@/recoll LIBS += @XAPIAN_LIBS@ \ @LIBICONV@ $(BDYNAMIC) @LIBQZEITGEIST@ @XSLT_LIBS@ -lz INCLUDEPATH += ../common @srcdir@/../common @srcdir@/../index \ @srcdir@/../internfile @srcdir@/../query @srcdir@/../unac \ @srcdir@/../utils @srcdir@/../aspell @srcdir@/../rcldb \ @srcdir@/../qtgui @srcdir@/../xaposix @srcdir@/confgui \ @srcdir@/widgets DEPENDPATH += $$INCLUDEPATH isEmpty(PREFIX) { PREFIX = /usr/local } message("Prefix is $$PREFIX") DEFINES += PREFIX=\\\"$$PREFIX\\\" # Installation stuff target.path = "$$PREFIX/bin" imdata.files = @srcdir@/mtpics/*.png imdata.path = $$PREFIX/share/recoll/images trdata.files = .qm/*.qm trdata.path = $$PREFIX/share/recoll/translations desktop.files += @srcdir@/../desktop/recoll-searchgui.desktop desktop.path = $$PREFIX/share/applications/ icona.files += @srcdir@/../desktop/recoll.png icona.path = $$PREFIX/share/icons/hicolor/48x48/apps/ iconb.files += @srcdir@/../desktop/recoll.png iconb.path = $$PREFIX/share/pixmaps/ appdata.files = @srcdir@/../desktop/recoll.appdata.xml appdata.path = $$PREFIX/share/metainfo/ INSTALLS += target imdata trdata desktop icona iconb appdata UNAME = $$system(uname -s) contains( UNAME, [lL]inux ) { LIBS += -ldl -lX11 } contains( UNAME, SunOS ) { LIBS += -ldl } } recoll-1.36.1/qtgui/winschedtool.h0000644000175000017500000000242614427373216014024 00000000000000/* Copyright (C) 2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _WINSCHEDTOOL_W_H_INCLUDED_ #define _WINSCHEDTOOL_W_H_INCLUDED_ #include "ui_winschedtool.h" class QPushButton; class ExecCmd; class WinSchedToolW : public QDialog, public Ui::WinSchedToolW { Q_OBJECT public: WinSchedToolW(QWidget * parent = 0) : QDialog(parent) { setupUi(this); init(); } QPushButton *startButton{nullptr}; private slots: void startWinScheduler(); private: void init(); ExecCmd *m_cmd{nullptr}; }; #endif /* _WINSCHEDTOOL_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/snippets_w.h0000644000175000017500000000624614473317547013527 00000000000000/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SNIPPETS_W_H_INCLUDED_ #define _SNIPPETS_W_H_INCLUDED_ #include "autoconfig.h" #include #include #include "rcldoc.h" #include "docseq.h" #include "rclmain_w.h" #include "ui_snippets.h" class SnippetsW : public QWidget, public Ui::Snippets { Q_OBJECT public: SnippetsW(Rcl::Doc doc, std::shared_ptr source, QWidget* parent = 0) : QWidget(parent) { setupUi((QDialog*)this); init(); onSetDoc(doc, source); } /** List shortcuts so that the prefs can be edited before any preview is created */ static void listShortcuts(); public slots: virtual void onLinkClicked(const QUrl &); virtual void onSetDoc(Rcl::Doc doc, std::shared_ptr source); virtual void createPopupMenu(const QPoint& pos); virtual void onNewShortcuts(); protected slots: virtual void slotEditFind(); virtual void slotEditFindNext(); virtual void slotEditFindPrevious(); virtual void slotSearchTextChanged(const QString&); virtual void slotZoomIn(); virtual void slotZoomOut(); virtual void reloadByRelevance(); virtual void reloadByPage(); virtual void onUiPrefsChanged(); signals: void startNativeViewer(Rcl::Doc, int pagenum, QString term, int line); void zoomIn(); void zoomOut(); private: void init(); std::shared_ptr m_source; Rcl::Doc m_doc; bool m_sortingByPage; QShortcut *m_find1sc{nullptr}; QShortcut *m_find2sc{nullptr}; QShortcut *m_findnextsc{nullptr}; QShortcut *m_findprevsc{nullptr}; QShortcut *m_hidesc{nullptr}; QShortcut *m_zisc{nullptr}; QShortcut *m_zosc{nullptr}; }; #ifdef USING_WEBENGINE #include // Subclass the page to hijack the link clicks class SnipWebPage: public QWebEnginePage { Q_OBJECT public: SnipWebPage(SnippetsW *parent) : QWebEnginePage((QWidget *)parent), m_parent(parent) {} protected: virtual bool acceptNavigationRequest(const QUrl& url, NavigationType tp, bool) { if (tp == QWebEnginePage::NavigationTypeLinkClicked) { m_parent->onLinkClicked(url); return false; } else { return true; } } private: SnippetsW *m_parent; }; #endif #endif /* _SNIPPETS_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/ptrans.ui0000644000175000017500000000775014410615043013011 00000000000000 EditTransBase 0 0 649 362 Path Translations Setting path translations for false Select one or several file types, then use the controls in the frame below to change how they are processed QFrame::StyledPanel QFrame::Sunken QAbstractItemView::NoEditTriggers QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows true true 2 true false 300 20 false true false Add false Delete Qt::Horizontal 40 20 Cancel Save recoll-1.36.1/qtgui/snippets_w.cpp0000644000175000017500000003011614473317525014047 00000000000000/* Copyright (C) 2012-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #if defined(USING_WEBKIT) # include # include # include # define QWEBSETTINGS QWebSettings # define QWEBPAGE QWebPage #elif defined(USING_WEBENGINE) // Notes for WebEngine // - All links must begin with http:// for acceptNavigationRequest to be // called. // - The links passed to acceptNav.. have the host part // lowercased -> we change S0 to http://h/S0, not http://S0 # include # include # include # define QWEBSETTINGS QWebEngineSettings # define QWEBPAGE QWebEnginePage #else #include #endif #include #include "log.h" #include "recoll.h" #include "snippets_w.h" #include "guiutils.h" #include "rcldb.h" #include "rclhelp.h" #include "plaintorich.h" #include "scbase.h" #include "readfile.h" using namespace std; #if defined(USING_WEBKIT) #define browser ((QWebView*)browserw) #elif defined(USING_WEBENGINE) #define browser ((QWebEngineView*)browserw) #else #define browser ((QTextBrowser*)browserw) #endif class PlainToRichQtSnippets : public PlainToRich { public: virtual string startMatch(unsigned int) { return string(""); } virtual string endMatch() { return string(""); } }; static PlainToRichQtSnippets g_hiliter; void SnippetsW::init() { m_sortingByPage = prefs.snipwSortByPage; QPushButton *searchButton = new QPushButton(tr("Search")); searchButton->setAutoDefault(false); buttonBox->addButton(searchButton, QDialogButtonBox::ActionRole); searchFM->hide(); onNewShortcuts(); connect(&SCBase::scBase(), SIGNAL(shortcutsChanged()), this, SLOT(onNewShortcuts())); QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); if (closeButton) connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); connect(searchButton, SIGNAL(clicked()), this, SLOT(slotEditFind())); connect(searchLE, SIGNAL(textChanged(const QString&)), this, SLOT(slotSearchTextChanged(const QString&))); connect(nextPB, SIGNAL(clicked()), this, SLOT(slotEditFindNext())); connect(prevPB, SIGNAL(clicked()), this, SLOT(slotEditFindPrevious())); // Get rid of the placeholder widget created from the .ui delete browserw; #if defined(USING_WEBKIT) browserw = new QWebView(this); verticalLayout->insertWidget(0, browserw); browser->setUrl(QUrl(QString::fromUtf8("about:blank"))); connect(browser, SIGNAL(linkClicked(const QUrl &)), this, SLOT(onLinkClicked(const QUrl &))); browser->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); browser->page()->currentFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); browserw->setContextMenuPolicy(Qt::CustomContextMenu); connect(browserw, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(createPopupMenu(const QPoint&))); #elif defined(USING_WEBENGINE) browserw = new QWebEngineView(this); verticalLayout->insertWidget(0, browserw); browser->setPage(new SnipWebPage(this)); // Stylesheet TBD browserw->setContextMenuPolicy(Qt::CustomContextMenu); connect(browserw, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(createPopupMenu(const QPoint&))); #else browserw = new QTextBrowser(this); verticalLayout->insertWidget(0, browserw); connect(browser, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(onLinkClicked(const QUrl &))); browser->setReadOnly(true); browser->setUndoRedoEnabled(false); browser->setOpenLinks(false); browser->setTabChangesFocus(true); if (prefs.reslistfontfamily != "") { QFont nfont(prefs.reslistfontfamily, prefs.reslistfontsize); browser->setFont(nfont); } else { browser->setFont(QFont()); } #endif } void SnippetsW::onNewShortcuts() { SETSHORTCUT(this, "snippets:156", tr("Snippets Window"), tr("Find"), "Ctrl+F", m_find1sc, slotEditFind); SETSHORTCUT(this, "snippets:158", tr("Snippets Window"), tr("Find (alt)"), "/", m_find2sc, slotEditFind); SETSHORTCUT(this, "snippets:160", tr("Snippets Window"), tr("Find next"), "F3", m_findnextsc, slotEditFindNext); SETSHORTCUT(this, "snippets:162", tr("Snippets Window"), tr("Find previous"), "Shift+F3", m_findprevsc, slotEditFindPrevious); SETSHORTCUT(this, "snippets:164", tr("Snippets Window"), tr("Close window"), "Esc", m_hidesc, hide); auto sseq = QKeySequence(QKeySequence::ZoomIn).toString(); SETSHORTCUT(this, "snippets:166", tr("Snippets Window"), tr("Increase font size"), sseq, m_zisc, slotZoomIn); sseq = QKeySequence(QKeySequence::ZoomOut).toString(); SETSHORTCUT(this, "snippets:168", tr("Snippets Window"), tr("Decrease font size"), sseq, m_zosc, slotZoomOut); } void SnippetsW::listShortcuts() { LISTSHORTCUT(this, "snippets:156", tr("Snippets Window"), tr("Find"), "Ctrl+F", m_find1sc, slotEditFind); LISTSHORTCUT(this, "snippets:158", tr("Snippets Window"), tr("Find (alt)"), "/", m_find2sc, slotEditFind); LISTSHORTCUT(this, "snippets:160",tr("Snippets Window"), tr("Find next"), "F3", m_find2sc, slotEditFindNext); LISTSHORTCUT(this, "snippets:162",tr("Snippets Window"), tr("Find previous"), "Shift+F3", m_find2sc, slotEditFindPrevious); LISTSHORTCUT(this, "snippets:164", tr("Snippets Window"), tr("Close window"), "Esc", m_hidesc, hide); auto sseq = QKeySequence(QKeySequence::ZoomIn).toString(); LISTSHORTCUT(this, "snippets:166", tr("Snippets Window"), tr("Increase font size"), sseq, m_zisc, slotZoomIn); sseq = QKeySequence(QKeySequence::ZoomOut).toString(); LISTSHORTCUT(this, "snippets:168", tr("Snippets Window"), tr("Decrease font size"), sseq, m_zosc, slotZoomOut); } void SnippetsW::createPopupMenu(const QPoint& pos) { QMenu *popup = new QMenu(this); if (m_sortingByPage) { popup->addAction(tr("Sort By Relevance"), this, SLOT(reloadByRelevance())); } else { popup->addAction(tr("Sort By Page"), this, SLOT(reloadByPage())); } popup->popup(mapToGlobal(pos)); } void SnippetsW::reloadByRelevance() { m_sortingByPage = false; onSetDoc(m_doc, m_source); } void SnippetsW::reloadByPage() { m_sortingByPage = true; onSetDoc(m_doc, m_source); } void SnippetsW::onSetDoc(Rcl::Doc doc, std::shared_ptr source) { m_doc = doc; m_source = source; if (!source) return; // Make title out of file name if none yet string titleOrFilename; string utf8fn; m_doc.getmeta(Rcl::Doc::keytt, &titleOrFilename); m_doc.getmeta(Rcl::Doc::keyfn, &utf8fn); if (titleOrFilename.empty()) { titleOrFilename = utf8fn; } QString title("Recoll - Snippets"); if (!titleOrFilename.empty()) { title += QString(" : ") + QString::fromUtf8(titleOrFilename.c_str()); } setWindowTitle(title); vector vpabs; source->getAbstract(m_doc, &g_hiliter, vpabs, prefs.snipwMaxLength, m_sortingByPage); std::string snipcss; if (!prefs.snipCssFile.isEmpty()) { file_to_string(qs2path(prefs.snipCssFile), snipcss); } ostringstream oss; oss << "\n" "\n"; oss << prefs.htmlHeaderContents() << snipcss; oss << "\n\n\n"; bool nomatch = true; for (const auto& snippet : vpabs) { if (snippet.page == -1) { oss << "" << "\n"; continue; } nomatch = false; oss << "" << "\n"; } oss << "
" << snippet.snippet << "
"; if (snippet.page > 0) { oss << "" << "P. " << snippet.page << ""; } else if (snippet.line > 0) { oss << "" << "L. " << snippet.line << ""; } oss << "" << snippet.snippet << "
" << "\n"; if (nomatch) { oss.str("\n"); oss << qs2utf8s(tr("

Sorry, no exact match was found within limits. " "Probably the document is very big and the snippets " "generator got lost in a maze...

")); } oss << "\n"; #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) browser->setHtml(u8s2qs(oss.str())); #else browser->clear(); browser->append("."); browser->clear(); browser->insertHtml(u8s2qs(oss.str())); browser->moveCursor (QTextCursor::Start); browser->ensureCursorVisible(); #endif raise(); } void SnippetsW::slotEditFind() { searchFM->show(); searchLE->selectAll(); searchLE->setFocus(); } void SnippetsW::slotEditFindNext() { if (!searchFM->isVisible()) slotEditFind(); #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) browser->findText(searchLE->text()); #else browser->find(searchLE->text()); #endif } void SnippetsW::slotEditFindPrevious() { if (!searchFM->isVisible()) slotEditFind(); #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) browser->findText(searchLE->text(), QWEBPAGE::FindBackward); #else browser->find(searchLE->text(), QTextDocument::FindBackward); #endif } void SnippetsW::onUiPrefsChanged() { if (m_sortingByPage) { reloadByPage(); } else { reloadByRelevance(); } } void SnippetsW::slotSearchTextChanged(const QString& txt) { #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) browser->findText(txt); #else // Cursor thing is so that we don't go to the next occurrence with // each character, but rather try to extend the current match QTextCursor cursor = browser->textCursor(); cursor.setPosition(cursor.anchor(), QTextCursor::KeepAnchor); browser->setTextCursor(cursor); browser->find(txt); #endif } void SnippetsW::slotZoomIn() { emit zoomIn(); } void SnippetsW::slotZoomOut() { emit zoomOut(); } void SnippetsW::onLinkClicked(const QUrl &url) { string ascurl = qs2u8s(url.toString()).substr(9); LOGDEB("Snippets::onLinkClicked: [" << ascurl << "]\n"); if (ascurl.size() > 3) { int what = ascurl[0]; switch (what) { case 'P': case 'L': { string::size_type numpos = ascurl.find_first_of("0123456789"); if (numpos == string::npos) return; int page = -1, line = -1; if (what == 'P') { page = atoi(ascurl.c_str() + numpos); } else { line = atoi(ascurl.c_str() + numpos); } string::size_type termpos = ascurl.find_first_of("T"); string term; if (termpos != string::npos) term = ascurl.substr(termpos+1); emit startNativeViewer(m_doc, page, u8s2qs(term), line); return; } } } LOGERR("Snippets::onLinkClicked: bad link [" << ascurl << "]\n"); } recoll-1.36.1/qtgui/guiutils.h0000644000175000017500000001537114473332232013164 00000000000000/* Copyright (C) 2005 Jean-Francois Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _GUIUTILS_H_INCLUDED_ #define _GUIUTILS_H_INCLUDED_ #include #include #include #include #include #include /** Holder for preferences (gets saved to user Qt prefs) */ class PrefsPack { public: // Simple search entry behaviour bool ssearchNoComplete; bool ssearchStartOnComplete; // Decide if we display the doc category filter control as a // toolbar+combobox or as a button group under simple search enum FilterCtlStyle {FCS_BT, FCS_CMB, FCS_MN}; int filterCtlStyle; int idxFilterTreeDepth{2}; int respagesize{8}; int historysize{-1}; int maxhltextkbs; QString reslistfontfamily; int reslistfontsize; QString qtermstyle; // CSS style for query terms in reslist and other places // Result list format string QString reslistformat; std::string creslistformat; QString reslistheadertext; // This is either empty or the contents of the recoll-dark.css // file if we are in dark mode. It is set in the header before the // possible user string above. Not saved/restored to prefs as it // is controled by darkMode QString darkreslistheadertext; // Date strftime format std::string reslistdateformat; // General Qt style sheet. QString qssFile; // Dark mode set-> style sheet is the default dark one. + special reslist header bool darkMode; QString snipCssFile; QString queryStemLang; enum ShowMode {SHOW_NORMAL, SHOW_MAX, SHOW_FULL}; int showmode{SHOW_NORMAL}; int pvwidth; // Preview window geom int pvheight; bool ssearchTypSav; // Remember last search mode (else always // start with same) int ssearchTyp{0}; // Use single app (default: xdg-open), instead of per-mime settings bool useDesktopOpen; // Remember sort state between invocations ? bool keepSort; QString sortField; bool sortActive; bool sortDesc; // Abstract preferences. Building abstracts can slow result display bool queryBuildAbstract{true}; bool queryReplaceAbstract{false}; // Synthetized abstract length (chars) and word context size (words) int syntAbsLen; int syntAbsCtx; // Abstract snippet separator QString abssep; // Snippets window max list size int snipwMaxLength; // Snippets window sort by page (dflt: by weight) bool snipwSortByPage; // Display Snippets links even for un-paged documents bool alwaysSnippets; bool startWithAdvSearchOpen{false}; // Try to display html if it exists in the internfile stack. bool previewHtml; bool previewActiveLinks; // Use
 tag to display highlighted text/plain inside html (else
    // we use 
at end of lines, which lets textedit wrap lines). enum PlainPre {PP_BR, PP_PRE, PP_PREWRAP}; int previewPlainPre; bool collapseDuplicates; bool showResultsAsTable; // Extra query indexes. This are stored in the history file, not qt prefs std::vector allExtraDbs; std::vector activeExtraDbs; // Temporary value while we run a saved query. Erased right after use. bool useTmpActiveExtraDbs{false}; std::vector tmpActiveExtraDbs; // Advanced search subdir restriction: we don't activate the last value // but just remember previously entered values QStringList asearchSubdirHist; // Textual history of simple searches (this is just the combobox list) QStringList ssearchHistory; // Make phrase out of search terms and add to search in simple search bool ssearchAutoPhrase; double ssearchAutoPhraseThreshPC; // Ignored file types in adv search (startup default) QStringList asearchIgnFilTyps; bool fileTypesByCats; // Words that are automatically turned to ext:xx specs in the query // language entry. QString autoSuffs; bool autoSuffsEnable; // Synonyms file QString synFile; bool synFileEnable; // Remembered term match mode int termMatchType{0}; // Program version that wrote this. Not used for now, in prevision // of the case where we might need an incompatible change int rclVersion{1505}; // Suppress all noises bool noBeeps; bool noToolbars{false}; bool noClearSearch{false}; bool noStatusBar{false}; bool noMenuBar{false}; bool noSSTypCMB{false}; bool resTableTextNoShift{false}; bool resTableNoHoverMeta{false}; bool noResTableHeader{false}; bool showResTableVHeader{false}; bool noResTableRowJumpSC{false}; bool showTrayIcon{false}; bool closeToTray{false}; bool trayMessages{false}; double wholeuiscale{1.0}; bool autoSpell{false}; int autoSpellMaxDist{1}; bool showcompleterhitcounts{false}; int ssearchCompleterHistCnt{0}; /*INSERTHERE*/ // See widgets/qxtconfirmationmessage. // Values -1/positive. -1 will trigger the dialog. int showTempFileWarning{-1}; // Advanced search window clause list state std::vector advSearchClauses; // Default paragraph format for result list static const char *dfltResListFormat; std::string stemlang(); void setupDarkCSS(); // HTML Header contents for both the result list, the snippets window and others std::string htmlHeaderContents(); // MIME types for which we prefer to use stored text from preview // rather than extracting the possibly nicer HTML because the // extractor is very slow. This is compiled in and there is no UI // for now. std::set preferStoredTextMimes{"application/x-hwp"}; // Scale font-sizes inside css or qss input and return changed sheet. The font-size statements // need to be on their own line. static std::string scaleFonts(const std::string& style, float multiplier); }; /** Global preferences record */ extern PrefsPack prefs; /** Read write settings from disk file */ extern void rwSettings(bool dowrite); extern QString g_stringAllStem, g_stringNoStem; #endif /* _GUIUTILS_H_INCLUDED_ */ recoll-1.36.1/qtgui/winschedtool.ui0000644000175000017500000000431414410615043014175 00000000000000 WinSchedToolW 0 0 508 292 Recoll Batch indexing 0 1 Qt::RichText true Start Windows Task Scheduler tool Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() WinSchedToolW accept() 248 254 157 274 buttonBox rejected() WinSchedToolW reject() 316 260 286 274 recoll-1.36.1/qtgui/recoll.qrc0000644000175000017500000000117214410615043013122 00000000000000 images/asearch.png images/cancel.png images/close.png images/code-block.png images/history.png images/nextpage.png images/prevpage.png images/firstpage.png images/sortparms.png images/spell.png images/table.png images/up.png images/down.png images/recoll.png images/interro.png images/clock.png images/menu.png recoll-1.36.1/qtgui/rclmain_w.h0000644000175000017500000002474214427373216013302 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef RCLMAIN_W_H #define RCLMAIN_W_H #include "autoconfig.h" #include #include #include #include "sortseq.h" #include "preview_w.h" #include "recoll.h" #include "advsearch_w.h" #include "uiprefs_w.h" #include "rcldb.h" #include "searchdata.h" #include "spell_w.h" #include #include "pathut.h" #include "guiutils.h" #include "rclutil.h" class SnippetsW; class IdxSchedW; class ExecCmd; class Preview; class ResTable; class CronToolW; class WinSchedToolW; class RTIToolW; class FragButs; class SpecIdxW; class WebcacheEdit; class ConfIndexW; class RclTrayIcon; class QShortcut; class QActionGroup; class ActSearchW; class IdxTreeModel; #include "ui_rclmain.h" class RclMain : public QMainWindow, public Ui::RclMainBase { Q_OBJECT public: RclMain(QWidget * parent = 0) : QMainWindow(parent) { setupUi(this); init(); } ~RclMain() {} RclMain(const RclMain&) = delete; RclMain& operator=(const RclMain&) = delete; QString getQueryDescription(); /** This is only called from main() to set an URL to be displayed (using recoll as a doc extracter for embedded docs */ virtual void setUrlToView(const QString& u) { m_urltoview = u; } /** Same usage: actually display the current urltoview */ virtual void viewUrl(); bool lastSearchSimple() { return m_searchIsSimple; } // Takes copies of the args instead of refs. Lazy and safe. void newDupsW(const Rcl::Doc doc, const std::vector dups); enum IndexerState {IXST_UNKNOWN, IXST_NOTRUNNING, IXST_RUNNINGMINE, IXST_RUNNINGNOTMINE}; IndexerState indexerState() const { return m_indexerState; } void enableTrayIcon(bool onoff); void setupToolbars(); void setupStatusBar(); void buildMenus(); void setupMenus(); void setupCategoryFiltering(); enum SideFilterUpdateReason{SFUR_INIT, SFUR_USERCONFIG, SFUR_INDEXCONTENTS}; public slots: virtual void fileExit(); virtual void periodic100(); virtual void toggleIndexing(); virtual void startMonitor(); virtual void bumpIndexing(); virtual void rebuildIndex(); virtual void specialIndex(); virtual void startSearch(std::shared_ptr sdata, bool issimple); virtual void previewClosed(Preview *w); virtual void showAdvSearchDialog(); virtual void showSpellDialog(); virtual void showWebcacheDialog(); virtual void showIndexStatistics(); virtual void showFragButs(); virtual void showSpecIdx(); virtual void showAboutDialog(); virtual void showMissingHelpers(); virtual void showActiveTypes(); virtual void startManual(); virtual void startManual(const std::string&); virtual void showDocHistory(); virtual void showExtIdxDialog(); virtual void setSynEnabled(bool); virtual void showUIPrefs(); virtual void showIndexConfig(); virtual void execIndexConfig(); virtual void showCronTool(); virtual void execCronTool(); virtual void showRTITool(); virtual void execRTITool(); virtual void showIndexSched(); virtual void execIndexSched(); virtual void setUIPrefs(); virtual void enableNextPage(bool); virtual void enablePrevPage(bool); virtual void docExpand(Rcl::Doc); virtual void showSubDocs(Rcl::Doc); virtual void showSnippets(Rcl::Doc); virtual void showActionsSearch(); virtual void startPreview(int docnum, Rcl::Doc doc, int keymods); virtual void startPreview(Rcl::Doc); virtual void startNativeViewer(Rcl::Doc, int pagenum = -1, QString term = QString(), int line = -1); virtual void openWith(Rcl::Doc, std::string); virtual void saveDocToFile(Rcl::Doc); virtual void populateSideFilters(SideFilterUpdateReason); virtual void previewNextInTab(Preview *, int sid, int docnum); virtual void previewPrevInTab(Preview *, int sid, int docnum); virtual void previewExposed(Preview *, int sid, int docnum); virtual void resetSearch(); virtual void eraseDocHistory(); virtual void eraseSearchHistory(); virtual void exportSimpleSearchHistory(); virtual void saveLastQuery(); virtual void loadSavedQuery(); virtual void setStemLang(QAction *id); virtual void adjustPrefsMenu(); virtual void catgFilter(int); virtual void catgFilter(QAction *); virtual void onFragmentsChanged(); virtual void initDbOpen(); virtual void toggleFullScreen(); virtual void on_actionSortByDateAsc_toggled(bool on); virtual void on_actionSortByDateDesc_toggled(bool on); virtual void on_actionShowResultsAsTable_toggled(bool on); virtual void onSortDataChanged(DocSeqSortSpec); virtual void onExtSortDataChanged(DocSeqSortSpec); virtual void resultCount(int); virtual void setFilterCtlStyle(int stl); virtual void showTrayMessage(const QString& text); virtual void onSetDescription(QString); virtual void onNewShortcuts(); virtual void toggleTable(); virtual void clearDirFilter(); virtual void hideToolTip(); virtual void zoomIn(); virtual void zoomOut(); virtual void setFiltSpec(); virtual void onSSearchTypeChanged(int); virtual void enableSideFilters(bool enable); private slots: virtual bool updateIdxStatus(); virtual void onWebcacheDestroyed(QObject *); virtual void onSSTypMenu(QAction *act); virtual void onSSTypCMB(int); virtual void sideFilterChanged(); signals: void docSourceChanged(std::shared_ptr); void stemLangChanged(const QString& lang); void sortDataChanged(DocSeqSortSpec); void resultsReady(); void searchReset(); void uiPrefsChanged(); protected: virtual void closeEvent(QCloseEvent *); virtual void showEvent(QShowEvent *); private: SnippetsW *m_snippets{0}; Preview *curPreview{0}; AdvSearch *asearchform{0}; UIPrefsDialog *uiprefs{0}; ConfIndexW *indexConfig{0}; IdxSchedW *indexSched{0}; #ifdef _WIN32 WinSchedToolW *cronTool{0}; #else CronToolW *cronTool{0}; #endif RTIToolW *rtiTool{0}; SpellW *spellform{0}; FragButs *fragbuts{0}; SpecIdxW *specidx{0}; QTimer *periodictimer{0}; WebcacheEdit *webcache{0}; ResTable *restable{0}; ResTable *m_dupsw{0}; bool displayingTable{false}; ActSearchW *actsearchw{0}; QAction *m_idNoStem{0}; QAction *m_idAllStem{0}; QToolBar *m_toolsTB{0}; QToolBar *m_resTB{0}; QFrame *m_filtFRM{0}; QComboBox *m_filtCMB{0}; QButtonGroup *m_filtBGRP{0}; QMenu *m_filtMN{0}; QShortcut *m_focustotablesc{0}; QShortcut *m_focustosearchsc{0}; QShortcut *m_focustosearcholdsc{0}; QShortcut *m_clearsearchsc{0}; QShortcut *m_toggletablesc{0}; QShortcut *m_actionssearchsc{0}; QShortcut *m_cleardirfiltersc{0}; QFileSystemWatcher m_watcher; std::vector m_viewers; ExecCmd *m_idxproc{0}; // Indexing process bool m_idxkilled{false}; // Killed my process TempFile *m_idxreasontmp{nullptr}; std::map m_stemLangToId; std::vector m_catgbutvec; int m_catgbutvecidx{0}; DocSeqFiltSpec m_filtspec; bool m_sortspecnochange{false}; DocSeqSortSpec m_sortspec; std::shared_ptr m_source; IndexerState m_indexerState{IXST_UNKNOWN}; bool m_queryActive{false}; bool m_firstIndexing{false}; // Last search was started from simple bool m_searchIsSimple{false}; // This is set to the query string by ssearch, and to empty by // advsearch, and used for the Preview window title. If empty, we // use the Xapian Query string. QString m_queryDescription; // If set on init, will be displayed either through ext app, or // preview (if no ext app set) QString m_urltoview; RclTrayIcon *m_trayicon{0}; // We sometimes take the indexer lock (e.g.: when editing the webcache) Pidfile *m_pidfile{0}; IdxTreeModel *m_idxtreemodel{nullptr}; // Menu for the button version of the top menu. QMenu *buttonTopMenu; // Entries/submenus for the top menu. QMenu *fileMenu; QMenu *viewMenu; QMenu *toolsMenu; QMenu *preferencesMenu; QMenu *helpMenu; QMenu *resultsMenu; QActionGroup *sstypGroup; QMenu *queryMenu; QShortcut *butmenuSC{nullptr}; virtual void init(); virtual void setupResTB(bool combo); virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum, bool next); // flags may contain ExecCmd::EXF_xx values virtual void execViewer(const std::map& subs, bool enterHistory, const std::string& execpath, const std::vector& lcmd, const std::string& cmd, Rcl::Doc doc, int flags=0); virtual void setStemLang(const QString& lang); virtual void onSortCtlChanged(); virtual void showIndexConfig(bool modal); virtual void showIndexSched(bool modal); virtual void showCronTool(bool modal); virtual void showRTITool(bool modal); virtual void updateIdxForDocs(std::vector&); virtual void initiateQuery(); virtual bool containerUpToDate(Rcl::Doc& doc); virtual bool checkIdxPaths(); virtual std::vector idxTreeGetDirs(); virtual void resultsSetFixedGeometry(); }; #endif // RCLMAIN_W_H recoll-1.36.1/qtgui/restable.cpp0000644000175000017500000013344514515661435013465 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "restable.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "recoll.h" #include "docseq.h" #include "log.h" #include "guiutils.h" #include "reslistpager.h" #include "reslist.h" #include "rclconfig.h" #include "plaintorich.h" #include "indexer.h" #include "respopup.h" #include "rclmain_w.h" #include "multisave.h" #include "appformime.h" #include "transcode.h" #include "scbase.h" using std::string; using std::vector; using std::map; using std::list; static const QKeySequence quitKeySeq("Ctrl+q"); static const QKeySequence closeKeySeq("Ctrl+w"); // Compensate for the default and somewhat bizarre vertical placement // of text in cells static const int ROWHEIGHTPAD = 3; static const int TEXTINCELLVTRANS = -4; // Adjust font size from prefs, display is slightly different in the table because the cells are // displayed by qt HTML not webkit/view? We need the same adjustment in the preview. static const int fsadjustdown = 3; static PlainToRichQtReslist g_hiliter; static const char *settingskey_fieldlist="/Recoll/prefs/query/restableFields"; static const char *settingskey_fieldwiths="/Recoll/prefs/query/restableWidths"; static const char *settingskey_splittersizes="resTableSplitterSizes"; ////////////////////////////////////////////////////////////////////////// // Restable "pager". We use it to print details for a document in the // detail area /// class ResTablePager : public ResListPager { public: ResTablePager(RclConfig *cnf, ResTable *p) : ResListPager(cnf, 1, prefs.alwaysSnippets), m_parent(p) {} virtual bool append(const string& data) override; virtual bool flush() override; virtual string trans(const string& in) override; virtual const string &parFormat() override; virtual string absSep() override { return (const char *)(prefs.abssep.toUtf8());} virtual string headerContent() override { return prefs.htmlHeaderContents(); } private: ResTable *m_parent; string m_data; }; bool ResTablePager::append(const string& data) { m_data += data; return true; } bool ResTablePager::flush() { #ifdef helps_discoverability_of_shiftclick_but_is_ennoying QString msg = QApplication::translate( "ResTable", "Use Shift+click to display the text instead."); if (!prefs.resTableTextNoShift) { m_data += std::string("

") + qs2utf8s(msg) + "

"; } #endif m_parent->m_detail->setHtml(u8s2qs(m_data)); m_data = ""; return true; } string ResTablePager::trans(const string& in) { return string((const char*)ResList::tr(in.c_str()).toUtf8()); } const string& ResTablePager::parFormat() { return prefs.creslistformat; } ///////////////////////////////////////////////////////////////////////////// /// Detail text area methods ResTableDetailArea::ResTableDetailArea(ResTable* parent) : QTextBrowser(parent), m_table(parent) { setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(createPopupMenu(const QPoint&))); } void ResTableDetailArea::createPopupMenu(const QPoint& pos) { if (m_table && m_table->m_model && m_table->m_detaildocnum >= 0) { int opts = m_table->m_ismainres ? ResultPopup::showExpand : 0; opts |= ResultPopup::showSaveOne; QMenu *popup = ResultPopup::create( m_table, opts, m_table->m_model->getDocSource(), m_table->m_detaildoc); popup->popup(mapToGlobal(pos)); } } void ResTableDetailArea::setFont() { int fs = m_table->fontsize(); if (prefs.reslistfontfamily != "") { QFont nfont(prefs.reslistfontfamily, fs); QTextBrowser::setFont(nfont); } else { QFont font; font.setPointSize(fs); QTextBrowser::setFont(font); } } void ResTableDetailArea::init() { setFont(); QTextBrowser::setHtml(""); } ////////////////////////////////////////////////////////////////////////////// //// Data model methods //// // Routines used to extract named data from an Rcl::Doc. The basic one // just uses the meta map. Others (ie: the date ones) need to do a // little processing static string gengetter(const string& fld, const Rcl::Doc& doc) { const auto it = doc.meta.find(fld); if (it == doc.meta.end()) { return string(); } return it->second; } static string sizegetter(const string& fld, const Rcl::Doc& doc) { const auto it = doc.meta.find(fld); if (it == doc.meta.end()) { return string(); } int64_t size = atoll(it->second.c_str()); return displayableBytes(size) + " (" + it->second + ")"; } static string dategetter(const string&, const Rcl::Doc& doc) { string sdate; if (!doc.dmtime.empty() || !doc.fmtime.empty()) { time_t mtime = doc.dmtime.empty() ? atoll(doc.fmtime.c_str()) : atoll(doc.dmtime.c_str()); struct tm *tm = localtime(&mtime); sdate = utf8datestring("%Y-%m-%d", tm); } return sdate; } static string datetimegetter(const string&, const Rcl::Doc& doc) { string datebuf; if (!doc.dmtime.empty() || !doc.fmtime.empty()) { time_t mtime = doc.dmtime.empty() ? atoll(doc.fmtime.c_str()) : atoll(doc.dmtime.c_str()); struct tm *tm = localtime(&mtime); // Can't use reslistdateformat because it's html (  etc.) datebuf = utf8datestring("%Y-%m-%d %H:%M:%S", tm); } return datebuf; } // Static map to translate from internal column names to displayable ones map RecollModel::o_displayableFields; FieldGetter *RecollModel::chooseGetter(const string& field) { if (!stringlowercmp("date", field)) return dategetter; else if (!stringlowercmp("datetime", field)) return datetimegetter; else if (!stringlowercmp("bytes", field.substr(1))) return sizegetter; else return gengetter; } string RecollModel::baseField(const string& field) { if (!stringlowercmp("date", field) || !stringlowercmp("datetime", field)) return "mtime"; else return field; } RecollModel::RecollModel(const QStringList fields, ResTable *tb, QObject *parent) : QAbstractTableModel(parent), m_table(tb), m_ignoreSort(false) { // Initialize the translated map for column headers o_displayableFields["abstract"] = tr("Abstract"); o_displayableFields["author"] = tr("Author"); o_displayableFields["dbytes"] = tr("Document size"); o_displayableFields["dmtime"] = tr("Document date"); o_displayableFields["fbytes"] = tr("File size"); o_displayableFields["filename"] = tr("File name"); o_displayableFields["fmtime"] = tr("File date"); o_displayableFields["ipath"] = tr("Ipath"); o_displayableFields["keywords"] = tr("Keywords"); o_displayableFields["mtype"] = tr("MIME type"); o_displayableFields["origcharset"] = tr("Original character set"); o_displayableFields["relevancyrating"] = tr("Relevancy rating"); o_displayableFields["title"] = tr("Title"); o_displayableFields["url"] = tr("URL"); o_displayableFields["mtime"] = tr("Date"); o_displayableFields["date"] = tr("Date"); o_displayableFields["datetime"] = tr("Date and time"); // Add dynamic "stored" fields to the full column list. This // could be protected to be done only once, but it's no real // problem if (theconfig) { const auto& stored = theconfig->getStoredFields(); for (const auto& field : stored) { if (o_displayableFields.find(field) == o_displayableFields.end()) { o_displayableFields[field] = u8s2qs(field); } } } // Construct the actual list of column names for (QStringList::const_iterator it = fields.begin(); it != fields.end(); it++) { m_fields.push_back((const char *)(it->toUtf8())); m_getters.push_back(chooseGetter(m_fields.back())); } g_hiliter.set_inputhtml(false); } int RecollModel::rowCount(const QModelIndex&) const { LOGDEB2("RecollModel::rowCount\n"); if (!m_source) return 0; return m_source->getResCnt(); } int RecollModel::columnCount(const QModelIndex&) const { LOGDEB2("RecollModel::columnCount\n"); return m_fields.size(); } void RecollModel::readDocSource() { LOGDEB("RecollModel::readDocSource()\n"); beginResetModel(); endResetModel(); } void RecollModel::setDocSource(std::shared_ptr nsource) { LOGDEB("RecollModel::setDocSource\n"); m_rowforcachedoc = -1; if (!nsource) { m_source = std::shared_ptr(); } else { // We used to allocate a new DocSource here instead of sharing // the input, but I can't see why. m_source = nsource; m_hdata.clear(); } } void RecollModel::deleteColumn(int col) { if (col > 0 && col < int(m_fields.size())) { vector::iterator it = m_fields.begin(); it += col; m_fields.erase(it); vector::iterator it1 = m_getters.begin(); it1 += col; m_getters.erase(it1); readDocSource(); } } void RecollModel::addColumn(int col, const string& field) { LOGDEB("AddColumn: col " << col << " fld [" << field << "]\n"); if (col >= 0 && col < int(m_fields.size())) { col++; vector::iterator it = m_fields.begin(); vector::iterator it1 = m_getters.begin(); if (col) { it += col; it1 += col; } m_fields.insert(it, field); m_getters.insert(it1, chooseGetter(field)); readDocSource(); } } QString RecollModel::displayableField(const std::string& in) { const auto it = o_displayableFields.find(in); return (it == o_displayableFields.end()) ? u8s2qs(in) : it->second; } QVariant RecollModel::headerData(int idx, Qt::Orientation orientation, int role) const { LOGDEB2("RecollModel::headerData: idx " << idx << " orientation " << (orientation == Qt::Vertical ? "vertical":"horizontal") << " role " << role << "\n"); if (orientation == Qt::Vertical && role == Qt::DisplayRole) { if (idx < 26) { return QString("%1/%2").arg(idx).arg(char('a'+idx)); } else { return idx; } } if (orientation == Qt::Horizontal && role == Qt::DisplayRole && idx < int(m_fields.size())) { return displayableField(m_fields[idx]); } return QVariant(); } QVariant RecollModel::data(const QModelIndex& index, int role) const { LOGDEB2("RecollModel::data: row " << index.row() << " col " << index.column() << " role " << role << "\n"); // The font is actually set in the custom delegate, but we need // this to adjust the row height (there is probably a better way // to do it in the delegate?) if (role == Qt::FontRole) { if (m_reslfntszforcached != m_table->fontsize()) { m_reslfntszforcached = m_table->fontsize(); m_table->setDefRowHeight(); m_cachedfont = m_table->font(); m_cachedfont.setPointSize(m_reslfntszforcached); } LOGDEB1("ResTable: cachedfont pointsize " << m_cachedfont.pointSize() << " pixelsize " << m_cachedfont.pixelSize() << "\n"); return m_cachedfont; } // Note that, because we use a style sheet, there is no way to dynamically set the background // color. See: https://forum.qt.io/topic/95940/model-backgroundrole-overridden-by-style-sheet/ // https://bugreports.qt.io/browse/QTBUG-70100 if (!m_source || role != Qt::DisplayRole || !index.isValid() || index.column() >= int(m_fields.size())) { return QVariant(); } if (m_rowforcachedoc != index.row()) { m_rowforcachedoc = index.row(); m_cachedoc = Rcl::Doc(); if (!m_source->getDoc(index.row(), m_cachedoc)) { return QVariant(); } } string colname = m_fields[index.column()]; string data = m_getters[index.column()](colname, m_cachedoc); #ifndef _WIN32 // Special case url, because it may not be utf-8. URL-encode in this case. // Not on windows, where we always read the paths as Unicode. if (!colname.compare("url")) { int ecnt; string data1; if (!transcode(data, data1, "UTF-8", "UTF-8", &ecnt) || ecnt > 0) { data = url_encode(data, 7); } } #endif list lr; g_hiliter.plaintorich(data, lr, m_hdata); return u8s2qs(lr.front()); } void RecollModel::saveAsCSV(std::fstream& fp) { if (!m_source) return; int cols = columnCount(); int rows = rowCount(); vector tokens; for (int col = 0; col < cols; col++) { QString qs = headerData(col, Qt::Horizontal,Qt::DisplayRole).toString(); tokens.push_back((const char *)qs.toUtf8()); } string csv; stringsToCSV(tokens, csv); fp << csv << "\n"; tokens.clear(); for (int row = 0; row < rows; row++) { Rcl::Doc doc; if (!m_source->getDoc(row, doc)) { continue; } for (int col = 0; col < cols; col++) { tokens.push_back(m_getters[col](m_fields[col], doc)); } stringsToCSV(tokens, csv); fp << csv << "\n"; tokens.clear(); } } // This gets called when the column headers are clicked void RecollModel::sort(int column, Qt::SortOrder order) { if (m_ignoreSort) { return; } LOGDEB("RecollModel::sort(" << column << ", " << order << ")\n"); DocSeqSortSpec spec; if (column >= 0 && column < int(m_fields.size())) { spec.field = m_fields[column]; if (!stringlowercmp("relevancyrating", spec.field) && order != Qt::AscendingOrder) { QMessageBox::warning(0, "Recoll", tr("Can't sort by inverse relevance")); QTimer::singleShot(0, m_table, SLOT(resetSort())); return; } if (!stringlowercmp("date", spec.field) || !stringlowercmp("datetime", spec.field)) spec.field = "mtime"; spec.desc = (order != Qt::AscendingOrder); } emit sortDataChanged(spec); } /////////////////////////// // ResTable panel methods // We use a custom delegate to display the cells because the base // tableview's can't handle rich text to highlight the match terms class ResTableDelegate: public QStyledItemDelegate { public: ResTableDelegate(QObject *parent) : QStyledItemDelegate(parent) {} // We might want to optimize by passing the data to the base // method if the text does not contain any term matches. Would // need a modif to plaintorich to return the match count (easy), // and a way to pass an indicator from data(), a bit more // difficult. Anyway, the display seems fast enough as is. void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QVariant value = index.data(Qt::DisplayRole); QString text; if (value.isValid() && !value.isNull()) { text = value.toString(); } if (text.isEmpty()) { QStyledItemDelegate::paint(painter, option, index); return; } QStyleOptionViewItem opt = option; initStyleOption(&opt, index); painter->save(); /* As we draw with a text document, not the normal tableview painter, we need to retrieve the appropriate colors and set them as HTML styles. */ QString color = opt.palette.color(QPalette::Base).name(); QString textcolor = opt.palette.color(QPalette::Text).name(); QString selcolor = opt.palette.color(QPalette::Highlight).name(); QString seltextcolor = opt.palette.color(QPalette::HighlightedText).name(); QString fstyle; QFont fnt = qvariant_cast(index.data(Qt::FontRole)); int fs = fnt.pointSize(); LOGDEB1("ResTable: delegate: Got fs " << fs << "\n"); fstyle = QString("font-size: %1pt").arg(fs) + ";"; QString ntxt("
") + text + QString("
"); text.swap(ntxt); painter->setClipRect(opt.rect); QPoint where = option.rect.topLeft(); where.ry() += TEXTINCELLVTRANS; painter->translate(where); QTextDocument document; document.setHtml(text); document.drawContents(painter); painter->restore(); } }; int ResTable::fontsize() { int fs; if (prefs.reslistfontsize > 0) { fs = prefs.reslistfontsize; LOGDEB1("ResTable::fontsize() got fs from prefs: " << fs << "\n"); } else { fs = QWidget(this).font().pointSize(); LOGDEB1("ResTable::fontsize() got fs from defaults: " << fs << "\n"); } if (fs < 0) fs = 12; fs -= fsadjustdown; fs = std::round(fs * prefs.wholeuiscale); return fs; } void ResTable::setDefRowHeight() { QHeaderView *header = tableView->verticalHeader(); if (header) { // Don't do this: it forces a query on the whole model (all // docs) to compute the height. No idea why this was needed, // things seem to work ok without it. The row height does not // shrink when the font is reduced, but I'm not sure that it // worked before. // header->setSectionResizeMode(QHeaderView::ResizeToContents); // Compute ourselves instead, for one row. QFont font = tableView->font(); int fs = fontsize(); if (fs > 0) font.setPointSize(fs); QFontMetrics fm(font); header->setDefaultSectionSize(fm.height() + ROWHEIGHTPAD); header->setSectionResizeMode(QHeaderView::Fixed); } } void ResTable::init(QStringList _ifields) { QSettings settings; QStringList restableFields; if (_ifields.empty()) { restableFields = settings.value(settingskey_fieldlist).toStringList(); if (restableFields.empty()) { restableFields.push_back("date"); restableFields.push_back("title"); restableFields.push_back("filename"); restableFields.push_back("author"); restableFields.push_back("url"); } } else { restableFields = _ifields; } if (!(m_model = new RecollModel(restableFields, this))) return; tableView->setModel(m_model); tableView->setMouseTracking(true); tableView->setSelectionBehavior(QAbstractItemView::SelectRows); tableView->setItemDelegate(new ResTableDelegate(this)); tableView->setContextMenuPolicy(Qt::CustomContextMenu); tableView->setAlternatingRowColors(true); onNewShortcuts(); connect(&SCBase::scBase(), SIGNAL(shortcutsChanged()), this, SLOT(onNewShortcuts())); auto sc = new QShortcut(QKeySequence(Qt::Key_Escape), this); connect(sc, SIGNAL(activated()), tableView->selectionModel(), SLOT(clear())); connect(tableView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(createPopupMenu(const QPoint&))); QHeaderView *header = tableView->horizontalHeader(); if (header) { if (_ifields.empty()) { QString qw = settings.value(settingskey_fieldwiths).toString(); vector vw; stringToStrings(qs2utf8s(qw), vw); vector restableColWidths; for (const auto& w : vw) { restableColWidths.push_back(atoi(w.c_str())); } if (int(restableColWidths.size()) == header->count()) { for (int i = 0; i < header->count(); i++) { header->resizeSection(i, restableColWidths[i]); } } header->setSortIndicatorShown(true); header->setSortIndicator(-1, Qt::AscendingOrder); header->setContextMenuPolicy(Qt::CustomContextMenu); connect(header, SIGNAL(sectionResized(int,int,int)), this, SLOT(saveColState())); connect(header, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(createHeaderPopupMenu(const QPoint&))); } else { header->setSortIndicatorShown(false); } header->setStretchLastSection(1); } #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) header->setSectionsMovable(true); #else header->setMovable(true); #endif setDefRowHeight(); connect(tableView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex &)), this, SLOT(onTableView_currentChanged(const QModelIndex&))); connect(tableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onDoubleClick(const QModelIndex&))); connect(tableView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(onClicked(const QModelIndex&))); m_pager = new ResTablePager(theconfig, this); m_pager->setHighLighter(&g_hiliter); deleteZ(textBrowser); m_detail = new ResTableDetailArea(this); m_detail->setReadOnly(true); m_detail->setUndoRedoEnabled(false); m_detail->setOpenLinks(false); m_detail->init(); // signals and slots connections connect(m_detail, SIGNAL(anchorClicked(const QUrl&)), this, SLOT(linkWasClicked(const QUrl&))); splitter->addWidget(m_detail); splitter->setOrientation(Qt::Vertical); QVariant saved = settings.value(settingskey_splittersizes); if (saved != QVariant()) { splitter->restoreState(saved.toByteArray()); } else { QList sizes; sizes << 355 << 125; splitter->setSizes(sizes); } installEventFilter(this); onUiPrefsChanged(); } void ResTable::onNewShortcuts() { if (prefs.noResTableRowJumpSC) { for (auto& lnk : m_rowlinks) { delete lnk; } m_rowlinks.clear(); for (auto& sc : m_rowsc) { delete sc; } m_rowsc.clear(); } else if (m_rowlinks.empty()) { // Set "go to row" accelerator shortcuts. letter or digit for 0-9, // then letter up to 25 std::function setrow = std::bind(&ResTable::setCurrentRowFromKbd, this, std::placeholders::_1); for (int i = 0; i <= 25; i++) { auto qs = QString("Ctrl+Shift+%1").arg(char('a'+i)); auto sc = new QShortcut(QKeySequence(qs2utf8s(qs).c_str()), this); m_rowlinks.push_back(new SCData(this, setrow, i)); m_rowsc.push_back(sc); connect(sc, SIGNAL(activated()), m_rowlinks.back(), SLOT(activate())); if (i > 9) continue; qs = QString("Ctrl+%1").arg(i); sc = new QShortcut(QKeySequence(qs2utf8s(qs).c_str()), this); m_rowsc.push_back(sc); m_rowlinks.push_back(new SCData(this, setrow, i)); connect(sc, SIGNAL(activated()), m_rowlinks.back(), SLOT(activate())); } } SETSHORTCUT(this, "restable:704", tr("Result Table"), tr("Open current result document"),"Ctrl+O", m_opensc, menuEdit); SETSHORTCUT(this, "restable:706", tr("Result Table"), tr("Open current result and quit"), "Ctrl+Alt+Shift+O", m_openquitsc, menuEditAndQuit); SETSHORTCUT(this, "restable:709", tr("Result Table"), tr("Preview"), "Ctrl+D", m_previewsc, menuPreview); SETSHORTCUT(this, "restable:711", tr("Result Table"), tr("Show snippets"), "Ctrl+E", m_showsnipssc, menuShowSnippets); SETSHORTCUT(this, "restable:713", tr("Result Table"), tr("Show header"), "Ctrl+H", m_showheadersc, toggleHeader); SETSHORTCUT(this, "restable:715", tr("Result Table"), tr("Show vertical header"), "Ctrl+V", m_showvheadersc, toggleVHeader); SETSHORTCUT(this, "restable:718", tr("Result Table"), tr("Copy current result text to clipboard"), "Ctrl+G", m_copycurtextsc, menuCopyText); SETSHORTCUT(this, "restable:734", tr("Result Table"), tr("Copy result text and quit"), "Ctrl+Alt+Shift+G", m_copycurtextquitsc, menuCopyTextAndQuit); std::vector scps={ m_opensc, m_openquitsc, m_previewsc, m_showsnipssc, m_showheadersc, m_showvheadersc, m_copycurtextsc, m_copycurtextquitsc}; for (auto& scp : scps) { scp->setContext(Qt::WidgetWithChildrenShortcut); } } bool ResTable::eventFilter(QObject*, QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent* key = static_cast(event); if ((key->key() == Qt::Key_Enter) || (key->key() == Qt::Key_Return)) { menuEdit(); return true; } } return false; } void ResTable::setRclMain(RclMain *m, bool ismain) { m_rclmain = m; m_ismainres = ismain; // We allow single selection only in the main table because this // may have a mix of file-level docs and subdocs and multisave // only works for subdocs if (m_ismainres) tableView->setSelectionMode(QAbstractItemView::SingleSelection); else tableView->setSelectionMode(QAbstractItemView::ExtendedSelection); if (!m_ismainres) { // Don't set this shortcut when we are a child of main, would be duplicate/ambiguous connect(new QShortcut(quitKeySeq, this), SIGNAL(activated()), m_rclmain, SLOT (fileExit())); new QShortcut(closeKeySeq, this, SLOT (close())); } connect(this, SIGNAL(previewRequested(Rcl::Doc)), m_rclmain, SLOT(startPreview(Rcl::Doc))); connect(this, SIGNAL(editRequested(Rcl::Doc)), m_rclmain, SLOT(startNativeViewer(Rcl::Doc))); connect(this, SIGNAL(docSaveToFileClicked(Rcl::Doc)), m_rclmain, SLOT(saveDocToFile(Rcl::Doc))); connect(this, SIGNAL(showSnippets(Rcl::Doc)), m_rclmain, SLOT(showSnippets(Rcl::Doc))); } void ResTable::toggleHeader() { if (tableView->horizontalHeader()->isVisible()) { prefs.noResTableHeader = true; tableView->horizontalHeader()->hide(); } else { prefs.noResTableHeader = false; tableView->horizontalHeader()->show(); } } void ResTable::toggleVHeader() { if (tableView->verticalHeader()->isVisible()) { prefs.showResTableVHeader = false; tableView->verticalHeader()->hide(); } else { prefs.showResTableVHeader = true; tableView->verticalHeader()->show(); } } void ResTable::onUiPrefsChanged() { if (m_detail) { m_detail->setFont(); } auto index = tableView->indexAt(QPoint(0, 0)); // There may be a better way to force repainting all visible rows // with the possibly new font, but this works... tableView->setAlternatingRowColors(false); tableView->setAlternatingRowColors(true); makeRowVisible(index.row()); if (prefs.noResTableHeader) { tableView->horizontalHeader()->hide(); } else { tableView->horizontalHeader()->show(); } if (prefs.showResTableVHeader) { tableView->verticalHeader()->show(); } else { tableView->verticalHeader()->hide(); } } void ResTable::setCurrentRowFromKbd(int row) { LOGDEB1("setCurrentRowFromKbd: " << row << "\n"); m_rowchangefromkbd = true; tableView->setFocus(Qt::ShortcutFocusReason); // After calling setCurrentIndex(), currentChanged() gets called // twice, once with row 0 and no selection, once with the actual // target row and selection set. It uses this fact to discriminate // this from hovering. For some reason, when row is zero, there is // only one call. So, in this case, we first select row 1, and // this so pretty hack gets things working if (row == 0) { tableView->selectionModel()->setCurrentIndex( m_model->index(1, 0), QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Rows); } tableView->selectionModel()->setCurrentIndex( m_model->index(row, 0), QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Rows); } int ResTable::getDetailDocNumOrTopRow() { if (m_detaildocnum >= 0) return m_detaildocnum; QModelIndex modelIndex = tableView->indexAt(QPoint(0, 0)); return modelIndex.row(); } void ResTable::makeRowVisible(int row) { LOGDEB("ResTable::showRow(" << row << ")\n"); QModelIndex modelIndex = m_model->index(row, 0); tableView->scrollTo(modelIndex, QAbstractItemView::PositionAtTop); tableView->selectionModel()->clear(); m_detail->init(); m_detaildocnum = -1; } // This is called by rclmain_w prior to exiting void ResTable::saveColState() { if (!m_ismainres) return; QSettings settings; settings.setValue(settingskey_splittersizes, splitter->saveState()); QHeaderView *header = tableView->horizontalHeader(); const vector& vf = m_model->getFields(); if (!header) { LOGERR("ResTable::saveColState: no table header ??\n"); return; } // Remember the current column order. Walk in visual order and // create new list QStringList newfields; QString newwidths; for (int vi = 0; vi < header->count(); vi++) { int li = header->logicalIndex(vi); if (li < 0 || li >= int(vf.size())) { LOGERR("saveColState: logical index beyond list size!\n"); continue; } newfields.push_back(u8s2qs(vf[li])); newwidths += QString().setNum(header->sectionSize(li)) + QString(" "); } settings.setValue(settingskey_fieldlist, newfields); settings.setValue(settingskey_fieldwiths, newwidths); } void ResTable::onTableView_currentChanged(const QModelIndex& index) { bool hasselection = tableView->selectionModel()->hasSelection(); LOGDEB2("ResTable::onTableView_currentChanged(" << index.row() << ", " << index.column() << ") from kbd " << m_rowchangefromkbd << " hasselection " << hasselection << "\n"); if (!m_model || !m_model->getDocSource()) return; Rcl::Doc doc; if (!m_model->getDocSource()->getDoc(index.row(), doc)) { m_detaildocnum = -1; return; } m_detail->init(); m_detaildocnum = index.row(); m_detaildoc = doc; bool isShift = (QApplication::keyboardModifiers() & Qt::ShiftModifier); bool showcontent{false}; bool showmeta{false}; if (m_rowchangefromkbd) { // Ctrl+... jump to row. Show text/meta as for simple click if (hasselection) { // When getting here from ctrl+... we get called twice, once with row 0 // and no selection, once with the actual row and selection. Only // reset fromkbd and set showcontent in the second case. m_rowchangefromkbd = false; showcontent = prefs.resTableTextNoShift; } } else { // Mouse click. Show text or meta depending on shift key. Never show text when hovering // (no selection). showcontent = hasselection && (isShift ^ prefs.resTableTextNoShift); } if (!showcontent) { showmeta = hasselection || !prefs.resTableNoHoverMeta; } bool displaydone{false}; if (showcontent) { // If it's an image, and simply stored in a file, display it. We don't go to the trouble of // extracting an embedded image here. if (mimeIsImage(m_detaildoc.mimetype) && m_detaildoc.ipath.empty()) { auto image = QImage(fileurltolocalpath(m_detaildoc.url).c_str()); if (!image.isNull()) { m_detail->setPlainText(""); auto w = m_detail->width(); auto h = m_detail->height(); if (image.width() > w || image.height() > h) { image = image.scaled(w, h, Qt::KeepAspectRatio); } m_detail->document()->addResource(QTextDocument::ImageResource,QUrl("image"),image); m_detail->textCursor().insertImage("image"); displaydone = true; } } if (!displaydone && rcldb->getDocRawText(m_detaildoc)) { m_detail->setPlainText(u8s2qs(m_detaildoc.text)); displaydone = true; } } if (!displaydone && showmeta) { m_pager->displaySingleDoc(theconfig, m_detaildocnum, m_detaildoc, m_model->m_hdata); } emit(detailDocChanged(doc, m_model->getDocSource())); } void ResTable::on_tableView_entered(const QModelIndex& index) { LOGDEB2("ResTable::on_tableView_entered(" << index.row() << ", " << index.column() << ")\n"); if (!tableView->selectionModel()->hasSelection()) onTableView_currentChanged(index); } void ResTable::takeFocus() { // LOGDEB("resTable: take focus\n"); tableView->setFocus(Qt::ShortcutFocusReason); } void ResTable::setDocSource(std::shared_ptr nsource) { LOGDEB("ResTable::setDocSource\n"); if (m_model) m_model->setDocSource(nsource); if (m_pager) m_pager->setDocSource(nsource, 0); if (m_detail) m_detail->init(); m_detaildocnum = -1; } void ResTable::resetSource() { LOGDEB("ResTable::resetSource\n"); setDocSource(std::shared_ptr()); readDocSource(); } void ResTable::saveAsCSV() { LOGDEB("ResTable::saveAsCSV\n"); if (!m_model) return; QString s = QFileDialog::getSaveFileName( this, tr("Save table to CSV file"), path2qs(path_home())); if (s.isEmpty()) return; std::string tofile = qs2path(s); std::fstream fp; if (!path_streamopen(tofile, std::ios::out|std::ios::trunc,fp)) { QMessageBox::warning(0, "Recoll", tr("Can't open/create file: ") + s); return; } m_model->saveAsCSV(fp); fp.close(); } // This is called when the sort order is changed from another widget void ResTable::onSortDataChanged(DocSeqSortSpec spec) { LOGDEB("ResTable::onSortDataChanged: [" << spec.field << "] desc " << spec.desc << "\n"); QHeaderView *header = tableView->horizontalHeader(); if (!header || !m_model) return; // Check if the specified field actually matches one of columns // and set indicator m_model->setIgnoreSort(true); bool matched = false; const vector fields = m_model->getFields(); for (unsigned int i = 0; i < fields.size(); i++) { if (!spec.field.compare(m_model->baseField(fields[i]))) { header->setSortIndicator(i, spec.desc ? Qt::DescendingOrder : Qt::AscendingOrder); matched = true; } } if (!matched) header->setSortIndicator(-1, Qt::AscendingOrder); m_model->setIgnoreSort(false); } void ResTable::resetSort() { LOGDEB("ResTable::resetSort()\n"); QHeaderView *header = tableView->horizontalHeader(); if (header) header->setSortIndicator(-1, Qt::AscendingOrder); // the model's sort slot is not called by qt in this case (qt 4.7) if (m_model) m_model->sort(-1, Qt::AscendingOrder); } void ResTable::readDocSource(bool resetPos) { LOGDEB("ResTable::readDocSource(" << resetPos << ")\n"); if (resetPos) tableView->verticalScrollBar()->setSliderPosition(0); if (m_model->m_source) { m_model->m_source->getTerms(m_model->m_hdata); } else { m_model->m_hdata.clear(); } m_model->readDocSource(); m_detail->init(); m_detaildocnum = -1; } void ResTable::linkWasClicked(const QUrl &url) { if (m_detaildocnum < 0) { return; } QString s = url.toString(); const char *ascurl = s.toUtf8(); LOGDEB("ResTable::linkWasClicked: [" << ascurl << "]\n"); int docseqnum = atoi(ascurl+1) -1; if (m_detaildocnum != docseqnum) { //? Really we should abort... LOGERR("ResTable::linkWasClicked: m_detaildocnum != docseqnum !\n"); return; } int what = ascurl[0]; switch (what) { // Open abstract/snippets window case 'A': emit(showSnippets(m_detaildoc)); break; case 'D': { vector dups; if (m_rclmain && m_model->getDocSource()->docDups(m_detaildoc, dups)) { m_rclmain->newDupsW(m_detaildoc, dups); } } break; // Open parent folder case 'F': { emit editRequested(ResultPopup::getFolder(m_detaildoc)); } break; case 'P': case 'E': { if (what == 'P') { if (m_ismainres) { emit docPreviewClicked(docseqnum, m_detaildoc, 0); } else { emit previewRequested(m_detaildoc); } } else { emit editRequested(m_detaildoc); } } break; // Run script. Link format Rnn|Script Name case 'R': { int bar = s.indexOf("|"); if (bar == -1 || bar >= s.size()-1) break; string cmdname = qs2utf8s(s.right(s.size() - (bar + 1))); DesktopDb ddb(path_cat(theconfig->getConfDir(), "scripts")); DesktopDb::AppDef app; if (ddb.appByName(cmdname, app)) { QAction act(QString::fromUtf8(app.name.c_str()), this); QVariant v(QString::fromUtf8(app.command.c_str())); act.setData(v); menuOpenWith(&act); } } break; default: LOGERR("ResTable::linkWasClicked: bad link [" << ascurl << "]\n"); break;// ?? } } void ResTable::onClicked(const QModelIndex& index) { // If the current row is the one clicked, currentChanged is not // called so that we would not do the text display if we did not // call it from here m_rowchangefromkbd = false; if (index.row() == m_detaildocnum) { onTableView_currentChanged(index); } } void ResTable::onDoubleClick(const QModelIndex& index) { m_rowchangefromkbd = false; if (!m_model || !m_model->getDocSource()) return; Rcl::Doc doc; if (m_model->getDocSource()->getDoc(index.row(), doc)) { if (m_detaildocnum != index.row()) { m_detail->init(); m_detaildocnum = index.row(); m_pager->displayDoc(theconfig, index.row(), m_detaildoc, m_model->m_hdata); } m_detaildoc = doc; if (m_detaildocnum >= 0) emit editRequested(m_detaildoc); } else { m_detaildocnum = -1; } } void ResTable::createPopupMenu(const QPoint& pos) { LOGDEB("ResTable::createPopupMenu: m_detaildocnum " << m_detaildocnum << "\n"); if (m_detaildocnum >= 0 && m_model) { int opts = m_ismainres? ResultPopup::isMain : 0; int selsz = tableView->selectionModel()->selectedRows().size(); if (selsz == 1) { opts |= ResultPopup::showSaveOne; } else if (selsz > 1 && !m_ismainres) { // We don't show save multiple for the main list because not all // docs are necessary subdocs and multisave only works with those. opts |= ResultPopup::showSaveSel; } QMenu *popup = ResultPopup::create(this, opts, m_model->getDocSource(), m_detaildoc); popup->popup(mapToGlobal(pos)); } } void ResTable::menuPreview() { if (m_detaildocnum >= 0) { if (m_ismainres) { emit docPreviewClicked(m_detaildocnum, m_detaildoc, 0); } else { emit previewRequested(m_detaildoc); } } } void ResTable::menuSaveToFile() { if (m_detaildocnum >= 0) emit docSaveToFileClicked(m_detaildoc); } void ResTable::menuSaveSelection() { if (m_model == 0 || !m_model->getDocSource()) return; QModelIndexList indexl = tableView->selectionModel()->selectedRows(); vector v; for (int i = 0; i < indexl.size(); i++) { Rcl::Doc doc; if (m_model->getDocSource()->getDoc(indexl[i].row(), doc)) v.push_back(doc); } if (v.size() == 0) { return; } else if (v.size() == 1) { emit docSaveToFileClicked(v[0]); } else { multiSave(this, v); } } void ResTable::menuPreviewParent() { if (m_detaildocnum >= 0 && m_model && m_model->getDocSource()) { Rcl::Doc pdoc = ResultPopup::getParent(m_model->getDocSource(), m_detaildoc); if (pdoc.mimetype == "inode/directory") { emit editRequested(pdoc); } else { emit previewRequested(pdoc); } } } void ResTable::menuOpenParent() { if (m_detaildocnum >= 0 && m_model && m_model->getDocSource()) { Rcl::Doc pdoc = ResultPopup::getParent(m_model->getDocSource(), m_detaildoc); if (!pdoc.url.empty()) { emit editRequested(pdoc); } } } void ResTable::menuOpenFolder() { if (m_detaildocnum >= 0) { Rcl::Doc pdoc = ResultPopup::getFolder(m_detaildoc); if (!pdoc.url.empty()) { emit editRequested(pdoc); } } } void ResTable::menuEdit() { if (m_detaildocnum >= 0) emit editRequested(m_detaildoc); } void ResTable::menuEditAndQuit() { if (m_detaildocnum >= 0) { emit editRequested(m_detaildoc); m_rclmain->fileExit(); } } void ResTable::menuOpenWith(QAction *act) { if (act == 0) return; string cmd = qs2utf8s(act->data().toString()); if (m_detaildocnum >= 0) emit openWithRequested(m_detaildoc, cmd); } void ResTable::menuCopyFN() { if (m_detaildocnum >= 0) ResultPopup::copyFN(m_detaildoc); } void ResTable::menuCopyPath() { if (m_detaildocnum >= 0) ResultPopup::copyPath(m_detaildoc); } void ResTable::menuCopyURL() { if (m_detaildocnum >= 0) ResultPopup::copyURL(m_detaildoc); } void ResTable::menuCopyText() { if (m_detaildocnum >= 0 && rcldb) { ResultPopup::copyText(m_detaildoc, m_rclmain); if (m_rclmain) { auto msg = tr("%1 bytes copied to clipboard").arg(m_detaildoc.text.size()); // Feedback was requested: tray messages are too ennoying, not // everybody displays the status bar, and the tool tip only // works when the copy is triggered through a shortcut (else, // it appears that the mouse event cancels it and it's not // shown). So let's do status bar if visible else tooltip. // Menu trigger with no status bar -> no feedback... // rclmain->showTrayMessage(msg); if (m_rclmain->statusBar()->isVisible()) { m_rclmain->statusBar()->showMessage(msg, 1000); } else { int x = tableView->columnViewportPosition(0) + tableView->width() / 2 ; int y = tableView->rowViewportPosition(m_detaildocnum); QPoint pos = tableView->mapToGlobal(QPoint(x,y)); QToolTip::showText(pos, msg); QTimer::singleShot(1500, m_rclmain, SLOT(hideToolTip())); } } } } void ResTable::menuCopyTextAndQuit() { if (m_detaildocnum >= 0 && rcldb) { menuCopyText(); m_rclmain->fileExit(); } } void ResTable::menuExpand() { if (m_detaildocnum >= 0) emit docExpand(m_detaildoc); } void ResTable::menuShowSnippets() { if (m_detaildocnum >= 0) emit showSnippets(m_detaildoc); } void ResTable::menuShowSubDocs() { if (m_detaildocnum >= 0) emit showSubDocs(m_detaildoc); } void ResTable::createHeaderPopupMenu(const QPoint& pos) { LOGDEB("ResTable::createHeaderPopupMenu(" << pos.x() << ", " << pos.y() << ")\n"); QHeaderView *header = tableView->horizontalHeader(); if (!header || !m_model) return; m_popcolumn = header->logicalIndexAt(pos); if (m_popcolumn < 0) return; const map& allfields = m_model->getAllFields(); const vector& fields = m_model->getFields(); QMenu *popup = new QMenu(this); popup->addAction(tr("&Reset sort"), this, SLOT(resetSort())); popup->addSeparator(); popup->addAction(tr("&Save as CSV"), this, SLOT(saveAsCSV())); popup->addSeparator(); popup->addAction(tr("&Delete column"), this, SLOT(deleteColumn())); popup->addSeparator(); QAction *act; for (const auto& field : allfields) { if (std::find(fields.begin(), fields.end(), field.first) != fields.end()) continue; act = new QAction(tr("Add \"%1\" column").arg(field.second), popup); act->setData(u8s2qs(field.first)); connect(act, SIGNAL(triggered(bool)), this , SLOT(addColumn())); popup->addAction(act); } popup->popup(mapToGlobal(pos)); } void ResTable::deleteColumn() { if (m_model) m_model->deleteColumn(m_popcolumn); } void ResTable::addColumn() { QAction *action = (QAction *)sender(); if (nullptr == action || nullptr == m_model) return; std::string field = qs2utf8s(action->data().toString()); LOGDEB("addColumn: text " << qs2utf8s(action->text()) << ", field " << field << "\n"); m_model->addColumn(m_popcolumn, field); } recoll-1.36.1/qtgui/mtpics/0000755000175000017500000000000014521161751012516 500000000000000recoll-1.36.1/qtgui/mtpics/soffice.png0000644000175000017500000001262114410615043014557 00000000000000PNG  IHDR@@iqgAMA7 cHRMz&u0`:pQ<bKGDIDATx՚itՕuޭ}k-^M166;%9 `2 a'$$gda 8>l![ ؖ%odEݭ^j>ZdYd9uW}[O`D6lp$Ik He u}ҺLTό~gNvH#B2?裏xSvW^y,t2#`l0 ۧ_4]NV?v"[n6l.p gQ N'{tGH% @7 t@ Khao]O_Fm1]t6}]j1J3=#g8KPM ]Xm&"'\UdFMx0k/=^J隁6)kF)`QuJuS;ɧG(evRϳP$T<4@==CTUq@`xźh n fb,4a yd+$IІI*aLhZ.ZZYI~ii JI'Lu?s\"й⊙Ӣ,1@ bQU>8Awn`- %Xd Qc$ک l9@Keh^?vHeq?iCtAg ij-*!Hq**\Tx\$:@J7VUIS 0Ѝ&F]308 TGu(xuY6Di,.J7˦c{&wO7F,fQo N u˶du!cEX oxR^I>Sq:MϚmAmG^)Jc+et l4m}uMmSRʞf=;}V;YZqVMX?a d:kX߾ǒo"gw&~rl:Se|.8`Ԭx۰y+(쟕%8a1՜kLק(N? @^ ~Z!v5-o>n-⪯\lѵ3TMf}AF$( 3:5ShFUdxx_OlXՃ8mP8V&Kd@G9S@dXg0yѪb^Z;L"}^oGEY\,ي;B8N[7V]IᷯanUiGxJ5AWm@LXvY&?+<\VOOm81\ʂt7u. d x̷RN~>xĢRCzp||㇕J9xؠ,Ǎ. qh _T),\]p$ga!t@ӑ,2$4ܵtwaB6fӳJv76G*4n<8H>])s(=*s#Ƣu,v6%"h0Jg , s٪$C 2ϡbCy`\7#] p?:T^V>blYS?Fe.X^D ˫8[Ǜ0xPtڊ Z{uxEijgAbLb,   ھ{|9N0*s' gM,a!Jc.khkSPrxTI~]:CWWd«d&Š;ϑ>אî9@RT Q$ IF|+38.1#}OoXjn$" 3re'RR7beȥw\s !PVzz­N:En~VpX[8 f:?9a@JP(e{XTsVIЉ2 uδ0  "ٚެ|8?_ˉV6Q1u!҇1C Ĭ27~5 Z@aBI"aNא"Ƀ'9ԅ#"#1#6D_0E˅#[d<Y1Mx7CnPuVnA ΁] D)Z _mweQD Or1D${ ~&nJ2hP.$4 ;EZTÔ-0^ar`WrZ9C7Yp8LCxV:.jfK0"*{b\}Mt,G#5ȶ|زV\1#;{_!h 3H *Im4t~w jbQ10@>=%cW4dZ='  a s Vd/{[vaeU-Vh!Rb`^JCDnE[feYnP6`8ÉЇ|nKl$^=Iox7uu@WAM?˞`',>խ.`x(>e`xִӹɬcl 7~i͜d|ƣ'Β8˪ xaA~T^O~~]=8)uoWfwwƭm,|a)9zhSD$I9yj]2-[s`*%Hwy#oP$*]9Ru֮vY93gnX'}9ļ:zN4}'^| JX w}-ʅQ;AsfXbJN 0%:崣(2n EQUP6W}_63vK K\t B]嬸e WCK;`d2͆̕Qlt XVd2&]$@$ b jFUMi:& , 8;Yϭ7ȬEh;wk[kl(0[beNnoov#IןL6x,tww駟vvuu 0Il$Oױ: `T4 "*t!-v?vשm|) KUUbEVe67&EaEaҥrnA+~zƍobB2dN&fedBh(PQ]ͭ2gA9}OºDk`+T0;Mj#s>?Ӻ?cyzg'Ā Pveӻa86&c;[[5'ԕE -'5c_fʢ( xǽ>ӡPh+&=&A@6,+fY)SǺ0VeEQ_x: mIqd>EH4>eQu>MhfP4_Y~?=~*[O xύ(u%Hɬ#?|O>yS~'N̓Jk;_$I}w|>_+1X8ҊO" ؘ$L#y, F%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/html.png0000644000175000017500000001160314410615043014104 00000000000000PNG  IHDR@@iqgAMA7 cHRMz&u0`:pQ<bKGDIDATx՚{]G}?3s}ǽkmǯ<5!hH  ъ?**T*BHJ N:)IEl$8qmu9gfǜݻ]{ӆt4眙3g~c~#<׉D.!ld Z@o{6t`ߡAFF2O -=J|_fU>tvv{_)̏sYik5s;j'kmw{B!?яz@؛ɒ`1D WjNZDֱ䞝u}N[ׯ{6ق1]emK=L6Za,_ZzB+^3 c CቈuLB[<"#|NbM GLS 5Z'DtҖM!ZCWJ{h(*\-.ŗ/WpJ¥bxZprz<źl IdhX)Niz4>0ZlVBSր$.ce:n-W4Z^<]& -Bo|*츅-Kƶ= xKl8oOWXWʐN.o[=7m0Y6E%LנGbyFk J`*&0U~XZ5Ҝ>o.lϢ<`tR Sn Qj830]3lO$w '4-?o,TB䔎'e,?yr݊)IR?;wva-$|ӯLsEI.\ybb`߲\ Б'Fn$*/8 ذ&E5VXtf<>_ZVLH(<)O)AOC)tUsQҼoahX4W% ]4Ch~/#Ak-a!dǍ)[KX[qSa`K7z$|EƗd1/БsUbVju6gz]ɩY#xjS)W측7t5mGm_ֆ$!IeN5CKfm{nәVҒL­<Rܻ/7ȲmCIر5Kʗ$}I*Jk</\`M.IWo%@PήM v±Q*-3$+ceo5})tDC3kmɲ,G_.6Iؼċ+цޜIOuSK- n3c+@ 9ͭg?g4kV=I$=%E#[KW]=tW.tz{@-0=>tK7ccc5ܜn팶>X ,7s(|`%sH,)1^ \b3t,_ud2:0t!no=+bx_ϰ{w{vЩ/b\ tcg>&8_!EuՏC9!?e3!S?$a{n HO#UOuZ!c!-4ޑ3_jߘfebSGS:RYR3գX7+њ%Yܭ`>* X}9j5iANvՙ BK4OKL z|DdyM!ג,+ W,kR_BN - &~xq/F0ezTHjREs[)a:I>>$bPA|?J,6P=ru \ .mNr>}r[m: .߲-pb£ =(ǩ9z$8iP`"bDguM °!+ @JÇ`۸1'5i  Yu+'2FΟoˋc<4[֥I&$DB2~yW2-R$Ɇ&Z|ҟ,?>dx2o*sR*m,ٌG684; MNb;IsWk={oA駟^&phP)E.CEhe oq} C ,cHx,ٴEa2CRJ,Ba$/Ѿ}MUS$9 cPe6q([ϖpXdbbb}~>88XgB bk-gh Wv_z T͂].3EQC[Czb-+ZA6'm~[mN/>MuZO<=yGiDj {>~xѣWSnG oU~oOuGo- sB\j{`S79TU{qRK>pv8?|ѣ98q?;&jZJq#!%$F߳s& tb wj#׷4,' H.Q %tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/message.png0000644000175000017500000001063014410615043014563 00000000000000PNG  IHDR@@iqgAMA7 cHRMz&u0`:pQ<bKGDIDATx՛u?{ݙ\EuY(X#_O6 8chK2#+0lؖ/D1$-Ӣ")Sr޻swWP3.r WjT )UM6Uqα9}=)%k^yFɎ=Sհ^~W>;J'~ 4}a("]kveֻnl_GT7F J'N@Uw&,HiPB#ԚE C5Q%͖ê!sӮNqsuK8PN5m~7;~4bǟ??5L,|7]Xq1>YǍń\( T!'* yC BQi:'B`.|oߛ+-ag/VoB*/|YDtdy>zg#{ Cc$h\21K \nPS9wNWY92_s@b,?>DN8ӏy`(2*SvUȅž}C!QLSw=Q/ή`mojY <e>>ra'bigqSTz-88/Osh$_V(Dumgޠ[Ud7oG޺R3K"R?-H0UlB-ԓC#}E򍖟pujʧ!0S5 nP~Pm:sz<;{ǏkvA?mWWqa |-2/ TO>Xdrc=rgķ"ao_qe*!zy^4 ξ?__Ƃ%0!& L.XNVyޟNh%>Me[~?Y{8{5Q]7M756.ԉ"񗏖Skxxro\E)F(!4B.4FAN]h!PUF @|d2t^Dl$7箵>o)}= |KSU Cy>vO^ÍOެsy*!\Q5 P)}{CNZtQ-73DĻh9Å9kSeȇG|X1+#eÞ?0h=YKr傰? H_4Ԛ>f͆hX;ƧA1ױbOCC`9"P0clNA1Pv[쒥twuHrbxVC!f5Uň_| ( ppϘ!y1FVO!Ƭ~ :60(x\tĉk Qf+K%82 ;!BZK$qLm Gb'RC|`dX̒cW'S3Ĺ܄#b<&2rNI/Y~q쀒 ` oWxx<| *O=]U_ۍzwP0)C6L-8JPCD ?s>-W'Q avu6Hlxã@ûI+GC>S{>Cl4؈0IIQ0د|_gL@s06j"#xZ80 G!e sUYO]OhDA a_s+%EZˇ7rJ!b|`Ǘptr o.-e%pǨo@YC!$V+}E?9B#ғSz( -WK1[J_L+WЏ(;~@Aa&UQ26h2HGy7 @w$̸0WSfɉ'y景 ^vbSe90bY sUb]4 8UPe}E \T=76T7eߍFgyfs&''_SmW,tSDhY/ 0gٗVmOw[JnÐz3Ɋ VyE:'H+DoSӑSEL3w~jg>o26"69&`=ȔT*@,[OIdC8bX{BӋ-yA)KrP@ Ҟ]RJuo/-D3ͻ?mwض䁠NЫ`XNpPC^du!2?)NBBBY6ULs+}gڄ¼w1\_^>]ӦiqlƶmsO,//3::裏@ Pk׮JWWyLV! EAQ&&&B҂EOJACCBP穩)N8A[[ D"ߟmۜ$---,E!{n|>T@ q/e*eۦ( `0oww7DTJI @Ӵbmm-kB,tuuIRn{ (+|E(?* :\#3>>N<OO BvڅiD"YZuYEOOn?χie?T$JiZAUUe``5t]w Sr;SPA$bYV^HR*}}}R)TU%HH$\>)b l8[Ns, 3\Sαǃ˲*$B~BϪ+sydj*'XJwMYg2iaTW6Eanng޽{Y:={d2IGGCCC˦Ἓ fUPͤX,ǩ#0::*}sAZl1˗/O? @gg'R)^k+<!$`&>B߹kJ4Mc˖-ZlllK #4M_f,LjR0[g'KeՅavv՜T+|] ׳gbxUU P(޽{I$YIN5W;tOKp1BE JJhS@PTLbϋ;{UriP*&; Pf mn,q܊Qm7o" k}5M#@[[uuui3)VVV|_4}6BQU6ؿ?yӧp300@wwwV2Wb%14/D"199---Y飯ʙ3gr LLLm6:;;Bl,pry3gP[[s=(LOO@(8?/b1&''I$^mrEy ܹsElvy47npe^xXYYann-mn<w b1]9oNss3e(d 9TUeiia d`0*v&K=pS^\\k< ڀҴ3Gfhsmmm4M,rB{t !0M' b RA)%4d)%eJ4Mioog||d22lT̫BKmm-333n+7B`zz4zUf25Q[[۱, UUIR.oEQ'Rj^9Î;H$Y%Bݭ[8J@fi߿DTUu+s[&s3͚wS!0UT4\5Ms\=û_TuK.qyjjj7/iF4ܹsqz!vܙW0 ׯc۷o3͠aPQ ҥK|Wر!nݺܜ{+ | r<cccZV]>}Ř/RUP [_~'|Gy4 ,--eiUu]Fcc#<{5?? ϟ?ρ8x eF4-HZJ TSSCSS à [lqC%B`C'#3cw gkeJkYO%}5t$Zɧp.ʺFPťQ=@%8i4⨊9gƜM*1m UQl>o=[qS+4kۖ@4NjF$I"_*p`YN3>QD7 umeS5 Ld 58hU29GAY8 S1Wmb$ ƥc~̱@c*:8},ޥ?]m39X;'~`)s`6.`ګ'/Tv:o 6O4/ZGu)[0KY F}?Y8StFG w{,I05ͼ|o\} {i|:}m{']L8^Pgf8 K5r!x6}JcK iH4/ gbBo*yƔNgsr~2[oo`r->exʀ~_^&xC>BzX ,8J|~w~RrxVf$4Qem^psN8 J1>>iA4%qER0P31vM^^?[~ H.rLKK Xn|J Is?&i*o}J/Kb>#   hq@I'uP/kySro1p*m#˿Ao.oUN.c``˲F l޴5_džI8@( mJ0 ǿUЌ@7w|@bEK Lf&-Bw$VRa׮]x<EX]K.]C9'hBIAE9>BDw o\q!C[.'02wᕃK#ypM=ߊ"+8"d2!JL&1M|%uȑ#l۶q`2dP,nd?ST;91՞eA}4^DZ ԮH>Q o,!@) POVZŞ={zdY+W$Be1 344ĶmȘ)IOaX Z@ކ^/4Id+ixh P1 <{nlڴ:ZZZK"p\ X E\H V?]?ۈ[.Z/0AzǪ~k᡹!(477(QL&C]C b^H ~$@Qf`47f;R-U xRo#[ eRѼg!1 7g?5kPUs H0JS纈dn0+UTS$ F(*׋dtK6OCZKoI EJIUlb C+b[6LH $Wu_˞D"zzz8uSXI*!dMD3'2Z#9r=}r5'z r|/(jXBIb5lµar܂%۶4 ]QEQhmmСC!];B$idRiN$%˒dl߱rL'RЗJq'":u=^^xsR CJ`9q!B-"-oTtsq~zFa|eYXE(btuu1_Saؖd*\x%7@-YOC}c趁9'-Aܱ?BJ|1|Ӯ48! +4\=ζNÑxc_%_k׬nci0x^H&d2 Jkk+:\Z\X @ 4j*]'^4T;:%dLQ*W!{n< 13߸G>2?6jvÇ81ñ6l~yX$rF|>Je(L*0 2 lzB.!-\BpoSrZl ݰ1%$#Y({pm{cdjVuol޽h4͛ 9Lߨş"*8c9!ב(5DׯDxټi RSi,&dwS*PTWyQ J_KL&[p O?-F/8LWV 0L5(#*0LԩXb%[7ob~AIA1SO;UU|/L@4(񑋼\FeJ7r $Egg'###477G@e</#YWj"+ L&W6~0ٲ ;UZP%e@+i0^bl i]㢽^>$IDtIJt:<f%H7F@<y%T(OrjrBv)8BP(9z|t:M.X,R*f 7-1@šk נ^ $) f28L_ ʃhůi/IRׇ8466ƍFȲ̷m*/{.?]{- xu<  2[v3m 5;/_Av{H{Wy%Ip;Mfm۞1|62=lQSᶶ6^l\{1褍)L)ϋc 0i8V\uA~isz}dv=}\/X &q-r+^a!VTU6@Oqާ82|455-B }{糧oZ$m9QEFGBeȿ NhOЀ*Hz-H|;l׿uV^0sazįF}s=jSP o;'&GChX_~>t>ݧp2?Wnzt.)я OI]|%#4%[/8."b&8B1#ϰi΍ Ki&2~pmΝC45^=Gq$5A ,JYDzBXqcM6ͿAb2_|o^|={K$V㌤PU/BzYi MygD"g wG`=K2~^#[la˖-N*"͙=%? \w:::[d,S>vϚ }32~Aeioo[o=#`6;wuY ^LdvvܹlO{̙׾i9']&vmB-M]'%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/mozilla_doc.png0000644000175000017500000001073214410615043015436 00000000000000PNG  IHDR@@iqgAMA a cHRMz&u0`:pQ<bKGDIDATxݛy|TUǿURJ*;{ eSmK 8ҍ"iVafF6({ZQ =Y> #!I!f#ld$-G*${s;{߻ xў={ +)SU['3ȵ-Hl6W/Zh;n <<|prr?>Qk$rT@UAU@Q[P}rYN_yUE/U]~˽S3.J[qH Z0Ֆ{f|-GY0<!W_Ĵ-`}NS;y)¯hLh.E` 'd_P; +v[ow|hT>ҩVP3. D%/:{M7Z.H;Fs$3ðU'вk%/r\ g@ [t5~;5]On+(Ƶ7H$u:k;@_7.Vz᦬,233)))I0LyRtt4O<MO3gX~=DGG3zhFMDD)))9r$>}]-[ҥK1Wev‹/fƍ]VI !^&H\;>=YήXVRSSOxWX`(LEQPUy@o)[z5}~6U1 : "?W֜sO -ߙF`4bGmWS8sd%IIIO?eÆ vm>~;J\ͳ7:?֩лZZǔ ||`t E4$~  b츣 pwNBz'̙3%Kxb"##{.m+ &;r?eo?dOB/5P_ sIȻ&qhɃk֬!11}y@Gȑ#lt=axx8ǏBaqk(N}V&{BNq>ZV.\Ȗ-[p5@477;#b!ܠ'1?VT0n8>#^_giCE >֪`XqSⁱ>e,tRl6O?tTioaTTT_%HhD׍:ϖ-[8(E\iҵaĊϒ}ñXc=v`SS`Νl޼EQFBE`*Ζ`)ճ)Bu8mVn n ~$QXs LʨJJJ &zk3=oAFFg7/MD>} :BNZk2m&يVP]28m@^#qU^2$SOΌ3: *((ॗ^bǎ 41?q8sHXN4` 7*`0Zv2k@& .]2gpnǔ)SHMM"233{Œ3(**"!6!ZF@` $sJ= 0Jw9B}:I؏ifGĪ='##u1eDQhlЋ/HQQOԾvb5[A ]\f5o]ے*Ո |mO͛)++cƍDEEv>3uנX)s(ἑ}ٻ^B']Me]>DMW+bڴil߾cv;*`СC8.Y?> ]P9/+2QZ&Z2 3D j i ϋyq! 9s&Ǐo__6 Պri +t,Y\b_o]EŜv.,iP.!4աGnn.=,_s,#2VKccJLB T )16'ފ˯IUU%''͛7[o U #F̔%*QO1ahKrS؃zߙq7OꩩG08D[\ Xd >(۶m֗gy];!zǒY&5ɑ xEQ6sP8UBD٢/֭[,\vmZ-> _|_- 0]ُ1/= R.*pgDʌQED48645.ުU0 k<0}tAo~NV$Ó8>kg? 続TsB؇"!1*XQRb0D0G_E@Ů/Zoݸq#s%''$^}Վ 5W(99ŋi&OPox<(K͚**9A}PHaMGvPdOJ Y4#+ ht NW1mD< %(R 7cgΜa޽ݻwf`07wfΝGgΜynLm۶ocX>q8?!cv-*1j4455qy :U_[(\8l0f3saڵ !!!7{=jx3g;vĉ*.`0~x#GwyK֭[GSSOٶG'1RZZ>IL6222|aIe~=p"%K|6%z4-Vu ԌW}g*(ѲR;0$I")) NӆFEܼkv8EFFi&VX(9aաE(5/+etfͻuF JRR_><;p | bm|yvwƛle˖-!כ4 O>$'lg,1SY|=Em@eEp<_zN52Ob6+:r#٬_>k׮]_^tCmIEt:---mv3UPĖdg AwGoٳހʻ)((AK83|!Wnw_p=?s<{; (6nd(-d zc>]]>9BviSB{(=` ;ԓo JKK=5bx<ՕbAчx"N Unn;AA(/on$b_!He?UU96PUo PU!C,2nGq@h'NVݿU)ukvٻwd>?{uuuoޢAiHYlhnn~eҥ{^SBtEXtAuthorUnknown! %tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ stEXtDescriptiona pictureAtEXtTitleMade with Sodipodi/'IENDB`recoll-1.36.1/qtgui/mtpics/folder.png0000644000175000017500000001231014410615043014407 00000000000000PNG  IHDR@@iqgAMA|Q cHRMz%u0`:o_FbKGD pHYs  IDATx՚yxT?w̒LHBHDDEqRPZŷV־-b>PVDd/{IȞLd{N03`j,reg5e7wz /o8 , ϝ} OBXZz@ )L=좉9KsK5%ZukB`/>m,E1\Je|;/>qc VYcԵ:ξaV 9՛Jo_Hq;ڿk{е%ܛekcXpQ>k+ORG#p@V=5>,CL&p`ֱVtp7*| kFsӥ8BMUuq%+Ĺ !}JaR-iWd X |/$q1eR2eXrHR?lo@_;Eprط/bHubjF(}pRR*<9E~CGԤI=,s2m_O@C'H.X,^$I"Hs`4~?W q)Y<|ݼ_kVJXL%/ Ivau鐺"؛ψW<2 7JLE*7KG_n<%C76eFsa>;-Bzus‹]1\n.K?wĤtpptޤ,]w$>JX EEh*E1L]V9\\4B9wqޭWrV ]|[gb}ˬBZ ڮ(_O+f]9,x  %c A^uM]REbuȰO#@9K벙 ގvB !$3JsY1cKLlm_,x0 [P%+6Vl] qk!A6v֣Ǒ*-ytWOKΕB_sx`TtqY.ࢱ Foj8Xg?!"as-|5q$5]a_#BpFc D6)~mR~za1JXq6Jl3̼i|<NMLLHE FˊasyEz'T卓JW = nHPsKIhb 8?=6[9k\նUDi;F0 O4-!L~41 CJ0[HD}h(0~o;^O5~NadqZ`I|q)$x6P,9qg2`L~U`Á=Nhz*9 hZK>&b 2 lG "Mf=`ɟy& Ĩ\)opvHR2}Fidh i ఛQIck0H 9sAZmK@2x{)!@+jB1PfLɀcvbk~WLJ 胩fe AwDt $#LII87!Ŗ+5GHqd $}c sєh~jeNx̛i)\!O>Oqp(&;`.xF9LqnS^"Oq0kǼtyk{;sxX{Z SKw~?-% Do|O ^gea6+qcyc ]Q<!?[h{'m8tMm1[P}~#42ɮG %>7#- )e"t 9N5l){|Vhkw.x]?ol)٬<k9ؿgRkD peInnppgW'81ǞGZbU`oIxx5}1ßoÙ ['-e䦧}]=j}\Hԧ5ɤ1q~9Q%i3JְLGb4E)ν랕RVTW*(U7p]!  ub u;2]pA~y[Æ>$Ea`IiB=o|0ޫ~>9;{+\I,Yu0 jP>kKD^R¤*}DRIa}e@yk˛}*ۚdFxs̬\Õ*%tii&>HH UUKRY/SfIIn,/o%TWTp aKh30Zc%%w=zV\X?L_/R~ H,y0*9E xi#Uь@3Ul4[DurhJǨKrȵfXAjzO cS |.XxA9gDu.@zvjI4^;4ꗞMFZm~pی7cHL 0qOϴx8mʲ뺠jX ^a#'߿4>@dΧm^ry.l *bzMOąKp$gFsz!zUG!A jdgUW[ƵڼYf@+ta@f"@[*8c#3Sll $Ɂ=@ qʢGI'aR)p]; !hhh7hT]G("M - =^2naař럶X̟R3̼i`2L,ˁݻ$& 7NsBq4cOՍl=M:V4&T6hoPV-o,xPO1:ljci9 E"3)= Ewrw!MMlg(^Iuol]p偸2n6l5t #뉲%tXW^.R5fX0Mp9q}]<10b]<~56y2V*jG8iyd9o[$)SȞaJOX?n!O Pޤ}fՓhYbλR7x0:4T`U2Ovz>oO&aJ $C( _졼1VV"t8P{0?q_۪̏ N+61VVĸ ;xj E*~_ `HrR^6馬>v\'`SsKy8=oE %Uc/3:ڎōsթpzё nQ{y Ww ="AgѸѭeu&^>BģI}@"ک !imbE;}z fC\qS);9J!ǡg R,p'H/Ϯmlb_Mc} =nvSk,a.,U.uӌm/]7޲'`l.,:ʃYG״+_щчI'z,mZc .ʽQDNswsEh4F]]+O];~XOYvD;b1߰Z,<PsRΈp7\9ܟ {۸zVo"ܶ]h˴- r,qEN[pn!d gd 90=:xzΦq;izGYVhN$w"vm Tݜ=~M9;/7 D>Vky6M{u~thǟkhّ^0aQ4.k5_#iG%[MZ].7DDϨ1e.ڭ8Y@aoM8;Y}" }!TO,c7p8c&^V݌l=+`'&FLWbUR ~{_3E%_vQ!WP a[) cl(0$q_?F筿(u@%ijg'd"%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/aptosid-manual.png0000644000175000017500000001103414410615043016054 00000000000000PNG  IHDR00WgAMA a cHRMz&u0`:pQ<bKGD pHYs11(RIDATh͚y]U?.o%~/!$$`D-hGG- 3 N`3 NXRh!bB!F! %t-}߻g?ү&jV>sϹsaSZ7X VѺj`NZ*zNok^}w`Zkmklk1 ڤ5Zi4F'1k ֔ XKڵ `QW^xh2իH_Ak-֘BTct^clźZݻ}-]Fͪռ[0lB{B1#寣~2yRzuKVu.h=WJ!^~',/M{1X m+eegojœ8a-TV$cA>&2=wty8~_'Zo祛oo|͗\’k떠llX B'°,p<TWUWby^Dnxh:[}yp#^ɟ﹇;vл{7'hz/!Zp?j_SK͊#M%@OjݩAMM3-tݸKȏ>v뮣qq߸7*=? =?yԑT\$O=Ŋ|k 鷏a%K@1LUyE5.f Ѻht*Cn 3qvV|&2ǻ|u0눵6JO`&7 gOTZŒ' \+?)dѥKxuG1ECw, 4F"۾gtK^ ),DG~ud{571255]}5㯼[oQz5ar^t%f ^hMO4~i kOGKKTcy-L>u72YA3ؿI\ nk0Jhq]p ]Rdg]L,3aSkr"--cG1Z1nDgц©rP3`D)V |p! A9!l@M7mD&<^x>XB#Zt. @䱪"B/J5`A Ā֓RJtj|uG:; kй&Pf&[c,"r)TEaz^Xkq 18)! ccȊe6EQp=:?r-O #Cǻ9kё zV(g>@8Κkkݢ/, !4Oi=!J39bI,ԺBK͙9环2Ky}˒kyΝ޵?Ù4mgCaVeUUgcrRaS4 IV=|eo፻b?Tц?ﺋKy׿-b$n .㪫>G0>^gOe%, Gp`f&`zlu'& \+s]jWf mh Lxk+^"{ń᜹6okEg2!J{1To4 xHk+O tVHF8k_yy>p$::p|57]ID"cǘ8|s︃h֙10 kذa}Ÿ3cڶo딀L: >kldwK-}a܉!&J8N03ZOK)kWt.C}V]ˋ7݄WSˆۿ4g`hW_Ee2dN1י 8jH1em(>\HnJeݶm{MLwwn#:a:MT0vj:;]͛iټ-w wGsB?7*$ g̔iٙZK?H㏓BxÆ_|#:jvLc>ʛ?L J[(Yy׬!R]Ͷ;ЇxɎ y# Dd]YR+8_hPQ?38ȁo~=/u}}SnLȕWĎڶ~΋/ yƐ`zhxGzF<};c _Ydv^}h;\\!֟Rn) zC*&3>N`l`^Ͼo$7߿0a;W_F)X> +W6ּJcd2dsK/>V{/w;p80GnBȆ[/)) raLȾ}$׭+x8ZYWkٜVge-RHJG>:m-]*Gshhh|(0,Ƃc.8xh,F '0FD2 j>!ĢERgfӺ6Ց;M~"y뺸c, !0h4Juu5hq0!B<#O,S'F#V- 3Yk'7Wk֬qD"kq@cr8S.fffH)(a%.H̞,=^]ZRmO!떖Xš7 yNTS<^@r!`NRy)%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ stEXtSoftwarewww.inkscape.org<IENDB`recoll-1.36.1/qtgui/mtpics/wordprocessing.png0000644000175000017500000001324114410615043016210 00000000000000PNG  IHDR@@iqgAMA7 cHRMz&u0`:pQ<bKGDIDATx՛ɏ\׹N5]U=&iHҋL;,gBdY@)l)CƋM"ohGO%J$nn6xnUX=2^>g7~x4iZ< Q d}]Dmag~gM,p-z70 ۶0vwwKo|>c!Z F}f);9 >?|'j2ZJ%W$z D D2ĕ!2ė!2!BdRHB~[ C°7 ~IHI]o}H{qsޅs8Bo}/x"R4;Z0 A"&hu%^I$4?(}Y W6[H_ !~}|c}~wl,RCE b!@0{߱ب\ZÐlRV^O?O;uӥ8{-=-hM>I{IBH=@cM;qJ=%`aB,FC>{qŇ lRp×k.Ӝ11 AhĿ$k EIJSה=0tE1_tX6WbXXXѣGy<ϥjy1Mnh64dsYl&L177.LnsJi\Ą⨧b\G| d!4u2 P,4M,+"XiNA4$HJ*f}H)q˲^n<M0uK.JtGAJyd:RrL2\.3==MP`ccc6>D0 Y[[ :F۶ 4I&H),,,H$B B"qh\<hZe `cc˗/I<+:Ai$ No,Rfɓ'O( A@V#Lb RG!=0}LDDx&\\\dvv˲x0 ^z !BN:v_W):!wyGj}u] @4 Pף=rssifOq>t}^Eubv$?'&"N/zVpR~Dh8&?AwDZC.^ݻwF[6`mm 0ÐXR)b;;;XqnW5"\ Hh4lll0??O }ZiH.#ă >#2==ޞRSu=&''T*J>,%*ne&DAR˲!ua΋yc"l˲,T*epEL&Wq5&ڄoEq9̫B`6{{{͍=eVD"A2Rb`dvM*lf1 VE2P(thZYALTa`&a q\ PD4deeFA2h099I٤Rp9=zit: PhZضdh055u?ab],DN=虳nbaB@/,p]W%("yeTdB8׮]Sm.sssSqٳgq]Wop D| kCk ]=82p Eg?~B[[[mt]L$+O0 bmm qHӸ˫ʝ;wxIRc#--F%!@#ؒX8N>A(x}%2JEun˩Sf躮M<ƁBõ(I ${C4F!F "v[8b1eam)%m(.y饗&@96#[+x3cgzP8((OvI&t:GQDL]c$F*'Ma"<Q("Qm۴Zq tu]WN`*-i_b( z[9BHӄalF3]*LO!+*h6*Oh8C*b~~UZ,麎8*|d2v3ځQ3M8q4M+uUM/QyF4qlfd2I^4M&ӬP(j* Zm111q"]v$aH9RބvD0L H)fzzZSSSAV#s5,DBآ4Z3OZ%͒Jv/v<+c&xn'G4=3_~%j*nN@O1_t .]۷RYXXj5O~r cjh m?s\W^a1 Ce:Ze"op]VVVzguy2'Ɗ@Ovãm :c?&L0 fggbd2fi4P9rUc[[[ӧBX0j-S)4a8ޤhBnSI 쑄Bnx@Jə3gX^^V=wX+Qb9sFU9 4()!gt]ck,--Kr< ƒKJIVuݡI(yyޡ8{'AjF:VN`4MϜ"@T }BZV_ jN\.w"1;;wg*Gmn޼qDxF S!GeYcIaܹXy>}6;XU>+xބ`f旿{[?tNDrQ2D<0x!?A^Gg0ƍկ>#0m* v[yTQxee|>iיEu666d2b1b89|ߧ\.+'f~~^sl=Q$:X'Opƍ7n|dXt:lmm)l6rj5677IRxG&,2tz.Orƍ =s`vvvh}Wgz-:Cё)\ibY?я"nl6eQEMduu۶ s1r[nٶ|8`cc۷oS* &pU2e׿Û7oF "?,yי}6S"H`bჃժ:JoҥK[יqUlH)ZR*X[[#ͪRJ `gg>h֭[ |8zD|MkQ:*H$W\u]JTJ(똦U<:!eY;w:@\zw*!?^yo]=?1}6?} P .駟"7޽{jܟ'4_5b`qn֭[u]Sz2ȏ%3gxwI$ضD4M?HR e""UUur@U*>o>߸oCR !!.$Iu?n811mפ :9R.s 8yyuu54g0 988пǟ|[# Zl0 Ng0D^t:/8"Vy Lze~0L%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/aptosid-manual-copyright.txt0000644000175000017500000003400214410615043020115 00000000000000This package was debianized by Kel Modderman on Mon, 9 Apr 2007 13:54:11 +1000. It was downloaded from http://developer.berlios.de/projects/fullstory/ Files: lib/* Copyright: © 2006-2010 Trevor Walkley (bluewater) License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: bg/* Copyright: © 2008-2009 manul License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: da/* Copyright: © 2006-2009 Rasmus Pørksen License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: de/* Copyright: © 2006-2010 Markus Huber © 2006-2009 Markus Müller © 2006-2009 Philipp Rudolph License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License.. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: el/* Copyright: © 2008-2008 Gregory Gretri (grigris) siduxgr@gmail.com © 2008-2010 Nikolas Poniros (edhunter)edhunter@TBA.com © 2007-2008 mixalis (miles) georgiou mechmg93@gmail.com © 2007-2008 Pavlos (lathspell) fubar.ath@gmail.com © 2007-2008 Lazaros (riddle3)lazarost@gmail.com © 2007-2008 spyros melcher (xouzouris) License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: en/* Copyright: © 2006-2010 Trevor Walkley License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: es/* Copyright: © 2006-2009 Richard Holt © 2009-2010 Luis_P License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: fr/* Copyright: © 2006-2009 Philippe Masson License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: hr/* Copyright: © 2006-2009 Dinko Sabo License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: hu/* Copyright: © 2008-2009 mrowl Bagoj Ur © 2008-2009 ruess reuss@chello.hu © 2008-2009 Siposs Zoltan © 2008-2009 honorshark honorshark@gmail.com License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License.Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: it/* Copyright: © 2008-2009 Renato Zanotti zenren@tiscali.it © 2008-2009 speedygeo speedygeo@email.it © 2007-2009 Stefano Tombolini dedo.tombolini@gmail.com © 2008-2010 Alessio Giustini alessio@alessiogiustini.com License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: ja/* Copyright: © 2006-2009 Mutsumu Nomura License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: nl/* Copyright: © 2006-2010 S R Eissens © 2007-2009 Ronald Stam © 2007-2009 HarzG License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: pl/* Copyright: © 2006-2008 Marcin Słotwiński © 2008-2009 Michael R' Tokarczyk © 2009 dongle License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: pt-br/* Copyright: © 2006-2010 Jose Tadeu Barros License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: ro/* Copyright: © 2006-2008 Gabriel Palade © 2009-2010 Dorin Vatavu License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: ru/* Copyright: © 2006-2010 Roland Engert © 2006-2007 Dmytro Kychenko © 2006-2007 Mikhail Burov © 2009 kostiagol License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: menu/* Copyright: © 2006-2010 Trevor Walkley License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: debian/* Copyright: © 2007, Kel Modderman License: GPL-2+ The Debian packaging information is licensed under the GNU General Public License, version 2 or later. Files: menu/* Copyright: © 2006-2010 Trevor Walkley License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License.. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: menu/icons/* Copyright: © 2010 Rick Battle © 2008-2010 Bernard Gray © 2008 David Creedy © 2007 David Vignoni © 2007 Johann Ollivier Lapeyre © 2007 Kenneth Wimer © 2007 Nuno Fernades Pinheiro © 2007 Riccardo Iaconelli © 2007 David Miller License: CC-ASA-3.0 | LGPL-2+ | GPL-2+ aptosid-manual.svg also includes elements from The Oxygen Icon Theme. Licensed under the Creative Common Attribution-ShareAlike 3.0 license, as found here: http://creativecommons.org/licenses/by-sa/3.0/ or the GNU Library General Public License (with following clarification). Clarification: The GNU Lesser General Public License or LGPL is written for software libraries in the first place. We expressly want the LGPL to be valid for this artwork library too. KDE Oxygen theme icons is a special kind of software library, it is an artwork library, it's elements can be used in a Graphical User Interface, or GUI. Source code, for this library means: - where they exist, SVG; - otherwise, if applicable, the multi-layered formats xcf or psd, or otherwise png. The LGPL in some sections obliges you to make the files carry notices. With images this is in some cases impossible or hardly useful. With this library a notice is placed at a prominent place in the directory containing the elements. You may follow this practice. The exception in section 6 of the GNU Lesser General Public License covers the use of elements of this art library in a GUI. Files: * Copyright: © 2006-2010 Trevor Walkley License: GFDL-1.2+ All content is © 2006-2010 and released under GNU Free Documentation License. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. Files: *Coloured sidux Book Icons (type2) Copyright (C) 2007  by spacepenguin, cako and cathbard (http://sidux.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA I suggest a copy be kept in your sources for safe keeping ________________________________________________________________________________ On Debian systems, the complete text of the GNU Free Documentation License, version 2, can be found in `/usr/share/common-licenses/GFDL-1.2'. On Debian systems, the complete text of the GNU General Public License, version 2, can be found in `/usr/share/common-licenses/GPL-2'. On Debian systems, the complete text of the GNU Lesser General Public License, version 2, can be found in `/usr/share/common-licenses/LGPL-2'. The Debian packaging is © 2007--2010, Kel Modderman and is licensed under the GPL, see `/usr/share/common-licenses/GPL'. recoll-1.36.1/qtgui/mtpics/presentation.png0000644000175000017500000000724214410615043015657 00000000000000PNG  IHDR@@iqgAMA a cHRMz&u0`:pQ<bKGD pHYs  ~tIME 1  IDATx{pT}?ݻoJE # p`;MvxS)vęcM2lx&θ&5 uGL`cc YʱC'v1%ޡ.֩2fQ- B C$gd`"O'SHJ$L4RN$Iʋ/H@»e (JE*HjT;wn^J:ˉR t]׋_V>N%K4< 921eYTSO6RyRbHTԪ 7npz2iܺVjSe͝d{k8u3tf=?# x kr9*7; |7}{c?T0O1YO_zywk.\W hk~~^8ߺ[̚5+݋SOaF= ac?]S$IbD,|+B{o;~ӉiZOn#H\6SkKZp8L4EUI{_5S, `ViWՎ5s8F0!5wb0L4?EH,+jA?5KJ;y=^1"QVyA{ |zl6[zL9B] jAÄ!='>#ؑIX+&{M4遴rLLyixR8?«2*{П #﯁{w6:,;vb0ƒ{X@< Fr R=x=x}*{F@^э|?Eź6,sPe }4CA {9>z u8ICm#@DDWawX֬etE- Z@!p.t.KyπO'>0vj9k>4' %V~Adߡ Y.+@5{-@0=5}GDCS꣄caLqS>!=ĸ>NLgp20!t,-G@u2 ‰6B)A$*,d!`H"DG N*7}̖k_N4MZgȲd@"DG"ad EQHEԊ $ā8/_' '0駢R!2b Ub2fPX J yla\VZVeBm-LJ.3 `mȲ?~yG!, $ s*8GF2 nYvKZf}45 ?~BלxY\sƣ!iSĔbeʗ)cM`r"YToa_$X%+&9{RV)1fY%+fɜ[] @&{,#BY+r,@Q5'^JQq#j Yq5 krd$˜||ZXySҡՒcϊLBqW == 2"x?NFW5\_s^nۯES6mv Ϛ䶵kc(X啩(8Nc:t96eYwaڵtӔ-TȎ;Bb\TtFjjjlD"{NCMr'.-eb6M:  Kp-?3eYb`Ll:A&zWL=,]€7?_e\ D߿2Q [j>lWn4T.M֪#rIx'Xl 6`T d2Q:<`08FٙE@!oNcc㔔|C}}=nh4:?Da:sO=))_J^U%o8B,`Ν444|Q>(4 >3444l޸qcYʗ$r_.twLK|{dz{Y4M H|?M---3>L>hEz>< w .kM~Sn %tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/txt.png0000644000175000017500000000613414410615043013762 00000000000000PNG  IHDR@@iqgAMA a cHRMz&u0`:pQ<bKGD pHYs  tIME 9'[ KIDATxݛklwfvv<@B'PZZPB)(TB@PUCU%"-HUK+ZRP[ 8N8vb~wwvggn?̮YC葮vvΝǽ̬ WD?L`8pw۶mo~۶R^~.u\ι챊fbOMMi?&BKDQuڵm߾Ͳ fB$W:M8xH00pABN1˖N.%'oߜfQo5v7ۖضbeDrH*:=4#Ad:lf[Z4֬ &ۻ!±ʬ"RJyMp;lDr+Α8mԩ >6y0ј_0̋|9@@xNJq(EۼtIz>n0eI9U˾@PVW U6=&!TE,,Rb/xJ~ is~DfS*h"W,bkU7yjBKDz,w1JR/t۶3gKҕi̡KFH6^$ BA}㭬4|jgU mfoS1>ԱC~Եe{FmpOlZrjxAQ%55!Vr|x|l'.J˲CvK3cMlJLӬ؀U *":W<_ѕхc+~~A%(?:Į(C¸BK=4|{Yzb$[Iqe<[փvne U#]M/IlJlv+${@I%"jzw,/<0Th^ pm{˾PJI1V BM x7$([ԭ(t7$VfF %Fc}UhYFgKU9Atmy6GAP5Fו$6.ظ˱>Ϡ]>07L{f'3Ѽ{_Q)W+%c3TpI4 @%BjJS!^~MuZR$XNB~v@w>|> %fhIY+J@9M;sI^xzC]Yqݭ9dr+R@Y_|ugqza^Z; P5L8镜FB!(DR $"sO$:<ɅS9DίgOmur뀢ӠiM4 E5GDlD'KV0bh*y/)LG18>{@,PD"PoKW/e߆X=sO6 WY]JaĉD L3SXd~>B}} pP6:ʥ;CxYr f P5 /u۶D "._8l^q.n\ (;`kLN}XḾ=<3^B|>uVfރƭ3kY&_bjٰX'.,xDݽ3Dva%͠A* mw$g|n^0`AYxXRQ  KR{ fy&&fr%7]_7 ixCl|-@{GiB`1a"eK׫g~Ah^ MN_񁩀!?s UGcru n0S1:u>}$@!QN9_P(zlpcYe (;YUUK_xѳN: hMXfOmqWbֵ'NȱP(d༏UJBs9?CϾ%W1N$OyG$O䄀I^xO' bfF 3'$0Q*"0f߰'ikB|>.I;tHŚ ~1?h_r%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/image.png0000644000175000017500000001057314410615043014227 00000000000000PNG  IHDR@@iqgAMA7 cHRMz&u0`:pQ<bKGDIDATx՛[ys!%{YYo$_jDqN(Pp PO~H5$iV=C )ԮVuۍF.{ms@x8Kr-Q>s9sw=gH&MOO!H1)h~$ ]{߭xc9sZƄh1_/n)n懯?"klnnxG\i咀i9%su}WVP;(؎˅ F6T*ZOOܭ~7(WHADO<8nCnShI)AD6R"dWHh6ƚm K)qe\B4d` IJiWAywBDOD, 8euRS5+k<}4,9CQ1hJ ઠ d[( hZhI*1uO4ѰW! q}3*ccT5"(##QKk|pDUJDSi܋@x kEtBa,PUx(fMmJPryh2q J&R2]fn[4L. $.HfW\*6 9IɔĢ>eaڒ' f,F/enQ}*LjhBAni5A͖(`6BJz.&lCoBA (RHAnAUXBI k_id)і?KѽH.[2 f/`r$v1cdv;eOO2pHW5Em߭ Uq.߻s;+p ySukٍO<*}6^s][p:G]qjxxY]|\(J\^p_2$`vWb e2 خm?g{{]3öm=thuu5ŋܺuQTH??{(ܺu!^xTy.</^$Hp=}A>˗嶓!ށv;qI\4Mj㪪DR8hz MSQUPY&)æ_g5y[&Ocv <^J>0 D"В$A\f{{xf,'|sz`Joo/Lqfgg334idZF>g``I4Mcrr+WMky5t ә8Rh4˗I,--GQ* 묯d?fjj !x>ؒ]ܑ"KKK-٠]m>m_L\g!J@;33$IoG}<Ì+Wp1lfhh|>OT"(>M7 _6LXa^JRLMMsϓdr1::ʫʏ||~;t//sssul&ߏL&CP`tt `O خoO^g||D"iI" 211mΟ?d]{4Mo2??O__lL&8R)[J{ZaU^GQ9Khj/^l [zt*Jv7ξ|s 4eD0MʊP4W|uw)` '<.ߟX,;d2OSbS/9nٴb|C8MP?$LJ%YQ;j{_1 ^Aso'h:-Ҳ,RX 4Y\ @_e6[#_j Rجדz¯Y]]měGA4X,ZRR*,β(aw g*_ eI =(؎GW&@y{RիW#@RRpĉ;sv鮝ƃA~O[vFUU 'FT^d|ARiX-uOpp jw0558$ _Vd|:mŎݖO. /*lM$L&O?|2Ca8b6sss 8_lnnRV[Nw<vfߩ?heq5N<8BR!Loo/O?BPY__'144(NojNiPQ_ lz{{)-a˲T*H)SZ<) - 8Nz>\ xia,ҥKTUpp4}60 X,F"x|1v'kGiށaDQT*|FpO9B\.G:FJaض\%(vnA *gΜks`+ p]a0Ms߹n !o9j@fS_+<t]'Nw-ARJ8jշk]YYY_=s'UnGb~ p\_0X^^ٳ7xszN·p/SK*B44ٳA+'@7(o=믿^wn[E~zzo#˲.4+"h4J6?oyβ_ѥ@`$auusNOO,+ {S7ul6;3;==æ-|GUh,[o/z3 ''O~o{t]gmmw} Tx@J(I)THRv͛iǮCEO>}…5k"&n{cydDg%1Y_+r>w=,F6:ԁ?Xl&,!I[m%tAbߊ3m6Y?۠{l>4Y^2`JX_h}} \},*Cz@^S〄nƦ3zjlՕj=.=:^U'KS'NE:M WCVsT{싌#jpuJOۼYq&$Rמ{+,]}?;[[u5g(l&U D@s(=41RI^r&5_wÃ6U  im"ZK *SEB@FյE$,˸kO<+@BM, >q{bwRAqx4]uDC1l0]&! J J'CvlxH۲(iVK'rT|ATQb#G)hi- Z"J} /jr1B*C"D H>Jq :CkTtJF.^%WJR&u-syD@D%m,e=O94fB*Ga"TX-̻t%E0h< 0ܦmR*| FrEiYx HM!x4S*8)(x6ڊfC: ep3\IOj`@ nx-:.#T'$z`JS~9u*"dD$ـHҢ>-и(Hw}G]7y.|sIUm!BJg6"=AS+G\hr蔴r{RAVU#Qɗvԩ|⦑i ZEwB([tČ dU|$=M_39j s  o'hH@Tlc=DC5uB!ZuBLu4 JϴikE)pH^ $yvB8H5GD8{{7@hyFDu1b̠ĸL<f4e3g>_~%1@ U IV. ДuFE"(ww9F?ɽk8/ӧ8uC\z_yfѴpUx:X 2WTBSP%lAMD[?:u-l޾;oK2'1\v< W"4xk:/! QB{߄u=nܸϋ.]B0o_G*`|w.IA"Jŏyx_׌G#֭[_{׀w!gДĀH|I 5i⾤/*G-2шw}g}UUUJp u+#Aos=<V?oYP3ϼnmxQL;&B!KGh\q"QU$%~g{~%q~sQd"\ KX@IY $Bi":ݻlomx^uν~aeD$Vs$14Q@'Ua}ﳹ  iQ+W`00\.?*.Ws"!͛<+#G}3dURckC_o{|=MFO!ID37/w~K/s2DV,xV~"F6x?~_t;"ۀ$+q{  t@=w__|ׁ/y8~Lm}}+W^֘qf?qĖsI4M &#}=Cu+. 19;*]{p6;V1 H%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/pidgin.png0000644000175000017500000000717214410615043014420 00000000000000PNG  IHDR00WgAMA a cHRMz&u0`:pQ<bKGD pHYs B(x WIDATh՚{tUU~?{sν7$%$H!5bE>UgFkڪbNRhr|"`㈊Ԏ oȋ}&D DtVwrdݿߍYS1mq"GJra7u֢{ZpXc??v3Xq 0>,Ӿsj;tAQD.%+E;%k|vJG_՝]Ģ*t?;1~b\t[ݻ ]o`NLĢ)_I*_kJg0‘k.TLWl:O۞$GDNT3(;>}q7^w~;1~F67q<>‹ϚBW[ )%B!!)|q&ѻY;ꔁ,0䕟8 uhѓ7rKrN]\X4Zhtۗǔqt!1H%JJ#PB Ǔ(Wxs6Ў#̬Ayq9? SFBxLmmݤ'# %RI# UHGHn8up~R~|c))'ZgǸ{«w6=Iwka Am X+LɶON8m$X0w>u7^w \YHqs]i]4771~XG9^̺2n4 @@XHwٳs/3/Kf 7ϼ?~7Ht.7mo"P1z;@5<{KOB0 |/;5w,0skNژQIwug9sd \4Ʉc_x~x2 fG.;a6 qxAVS Xaר3 E/*pBuQE^^&U+Yu&ϙ@-KX !0(5rXNXI/\F=5E(B7\u^-ZG$ەW˞&ʅ1d/FI:%'F#4j` 3<ߛrɃD"BcqJ)kJf]5Y|XDٍ7c/asv5 ?Ŝk{Xkn2]9hYؐ(6qsiݗdsQǓL<IK%XcdU:FyUÖWvL3tR9 H>!ؐ? qTTl|i+4r)usO qC]r-n$'o ȶw(p`c(*XrL.Hk3KSG!`8QX;fnTgW`1kA k57!AG;=\g6Kw|nj]cՒs/:9Fxm-EB@ei%ON}3w?ܫ Kc,҄VPU3kpI'a%G)ü}!oɒy4[k[P&s8fDy4nJQ燄K 6`(F[vOw1c){N(Sɘ6@Eܟ|E<5̤3?R luVF"-B 2}̟!(wޘc)6úFֶi)+)lʙ$ # 0M : >zn~X<@O4H!8oD:[RXc +:0mƠ47q]x+Y@cCk2'OeFEjE[6w{0:i x1-YS*RYThpUoVjh0@k !hZS E,V~Thpkk-)q0~Ybq˝nr } շDxDimo` U WIfV=ն+0w5~*\;slꢮґD;HWi PDtg@>btc-^Ѷƿ[5B D' .MƸhlF@&hXcg>A:нZZhm::v0i)?<{^ڻ#Np\ǫR YG[$FZgl++JY $(Dþ赀3fZXkiFa-UJy:qp5_X>|2i GZBKy$!RI#Rɞ@X 'Za+'*ʄ1&FkL$R9fa @ 8Sڌ ;:Jq7:^tM[oMh +h!"Ar` AtDFD<OԕѽDK:Hq[=h/ .I& L8@%Iz-a{?b21;R,N,L6LxiAknJı NQ2Aq< AH|$g! A$wD A zABo%I0T0CR`&T&C^ sEɌ0\|p+B54 k'8i,Fq68k`1d~M8L ֏`6ORSG X|zoAw cE)!YO4Rh PLȸ+F#b,/-M^{cFPQ<ۄP!Nlr+>7ygWs5x'N Q|DYHNـ89l3dϪ#6`a\ &řPS u#Qbkܸda1A$G@hiŁ P] iţoD]r`UxQ' I2R^ n,牓 )WRġ{GO#??$jN+M7 0^ZL&CEQD$0 h4iDQiE,Z`idNC`08mcvf33$Irh8<M y#<;;;Aq1MfI*իضݻwj C]NB@VnJ0 ˲4_~XNY?W `nnM4(XZZB4,en K.1 BEP`ff8Y\\ "ǡRE\~0Ԁ$4M9cOf.c355>N8in߾aEZJzNE,//O'"t]l"h0==͍7HR&{TweُyR\|e%F74$Iڲ0 0$I\%"(Bij8>stǣ'08(mcBjw;Q.2svt:2 Zt:i8F< ÐT*z=,q1$ A`6t`@u5d2!hLLL>rm4Mj38ZѠn#"<ϣP(l6u(0M ^c&w0 \Ŷm:Z}qhZضjfqd2 B:|JB\tI.$l68iTUKE\)vwwrTU%-GQz'Q1 ]yR}%tgss˲)}l,,,bnnN `0X, C"f`@T¶m8> (:w8J? `D:$ߟ\.c&[[[A矧+!_:V<@r^-ˢaVDQm'NcYi+&&&q̕+W4M\U$!)$$IHRܻwRDj#j7hdY%abP%W$iRf0Et=//,333R(&!jHe1q,x+v4t=$IBR91zb^ph%"麮D𤉞JPJ\$GLa}}T!RvMC¢("J|+:+1fVyr({T|GA>?ٙѶmR2L?HQuF>׍׏'PLQQU`kkKkkk4ML$I,Btz=֨VeZV>쐊\Hd'g$y MTq AU5 qzJ7#S*Q,(*hj$%SbrkkkxawSgY*#$]a:& C?P. P_]honn8@`ccme&&&(|G\r?\I"a< 8?cYadG&mJ"7rb2}&il&!\ץT*lH/!cMӔݰ,K2V Zfu]x,KIEʩ^ IXXX8T,,,裓$a~~^Ue78ko (7^&=gffȱC/X[8\O#Hi~_YBp !T46aYz/Am[C&V4Y__Oz\|YmK/6Lu6;;;T*lիܿZt]gaafݻwUN)}jɭu5$I *@ZXXPnSn])r˲d2ڵklnn};Y_,d2ꀄL4vnK:&ɐ嘛S@Ν;xǕ+Wy?iwXE??@x&g[[[|'_WceeE"G4^J^֭[ʝ4) r9666@i4MoPϔ+woߺu |#g5c)F_{-3Km秊%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/aptosid-book.png0000644000175000017500000001103414410615043015531 00000000000000PNG  IHDR00WgAMA a cHRMz&u0`:pQ<bKGD pHYs11(RIDATh͚y]U?.o%~/!$$`D-hGG- 3 N`3 NXRh!bB!F! %t-}߻g?ү&jV>sϹsaSZ7X VѺj`NZ*zNok^}w`Zkmklk1 ڤ5Zi4F'1k ֔ XKڵ `QW^xh2իH_Ak-֘BTct^clźZݻ}-]Fͪռ[0lB{B1#寣~2yRzuKVu.h=WJ!^~',/M{1X m+eegojœ8a-TV$cA>&2=wty8~_'Zo祛oo|͗\’k떠llX B'°,p<TWUWby^Dnxh:[}yp#^ɟ﹇;vл{7'hz/!Zp?j_SK͊#M%@OjݩAMM3-tݸKȏ>v뮣qq߸7*=? =?yԑT\$O=Ŋ|k 鷏a%K@1LUyE5.f Ѻht*Cn 3qvV|&2ǻ|u0눵6JO`&7 gOTZŒ' \+?)dѥKxuG1ECw, 4F"۾gtK^ ),DG~ud{571255]}5㯼[oQz5ar^t%f ^hMO4~i kOGKKTcy-L>u72YA3ؿI\ nk0Jhq]p ]Rdg]L,3aSkr"--cG1Z1nDgц©rP3`D)V |p! A9!l@M7mD&<^x>XB#Zt. @䱪"B/J5`A Ā֓RJtj|uG:; kй&Pf&[c,"r)TEaz^Xkq 18)! ccȊe6EQp=:?r-O #Cǻ9kё zV(g>@8Κkkݢ/, !4Oi=!J39bI,ԺBK͙9环2Ky}˒kyΝ޵?Ù4mgCaVeUUgcrRaS4 IV=|eo፻b?Tц?ﺋKy׿-b$n .㪫>G0>^gOe%, Gp`f&`zlu'& \+s]jWf mh Lxk+^"{ń᜹6okEg2!J{1To4 xHk+O tVHF8k_yy>p$::p|57]ID"cǘ8|s︃h֙10 kذa}Ÿ3cڶo딀L: >kldwK-}a܉!&J8N03ZOK)kWt.C}V]ˋ7݄WSˆۿ4g`hW_Ee2dN1י 8jH1em(>\HnJeݶm{MLwwn#:a:MT0vj:;]͛iټ-w wGsB?7*$ g̔iٙZK?H㏓BxÆ_|#:jvLc>ʛ?L J[(Yy׬!R]Ͷ;ЇxɎ y# Dd]YR+8_hPQ?38ȁo~=/u}}SnLȕWĎڶ~΋/ yƐ`zhxGzF<};c _Ydv^}h;\\!֟Rn) zC*&3>N`l`^Ͼo$7߿0a;W_F)X> +W6ּJcd2dsK/>V{/w;p80GnBȆ[/)) raLȾ}$׭+x8ZYWkٜVge-RHJG>:m-]*Gshhh|(0,Ƃc.8xh,F '0FD2 j>!ĢERgfӺ6Ց;M~"y뺸c, !0h4Juu5hq0!B<#O,S'F#V- 3Yk'7Wk֬qD"kq@cr8S.fffH)(a%.H̞,=^]ZRmO!떖Xš7 yNTS<^@r!`NRy)%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ stEXtSoftwarewww.inkscape.org<IENDB`recoll-1.36.1/qtgui/mtpics/spreadsheet.png0000644000175000017500000000671014410615043015452 00000000000000PNG  IHDR@@iqgAMA7 cHRMz&u0`:pQ<bKGD IDATxݛMlr=䮸hHӢa lA"86>w In6$ ,'ɲ C(ْi X-~GwdgwcI{)`1UWUWm'4ͿUJ9RkRꇨ;@Нȶtvzm8ھBso}4J4\&7Ο7v< !}6|۷9{%S<ȿ- ܫs=؄k`1~ah`Y㡼J)ݻdDu>a?fu] @aFTgu]RTJݻwq]d22۶#֢t:M*u]8yd'ɠ믿f}}^{>jfL&J7|Ç@)뺁)@!n,@ ܹa 럔RD>X,#`ee49|0x4'iգzoA6ŶPm 2>>YD;I)EAT4@58ڇ=8Aqq]fROSem!o8ضSNy- 1M_~rBߢ۶YMz h߇(uKKKl'!mc6j5ZRyA9k#?RJ9y7H\eee~;Ԝ| pرP%  pj5jZ`H5 M*IAm][o.Ft% >JGA~P\.G>̙3OR uB"R) N jW%N3009?-rz3UB8NJh@]/8#СCrd*\T*HYP׻8V/y~W1 ë–TUFFFB :tP20 B7C ZO>N344۶Y]]edde J%FGG#M<100p(Vȑ#GBy݆(M1*el!ܸq#ETt+'na^oSLƫQh&I]PM0 y~a ^VJQ, r9_BfdD;Z.w@(-O[RӧOj ܹCw#8sLd9Aֱ`2$N#`aaZӧ[H$E&C===it(O&Zb&eouY[[cqq1yAJ`$A?[bȋ/>tS7QZl4Mo%l]ž}U.D})٠9r+l~9ARKɱ_IA'T_|sҥw}h~o4۶)(zEeQ,#}@ѠRP.CSJB^gccëAA/:ρĞor;2 lP NJB`eejrܞ pw|nn3`9_.p)%?zSO1>>X]]ĉz0`… ZG}鼧d7oRxW[Znac ޮ,{),i!4 l&Jł8_>>(B^x* TE:DQ7]W~ŋ?fff  @^T*Rl6K6eeeESϑ>P(x̄B z{{xۗ/裏>m# _ φ~7I)1r4[/_vΞ=̌죫qT*np{lT*yП}Wιsv-|,aukիW?xO'EըmH+¾|Rigjj곹s,oi ׮] WB;`\ǯ\bOMM}vu-|Kn O?*peYbyyY^|ZuI mrm#  l"E@@B,%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/pdf.png0000644000175000017500000000745714410615043013725 00000000000000PNG  IHDR@@iqgAMA7 cHRMz&u0`:pQ<bKGDFIDATx͛{}?$"#B[1ryN%NaRTJA) c:6FF,B[@<C=w=cnvow(~US3;}ۿ_ozqƅpߤA=D)*GY+P3=Tѳ6QQ%'PLRn ] ̽ n Ww}lWo=PqwpW\!WDL`3@)=.2gZOR67қwh=vw52z7_sw‘iYeU*j]?#ƅB]&ІD? P]oOlw;3nXzs뎝ˠ-K.l PZf@]#GO4H3HwM"ܤWRq~3@ -m "8Q2k$JTzJ Vo$|Ǻǭn;SdàyMr |QjB_TY`bw*F5D>Ng> ԯf i ‰644bPr*QV6**W"tZ4b~)W3-cp4IzRXEf@kTc3:4"je;~n L{J7^)U竉3,9!6h}GrңjedOڛov_ڰzh|##(U &;Nx`RVy! 1GaMuMɿI:CS}zILҀ6^u-مKZc}Dg٠gdoNp:2)u o_ M{dKOcRuWX:|~u?dS8fkKKQ,KG})_KDTڪ Sbr;4QM =D:`˗(ή 2 8vv LT(;H 2i0LZo"~н*qtYo[ wB p:KGm`{psl ޻ui0TS+ FD( ěaY;18fPخ@mX |/ȶ-#.w<pU1lY-[Ȏ N[*@^~ơx^p&ᥟJ3ȡ>D=pẹ8BLo2;C Jb?АZr>2a}]ȣ]hS]x&×`K.>2P H0&x--a5G40F3JSwfCW"cCa} ch|k+UW_q9s## dswП[E; 1ifhj!Ӣgkf4Ѧ}䶍N":"rEKh,BE!"B[@_A}EI;L~\4F"U~{}|i9cݘDQk1;w Mmavm|H!NgȴJ>>2RWr=R,eaʧ@# F)HaǾ3LK8HضKvLs hwԳeMWWW%RMpr_=~k{*Ui8[ȤP6p\yaլf,㡇ضmۣp1%  Axi3o1^!ihh}(,̼Dhڍb]v߃>SpNm m{[H^%&2oA6h=b㋯cwuWǨlUd @E;y-ĩsJ17EF\s+m4BBRc*=mzr~=M$qw??)1B{BM_ps3-^7OOc;Vp9 ɺu^` ?)oZ}#Q n6x_aΚUD- @(7fݺu7o<)+0ؿ~Iw e^^R_ʉ>b\j u@tvvrwWEL`Ұ0k~'9 \wfMi{x?QjYf?Vk|Y&H6+.CS=O}iYt饟C}j1`h$i3,@X,Ɔ :l;_Q@9p#E؎",4X5X?u۶{q˖-^d|Y.,|?47,"q}?j6v>Fc $qۺuk޾&%Z#_gӦM{n3Hs 08r6mW/`PJil4M:ݻ=NqB޽{^}h5?.QJ122btq HlIGŻ%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/sownd.png0000644000175000017500000001101014410615043014262 00000000000000PNG  IHDR@@iqgAMA7 cHRMz&u0`:pQ<bKGDIDATxyp\}?C]%$ c˒]402-H!N2 6 I&!%ĥ1iCNCDXDkdi]J+i%dy=~LsC79DY-i$I4ML䝏llo%N4!>:0kZ{$FK鄘Z@ØIW7n'@0zz)Iښy[;6^sغ>8'Ob&?³<#l|sy[z-j4mw}ߕ~O,kq>(9-6=g}6aX ȲHKϯD%|Ӧ ?: 0MeYϯB%uWoޙC#&,!I"$dI3UsAC\\ E!g,ti uuU\7>P292 hI ,[ʂ;Ys I`&w8?A Hm&++K:w5IןIg" ԩ!Wm5Mn{Ę$m=ܾ*d"Vƞc[zu=X^?LpBՆaSUӳg2hhl@č̀2wOĞX=F;ϰ:&IbAbEiλ/$Iľ}9]A+x4ƧQ3U3ʼn5G۸4q}It2$[FguzÚCx+ p8V^~oq`;?}7bodi}t3k(K+ ر?3M3 SŀgF x_v. @OȲ:<`Cn\v#݇k_۽b5( 5hTtmA׍KZF0kJ &<\AxU}l 㠟|4"X~s¿FNKEQp&X?u10 Clǀ&F_8e>, <8-ұk߾nU,nUP`DM4}(MM&10[7TKkT?"phtZ?+ƷBŃ:z_OyFNaI&ɦ.T5 utzH$ZD(cl:f9o2%0Xΰ0,,rSzj~|O?H03MT@Oa0`H| t?ϐ-jI;v顇Ku$=t],d!]J],X7ijTJG,7s-FOb=i̞_p ;2̖`{tAl4^[Ϧ/i!m^ JeÐ %AΩ+2Ƭ#ˉR9YtPnAm@: hG{ x^hk֯sUÕm.s{0 L JFo[ _Uyg|+Wjذa-|vtuevv-eﺖ;?#+Zẅ́mnt Hg4y?AN@^qCn]r9v۴|QM}܇w#o>–-l>Ǟy1Os&DC,S^:t2 |C}7 UUfhj7wN^>;'6H&)t uk??/ix}V) |g*& "|ɯD |~z Fy"p*r'yexSܶN:?2M =.JM l){"^0NXPv7O& 紤i9-R:d:gc7>* q:s!@vfLZ٭>IP?S'[V,$iydYF>p8qʩQ6akoJ$ Biy6|oƍ{E,˲Fa ]g+<]O32̙PyTUP^ ؓX<3P<]_ArVʫW_Ӵ/)bX HteUU4 UU̝x(//'˨Aa8V3f/qn36-@H$2ײWU EAe4pD"nrGC/4U,,/a.p=|ZUv;hp8tMdY ƃn(Jrb#xJOѤf6fDQ|n3<< H _Lŀ_SU5#gVUR3AA~P^^N(*I|/UxYݺ,|}}=\|Ź@EE B YBuu5>] 8tK(p0222୭^zL$Ә$ITVVRUUE8&022B4BTժ(ED@>[( @2dT(vӧO:DD"A2$H022ah"v;Sj?$I"ͦMYdGQI!D8~8$100($I8xtzl5ȑ#( gΜI(Kީ hx$  "N.`M0$LZTDUU9{,55l!(Q2o^30L0NnkY`'KD[[~?'IR؋"IRnu]BQ4M0 RXW^y?~5yonCiFR }m۶m;::k֬*g axhnn& qrv袋Xx1MMM*r.]ʕa+#MxL|Ǐjnnvn7,i,p`rCUUv{n^D"|>^/'Oĉ444q Ҳj x>f$f=$]Dvñ Ν[o8ꨭ:E9qdYĉȲ+?C~?vnӴ4SY 8[ h4Fy[DszJp]n |>rwp9"l dKƄ$UUD4 H]su,˔vihh׋$I^ WiN i@CD"_ B`0|MfBE|>{!a_d܌_/^/OĦn?]]]o籘5 d2IEE%+WbŊ?6Յ|CEkݴj9B2$&Є)h%=-!"b4AQd.]2JŒ<{pⅿ]X;<9xL.(=L4z" cpd$=NʘƋQr[jؾ%J͜s(7+%}5MӘKVN!fy &+#(Od %tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ stEXtSoftwareAdobe ImageReadyqe<IENDB`recoll-1.36.1/qtgui/mtpics/text-x-python.png0000644000175000017500000001035414410615043015712 00000000000000PNG  IHDR@@iqgAMA a cHRMz&u0`:pQ<bKGD pHYsu85IDATx[lWzgfHNf˖-+ihMNI<EЇHq"n>(ڷŢ6hP4mwE\ر[%Q%JH>$>p8hHb?`t8sFw9gRe1u!>~EهTC,-ܮ{j?iMQ$j@ a Ёp 8sO :3Vߪ_?~hWu׋OU< W+UJ BlAF3 3=l|[:[񶲥R;wvS,BYϠ/T*t::ivdxܹ&}=Dスm?[^,JD"A4A۷Ƞ:˄aa,H$B`Y lňp83, #br<@m(lJ!ˑL&!L+8`׋{(cj>DW^B>{J%fQ@Hgg1 'r@w]8[[[:u &h0b+e"JFJ!4[fP dlp`zj%4MC ( t.T* ]g#\w቟{p(f~ 0K3b_jYTV)VӷSS`6Z/^  ƒr$Eypl6beղR8;=뗎#A)>{GIٵzOatq u:͛\x:bR:M[ Acj ҅Nh*/e9~(1dn1 ts9>=`qap$n3(uFSR7%,b}۟qq{4[-!xꩧw_|ٹǏ7Yؔ(yz>= V!дdE;vL5{\;(e(BMne׆XX 0&Qj@`{kp$a;# to7 RB@>lK<GuH&7 iשV+4~p$V6K(&QMdӧ)(>ý=) ;amm7<䓔EZˑ'iQݰP* N!Cr;ށB-ѱ'xEJ{* JcsH)٭m:_tS gRsMjJjHqЍRJToh'bT;l# 7JƂLy[m7Cހ72FrLŞMEjxڵkH;Xu}q 5~4M2V_[0}_뿯#ELz'bԊ  jZMhIoeXN.Q;wKR)J*q>8ud<@hj@p4LVEJ9\tXj(T=4rPG;-/ jqzNZ=Ҳ({k-< .OkĂgC9@P^#7at,@'1aUakY\&8l0 ;dw5H4D,tة0489=5+BZom&^x(Zva"'} u40C9a)=?$zqskR_ѩ1G6О7W/>rMӰ, ˲z蔘mDs엘#G FæԶ= 6i+O"PשT*NAtL(jz^|έ7-Z&@H"xi`)7xgj;jeX]]]{WmY @SP M?x"T ˗/4M Ǹ)qIWE96x?O,BJI  :Z~cc#wʕ?*˶{ }3( 7D^X|;{Q*^AѴdGX)<ӤAt]GJEZ^79v>s\իܼ q'2 P\.G  h4x<ΎѾ/%G3òlj̫i4iRӜ9};ѿ^#H>˕~?qaׯsYR:ӧGiBeoכ;JwfsR),Kb&@|&ٹznܸq Xv4{[hmr>wTs{h6[fl6K0S*XYYY{WoE(-äl"uR^z魞 'PRJ+}hfIV#a&?]IRu\fwwbɓ'G" Q.~'/X'AhE^|駤R)H`ἧenn9<Cّ')%|_xoYuOd0'J!3_h rZ^+z[(r 4 ]5றV~̆CΜ9+ kjj{շ : ao@ [~3 iM&"vOIe>q'z@0'!7M ϊ/YȬ%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ stEXtSoftwarewww.inkscape.org<IENDB`recoll-1.36.1/qtgui/mtpics/sidux-book.png0000644000175000017500000000367714410615043015240 00000000000000PNG  IHDR00` gAMA a cHRMz&u0`:pQ<PLTEϢccCC$$oϷxx99Z:.C9mc$$xϬzxx$9$C.N9x.$ϗoccNN$XNcXxcڬ9.zcXO99/xx/CN/$xᏂ:.$dXcxmXXNC..9$$x$.$ccXXXXdXN:$N.X9X9D99zmmZCCOCC$$$DZ$.$.cCxcZXC9$NCxmC9mcZNN.$ocXN9ĂmdNNdXX.zC9:$d9.ccommxx:$$$mcNCOC9.$9.ڢCC$NNXXύC9NC/$$xxςzccmmėZXXXNڍڗ䷷9.C9ύϗq5RtRNS@fbKGDH pHYs  tIME &3dT{IDATHDž_DL.pi h)Bi6AteW;,|!(>Vfa :WdsA/ m:~'~.]c((]. LMUBCAiƊy Ɨ8xUTCF6e,d2l$`^c!1UUShf 5Fst8ȣZa k@ɽRsNrACYa.5W 6ó\,0\fLσX&XPo76aQB`p$$f%@UZIVDܢ\ ؠ%QQ[[[y4:b$3s !Ohi9]񞄦i,_P,S' x2iU2NS-mg&=̳t>d;G.<1T/jK[ŋݣci1#O/Mi}@ geӡK=:˯\zg-sS%d{UIb,b]ͷ~&P6^",~&HLAHgd0W<= z)u}XYY]2$N߸OJf?>LZr/?%Qu]XUozuuw_x[Pr[؈[9}Z@S2cf{涶Ggƴڄe07Bv )?wx\UnMvzF~X8' Ѡ$)x6EwpR+I#ȝT.4 X_og@$ V!D*iwRXg]~)Z1Kɝ$XBKuno(M!C+4X[Y@{4b!#d + [qzvy95"8*t|\wСK ^f_$ȳ0zk+(*NqT1:,m?8IE@D>w׾ cLj2|x?!  #) AD M+ !ǎ?1f]Hi) J@XdaQc,^_8-}HIp&=EZIφ DvJGs2q()|>/S]f@˃LyL $YH UM]S^ǵN f@f2*IcL.v)3ɒJߵ. S.SSbUG;d"|nV'CIO|CEu{nx)Jfe).)}Ŷe+zqkoϨDyyI9¡ǘ[8Bo0TE<] 25("`~|U'_z}7hU1ox5\Cޣ,/]Nsg G];+0w'ufbeRf>rX4ɼtpzs̡f*AxUߟ*QZҶ- 6Ww B1ҙ~ԴcPӛ*#Yȉb*F{{xx,,,pBx`JNU) YHc+sl\!Nkt› s}F!ַ4m5|TssgN1$Zmwx?@lo;4μ ,|/5n-/|:*CUj4hhJ'BUInjΘGxI>> Ne*yi6oF6ʰ },2KeRIB+XҌ[n/?Oqt,T~*!jyvuH+=h 3UzbLrzȩc1^-fj uc0+2C\5@%T WŔ<490,rT (UFͰF1t`|])QPKU}8?cz $(")̐jUf%!~)12Ԩ^q$YBsiAۦ8 oq_$f=DCݮx&WHͩ0A-h^-|4k߆VSe#4|p&Obkk?_D*?wŋbH/ʷ na?8,.,P8mۙ~r}c/xfsckׯ_ݯ|M$gf.]=꺦QU"L䜞y}$?41Ν?k\PNY)f ;Cڱ<5 Cο.]ڟ׀Y nk" !C" @̺g9^}Wg-995-+++x{weVdž '#m`@pFL#QOEY@l2=,=aOEӿY7": %tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/emblem-symbolic-link.png0000644000175000017500000000426214410615043017156 00000000000000PNG  IHDR@@iqgAMA a cHRMz&u0`:pQ<bKGD pHYsu85IDATx[OlT}ÊصREu A*KHn(JI5USPJRɁ#PosJ B VpJWZNupЂ ;̼7o{z|y~ߟy%fjZ- hMO@C""BVF*ҞY[tx@%l V 4yVW^G6skFm3=;v(LLL- |(kRT*GFF,":53h+_xʕ+o۷\./X=:]vo*륍 &_, 6x(˅BaL趧aM#`#Q>IyTIՒ#/.vOOە"(y9}Ӎll9r)/<|W; ]'F@j?  H9X9 $X aȄfR-3CAZ lG,$XgkvL9B f$l cp`ndPy4^R `YΟl 1.?4y 0\AHc A ^5ڹ{iCc աĀHܹO!"Yu,ޣC&gD@ ± BP|jI>8^‹N9g.Cb^G 0Ξ=^ځ}"@k a-BBC^ ƛ 7Os94ns|gmL=h)@ '8>p ?z(gM9uD[$fǬocΝx?5<`< X B 3Dݑ0ppmmmM#]GZ'{-{iv@ pؽ{wME|7;7n F-ͳ\Y_d)Mq L}1] hdttDXYY%,,,Ğ"?J>cLu\AU 9p`Qވ~pXk:uʳ=>Wϱ雤 #?~86{c|ءR-j2ͯxf il31:j5?2g fBx{V$+ȨUZ$ %,Z7ofӦXدSgI $)z ׸@pde%aHVx eW16TOu}9.?tIWv #Ga!O,?c{ ݎſ{Y`>FZ˂'R2m؜Τ8nrId!dhn<ԅp hl}6|^o'dd5("|;owyVO$N-@ADQHF,_4&7]cwbc P$!:<,FFAzzz5ARɅ66^۱@J*j$MiXkshFkjy̝ízܹ+TST*/f۶mX Kɩ{9J\T;N4#8hH6TT,$1{M^Wq?~4us$j5~\_ %̙; Yp!g,XpF p)IڭJIG`DUA$.R2<-{+gad߾16mĖ-[صk<ŧ {tj!ZJ%"IP :jD9k˨mӳEK.{~@HYe͚YrCC8WpB׋VEnQI[W4 I]nE?"=QYGWD(‚qyP>lƛhOpp ͘ Ͻ̅^Ȳ18x"ww~i sYgqiQSX$bҥ]ySG)Ž…'۠VͬKFcsVNd@ m4 OXN+/bժU> ߸N:5lXz5Zkm.N>tǏJ&& P{HdXR*%U qk榏^OF̟?RO\jg>%s$泟t>'"0{@fJflv6 "E!\V( ̪UWWO<ΝWTkz˗܆y-߿v; [_'S;!BR"E:ऐyleY ]bG:&j}L*NKXKcF)l6'nfVedR䵄̉fg8 \t\GAuATXyu$NNY,ε fq+r1H g5}ߺoE0+[kmkwqAct^JokBCј`h@i@^)P֨T"O?ᥗifGqRPV9jPNH+HM>9~CDNd}iD#kqBcIw]Z-zmqidEa{-͵^?ΝZ~l;?g1愳%]e $_r\D!fI B*>5.zH~:\q;k02R-JVvfY >z<:*! o!A" B2 knuOVZˊp+x䑵EQC+JuPqF!֔~g=RiML++0{{QƠl?YDQ̙g>`crcZ =, ^4!/d}?II X%c>v-|R Qb>Ry#C͝'ssX~0u"hQl<{>qDiw޽w/B ڱnypn;]nzhNLU*yS/83{G1Zi&&&8;Ax=J)cu+|'̼T|<_]F ;dTY譴~7$1v,{w >(c^ztu &&:)L_ìٳ!˥pY+ q}Fw^C~t߾]W ߽-h@: Yct"#Td^lRuS4MI{GG{yJ)7x?j}8#$) 2 Q&%=vvś{l9B<{Ȅwbe?P(W𮿿Vv&*%MZ+?>cރlϚ= k- vw\tчQxӕJ oY3 xvJӼ@~~4M͝ҥKynÆ/q$g?glw$Sɖf%Y8\ZɌ͝%<~U7ьSbg^C(D p v\I8DQ8 AA2H^Q^xƏdC5=T~1OI3F (p:6j`>6?^)J{vHO?3x'I!D#IZ?h>{yJ beY&ud2o`eH$WWM::캅06Z/V_Mݫ/ 'V*`O? ,+k Z (/]+^xf=`|YgW\3 __jh"a1&_eI@Q`~`i fJI:d˲=FPKA1@UްQ[w&&)[ :;!zDu@T >8}՗^9Iz(lT`rҶ۷ǏmhWId' b7 6S##[Ӏeᇁ;EocᙫƇ`7ȯIJ޽$׶TAJjѫ9)ί^ʌ!`Xly2',\nz5}1c0@Oapx$ @eϕ8.(0MBCUB!nyLNN"@^7 رcLq/5T^EV͛hooǞ={`YVhfcU+vM"a EU$I:;;uH, e4M7R)*cW[*UDdAσ0.`&:.\ V$I/WݯpDQ?nԚes$O$0HUFO2'A|0khyPxqY X_F^Eޫ& ^@eJwo'0z HMK{riy Z}gCp!ĉ)|xB * h8i Jܾ}bщc$g]$I"R`qq"N{i@|>9:t=*&׽Xc4 ^ytww㡇B{{{$4kƍā`& <c W^pG wb%YZ3| ÀeY4 r ,0 #rY5"$R`ee?3#16~n:~/I ~W"NCUպ@ꡥ%΢T* =zzzBS8{ea׮]XXXobm۶1Am)s1@,\Ie2ڵ b󘘘6umGr# ̙3g0??eR<0 Dud2Ϝ]]]]GRܹsp + x D"h>)@&w8"`޽򠍏,˘?/uc ,((5㫪Z ʕ+t&_Ƌ/]CwgaY1>>Yٳa\qR 1Oƙd2wzA vܹd2 By[$ 7n>L&*Q1 W_믿"|~t3gshX{o>\~\bT Dib˖-,ˈ`,dYdY0ƐL&H$ru_~?[ c  Q(#D1:q;'KR/B?xp}))a#/EQf}v'\ d>TD"QNw;+!eɓ|pbzz ?@Qܕd2yQPG1) ^ p}llL;x?|r9߿Ĉ%THPJ:޽)3ɱ1#O-MI^t}ocTN::tI}ʶl Ti1MTjk,WÇO\pa : @!1@:O)_9r󩩩SP63qx&RoF?>9_|_En]ٳѣGOLMMZج$~W|Yѣu| Q?.-|wڱcF/^XIǏ|+ !M'<ǎ;133W~0M=]H,BTĄW_̈:ߴ~7E)}0xc,|LOO ` M|՗)WA;cKQ$%tEXtdate:create2019-05-13T15:38:51+02:00 }V%tEXtdate:modify2019-05-13T15:38:51+02:00{ sIENDB`recoll-1.36.1/qtgui/mtpics/README0000644000175000017500000000025214410615043013310 00000000000000Most icons thanks to KDE crystalsvg Oxygen (www.oxygen-icons.org) (GPL): archive.png book.png bookchap.png text-x-python.png The Pidgin project (GPL): pidgin.png recoll-1.36.1/qtgui/mtpics/book.png0000644000175000017500000001152314410615043014073 00000000000000PNG  IHDR@@iqgAMA a cHRMz&u0`:pQ<bKGD pHYs7]7]F]tIME 9O BIDATxKdQqιU==؞S#@   H ;>o`FXϱMLOwU',"nV3rY,씪+uO?$oM.{\{<'/`lky'u>/6Sr:aN/Cԝ1`۔B!j M;:4* ,tFm S,Y6 Aws~.=ibZ "lsripwZYz#XF!#Gõv,Ke|Y BUdò4?m)DC$뼵nflN >v?̎$XfH"I% ,;#K;-d BptcHNw@YHQ.&J"Iuڋp D(Q ecKwN2:3։EwL5/$>i-0Mض31UdڟZ 0x%"mV-餓59&ۺL]f"r1Z 0UFk|osw{;'QՄW /?QtIM#5ܲD=}M7I4P%wg|ﱞ&l3@P +**KBx t | fetk9_WY[x<-;%ˤv7?wq[?O?y>z>Oe Yr  0\ =pGWȏ=T7|c] ]o ~\__;~ﭧ,3ӧ71x~|IX4ńpUۗa& a4 KZ{jLY>!ћ`MS5uȍV)xۥoc۔ѓ-<;eL>x%,7[@~`n[φJ"n5H/“F~!YB+bnۼhMa맑Q_ij zMZIR%Ԍ.BX^|k}3t :IU|!M KkgF! Eh2Y^,-S+[m]q\<2-N$2A wƐbE֒k ޳I.݉bf~B r6z#wҲdtsNqMV7gޅ%Luwp&Hpj 7BY sfCQ z|86-sr~P #iU04s\䶩?p8vTPHnΜBeffcio t_יIg45-xWOM=?CU=ѣ0%Ny0 czK)X . 4ETkB6YFcN]H^t؁Pm$XY?/Pa)K" "WU_ vo){n_z!s3N4!moXi$4~4iުV@w[3+/߅UHң 4D91cخj!te@Zrhl'M`PDj{KЂG n:)~3bh/boHK$'GhRނM@mID~DfP+j6}$9{ U&p Zx,H胂c* zHT9ט\3j3g,9.{F }D̀3Ky9'X:g԰M9 RԘSMU;^nE"gna~^3RB;1ձwSd۔e-!KܨiNc]8)<ͬZhfBL*lNwIH^%gAQ?, pMj8Qi2R/dH7jMdd7aۈD檴CX߀oIQ\=G݌9ֲx\ 8ǥNVEzJN{̚?AKsG_QnPNrrjgcj^A!-Oy^6k{azv/PDkSFG>2~2@"huH~(wɽ2 w$}tTi*#3ai{9Ö@?ח{G[̈řj;!ܹ. i5.ݏӎaeey+@=  ų-3Oxμ}Pc[3f~Wil_swRO{ױxa&5Fy_yPΐqu(O-]}4aI9'#=Y,cֻ4Ij5`PPV%f]ܼEHyXzn'ҒL K;+LArlh&qoXD5tn1- #include #include #include #include #include #include using std::string; using std::vector; using std::map; using std::list; using std::set; using namespace Rcl; /* * Constructs a SearchClauseW as a child of 'parent', with the * name 'name' and widget flags set to 'f'. */ SearchClauseW::SearchClauseW(QWidget* parent) : QWidget(parent) { QHBoxLayout* hLayout = new QHBoxLayout(this); sTpCMB = new QComboBox(this); sTpCMB->setEditable(false); hLayout->addWidget(sTpCMB); fldCMB = new QComboBox(this); fldCMB->setEditable(false); hLayout->addWidget(fldCMB); proxSlackSB = new QSpinBox(this); hLayout->addWidget(proxSlackSB); wordsLE = new QLineEdit(this); wordsLE->setMinimumSize(QSize(190, 0)); hLayout->addWidget(wordsLE); languageChange(); resize(QSize(0, 0).expandedTo(minimumSizeHint())); connect(sTpCMB, SIGNAL(activated(int)), this, SLOT(tpChange(int))); } /* * Sets the strings of the subwidgets using the current * language. */ void SearchClauseW::languageChange() { sTpCMB->clear(); sTpCMB->addItem(tr("Any")); // 0 sTpCMB->addItem(tr("All")); //1 sTpCMB->addItem(tr("None"));//2 sTpCMB->addItem(tr("Phrase"));//3 sTpCMB->addItem(tr("Proximity"));//4 sTpCMB->addItem(tr("File name"));//5 // sTpCMB->insertItem(tr("Complex clause"));//6 fldCMB->addItem(tr("No field")); if (theconfig) { set fields = theconfig->getIndexedFields(); for (set::const_iterator it = fields.begin(); it != fields.end(); it++) { // Some fields don't make sense here if (it->compare("filename")) { fldCMB->addItem(QString::fromUtf8(it->c_str())); } } } // Ensure that the spinbox will be enabled/disabled depending on // combobox state tpChange(0); sTpCMB->setToolTip(tr("Select the type of query that will be performed with the words")); proxSlackSB->setToolTip(tr("Number of additional words that may be interspersed " "with the chosen ones")); } // Translate my window state into an Rcl search clause SearchDataClause *SearchClauseW::getClause() { if (wordsLE->text().isEmpty()) return 0; string field; if (fldCMB->currentIndex() != 0) { field = (const char *)fldCMB->currentText().toUtf8(); } string text = (const char *)wordsLE->text().toUtf8(); switch (sTpCMB->currentIndex()) { case 0: return new SearchDataClauseSimple(SCLT_OR, text, field); case 1: return new SearchDataClauseSimple(SCLT_AND, text, field); case 2: { SearchDataClauseSimple *cl = new SearchDataClauseSimple(SCLT_OR, text, field); cl->setexclude(true); return cl; } case 3: return new SearchDataClauseDist(SCLT_PHRASE, text, proxSlackSB->value(), field); case 4: return new SearchDataClauseDist(SCLT_NEAR, text, proxSlackSB->value(), field); case 5: return new SearchDataClauseFilename(text); case 6: default: return 0; } } void SearchClauseW::setFromClause(SearchDataClauseSimple *cl) { LOGDEB("SearchClauseW::setFromClause\n" ); switch(cl->getTp()) { case SCLT_OR: if (cl->getexclude()) tpChange(2); else tpChange(0); break; case SCLT_AND: tpChange(1); break; case SCLT_PHRASE: tpChange(3); break; case SCLT_NEAR: tpChange(4); break; case SCLT_FILENAME: tpChange(5); break; default: return; } LOGDEB("SearchClauseW::setFromClause: calling erase\n" ); clear(); QString text = QString::fromUtf8(cl->gettext().c_str()); QString field = QString::fromUtf8(cl->getfield().c_str()); switch(cl->getTp()) { case SCLT_OR: case SCLT_AND: case SCLT_PHRASE: case SCLT_NEAR: if (!field.isEmpty()) { int idx = fldCMB->findText(field); if (idx >= 0) { fldCMB->setCurrentIndex(idx); } else { fldCMB->setEditText(field); } } /* FALLTHROUGH */ case SCLT_FILENAME: wordsLE->setText(text); break; default: break; } switch(cl->getTp()) { case SCLT_PHRASE: case SCLT_NEAR: { SearchDataClauseDist *cls = dynamic_cast(cl); proxSlackSB->setValue(cls->getslack()); } break; default: break; } } void SearchClauseW::clear() { wordsLE->setText(""); fldCMB->setCurrentIndex(0); proxSlackSB->setValue(0); } // Handle combobox change: may need to enable/disable the distance // spinbox and field spec void SearchClauseW::tpChange(int index) { if (index < 0 || index > 5) return; if (sTpCMB->currentIndex() != index) sTpCMB->setCurrentIndex(index); switch (index) { case 3: case 4: proxSlackSB->show(); proxSlackSB->setEnabled(true); if (index == 4) proxSlackSB->setValue(10); else proxSlackSB->setValue(0); break; default: proxSlackSB->close(); } if (index == 5) { fldCMB->close(); } else { fldCMB->show(); } } recoll-1.36.1/qtgui/uiprefs_w.h0000644000175000017500000000577014410615043013317 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _UIPREFS_W_H_INCLUDED_ #define _UIPREFS_W_H_INCLUDED_ #include #include #include "ui_uiprefs.h" #include #include class QDialog; class ViewAction; class RclMain; class UIPrefsDialog : public QDialog, public Ui::uiPrefsDialogBase { Q_OBJECT public: UIPrefsDialog(RclMain* parent) : QDialog((QWidget*)parent), m_mainWindow(parent) { setupUi(this); init(); } ~UIPrefsDialog(){}; UIPrefsDialog(const UIPrefsDialog&) = delete; UIPrefsDialog& operator=(const UIPrefsDialog&) = delete; virtual void init(); void setFromPrefs(); public slots: virtual void showFontDialog(); virtual void resetReslistFont(); virtual void showStylesheetDialog(); virtual void resetStylesheet(); virtual void setDarkMode(); virtual void showSynFileDialog(); virtual void showSnipCssDialog(); virtual void resetSnipCss(); virtual void showViewAction(); virtual void showViewAction(const QString& mt); virtual void addExtraDbPB_clicked(); virtual void delExtraDbPB_clicked(); virtual void togExtraDbPB_clicked(); virtual void on_showTrayIconCB_clicked(); virtual void actAllExtraDbPB_clicked(); virtual void unacAllExtraDbPB_clicked(); virtual void setStemLang(const QString& lang); virtual void editParaFormat(); virtual void editHeaderText(); virtual void extradDbSelectChanged(); virtual void extraDbEditPtrans(); virtual void resetShortcuts(); signals: void uiprefsDone(); protected slots: virtual void accept(); virtual void reject(); private: void setupReslistFontPB(); void readShortcuts(); void storeShortcuts(); void readShortcutsInternal(const QStringList&); void setSSButState(); ViewAction *m_viewAction{nullptr}; RclMain *m_mainWindow; // Locally stored data (pending ok/cancel), for the parameters for // which our UI state is not enough. QString paraFormat; QString headerText; std::vector m_scids; QString reslistFontFamily; int reslistFontSize; QString qssFile; bool darkMode{false}; QString snipCssFile; QString synFile; }; #endif /* _UIPREFS_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/spell_w.h0000644000175000017500000000370014427373216012763 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SPELL_W_H_INCLUDED_ #define _SPELL_W_H_INCLUDED_ #include #include #include #include "ui_spell.h" class SpellW : public QWidget, public Ui::SpellBase { Q_OBJECT public: SpellW(QWidget* parent = 0) : QWidget(parent), m_prevmode(TYPECMB_NONE) { setupUi(this); init(); } virtual bool eventFilter(QObject *target, QEvent *event ); enum comboboxchoice {TYPECMB_NONE, TYPECMB_WILD, TYPECMB_REG, TYPECMB_STEM, TYPECMB_SPELL, TYPECMB_STATS, TYPECMB_FAILED}; public slots: virtual void doExpand(); virtual void wordChanged(const QString&); virtual void textDoubleClicked(); virtual void textDoubleClicked(int, int); virtual void setMode(comboboxchoice); private slots: virtual void onModeChanged(int); signals: void wordSelect(QString); private: // combobox index to expansion type std::vector m_c2t; comboboxchoice m_prevmode; void init(); void copy(); void showStats(); void showFailed(); int cmbIdx(comboboxchoice mode); void setModeCommon(comboboxchoice mode); }; #endif /* _SPELL_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/preview_w.cpp0000644000175000017500000011524414515661537013673 00000000000000/* Copyright (C) 2005-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "preview_w.h" #include "log.h" #include "pathut.h" #include "internfile.h" #include "recoll.h" #include "smallut.h" #include "chrono.h" #include "cancelcheck.h" #include "guiutils.h" #include "docseqhist.h" #include "rclhelp.h" #include "preview_load.h" #include "preview_plaintorich.h" #include "rclmain_w.h" #include "scbase.h" #include "appformime.h" using std::string; // Adjust font size from prefs, display is slightly different because the text is // displayed by Qt HTML not webkit/view? We need the same adjustment in the result table. static const int fsadjustdown = 3; // Make an attempt at trimming wildcard exprs at both ends of string static void trimwildcards(string& elt) { if (elt.empty()) return; string::size_type initsize; do { initsize = elt.size(); // Trim wildcard chars. trimstring(elt, " *?"); // Trim wildcard char classes if (elt.size() && elt.back() == ']') { string::size_type offs = elt.find_last_of("["); if (offs != string::npos) { elt = elt.substr(0, offs); if (elt.size() && elt.back() == '[') { elt.erase(elt.end()-1); } } } if (elt.size() && elt.front() == '[') { string::size_type offs = elt.find_first_of("]"); if (offs != string::npos) { elt.erase(0, offs+1); } } } while (elt.size() && elt.size() != initsize); } void Preview::init() { setAttribute(Qt::WA_DeleteOnClose); // Create the first tab (the tab widget is created with one // initial tab for ease of use in designer, we remove it). addEditorTab(); pvTab->removeTab(0); for (const auto& ugroup : m_hData.ugroups) { QString s; for (auto elt : ugroup) { trimwildcards(elt); if (!elt.empty()) { if (!s.isEmpty()) { s.append(" "); } s.append(u8s2qs(elt)); } } s = s.trimmed(); searchTextCMB->addItem(s); } searchTextCMB->setCompleter(0); if (prefs.pvwidth > 100) { resize(prefs.pvwidth, prefs.pvheight); } else { resize(QSize(640, 480).expandedTo(minimumSizeHint())); } if (prefs.reslistfontfamily != "") { m_font = QFont(prefs.reslistfontfamily); } else { m_font = QFont(); } int fs = prefs.reslistfontsize ? prefs.reslistfontsize : m_font.pointSize(); if (fs <= 3) fs = 12; fs -= fsadjustdown; float scale = prefs.wholeuiscale > 0 ? prefs.wholeuiscale : 1.0; fs = std::round(fs * scale); LOGDEB("Preview: using font point size " << fs <<"\n"); m_font.setPointSize(fs); (void)new HelpClient(this); HelpClient::installMap((const char *)objectName().toUtf8(), "RCL.SEARCH.GUI.PREVIEW"); // signals and slots connections connect(new QShortcut(QKeySequence::ZoomIn,this), SIGNAL (activated()), this, SLOT (zoomIn())); connect(new QShortcut(QKeySequence::ZoomOut,this),SIGNAL (activated()), this, SLOT (zoomOut())); connect(searchTextCMB, SIGNAL(editTextChanged(const QString&)), this, SLOT(searchTextChanged(const QString&))); connect(nextPB, SIGNAL(clicked()), this, SLOT(nextPressed())); connect(prevPB, SIGNAL(clicked()), this, SLOT(prevPressed())); connect(clearPB, SIGNAL(clicked()), searchTextCMB, SLOT(clearEditText())); connect(editPB, SIGNAL(clicked()), this, SLOT(emitEditRequested())); connect(pvTab, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int))); connect(pvTab, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); onNewShortcuts(); connect(&SCBase::scBase(), SIGNAL(shortcutsChanged()), this, SLOT(onNewShortcuts())); connect(nextInTabPB, SIGNAL (clicked()), this, SLOT (emitShowNext())); connect(prevInTabPB, SIGNAL (clicked()), this, SLOT (emitShowPrev())); currentChanged(pvTab->currentIndex()); } void Preview::onNewShortcuts() { SETSHORTCUT(this, "preview:151", tr("Preview Window"), tr("Close preview window"), "Esc", m_closewinsc, close); SETSHORTCUT(this, "preview:153",tr("Preview Window"), tr("Show next result"), "Shift+Down", m_nextdocsc, emitShowNext); SETSHORTCUT(this, "preview:155", tr("Preview Window"), tr("Show previous result"), "Shift+Up", m_prevdocsc, emitShowPrev); SETSHORTCUT(this, "preview:159", tr("Preview Window"), tr("Close tab"), "Ctrl+W", m_closetabsc, closeCurrentTab); QKeySequence ks = SCBase::scBase().get("preview:162", tr("Preview Window"), tr("Print"), "Ctrl+P"); if (!ks.isEmpty()) { delete m_printtabsc; m_printtabsc = new QShortcut(ks, this, SIGNAL(printCurrentPreviewRequest())); } } void Preview::zoomIn() { m_font.setPointSize(m_font.pointSize()+1); PreviewTextEdit *edit = currentEditor(); if (edit) { edit->displayText(); } } void Preview::zoomOut() { m_font.setPointSize(m_font.pointSize()-1); PreviewTextEdit *edit = currentEditor(); if (edit) { edit->displayText(); } } void Preview::listShortcuts() { LISTSHORTCUT(null, "preview:151", tr("Preview Window"), tr("Close preview window"), "Esc", m_closewinsc, close); LISTSHORTCUT(null, "preview:153", tr("Preview Window"), tr("Show next result"), "Shift+Down", m_nextdocsc, emitShowNext); LISTSHORTCUT(null, "preview:155", tr("Preview Window"), tr("Show previous result"), "Shift+Up",m_prevdocsc, emitShowPrev); LISTSHORTCUT(null, "preview:159", tr("Preview Window"), tr("Close tab"), "Ctrl+W", m_closetabsc, closeCurrentTab); LISTSHORTCUT(null, "preview:162", tr("Preview Window"), tr("Print"), "Ctrl+P", m_printtabsc, print); } void Preview::emitShowNext() { if (m_loading) return; PreviewTextEdit *edit = currentEditor(); if (edit) { emit(showNext(this, m_searchId, edit->m_docnum)); } } void Preview::emitShowPrev() { if (m_loading) return; PreviewTextEdit *edit = currentEditor(); if (edit) { emit(showPrev(this, m_searchId, edit->m_docnum)); } } void Preview::closeEvent(QCloseEvent *e) { LOGDEB("Preview::closeEvent. m_loading " << m_loading << "\n"); if (m_loading) { CancelCheck::instance().setCancel(); e->ignore(); return; } prefs.pvwidth = width(); prefs.pvheight = height(); /* Release all temporary files (but maybe none is actually set) */ for (int i = 0; i < pvTab->count(); i++) { PreviewTextEdit *edit = editor(i); if (edit) { forgetTempFile(edit->m_tmpfilename); } } emit previewExposed(this, m_searchId, -1); emit previewClosed(this); QWidget::closeEvent(e); } extern const char *eventTypeToStr(int tp); bool Preview::eventFilter(QObject *target, QEvent *event) { if (event->type() != QEvent::KeyPress) { #if 0 LOGDEB("Preview::eventFilter(): " << eventTypeToStr(event->type()) << "\n"); if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent *mev = (QMouseEvent *)event; LOGDEB("Mouse: GlobalY " << mev->globalY() << " y " << mev->y() << "\n"); } #endif return false; } PreviewTextEdit *edit = currentEditor(); QKeyEvent *keyEvent = (QKeyEvent *)event; if (m_dynSearchActive) { if (keyEvent->key() == Qt::Key_F3) { LOGDEB2("Preview::eventFilter: got F3\n"); doSearch(searchTextCMB->currentText(), true, (keyEvent->modifiers() & Qt::ShiftModifier) != 0); return true; } if (target != searchTextCMB) return QApplication::sendEvent(searchTextCMB, event); } else { if (edit && (target == edit || target == edit->viewport())) { if (keyEvent->key() == Qt::Key_Slash || (keyEvent->key() == Qt::Key_F && (keyEvent->modifiers() & Qt::ControlModifier))) { LOGDEB2("Preview::eventFilter: got / or C-F\n"); searchTextCMB->setFocus(); m_dynSearchActive = true; return true; } else if (keyEvent->key() == Qt::Key_Space) { LOGDEB2("Preview::eventFilter: got Space\n"); int value = edit->verticalScrollBar()->value(); value += edit->verticalScrollBar()->pageStep(); edit->verticalScrollBar()->setValue(value); return true; } else if (keyEvent->key() == Qt::Key_Backspace) { LOGDEB2("Preview::eventFilter: got Backspace\n"); int value = edit->verticalScrollBar()->value(); value -= edit->verticalScrollBar()->pageStep(); edit->verticalScrollBar()->setValue(value); return true; } } } return false; } void Preview::searchTextChanged(const QString & text) { LOGDEB("Preview::searchTextChanged:(" << qs2utf8s(text) << ") current: ("<< qs2utf8s(searchTextCMB->currentText()) << ") currentindex " << searchTextCMB->currentIndex() << "\n"); if (!searchTextCMB->itemText(searchTextCMB->currentIndex()).compare(text)) { // Then we assume that the text was set by selecting in the // combobox There does not seem to be another way to // discriminate select and hand edit. Note that the // activated() signal is called *after* the editTextChanged() // one, so it is useless. m_searchTextFromIndex = searchTextCMB->currentIndex(); doSearch("", false, false); } else { m_searchTextFromIndex = -1; if (text.isEmpty()) { m_dynSearchActive = false; clearPB->setEnabled(false); } else { m_dynSearchActive = true; clearPB->setEnabled(true); doSearch(text, false, false); } } } void Preview::emitSaveDocToFile() { PreviewTextEdit *ce = currentEditor(); if (ce && !ce->m_dbdoc.url.empty()) { emit saveDocToFile(ce->m_dbdoc); } } void Preview::emitEditRequested() { PreviewTextEdit *ce = currentEditor(); if (ce && !ce->m_dbdoc.url.empty()) { emit editRequested(ce->m_dbdoc); } } // Perform text search. If next is true, we look for the next match of the // current search, trying to advance and possibly wrapping around. If next is // false, the search string has been modified, we search for the new string, // starting from the current position void Preview::doSearch(const QString &_text, bool next, bool reverse, bool wordOnly) { LOGDEB("Preview::doSearch: text [" << qs2utf8s(_text) << "] idx " << m_searchTextFromIndex << " next " << next << " rev " << reverse << " word " << wordOnly << "\n"); QString text = _text; bool matchCase = casematchCB->isChecked(); PreviewTextEdit *edit = currentEditor(); if (edit == 0) { // ?? return; } if (text.isEmpty() || m_searchTextFromIndex != -1) { if (!edit->m_plaintorich->haveAnchors()) { LOGDEB("NO ANCHORS\n"); return; } // The combobox indices are equal to the search ugroup indices // in hldata, that's how we built the list. if (reverse) { edit->m_plaintorich->prevAnchorNum(m_searchTextFromIndex); } else { edit->m_plaintorich->nextAnchorNum(m_searchTextFromIndex); } QString aname = edit->m_plaintorich->curAnchorName(); LOGDEB("Calling scrollToAnchor(" << qs2utf8s(aname) << ")\n"); edit->scrollToAnchor(aname); // Position the cursor approximately at the anchor (top of // viewport) so that searches start from here QTextCursor cursor = edit->cursorForPosition(QPoint(0, 0)); edit->setTextCursor(cursor); return; } // If next is false, the user added characters to the current // search string. We need to reset the cursor position to the // start of the previous match, else incremental search is going // to look for the next occurrence instead of trying to lenghten // the current match if (!next) { QTextCursor cursor = edit->textCursor(); cursor.setPosition(cursor.anchor(), QTextCursor::KeepAnchor); edit->setTextCursor(cursor); } Chrono chron; LOGDEB("Preview::doSearch: first find call\n"); // FindFlags is a QFlags class with default constructor to empty. QTextDocument::FindFlags flags; if (reverse) flags |= QTextDocument::FindBackward; if (wordOnly) flags |= QTextDocument::FindWholeWords; if (matchCase) flags |= QTextDocument::FindCaseSensitively; bool found = edit->find(text, flags); LOGDEB("Preview::doSearch: first find call return: found " << found << " " << chron.secs() << " S\n"); // If not found, try to wrap around. if (!found) { LOGDEB("Preview::doSearch: wrapping around\n"); if (reverse) { edit->moveCursor (QTextCursor::End); } else { edit->moveCursor (QTextCursor::Start); } LOGDEB("Preview::doSearch: 2nd find call\n"); chron.restart(); found = edit->find(text, flags); LOGDEB("Preview::doSearch: 2nd find call return found " << found << " " << chron.secs() << " S\n"); } if (found) { m_canBeep = true; } else { if (m_canBeep && !prefs.noBeeps) QApplication::beep(); m_canBeep = false; } LOGDEB("Preview::doSearch: return\n"); } void Preview::nextPressed() { LOGDEB2("Preview::nextPressed\n"); doSearch(searchTextCMB->currentText(), true, false); } void Preview::prevPressed() { LOGDEB2("Preview::prevPressed\n"); doSearch(searchTextCMB->currentText(), true, true); } // Called when user clicks on tab void Preview::currentChanged(int index) { LOGDEB2("PreviewTextEdit::currentChanged\n"); PreviewTextEdit *edit = editor(index); LOGDEB1("Preview::currentChanged(). Editor: " << edit << "\n"); if (edit == 0) { LOGERR("Editor child not found\n"); return; } edit->setFocus(); editPB->setEnabled(canOpen(&edit->m_dbdoc, theconfig)); // Disconnect the print signal and reconnect it to the current editor LOGDEB1("Disconnecting reconnecting print signal\n"); disconnect(this, SIGNAL(printCurrentPreviewRequest()), 0, 0); connect(this, SIGNAL(printCurrentPreviewRequest()), edit, SLOT(print())); edit->installEventFilter(this); edit->viewport()->installEventFilter(this); searchTextCMB->installEventFilter(this); emit(previewExposed(this, m_searchId, edit->m_docnum)); } void Preview::closeCurrentTab() { LOGDEB1("Preview::closeCurrentTab: m_loading " << m_loading << "\n"); if (m_loading) { CancelCheck::instance().setCancel(); return; } closeTab(pvTab->currentIndex()); } void Preview::closeTab(int index) { LOGDEB1("Preview::closeTab: m_loading " << m_loading << "\n"); if (m_loading) { CancelCheck::instance().setCancel(); return; } PreviewTextEdit *edit = editor(index); if (edit) forgetTempFile(edit->m_tmpfilename); if (pvTab->count() > 1) { pvTab->removeTab(index); } else { close(); } } PreviewTextEdit *Preview::editor(int index) { return dynamic_cast(pvTab->widget(index)); } PreviewTextEdit *Preview::currentEditor() { LOGDEB2("Preview::currentEditor()\n"); return editor(pvTab->currentIndex()); } PreviewTextEdit *Preview::addEditorTab() { LOGDEB1("PreviewTextEdit::addEditorTab()\n"); PreviewTextEdit *editor = new PreviewTextEdit(pvTab, "pvEdit", this); editor->setReadOnly(true); editor->setUndoRedoEnabled(false ); pvTab->addTab(editor, "Tab"); pvTab->setCurrentIndex(pvTab->count() - 1); return editor; } void Preview::setCurTabProps(const Rcl::Doc &doc, int docnum) { LOGDEB1("Preview::setCurTabProps\n"); QString title; string ctitle; if (doc.getmeta(Rcl::Doc::keytt, &ctitle) && !ctitle.empty()) { title = u8s2qs(ctitle); } else if (doc.getmeta(Rcl::Doc::keyfn, &ctitle) && !ctitle.empty()) { title = u8s2qs(ctitle); } else { title = path2qs(path_getsimple(doc.url)); } if (title.length() > 20) { title = title.left(10) + "..." + title.right(10); } int curidx = pvTab->currentIndex(); pvTab->setTabText(curidx, title); string datebuf; if (!doc.fmtime.empty() || !doc.dmtime.empty()) { time_t mtime = doc.dmtime.empty() ? atoll(doc.fmtime.c_str()) : atoll(doc.dmtime.c_str()); struct tm *tm = localtime(&mtime); datebuf = utf8datestring("%Y-%m-%d %H:%M:%S", tm); } LOGDEB("Doc.url: [" << doc.url << "]\n"); string url; printableUrl(theconfig->getDefCharset(), doc.url, url); string tiptxt = url + string("\n"); tiptxt += doc.mimetype + " " + datebuf + "\n"; if (!ctitle.empty()) tiptxt += ctitle + "\n"; pvTab->setTabToolTip(curidx, u8s2qs(tiptxt)); PreviewTextEdit *e = currentEditor(); if (e) { e->m_url = doc.url; e->m_ipath = doc.ipath; e->m_docnum = docnum; } } bool Preview::makeDocCurrent(const Rcl::Doc& doc, int docnum, bool sametab) { LOGDEB("Preview::makeDocCurrent: " << doc.url << "\n"); if (m_loading) { LOGERR("Already loading\n"); return false; } /* Check if we already have this page */ for (int i = 0; i < pvTab->count(); i++) { PreviewTextEdit *edit = editor(i); if (edit && !edit->m_url.compare(doc.url) && !edit->m_ipath.compare(doc.ipath)) { pvTab->setCurrentIndex(i); return true; } } // if just created the first tab was created during init if (!sametab && !m_justCreated && !addEditorTab()) { return false; } m_justCreated = false; if (!loadDocInCurrentTab(doc, docnum)) { closeCurrentTab(); return false; } raise(); return true; } void Preview::togglePlainPre() { switch (prefs.previewPlainPre) { case PrefsPack::PP_BR: prefs.previewPlainPre = PrefsPack::PP_PRE; break; case PrefsPack::PP_PRE: prefs.previewPlainPre = PrefsPack::PP_BR; break; case PrefsPack::PP_PREWRAP: default: prefs.previewPlainPre = PrefsPack::PP_PRE; break; } PreviewTextEdit *editor = currentEditor(); if (editor) loadDocInCurrentTab(editor->m_dbdoc, editor->m_docnum); } void Preview::emitWordSelect(QString word) { emit(wordSelect(word)); } // Display message dialog after load failed void Preview::displayLoadError( FileInterner::ErrorPossibleCause explain, bool canGetRawText) { // Note that we can't easily check for a readable file // because it's possible that only a region is locked // (e.g. on Windows for an ost file the first block is // readable even if Outlook is running). QString msg; switch (explain) { case FileInterner::FetchMissing: msg = tr("Error loading the document: file missing."); break; case FileInterner::FetchPerm: msg = tr("Error loading the document: no permission."); break; case FileInterner::FetchNoBackend: msg = tr("Error loading: backend not configured."); break; case FileInterner::InternfileOther: #ifdef _WIN32 msg = tr("Error loading the document: other handler error
" "Maybe the application is locking the file ?"); #else msg = tr("Error loading the document: other handler error."); #endif break; } if (canGetRawText) { msg += tr("
Attempting to display from stored text."); } QMessageBox::warning(0, "Recoll", msg); } bool Preview::runLoadThread(LoadThread& lthr, QTimer& tT, QEventLoop& loop, QProgressDialog& progress, bool canGetRawText) { lthr.start(); for (int i = 0;;i++) { tT.start(1000); loop.exec(); if (lthr.isFinished()) break; if (progress.wasCanceled()) { CancelCheck::instance().setCancel(); } if (i == 1) progress.show(); } LOGDEB("loadDocInCurrentTab: after file load: cancel " << CancelCheck::instance().cancelState() << " status " << lthr.status << " text length " << lthr.fdoc.text.length() << "\n"); if (lthr.status == 0) { return true; } if (CancelCheck::instance().cancelState()) return false; QString explain; if (!lthr.missing.empty()) { explain = QString::fromUtf8("
") + tr("Missing helper program: ") + path2qs(lthr.missing); QMessageBox::warning(0, "Recoll", tr("Can't turn doc into internal representation for ") + lthr.fdoc.mimetype.c_str() + explain); } else { if (progress.wasCanceled()) { QMessageBox::warning(0, "Recoll", tr("Canceled")); } else { progress.reset(); displayLoadError(lthr.explain, canGetRawText); } } return false; } /* Code for loading a file into an editor window. The operations that we call have no provision to indicate progression, and it would be complicated or impossible to modify them to do so (Ie: for external format converters). We implement a complicated and ugly mechanism based on threads to indicate to the user that the app is doing things: lengthy operations are done in threads and we update a progress indicator while they proceed (but we have no estimate of their total duration). It might be possible, but complicated (need modifications in handler) to implement a kind of bucket brigade, to have the beginning of the text displayed faster */ // Insert into editor by chunks so that the top becomes visible // earlier for big texts. This provokes some artifacts (adds empty line), // so we can't set it too low. #define CHUNKL 500*1000 // Make sure we don't ever reenter loadDocInCurrentTab: note that I // don't think it's actually possible, this must be the result of a // misguided debug session. class LoadGuard { bool *m_bp; public: LoadGuard(bool *bp) {m_bp = bp ; *m_bp = true;} ~LoadGuard() {*m_bp = false; CancelCheck::instance().setCancel(false);} }; bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum) { LOGDEB1("Preview::loadDocInCurrentTab()\n"); LoadGuard guard(&m_loading); CancelCheck::instance().setCancel(false); setCurTabProps(idoc, docnum); QString msg = QString("Loading: %1 (size %2 bytes)") .arg(path2qs(idoc.url)).arg(u8s2qs(idoc.fbytes)); QProgressDialog progress(msg, tr("Cancel"), 0, 0, this); progress.setMinimumDuration(2000); QEventLoop loop; QTimer tT; tT.setSingleShot(true); connect(&tT, SIGNAL(timeout()), &loop, SLOT(quit())); //////////////////////////////////////////////////////////////////////// // Load and convert document // - idoc came out of the index data (main text and some fields missing). // - fdoc is the complete one what we are going to extract from storage. // // If the preference to use the stored text is set, we still // create the LoadThread object for convenience (using its fdoc // field, but don't start it. LoadThread lthr(theconfig, idoc, prefs.previewHtml, this); connect(<hr, SIGNAL(finished()), &loop, SLOT(quit())); bool canGetRawText = rcldb && rcldb->storesDocText(); bool preferStoredText = std::find(prefs.preferStoredTextMimes.begin(), prefs.preferStoredTextMimes.end(), idoc.mimetype) != prefs.preferStoredTextMimes.end(); bool loadok{false}; if (!preferStoredText || !canGetRawText) { // Try load from actual document loadok = runLoadThread(lthr, tT, loop, progress, canGetRawText); } if (!loadok && canGetRawText) { // Preferring/able to use stored text or extern load failed lthr.fdoc = idoc; loadok = rcldb->getDocRawText(lthr.fdoc); if (!loadok) { QMessageBox::warning(0,"Recoll",tr("Could not fetch stored text")); } lthr.fdoc.mimetype = "text/plain"; } if (!loadok) { // Everything failed. progress.close(); return false; } // Reset config just in case. theconfig->setKeyDir(""); //////////////////////////////////////////////////////////////////////// // Create preview text: highlight search terms // We don't do the highlighting for very big texts: too long. We // should at least do special char escaping, in case a '&' or '<' // somehow slipped through previous processing. bool highlightTerms = int(lthr.fdoc.text.length()) < prefs.maxhltextkbs * 1024; // Final text is produced in chunks so that we can display the top // while still inserting at bottom PreviewTextEdit *editor = currentEditor(); editor->m_plaintorich->clear(); // For an actual html file, if we want to have the images and // style loaded in the preview, we need to set the search // path. Not too sure this is a good idea as I find them rather // distracting when looking for text, esp. with qtextedit // relatively limited html support (text sometimes get hidden by // images). #if 0 string path = fileurltolocalpath(idoc.url); if (!path.empty()) { path = path_getfather(path); QStringList paths(path2qs(path)); editor->setSearchPaths(paths); } #endif editor->setFont(m_font); editor->setHtml(""); editor->m_format = Qt::RichText; bool inputishtml = !lthr.fdoc.mimetype.compare("text/html"); QStringList qrichlst; editor->m_plaintorich->set_activatelinks(prefs.previewActiveLinks); if (highlightTerms) { progress.setLabelText(tr("Creating preview text")); qApp->processEvents(); if (inputishtml) { LOGDEB1("Preview: got text/html " << lthr.fdoc.text.substr(0,100) << "\n"); editor->m_plaintorich->set_inputhtml(true); } else { LOGDEB1("Preview: got text/plain " << lthr.fdoc.text.substr(0,100) << "\n"); editor->m_plaintorich->set_inputhtml(false); } ToRichThread rthr(lthr.fdoc.text, m_hData, editor->m_plaintorich, qrichlst, this); connect(&rthr, SIGNAL(finished()), &loop, SLOT(quit())); rthr.start(); for (;;) { tT.start(1000); loop.exec(); if (rthr.isFinished()) break; if (progress.wasCanceled()) { CancelCheck::instance().setCancel(); } } // Conversion to rich text done if (CancelCheck::instance().cancelState()) { if (qrichlst.size() == 0 || qrichlst.front().size() == 0) { // We can't call closeCurrentTab here as it might delete // the object which would be a nasty surprise to our // caller. return false; } else { qrichlst.back() += "Cancelled !"; } } } else { LOGDEB("Preview: no highlighting, loading " << lthr.fdoc.text.size() << " bytes\n"); // No plaintorich() call. In this case, either the text is // html and the html quoting is hopefully correct, or it's // plain-text and there is no need to escape special // characters. We'd still want to split in chunks (so that the // top is displayed faster), but we must not cut tags, and // it's too difficult on html. For text we do the splitting on // a QString to avoid utf8 issues. QString qr = QString::fromUtf8(lthr.fdoc.text.c_str(), lthr.fdoc.text.length()); int l = 0; if (inputishtml) { qrichlst.push_back(qr); } else { editor->setPlainText(""); editor->m_format = Qt::PlainText; for (int pos = 0; pos < (int)qr.length(); pos += l) { l = MIN(CHUNKL, qr.length() - pos); qrichlst.push_back(qr.mid(pos, l)); } } } /////////////////////////////////////////////////////////// // Load text into editor window. progress.setLabelText(tr("Loading preview text into editor")); qApp->processEvents(); editor->m_richtxt.clear(); for (QStringList::iterator it = qrichlst.begin(); it != qrichlst.end(); it++) { qApp->processEvents(); editor->append(*it); // We need to save the rich text for printing, the editor does // not do it consistently for us. editor->m_richtxt.append(*it); if (progress.wasCanceled()) { editor->append("Cancelled !"); LOGDEB("loadDocInCurrentTab: cancelled in editor load\n"); break; } } progress.close(); editor->m_curdsp = PreviewTextEdit::PTE_DSPTXT; //////////////////////////////////////////////////////////////////////// // Finishing steps // Maybe the text was actually empty ? Switch to fields then. Else free-up // the text memory in the loaded document. We still have a copy of the text // in editor->m_richtxt bool textempty = lthr.fdoc.text.empty(); if (!textempty) lthr.fdoc.text.clear(); editor->m_fdoc = lthr.fdoc; editor->m_dbdoc = idoc; editPB->setEnabled(canOpen(&editor->m_dbdoc, theconfig)); if (textempty) editor->displayFields(); // If this is an image, display it instead of the text. if (mimeIsImage(idoc.mimetype)) { string fn = fileurltolocalpath(idoc.url); theconfig->setKeyDir(fn.empty() ? "" : path_getfather(fn)); // We want a real file, so if this comes from data or we have an ipath, create it. if (fn.empty() || !idoc.ipath.empty()) { TempFile temp = lthr.tmpimg; if (temp.ok()) { LOGDEB1("Preview: load: got temp file from internfile\n"); } else if (!FileInterner::idocToFile(temp, string(), theconfig, idoc)) { temp = TempFile(); // just in case. } if (temp.ok()) { rememberTempFile(temp); fn = temp.filename(); editor->m_tmpfilename = fn; } else { editor->m_tmpfilename.erase(); fn.erase(); } } if (!fn.empty()) { editor->m_image = QImage(fn.c_str()); if (!editor->m_image.isNull()) editor->displayImage(); } } // Position the editor so that the first search term is visible if (searchTextCMB->currentText().length() != 0) { // If there is a current search string, perform the search. // Do not beep for an automatic search, this is ennoying. m_canBeep = false; doSearch(searchTextCMB->currentText(), true, false); } else { // Position to the first query term if (editor->m_plaintorich->haveAnchors()) { QString aname = editor->m_plaintorich->curAnchorName(); LOGDEB2("Call movetoanchor(" << qs2utf8s(aname) << ")\n"); editor->scrollToAnchor(aname); // Position the cursor approximately at the anchor (top of // viewport) so that searches start from here QTextCursor cursor = editor->cursorForPosition(QPoint(0, 0)); editor->setTextCursor(cursor); } } // Enter document in document history historyEnterDoc(rcldb.get(), g_dynconf, idoc); editor->setFocus(); emit(previewExposed(this, m_searchId, docnum)); LOGDEB("loadDocInCurrentTab: returning true\n"); return true; } PreviewTextEdit::PreviewTextEdit(QWidget* parent, const char* nm, Preview *pv) : QTextBrowser(parent), m_preview(pv), m_plaintorich(new PlainToRichQtPreview()), m_dspflds(false), m_docnum(-1) { setContextMenuPolicy(Qt::CustomContextMenu); setObjectName(nm); connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(createPopupMenu(const QPoint&))); connect(this, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(onAnchorClicked(const QUrl&))); setOpenExternalLinks(false); setOpenLinks(false); } void PreviewTextEdit::onAnchorClicked(const QUrl& url) { LOGDEB("PreviewTextEdit::onAnchorClicked: " << qs2utf8s(url.toString()) << std::endl); if (prefs.previewActiveLinks && m_preview->m_rclmain) { Rcl::Doc doc; doc.url = qs2utf8s(url.toString()).c_str(); doc.mimetype = "text/html"; m_preview->m_rclmain->startNativeViewer(doc); } } void PreviewTextEdit::createPopupMenu(const QPoint& pos) { LOGDEB1("PreviewTextEdit::createPopupMenu()\n"); QMenu *popup = new QMenu(this); switch (m_curdsp) { case PTE_DSPTXT: popup->addAction(tr("Show fields"), this, SLOT(displayFields())); if (!m_image.isNull()) popup->addAction(tr("Show image"), this, SLOT(displayImage())); break; case PTE_DSPFLDS: popup->addAction(tr("Show main text"), this, SLOT(displayText())); if (!m_image.isNull()) popup->addAction(tr("Show image"), this, SLOT(displayImage())); break; case PTE_DSPIMG: default: popup->addAction(tr("Show fields"), this, SLOT(displayFields())); popup->addAction(tr("Show main text"), this, SLOT(displayText())); break; } popup->addAction(tr("Reload as Plain Text"), this, SLOT(reloadAsPlainText())); popup->addAction(tr("Reload as HTML"), this, SLOT(reloadAsHTML())); popup->addAction(tr("Select All"), this, SLOT(selectAll())); popup->addAction(tr("Copy"), this, SLOT(copy())); popup->addAction(tr("Print"), this, SLOT(print())); if (prefs.previewPlainPre) { popup->addAction(tr("Fold lines"), m_preview, SLOT(togglePlainPre())); } else { popup->addAction(tr("Preserve indentation"), m_preview, SLOT(togglePlainPre())); } if (!m_dbdoc.url.empty()) { popup->addAction(tr("Save document to file"), m_preview, SLOT(emitSaveDocToFile())); if (canOpen(&m_dbdoc, theconfig)) { popup->addAction(tr("Open document"), m_preview, SLOT(emitEditRequested())); } } popup->popup(mapToGlobal(pos)); } void PreviewTextEdit::reloadAsPlainText() { auto saved = prefs.previewHtml; prefs.previewHtml = false; m_preview->loadDocInCurrentTab(m_dbdoc, m_docnum); prefs.previewHtml = saved; } void PreviewTextEdit::reloadAsHTML() { auto saved = prefs.previewHtml; prefs.previewHtml = true; m_preview->loadDocInCurrentTab(m_dbdoc, m_docnum); prefs.previewHtml = saved; } // Display main text void PreviewTextEdit::displayText() { LOGDEB1("PreviewTextEdit::displayText()\n"); // Ensuring that the view does not move when changing the font // size and redisplaying the text: can't find a good way to do // it. The only imperfect way I found was to get the position for // the last line (approximately), and make the position visible // after the change. auto c = cursorForPosition(QPoint(0,height()-20)); int pos = c.position(); setFont(m_preview->m_font); if (m_format == Qt::PlainText) { setPlainText(m_richtxt); } else { setHtml(m_richtxt); } if (m_curdsp == PTE_DSPTXT) { auto cursor = textCursor(); cursor.setPosition(pos); setTextCursor(cursor); ensureCursorVisible(); } m_curdsp = PTE_DSPTXT; } // Display field values void PreviewTextEdit::displayFields() { LOGDEB1("PreviewTextEdit::displayFields()\n"); setFont(m_preview->m_font); QString txt = "\n"; txt += "" + path2qs(m_url); if (!m_ipath.empty()) txt += "|" + u8s2qs(m_ipath); txt += "

"; txt += "
\n"; for (const auto& entry: m_fdoc.meta) { if (!entry.second.empty()) { txt += "
" + u8s2qs(entry.first) + "
" + "
" + u8s2qs(escapeHtml(entry.second)) + "
\n"; } } txt += "
"; setHtml(txt); m_curdsp = PTE_DSPFLDS; } void PreviewTextEdit::displayImage() { LOGDEB1("PreviewTextEdit::displayImage()\n"); setFont(m_preview->m_font); if (m_image.isNull()) displayText(); setPlainText(""); if (m_image.width() > width() || m_image.height() > height()) { m_image = m_image.scaled(width(), height(), Qt::KeepAspectRatio); } document()->addResource(QTextDocument::ImageResource, QUrl("image"), m_image); textCursor().insertImage("image"); m_curdsp = PTE_DSPIMG; } void PreviewTextEdit::mouseDoubleClickEvent(QMouseEvent *event) { LOGDEB2("PreviewTextEdit::mouseDoubleClickEvent\n"); QTextEdit::mouseDoubleClickEvent(event); if (textCursor().hasSelection() && m_preview) m_preview->emitWordSelect(textCursor().selectedText()); } void PreviewTextEdit::print() { LOGDEB("PreviewTextEdit::print\n"); if (!m_preview) return; #ifndef QT_NO_PRINTER QPrinter printer; QPrintDialog *dialog = new QPrintDialog(&printer, this); dialog->setWindowTitle(tr("Print Current Preview")); if (dialog->exec() != QDialog::Accepted) return; QTextEdit::print(&printer); #endif } recoll-1.36.1/qtgui/main.cpp0000644000175000017500000004155514506024035012575 00000000000000/* Copyright (C) 2005-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rcldb.h" #include "rclconfig.h" #include "pathut.h" #include "recoll.h" #include "smallut.h" #include "rclinit.h" #include "log.h" #include "rclmain_w.h" #include "ssearch_w.h" #include "guiutils.h" #include "smallut.h" #include "readfile.h" #include "uncomp.h" #include "cstr.h" #include "dynconf.h" #include "recollq.h" using std::string; using std::list; using std::vector; extern RclConfig *theconfig; AdvSearchHist *g_advshistory; std::mutex thetempfileslock; // Use a list not a vector so that contained objects have stable // addresses when extending. static list o_tempfiles; /* Keep an array of temporary files for deletion at exit. It happens that we erase some of them before exiting (ie: when closing a preview tab), we don't reuse the array holes for now */ TempFile *rememberTempFile(TempFile temp) { std::unique_lock locker(thetempfileslock); o_tempfiles.push_back(temp); return &o_tempfiles.back(); } void forgetTempFile(string &fn) { if (fn.empty()) return; std::unique_lock locker(thetempfileslock); for (auto& entry : o_tempfiles) { if (entry.ok() && !fn.compare(entry.filename())) { entry = TempFile(); } } fn.erase(); } void deleteAllTempFiles() { std::unique_lock locker(thetempfileslock); o_tempfiles.clear(); Uncomp::clearcache(); } std::shared_ptr rcldb; int recollNeedsExit; RclMain *mainWindow; void startManual(const string& helpindex) { if (mainWindow) mainWindow->startManual(helpindex); } const vector *getCurrentExtraDbs() { auto edbs = &prefs.activeExtraDbs; if (prefs.useTmpActiveExtraDbs) { edbs = &prefs.tmpActiveExtraDbs; } return edbs; } bool maybeOpenDb(string &reason, bool force, bool *maindberror) { LOGDEB1("maybeOpenDb: force " << force << "\n"); if (force || nullptr == rcldb) { rcldb = std::make_shared(theconfig); } rcldb->rmQueryDb(""); auto edbs = getCurrentExtraDbs(); if (!edbs->empty()) { rcldb->setExtraQueryDbs(*edbs); } Rcl::Db::OpenError error; if (!rcldb->isopen() && !rcldb->open(Rcl::Db::DbRO, &error)) { reason = "Could not open database"; if (maindberror) { reason += " in " + theconfig->getDbDir() + " : " + rcldb->getReason(); *maindberror = (error == Rcl::Db::DbOpenMainDb) ? true : false; } return false; } rcldb->setAbstractParams(-1, prefs.syntAbsLen, prefs.syntAbsCtx); rcldb->setUseSpellFuzz(prefs.autoSpell); rcldb->setMaxSpellDist(prefs.autoSpellMaxDist); return true; } // Retrieve the list currently active stemming languages. We try to // get this from the db, as some may have been added from recollindex // without changing the config. If this fails, use the config. This is // used for setting up choice menus, not updating the configuration. bool getStemLangs(vector& vlangs) { // Try from db string reason; if (maybeOpenDb(reason, false)) { vlangs = rcldb->getStemLangs(); LOGDEB0("getStemLangs: from index: " << stringsToString(vlangs) <<"\n"); return true; } else { // Cant get the langs from the index. Maybe it just does not // exist yet. So get them from the config string slangs; if (theconfig->getConfParam("indexstemminglanguages", slangs)) { stringToStrings(slangs, vlangs); return true; } return false; } } // This is never called because we _Exit() in rclmain_w.cpp static void recollCleanup() { LOGDEB2("recollCleanup: closing database\n" ); rcldb.reset(); deleteZ(theconfig); deleteAllTempFiles(); LOGDEB2("recollCleanup: done\n" ); } void applyStyleSheet(const QString& qssfn) { auto comfn = path_cat(path_cat(theconfig->getDatadir(), "examples"), "recoll-common.qss"); std::string qss; file_to_string(comfn, qss); if (!qssfn.isEmpty()) { LOGDEB0("Using custom style sheet: [" << qs2path(qssfn) << "]\n"); string customqss; file_to_string(qs2path(qssfn), customqss); qss += customqss; } qss = prefs.scaleFonts(qss, prefs.wholeuiscale); qApp->setStyleSheet(u8s2qs(qss)); } extern void qInitImages_recoll(); static const char *thisprog; // BEWARE COMPATIBILITY WITH recollq OPTIONS letters static int op_flags; #define OPT_a 0x1 #define OPT_c 0x2 #define OPT_f 0x4 #define OPT_h 0x8 #define OPT_L 0x10 #define OPT_l 0x20 #define OPT_o 0x40 #define OPT_q 0x80 #define OPT_t 0x100 #define OPT_v 0x200 #define OPT_W 0x400 #define OPT_w 0x800 static const char usage [] = "\n" "recoll [-h] [-c ] [-q query]\n" " -h : Print help and exit\n" " -c : specify config directory, overriding $RECOLL_CONFDIR\n" " -L : force language for GUI messages (e.g. -L fr)\n" " [-o|l|f|a] [-t] -q 'query' : search query to be executed as if entered\n" " into simple search. The default is to interpret the argument as a \n" " query language string (but see modifier options)\n" " In most cases, the query string should be quoted with single-quotes to\n" " avoid shell interpretation\n" " -a : the query will be interpreted as an AND query.\n" " -o : the query will be interpreted as an OR query.\n" " -f : the query will be interpreted as a filename search\n" " -l : the query will be interpreted as a query language string (default)\n" " -t : terminal display: no gui. Results go to stdout. MUST be given\n" " explicitly as -t (not ie, -at), and -q MUST\n" " be last on the command line if this is used.\n" " Use -t -h to see the additional non-gui options\n" " -w : open minimized\n" " -W : do not open at all, only system tray icon (needs system tray!)\n" "recoll -v : print version\n" "recoll \n" " This is used to open a recoll url (including an ipath), and called\n" " typically from another search interface like the Unity Dash\n" ; static void Usage(void) { FILE *fp = (op_flags & OPT_h) ? stdout : stderr; fprintf(fp, "%s\n", Rcl::version_string().c_str()); fprintf(fp, "%s: Usage: %s", thisprog, usage); exit((op_flags & OPT_h)==0); } int main(int argc, char **argv) { // if we are named recollq or option "-t" is present at all, we // don't do the GUI thing and pass the whole to recollq for // command line / pipe usage. if (!strcmp(argv[0], "recollq")) exit(recollq(&theconfig, argc, argv)); for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], "-t")) { exit(recollq(&theconfig, argc, argv)); } } #ifdef USING_WEBENGINE // This is necessary for allowing webengine to load local resources (icons) // It is not an issue because we never access remote sites. char arg_disable_web_security[] = "--disable-web-security"; int appargc = argc + 1; char** appargv = new char*[appargc+1]; for(int i = 0; i < argc; i++) { appargv[i] = argv[i]; } appargv[argc] = arg_disable_web_security; appargv[argc+1] = nullptr; QApplication app(appargc, appargv); #else QApplication app(argc, argv); #endif QCoreApplication::setOrganizationName("Recoll.org"); QCoreApplication::setApplicationName("recoll"); string a_config; string a_lang; string question; string urltoview; // Avoid disturbing argc and argv. Especially, setting argc to 0 // prevents WM_CLASS to be set from argv[0] (it appears that qt // keeps a ref to argc, and that it is used at exec() time to set // WM_CLASS from argv[0]). Curiously, it seems that the argv // pointer can be modified without consequences, but we use a copy // to play it safe int myargc = argc; char **myargv = argv; thisprog = myargv[0]; myargc--; myargv++; while (myargc > 0 && **myargv == '-') { (*myargv)++; if (!(**myargv)) Usage(); while (**myargv) switch (*(*myargv)++) { case 'a': op_flags |= OPT_a; break; case 'c': op_flags |= OPT_c; if (myargc < 2) Usage(); a_config = *(++myargv); myargc--; goto b1; case 'f': op_flags |= OPT_f; break; case 'h': op_flags |= OPT_h; Usage();break; case 'L': op_flags |= OPT_L; if (myargc < 2) Usage(); a_lang = *(++myargv); myargc--; goto b1; case 'l': op_flags |= OPT_l; break; case 'o': op_flags |= OPT_o; break; case 'q': op_flags |= OPT_q; if (myargc < 2) Usage(); question = *(++myargv); myargc--; goto b1; case 't': op_flags |= OPT_t; break; case 'v': op_flags |= OPT_v; fprintf(stdout, "%s\n", Rcl::version_string().c_str()); return 0; case 'W': op_flags |= OPT_W; break; case 'w': op_flags |= OPT_w; break; default: Usage(); } b1: myargc--; myargv++; } // If -q was given, all remaining non-option args are concatenated // to the query. This is for the common case recoll -q x y z to // avoid needing quoting "x y z" if (op_flags & OPT_q) while (myargc > 0) { question += " "; question += *myargv++; myargc--; } // Else the remaining argument should be an URL to be opened if (myargc == 1) { urltoview = *myargv++;myargc--; if (urltoview.compare(0, 7, cstr_fileu)) { Usage(); } } else if (myargc > 0) Usage(); string reason; theconfig = recollinit(0, recollCleanup, 0, reason, &a_config); if (!theconfig || !theconfig->ok()) { QString msg = "Configuration problem: "; msg += QString::fromUtf8(reason.c_str()); QMessageBox::critical(0, "Recoll", msg); exit(1); } // fprintf(stderr, "recollinit done\n"); // Translations for Qt standard widgets QString slang; if (op_flags & OPT_L) { slang = u8s2qs(a_lang); } else { slang = QLocale::system().name().left(2); } QTranslator qt_trans(0); qt_trans.load(QString("qt_%1").arg(slang), #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) QLibraryInfo::path #else QLibraryInfo::location #endif (QLibraryInfo::TranslationsPath)); app.installTranslator(&qt_trans); // Translations for Recoll string translatdir = path_cat(theconfig->getDatadir(), "translations"); QTranslator translator(0); auto loaded = translator.load(QString("recoll_") + slang, translatdir.c_str()); if (loaded) app.installTranslator(&translator); // fprintf(stderr, "Translations installed\n"); string historyfile = path_cat(theconfig->getConfDir(), "history"); g_dynconf = new RclDynConf(historyfile); if (!g_dynconf || !g_dynconf->ok()) { QString msg = app.translate("Main", "\"history\" file is damaged, please check " "or remove it: ") + path2qs(historyfile); QMessageBox::critical(0, "Recoll", msg); exit(1); } g_advshistory = new AdvSearchHist; // fprintf(stderr, "History done\n"); rwSettings(false); // fprintf(stderr, "Settings done\n"); applyStyleSheet(prefs.qssFile); QIcon icon; icon.addFile(QString::fromUtf8(":/images/recoll.png")); app.setWindowIcon(icon); // Create main window and set its size to previous session's RclMain w; mainWindow = &w; string dbdir = theconfig->getDbDir(); if (dbdir.empty()) { QMessageBox::critical( 0, "Recoll", app.translate("Main", "No db directory in configuration")); exit(1); } maybeOpenDb(reason, false); if (op_flags & OPT_w) { mainWindow->showMinimized(); } else if (op_flags & OPT_W) { // Don't show anything except the systray icon if (!prefs.showTrayIcon) { QMessageBox::critical( 0, "Recoll", app.translate( "Main", "Needs \"Show system tray icon\" to be set in preferences!\n")); mainWindow->show(); } } else { switch (prefs.showmode) { case PrefsPack::SHOW_NORMAL: mainWindow->show(); break; case PrefsPack::SHOW_MAX: mainWindow->showMaximized(); break; case PrefsPack::SHOW_FULL: mainWindow->showFullScreen(); break; } } QTimer::singleShot(0, mainWindow, SLOT(initDbOpen())); mainWindow->sSearch->searchTypCMB->setCurrentIndex(prefs.ssearchTyp); mainWindow->sSearch->onSearchTypeChanged(prefs.ssearchTyp); if (op_flags & OPT_q) { SSearch::SSearchType stype; if (op_flags & OPT_o) { stype = SSearch::SST_ANY; } else if (op_flags & OPT_f) { stype = SSearch::SST_FNM; } else if (op_flags & OPT_a) { stype = SSearch::SST_ALL; } else { stype = SSearch::SST_LANG; } mainWindow->sSearch->searchTypCMB->setCurrentIndex(int(stype)); mainWindow->sSearch->setSearchString(QString::fromLocal8Bit(question.c_str())); } else if (!urltoview.empty()) { LOGDEB("MAIN: got urltoview [" << urltoview << "]\n"); mainWindow->setUrlToView(QString::fromLocal8Bit(urltoview.c_str())); } return app.exec(); } QString myGetFileName(bool isdir, QString caption, bool filenosave, QString dirloc, QString dfltnm) { MyGFNParams parms; parms.caption = caption; parms.filenosave = filenosave; parms.dirlocation = dirloc; parms.dfltnm = dfltnm; return myGetFileName(isdir, parms); } QString myGetFileName(bool isdir, MyGFNParams &parms) { LOGDEB1("myGetFileName: isdir " << isdir << "\n"); QFileDialog dialog(0, parms.caption); #ifdef _WIN32 // The default initial directory on Windows is the Recoll install, // which is not appropriate. Change it, only for the first call // (next will start with the previous selection). static bool first{true}; if (first && parms.dirlocation.isEmpty()) { first = false; // See https://doc.qt.io/qt-5/qfiledialog.html#setDirectoryUrl // about the clsid magic (this one points to the desktop). dialog.setDirectoryUrl(QUrl("clsid:B4BFCC3A-DB2C-424C-B029-7FE99A87C641")); } #endif if (!parms.dirlocation.isEmpty()) { dialog.setDirectory(parms.dirlocation); } if (!parms.dfltnm.isEmpty()) { dialog.selectFile(parms.dfltnm); } // DontUseNativeDialog is needed for sidebarurls QFileDialog::Options opts = QFileDialog::DontUseNativeDialog; if (parms.readonly) { opts |= QFileDialog::ReadOnly; } if (isdir) { dialog.setFileMode(QFileDialog::Directory); opts |= QFileDialog::ShowDirsOnly; } else { dialog.setFileMode(QFileDialog::AnyFile); if (parms.filenosave) dialog.setAcceptMode(QFileDialog::AcceptOpen); else dialog.setAcceptMode(QFileDialog::AcceptSave); } dialog.setOptions(opts); dialog.setViewMode(QFileDialog::List); QFlags flags = QDir::NoDotAndDotDot | QDir::Hidden; if (isdir) flags |= QDir::Dirs; else flags |= QDir::Dirs | QDir::Files; dialog.setFilter(flags); if (!parms.sidedirs.empty()) { QList sidebarurls; for (const auto& dir : parms.sidedirs) { sidebarurls.push_back(QUrl::fromLocalFile(path2qs(dir))); } dialog.setSidebarUrls(sidebarurls); } if (dialog.exec() == QDialog::Accepted) { parms.dirlocation = dialog.directory().absolutePath(); return dialog.selectedFiles().value(0); } return QString(); } recoll-1.36.1/qtgui/webcache.ui0000644000175000017500000000262214444307651013246 00000000000000 Webcache 0 0 400 300 Webcache editor TextLabel Search regexp searchLE QAbstractItemView::NoEditTriggers true false recoll-1.36.1/qtgui/reslist.h0000644000175000017500000001535014427373216013007 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RESLIST_H_INCLUDED_ #define _RESLIST_H_INCLUDED_ #include "autoconfig.h" #include #include #include "plaintorich.h" #if defined(USING_WEBENGINE) # include # define RESLIST_PARENTCLASS QWebEngineView #elif defined(USING_WEBKIT) # include # define RESLIST_PARENTCLASS QWebView #else # include # define RESLIST_PARENTCLASS QTextBrowser #endif class RclMain; class QtGuiResListPager; class QEvent; class QProgressDialog; namespace Rcl { class Doc; } /** * Display a list of document records. The data can be out of the history * manager or from an index query, both abstracted as a DocSequence. */ class ResList : public RESLIST_PARENTCLASS { Q_OBJECT friend class QtGuiResListPager; public: ResList(QWidget* parent = 0, const char* name = 0); virtual ~ResList(); ResList(const ResList&) = delete; ResList& operator=(const ResList&) = delete; // Return document for given docnum. We mostly act as an // intermediary to the docseq here, but this has also the // side-effect of making the entry current (visible and // highlighted), and only works if the num is inside the current // page or its immediate neighbours. bool getDoc(int docnum, Rcl::Doc &); bool displayingHistory(); int listId() const {return m_listId;} int pageFirstDocNum(); void setFont(); void setRclMain(RclMain *m, bool ismain); public slots: virtual void setDocSource(std::shared_ptr nsource); virtual void resetList(); // Erase current list virtual void resPageUpOrBack(); // Page up pressed virtual void resPageDownOrNext(); // Page down pressed virtual void resultPageBack(); // Previous page of results virtual void resultPageFirst(); // First page of results virtual void resultPageNext(); // Next (or first) page of results virtual void resultPageFor(int docnum); // Page containing docnum virtual void menuPreview(); virtual void menuSaveToFile(); virtual void menuEdit(); virtual void menuOpenWith(QAction *); virtual void menuCopyFN(); virtual void menuCopyPath(); virtual void menuCopyURL(); virtual void menuCopyText(); virtual void menuExpand(); virtual void menuPreviewParent(); virtual void menuOpenParent(); virtual void menuOpenFolder(); virtual void menuShowSnippets(); virtual void menuShowSubDocs(); virtual void previewExposed(int); virtual void append(const QString &text); virtual void readDocSource(); virtual void highlighted(const QString& link); virtual void createPopupMenu(const QPoint& pos); virtual void showQueryDetails(); virtual void onUiPrefsChanged(); signals: void nextPageAvailable(bool); void prevPageAvailable(bool); void docPreviewClicked(int, Rcl::Doc, int); void docSaveToFileClicked(Rcl::Doc); void previewRequested(Rcl::Doc); void showSnippets(Rcl::Doc); void showSubDocs(Rcl::Doc); void editRequested(Rcl::Doc); void openWithRequested(Rcl::Doc, std::string cmd); void docExpand(Rcl::Doc); void wordSelect(QString); void wordReplace(const QString&, const QString&); void hasResults(int); protected: void keyPressEvent(QKeyEvent *e); void mouseReleaseEvent(QMouseEvent *e); void mouseDoubleClickEvent(QMouseEvent*); public slots: virtual void onLinkClicked(const QUrl &); virtual void onPopupJsDone(const QVariant&); protected slots: virtual void languageChange(); void setupArrows(); #if defined(USING_WEBENGINE) void runStoredJS(bool); void onPageScrollPositionChanged(const QPointF &position); void onPageContentsSizeChanged(const QSizeF &size); #endif // USING_WEBENGINE private: QtGuiResListPager *m_pager{0}; std::shared_ptr m_source; int m_popDoc{-1}; // Docnum for the popup menu. QPoint m_popPos; int m_curPvDoc{-1};// Docnum for current preview int m_lstClckMod{0}; // Last click modifier. int m_listId{0}; // query Id for matching with preview windows #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) // Webview makes it more difficult to append text incrementally, // so we store the page and display it when done. QString m_text; QProgressDialog *m_progress{nullptr}; int m_residx{0}; // result index in page QString m_lasttext; void runJS(const QString& js); #if defined(USING_WEBENGINE) // Webengine local image display appears to break randomly (for some versions and platforms, // circa 2022) when we display the same data multiple times. Detect and avoid. QPointF m_scrollpos{0,0}; QSizeF m_contentsize{0,0}; #endif // WEBENGINE #else // Translate from textedit paragraph number to relative // docnum. Built while we insert text into the qtextedit std::map m_pageParaToReldocnums; virtual int docnumfromparnum(int); virtual std::pair parnumfromdocnum(int); #endif // Running js after page load. Sometimes we don't want to do it on the first event (which may be // a reset), hence the countdown. QString m_js; int m_js_countdown{0}; RclMain *m_rclmain{0}; bool m_ismainres{true}; void doCreatePopupMenu(); virtual void displayPage(); static int newListId(); void resetView(); bool scrollIsAtTop(); bool scrollIsAtBottom(); }; #ifdef USING_WEBENGINE // Subclass the page to hijack the link clicks class RclWebPage : public QWebEnginePage { Q_OBJECT public: RclWebPage(ResList *parent) : QWebEnginePage((QWidget *)parent), m_reslist(parent) {} protected: virtual bool acceptNavigationRequest( const QUrl& url, NavigationType tp, bool isMainFrame); private: ResList *m_reslist; }; #else // Using Qt Webkit #define RclWebPage QWebPage #endif class PlainToRichQtReslist : public PlainToRich { public: virtual std::string startMatch(unsigned int idx); virtual std::string endMatch(); }; #endif /* _RESLIST_H_INCLUDED_ */ recoll-1.36.1/qtgui/reslist.cpp0000644000175000017500000012104714444307651013342 00000000000000/* Copyright (C) 2005-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "smallut.h" #include "recoll.h" #include "guiutils.h" #include "pathut.h" #include "docseq.h" #include "pathut.h" #include "mimehandler.h" #include "plaintorich.h" #include "internfile.h" #include "indexer.h" #include "snippets_w.h" #include "listdialog.h" #include "reslist.h" #include "moc_reslist.cpp" #include "rclhelp.h" #include "appformime.h" #include "respopup.h" #include "reslistpager.h" using std::string; using std::vector; using std::map; using std::list; static const QKeySequence quitKeySeq("Ctrl+q"); static const QKeySequence closeKeySeq("Ctrl+w"); #if defined(USING_WEBKIT) # include # include # include # define QWEBSETTINGS QWebSettings #elif defined(USING_WEBENGINE) // Notes for WebEngine: // - It used to be that all links must begin with http:// for // acceptNavigationRequest to be called. Can't remember if file:// // would work. Anyway not any more since we set baseURL. See comments // in linkClicked(). // - The links passed to acceptNav.. have the host part // lowercased -> we change S0 to http://localhost/S0, not http://S0 # include # include # include # define QWEBSETTINGS QWebEngineSettings #endif #ifdef USING_WEBENGINE // This script saves the location details when a mouse button is // clicked. This is for replacing data provided by Webkit QWebElement // on a right-click as QT WebEngine does not have an equivalent service. static const string locdetailscript(R"raw( var locDetails = ''; function saveLoc(ev) { el = ev.target; locDetails = ''; while (el && el.attributes && !el.attributes.getNamedItem("rcldocnum")) { el = el.parentNode; } rcldocnum = el.attributes.getNamedItem("rcldocnum"); if (rcldocnum) { rcldocnumvalue = rcldocnum.value; } else { rcldocnumvalue = ""; } if (el && el.attributes) { locDetails = 'rcldocnum = ' + rcldocnumvalue } } )raw"); bool RclWebPage::acceptNavigationRequest(const QUrl& url, NavigationType tp, bool isMainFrame) { Q_UNUSED(isMainFrame); LOGDEB0("QWebEnginePage::acceptNavigationRequest. Type: " << tp << " isMainFrame " << isMainFrame << std::endl); if (tp == QWebEnginePage::NavigationTypeLinkClicked) { m_reslist->onLinkClicked(url); return false; } else { return true; } } #endif // WEBENGINE class QtGuiResListPager : public ResListPager { public: QtGuiResListPager(RclConfig *cnf, ResList *p, int ps, bool alwayssnip) : ResListPager(cnf, ps, alwayssnip), m_reslist(p) {} virtual bool append(const string& data) override; virtual bool append(const string& data, int idx, const Rcl::Doc& doc) override; virtual string trans(const string& in) override; virtual string detailsLink() override; virtual const string &parFormat() override; virtual const string &dateFormat() override; virtual string nextUrl() override; virtual string prevUrl() override; virtual string headerContent() override; virtual void suggest(const vectoruterms, map >& sugg) override; virtual string absSep() override {return (const char *)(prefs.abssep.toUtf8());} virtual bool useAll() override {return prefs.useDesktopOpen;} #if defined(USING_WEBENGINE) || defined(USING_WEBKIT) // We used to use http://localhost/. Now use file:/// as this is // what Webengine will prepend relative links with (as // baseURL). This is for the case where a user adds a link like // P%N, which would not work if linkPrefix and baseURL were not // the same. // // Now also set for webkit because, as we set baseURL to file://, // the relative links we set in the list will also be prefixed (by // the HTML engine) virtual string linkPrefix() override {return "file:///recoll-links/";} virtual string bodyAttrs() override { return "onload=\"addEventListener('contextmenu', saveLoc)\""; } #endif private: ResList *m_reslist; }; ////////////////////////////// // /// QtGuiResListPager methods: bool QtGuiResListPager::append(const string& data) { m_reslist->append(u8s2qs(data)); return true; } bool QtGuiResListPager::append(const string& data, int docnum, const Rcl::Doc&) { #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) m_reslist->append(QString("
").arg(docnum)); m_reslist->append(u8s2qs(data)); m_reslist->append("
"); #else int blkcnt0 = m_reslist->document()->blockCount(); m_reslist->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); m_reslist->textCursor().insertBlock(); m_reslist->insertHtml(QString::fromUtf8(data.c_str())); m_reslist->moveCursor(QTextCursor::Start, QTextCursor::MoveAnchor); m_reslist->ensureCursorVisible(); int blkcnt1 = m_reslist->document()->blockCount(); for (int block = blkcnt0; block < blkcnt1; block++) { m_reslist->m_pageParaToReldocnums[block] = docnum; } #endif return true; } string QtGuiResListPager::trans(const string& in) { return qs2utf8s(ResList::tr(in.c_str())); } string QtGuiResListPager::detailsLink() { string chunk = string(""; chunk += trans("(show query)"); chunk += ""; return chunk; } const string& QtGuiResListPager::parFormat() { return prefs.creslistformat; } const string& QtGuiResListPager::dateFormat() { return prefs.reslistdateformat; } string QtGuiResListPager::nextUrl() { return "n-1"; } string QtGuiResListPager::prevUrl() { return "p-1"; } string QtGuiResListPager::headerContent() { string out; #if defined(USING_WEBENGINE) out += "\n"; #endif return out + prefs.htmlHeaderContents(); } void QtGuiResListPager::suggest(const vectoruterms, map>& sugg) { sugg.clear(); bool issimple = m_reslist && m_reslist->m_rclmain && m_reslist->m_rclmain->lastSearchSimple(); for (const auto& userterm : uterms) { vector spellersuggs; // If the term is in the dictionary, by default, aspell won't list alternatives, but the // recoll utility based on python-aspell. In any case, we might want to check the term // frequencies and taylor our suggestions accordingly ? For example propose a replacement // for a valid term only if the replacement is much more frequent ? (as google seems to do)? // To mimick the old code, we could check for the user term presence in the suggestion list, // but it's not clear that giving no replacements in this case would be better ? // // Also we should check that the term stems differently from the base word (else it's not // useful to expand the search). Or is it ? This should depend if stemming is turned on or // not if (!rcldb->getSpellingSuggestions(userterm, spellersuggs)) { continue; } if (!spellersuggs.empty()) { sugg[userterm] = vector(); int cnt = 0; for (auto subst : spellersuggs) { if (subst == userterm) continue; if (++cnt > 5) break; if (issimple) { // If this is a simple search, we set up links as a , else we // just list the replacements. subst = string("" + subst + ""; } sugg[userterm].push_back(subst); } } } } /////// /////// End reslistpager methods string PlainToRichQtReslist::startMatch(unsigned int idx) { (void)idx; #if 0 if (m_hdata) { string s1, s2; stringsToString >(m_hdata->groups[idx], s1); stringsToString >( m_hdata->ugroups[m_hdata->grpsugidx[idx]], s2); LOGDEB2("Reslist startmatch: group " << s1 << " user group " << s2 << "\n"); } #endif return string(""); } string PlainToRichQtReslist::endMatch() { return string(""); } static PlainToRichQtReslist g_hiliter; ///////////////////////////////////// ResList::ResList(QWidget* parent, const char* name) : RESLIST_PARENTCLASS(parent) { if (!name) setObjectName("resList"); else setObjectName(name); #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) setPage(new RclWebPage(this)); settings()->setAttribute(QWEBSETTINGS::JavascriptEnabled, true); #ifdef USING_WEBKIT LOGDEB("Reslist: using Webkit\n"); page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); // signals and slots connections connect(this, SIGNAL(linkClicked(const QUrl &)), this, SLOT(onLinkClicked(const QUrl &))); #else LOGDEB("Reslist: using Webengine\n"); connect(page(), SIGNAL(loadFinished(bool)), this, SLOT(runStoredJS(bool))); // These appear to get randomly disconnected or never connected. connect(page(), SIGNAL(scrollPositionChanged(const QPointF &)), this, SLOT(onPageScrollPositionChanged(const QPointF &))); connect(page(), SIGNAL(contentsSizeChanged(const QSizeF &)), this, SLOT(onPageContentsSizeChanged(const QSizeF &))); #endif #else LOGDEB("Reslist: using QTextBrowser\n"); setReadOnly(true); setUndoRedoEnabled(false); setOpenLinks(false); setTabChangesFocus(true); // signals and slots connections connect(this, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(onLinkClicked(const QUrl &))); #endif setFont(); languageChange(); (void)new HelpClient(this); HelpClient::installMap(qs2utf8s(this->objectName()), "RCL.SEARCH.GUI.RESLIST"); #if 0 // See comments in "highlighted connect(this, SIGNAL(highlighted(const QString &)), this, SLOT(highlighted(const QString &))); #endif setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(createPopupMenu(const QPoint&))); m_pager = new QtGuiResListPager(theconfig, this, prefs.respagesize, prefs.alwaysSnippets); m_pager->setHighLighter(&g_hiliter); resetView(); } ResList::~ResList() { // These have to exist somewhere for translations to work #ifdef __GNUC__ __attribute__((unused)) #endif static const char* strings[] = { QT_TR_NOOP("

No results found
"), QT_TR_NOOP("Documents"), QT_TR_NOOP("out of at least"), QT_TR_NOOP("for"), QT_TR_NOOP("Previous"), QT_TR_NOOP("Next"), QT_TR_NOOP("Unavailable document"), QT_TR_NOOP("Preview"), QT_TR_NOOP("Open"), QT_TR_NOOP("Snippets"), QT_TR_NOOP("(show query)"), QT_TR_NOOP("

Alternate spellings (accents suppressed): "), QT_TR_NOOP("

Alternate spellings: "), QT_TR_NOOP("This spelling guess was added to the search:"), QT_TR_NOOP("These spelling guesses were added to the search:"), }; } void ResList::setRclMain(RclMain *m, bool ismain) { m_rclmain = m; m_ismainres = ismain; if (!m_ismainres) { connect(new QShortcut(closeKeySeq, this), SIGNAL (activated()), this, SLOT (close())); connect(new QShortcut(quitKeySeq, this), SIGNAL (activated()), m_rclmain, SLOT (fileExit())); connect(this, SIGNAL(previewRequested(Rcl::Doc)), m_rclmain, SLOT(startPreview(Rcl::Doc))); connect(this, SIGNAL(docSaveToFileClicked(Rcl::Doc)), m_rclmain, SLOT(saveDocToFile(Rcl::Doc))); connect(this, SIGNAL(editRequested(Rcl::Doc)), m_rclmain, SLOT(startNativeViewer(Rcl::Doc))); } } #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) void ResList::runJS(const QString& js) { LOGDEB("runJS: " << qs2utf8s(js) << "\n"); #if defined(USING_WEBKIT) page()->mainFrame()->evaluateJavaScript(js); #elif defined(USING_WEBENGINE) page()->runJavaScript(js); // page()->runJavaScript(js, [](const QVariant &v) {qDebug() << v.toString();}); #endif } #if defined(USING_WEBENGINE) void ResList::runStoredJS(bool res) { if (m_js.isEmpty()) { return; } LOGDEB0("ResList::runStoredJS: res " << res << " cnt " << m_js_countdown << " m_js [" << qs2utf8s(m_js) << "]\n"); if (m_js_countdown > 0) { m_js_countdown--; return; } runJS(m_js); m_js.clear(); } void ResList::onPageScrollPositionChanged(const QPointF &position) { LOGDEB0("ResList::onPageScrollPositionChanged: y : " << position.y() << "\n"); m_scrollpos = position; } void ResList::onPageContentsSizeChanged(const QSizeF &size) { LOGDEB0("ResList::onPageContentsSizeChanged: y : " << size.height() << "\n"); m_contentsize = size; } #endif // WEBENGINE static void maybeDump(const QString& text) { std::string dumpfile; if (!theconfig->getConfParam("reslisthtmldumpfile", dumpfile) || dumpfile.empty()) { return; } dumpfile = path_tildexpand(dumpfile); if (path_exists(dumpfile)) { return; } auto fp = fopen(dumpfile.c_str(), "w"); if (fp) { auto s = qs2utf8s(text); fwrite(s.c_str(), 1, s.size(), fp); fclose(fp); } } #endif // WEBKIT or WEBENGINE void ResList::onUiPrefsChanged() { setFont(); displayPage(); } void ResList::setFont() { #if !defined(USING_WEBKIT) && !defined(USING_WEBENGINE) // Using QTextBrowser if (prefs.reslistfontfamily != "") { QFont nfont(prefs.reslistfontfamily, prefs.reslistfontsize); QTextBrowser::setFont(nfont); } else { QFont font; font.setPointSize(prefs.reslistfontsize); QTextBrowser::setFont(font); } #endif } int ResList::newListId() { static int id; return ++id; } void ResList::setDocSource(std::shared_ptr nsource) { LOGDEB("ResList::setDocSource()\n"); m_source = std::shared_ptr(new DocSource(theconfig, nsource)); if (m_pager) m_pager->setDocSource(m_source); } // A query was executed, or the filtering/sorting parameters changed, // re-read the results. void ResList::readDocSource() { LOGDEB("ResList::readDocSource()\n"); m_curPvDoc = -1; if (!m_source) return; m_listId = newListId(); // Reset the page size in case the preference was changed m_pager->setPageSize(prefs.respagesize); m_pager->setDocSource(m_source); resultPageNext(); emit hasResults(m_source->getResCnt()); } void ResList::resetList() { LOGDEB("ResList::resetList()\n"); setDocSource(std::shared_ptr()); resetView(); } void ResList::resetView() { m_curPvDoc = -1; // There should be a progress bar for long searches but there isn't // We really want the old result list to go away, otherwise, for a // slow search, the user will wonder if anything happened. The // following helps making sure that the textedit is really // blank. Else, there are often icons or text left around #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) m_text = ""; QString html(""); html += u8s2qs(m_pager->headerContent()) + ""; html += ""; setHtml(html); #else m_pageParaToReldocnums.clear(); clear(); QTextBrowser::append("."); clear(); #endif } bool ResList::displayingHistory() { // We want to reset the displayed history if it is currently // shown. Using the title value is an ugly hack string htstring = string((const char *)tr("Document history").toUtf8()); if (!m_source || m_source->title().empty()) return false; return m_source->title().find(htstring) == 0; } void ResList::languageChange() { setWindowTitle(tr("Result list")); } #if !defined(USING_WEBKIT) && !defined(USING_WEBENGINE) // Get document number from text block number int ResList::docnumfromparnum(int block) { if (m_pager->pageNumber() < 0) return -1; // Try to find the first number < input and actually in the map // (result blocks can be made of several text blocks) std::map::iterator it; do { it = m_pageParaToReldocnums.find(block); if (it != m_pageParaToReldocnums.end()) return pageFirstDocNum() + it->second; } while (--block >= 0); return -1; } // Get range of paragraph numbers which make up the result for document number std::pair ResList::parnumfromdocnum(int docnum) { LOGDEB("parnumfromdocnum: docnum " << docnum << "\n"); if (m_pager->pageNumber() < 0) { LOGDEB("parnumfromdocnum: no page return -1,-1\n"); return {-1, -1}; } int winfirst = pageFirstDocNum(); if (docnum - winfirst < 0) { LOGDEB("parnumfromdocnum: docnum " << docnum << " < winfirst " << winfirst << " return -1,-1\n"); return {-1, -1}; } docnum -= winfirst; for (const auto& entry : m_pageParaToReldocnums) { if (docnum == entry.second) { int first = entry.first; int last = first+1; std::map::iterator it1; while ((it1 = m_pageParaToReldocnums.find(last)) != m_pageParaToReldocnums.end() && it1->second == docnum) { last++; } LOGDEB("parnumfromdocnum: return " << first << "," << last << "\n"); return {first, last}; } } LOGDEB("parnumfromdocnum: not found return -1,-1\n"); return {-1,-1}; } #endif // TEXTBROWSER // Return doc from current or adjacent result pages. We can get called // for a document not in the current page if the user browses through // results inside a result window (with shift-arrow). This can only // result in a one-page change. bool ResList::getDoc(int docnum, Rcl::Doc &doc) { LOGDEB("ResList::getDoc: docnum " << docnum << " winfirst " << pageFirstDocNum() << "\n"); int winfirst = pageFirstDocNum(); int winlast = m_pager->pageLastDocNum(); if (docnum < 0 || winfirst < 0 || winlast < 0) return false; // Is docnum in current page ? Then all Ok if (docnum >= winfirst && docnum <= winlast) { return m_pager->getDoc(docnum, doc); } // Else we accept to page down or up but not further if (docnum < winfirst && docnum >= winfirst - prefs.respagesize) { resultPageBack(); } else if (docnum < winlast + 1 + prefs.respagesize) { resultPageNext(); } winfirst = pageFirstDocNum(); winlast = m_pager->pageLastDocNum(); if (docnum >= winfirst && docnum <= winlast) { return m_pager->getDoc(docnum, doc); } return false; } void ResList::keyPressEvent(QKeyEvent * e) { if ((e->modifiers() & Qt::ShiftModifier)) { if (e->key() == Qt::Key_PageUp) { // Shift-PageUp -> first page of results resultPageFirst(); return; } } else { if (e->key() == Qt::Key_PageUp || e->key() == Qt::Key_Backspace) { resPageUpOrBack(); return; } else if (e->key() == Qt::Key_PageDown || e->key() == Qt::Key_Space) { resPageDownOrNext(); return; } } RESLIST_PARENTCLASS::keyPressEvent(e); } void ResList::mouseReleaseEvent(QMouseEvent *e) { m_lstClckMod = 0; if (e->modifiers() & Qt::ControlModifier) { m_lstClckMod |= Qt::ControlModifier; } if (e->modifiers() & Qt::ShiftModifier) { m_lstClckMod |= Qt::ShiftModifier; } RESLIST_PARENTCLASS::mouseReleaseEvent(e); } void ResList::highlighted(const QString& ) { // This is supposedly called when a link is preactivated (hover or tab // traversal, but is not actually called for tabs. We would have liked to // give some kind of visual feedback for tab traversal } // Page Up/Down: we don't try to check if current paragraph is last or // first. We just page up/down and check if viewport moved. If it did, // fair enough, else we go to next/previous result page. void ResList::resPageUpOrBack() { #if defined(USING_WEBKIT) if (scrollIsAtTop()) { resultPageBack(); runJS("window.scrollBy(0,50000);"); } else { page()->mainFrame()->scroll(0, -int(0.9*geometry().height())); } setupArrows(); #elif defined(USING_WEBENGINE) if (scrollIsAtTop()) { // Displaypage used to call resetview() which caused a page load event. We wanted to run the // js on the second event, with countdown = 1. Not needed any more, but kept around. m_js_countdown = 0; m_js = "window.scrollBy(0,50000);"; resultPageBack(); } else { QString js = QString("window.scrollBy(%1, %2);").arg(0).arg(-int(0.9*geometry().height())); runJS(js); } QTimer::singleShot(50, this, SLOT(setupArrows())); #else int vpos = verticalScrollBar()->value(); verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); if (vpos == verticalScrollBar()->value()) resultPageBack(); #endif } void ResList::resPageDownOrNext() { #if defined(USING_WEBKIT) if (scrollIsAtBottom()) { resultPageNext(); } else { page()->mainFrame()->scroll(0, int(0.9*geometry().height())); } setupArrows(); #elif defined(USING_WEBENGINE) if (scrollIsAtBottom()) { LOGDEB0("downOrNext: at bottom: call resultPageNext\n"); resultPageNext(); } else { LOGDEB0("downOrNext: scroll\n"); QString js = QString("window.scrollBy(%1, %2);").arg(0).arg(int(0.9*geometry().height())); runJS(js); } QTimer::singleShot(50, this, SLOT(setupArrows())); #else int vpos = verticalScrollBar()->value(); verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); LOGDEB("ResList::resPageDownOrNext: vpos before " << vpos << ", after " << verticalScrollBar()->value() << "\n"); if (vpos == verticalScrollBar()->value()) resultPageNext(); #endif } bool ResList::scrollIsAtBottom() { #if defined(USING_WEBKIT) QWebFrame *frame = page()->mainFrame(); bool ret; if (!frame || frame->scrollBarGeometry(Qt::Vertical).isEmpty()) { ret = true; } else { int max = frame->scrollBarMaximum(Qt::Vertical); int cur = frame->scrollBarValue(Qt::Vertical); ret = (max != 0) && (cur == max); LOGDEB2("Scrollatbottom: cur " << cur << " max " << max << "\n"); } LOGDEB2("scrollIsAtBottom: returning " << ret << "\n"); return ret; #elif defined(USING_WEBENGINE) // TLDR: Could not find any way whatsoever to reliably get the page contents size or position // with either signals or direct calls. No obvious way to get this from javascript either. This // used to work, with direct calls and broke down around qt 5.15 QSize wss = size(); //QSize css = page()->contentsSize().toSize(); QSize css = m_contentsize.toSize(); // Does not work with recent (2022) qt releases: always 0 //auto spf = page()->scrollPosition(); // Found no easy way to block waiting for the js output //runJS("document.body.scrollTop", [](const QVariant &v) {qDebug() << v.toInt();}); QPoint sp = m_scrollpos.toPoint(); LOGDEB0("atBottom: contents W " << css.width() << " H " << css.height() << " widget W " << wss.width() << " Y " << wss.height() << " scroll X " << sp.x() << " Y " << sp.y() << "\n"); // This seems to work but it's mysterious as points and pixels // should not be the same return wss.height() + sp.y() >= css.height() - 10; #else return false; #endif } bool ResList::scrollIsAtTop() { #if defined(USING_WEBKIT) QWebFrame *frame = page()->mainFrame(); bool ret; if (!frame || frame->scrollBarGeometry(Qt::Vertical).isEmpty()) { ret = true; } else { int cur = frame->scrollBarValue(Qt::Vertical); int min = frame->scrollBarMinimum(Qt::Vertical); LOGDEB("Scrollattop: cur " << cur << " min " << min << "\n"); ret = (cur == min); } LOGDEB2("scrollIsAtTop: returning " << ret << "\n"); return ret; #elif defined(USING_WEBENGINE) //return page()->scrollPosition().toPoint().ry() == 0; return m_scrollpos.y() == 0; #else return false; #endif } void ResList::setupArrows() { emit prevPageAvailable(m_pager->hasPrev() || !scrollIsAtTop()); emit nextPageAvailable(m_pager->hasNext() || !scrollIsAtBottom()); } // Show previous page of results. We just set the current number back // 2 pages and show next page. void ResList::resultPageBack() { if (m_pager->hasPrev()) { m_pager->resultPageBack(); displayPage(); #ifdef USING_WEBENGINE runJS("window.scrollTo(0,0);"); #endif } } // Go to the first page void ResList::resultPageFirst() { // In case the preference was changed m_pager->setPageSize(prefs.respagesize); m_pager->resultPageFirst(); displayPage(); #ifdef USING_WEBENGINE runJS("window.scrollTo(0,0);"); #endif } // Fill up result list window with next screen of hits void ResList::resultPageNext() { if (m_pager->hasNext()) { m_pager->resultPageNext(); displayPage(); #ifdef USING_WEBENGINE runJS("window.scrollTo(0,0);"); #endif } } void ResList::resultPageFor(int docnum) { m_pager->resultPageFor(docnum); displayPage(); } void ResList::append(const QString &text) { #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) m_text += text; if (m_progress && text.startsWith("

setValue(m_residx++); } #else QTextBrowser::append(text); #endif } void ResList::displayPage() { #if defined(USING_WEBENGINE) || defined(USING_WEBKIT) const static QUrl baseUrl("file:///"); QProgressDialog progress("Generating text snippets...", "", 0, prefs.respagesize, this); m_residx = 0; progress.setWindowModality(Qt::WindowModal); progress.setCancelButton(nullptr); progress.setMinimumDuration(2000); m_progress = &progress; m_text = ""; #else clear(); #endif m_pager->displayPage(theconfig); #if defined(USING_WEBENGINE) || defined(USING_WEBKIT) // webengine from qt 5.15 on won't load local images if the base URL is // not set (previous versions worked with an empty one). Can't hurt anyway. if (m_progress) { m_progress->close(); m_progress = nullptr; } if (m_lasttext == m_text) return; maybeDump(m_text); setHtml(m_text, baseUrl); m_lasttext = m_text; #endif LOGDEB0("ResList::displayPg: hasNext " << m_pager->hasNext() << " atBot " << scrollIsAtBottom() << " hasPrev " << m_pager->hasPrev() << " at Top " << scrollIsAtTop() << " \n"); QTimer::singleShot(100, this, SLOT(setupArrows())); // Possibly color paragraph of current preview if any previewExposed(m_curPvDoc); } // Color paragraph (if any) of currently visible preview void ResList::previewExposed(int docnum) { LOGDEB("ResList::previewExposed: doc " << docnum << "\n"); // Possibly erase old one to white if (m_curPvDoc > -1) { #if defined(USING_WEBKIT) QString sel = QString("div[rcldocnum=\"%1\"]").arg(m_curPvDoc - pageFirstDocNum()); LOGDEB2("Searching for element, selector: [" << qs2utf8s(sel) << "]\n"); QWebElement elt = page()->mainFrame()->findFirstElement(sel); if (!elt.isNull()) { LOGDEB2("Found\n"); elt.removeAttribute("style"); } else { LOGDEB2("Not Found\n"); } #elif defined(USING_WEBENGINE) QString js = QString( "elt=document.getElementById('%1');" "if (elt){elt.removeAttribute('style');}" ).arg(m_curPvDoc - pageFirstDocNum()); runJS(js); #else std::pair blockrange = parnumfromdocnum(m_curPvDoc); if (blockrange.first != -1) { for (int blockn = blockrange.first; blockn < blockrange.second; blockn++) { QTextBlock block = document()->findBlockByNumber(blockn); QTextCursor cursor(block); QTextBlockFormat format = cursor.blockFormat(); format.clearBackground(); cursor.setBlockFormat(format); } } #endif m_curPvDoc = -1; } if ((m_curPvDoc = docnum) < 0) { return; } // Set background for active preview's doc entry #if defined(USING_WEBKIT) QString sel = QString("div[rcldocnum=\"%1\"]").arg(docnum - pageFirstDocNum()); LOGDEB2("Searching for element, selector: [" << qs2utf8s(sel) << "]\n"); QWebElement elt = page()->mainFrame()->findFirstElement(sel); if (!elt.isNull()) { LOGDEB2("Found\n"); elt.setAttribute("style", "background: LightBlue;}"); } else { LOGDEB2("Not Found\n"); } #elif defined(USING_WEBENGINE) QString js = QString( "elt=document.getElementById('%1');" "if(elt){elt.setAttribute('style', 'background: LightBlue');}" ).arg(docnum - pageFirstDocNum()); runJS(js); #else std::pair blockrange = parnumfromdocnum(docnum); // Maybe docnum is -1 or not in this window, if (blockrange.first < 0) return; // Color the new active paragraph QColor color("LightBlue"); for (int blockn = blockrange.first+1; blockn < blockrange.second; blockn++) { QTextBlock block = document()->findBlockByNumber(blockn); QTextCursor cursor(block); QTextBlockFormat format; format.setBackground(QBrush(color)); cursor.mergeBlockFormat(format); setTextCursor(cursor); ensureCursorVisible(); } #endif } // Double click in res list: add selection to simple search void ResList::mouseDoubleClickEvent(QMouseEvent *event) { RESLIST_PARENTCLASS::mouseDoubleClickEvent(event); #if defined(USING_WEBKIT) emit(wordSelect(selectedText())); #elif defined(USING_WEBENGINE) // webengineview does not have such an event function, and // reimplementing event() itself is not useful (tried) as it does // not get mouse clicks. We'd need javascript to do this, but it's // not that useful, so left aside for now. #else if (textCursor().hasSelection()) emit(wordSelect(textCursor().selectedText())); #endif } void ResList::showQueryDetails() { if (!m_source) return; string oq = breakIntoLines(m_source->getDescription(), 100, 50); QString str; QString desc = tr("Result count (est.)") + ": " + str.setNum(m_source->getResCnt()) + "
"; desc += tr("Query details") + ": " + QString::fromUtf8(oq.c_str()); QMessageBox::information(this, tr("Query details"), desc); } void ResList::onLinkClicked(const QUrl &qurl) { // qt5: url.toString() does not accept FullyDecoded, but that's what we // want. e.g. Suggestions links are like Sterm|spelling which we // receive as Sterm%7CSpelling string strurl = url_decode(qs2utf8s(qurl.toString())); // Link prefix remark: it used to be that webengine refused to // acknowledge link clicks on links like "%P1", it needed an // absolute URL like http://localhost/P1. This does not seem to be // the case any more, probably because we now set baseUrl (to fix // icons display which had stopped working). So the linkprefix // thing could probably go away. OTOH, we'd have to substract the // baseUrl because we receive links like baseUrl+P1 instead. LOGDEB1("ResList::onLinkClicked: [" << strurl << "] prefix " << m_pager->linkPrefix() << "\n"); if (m_pager->linkPrefix().size() > 0 && (strurl.size() <= m_pager->linkPrefix().size() || !beginswith(strurl, m_pager->linkPrefix()))) { return; } strurl = strurl.substr(m_pager->linkPrefix().size()); if (strurl.size() == 0) { return; } int docnum{-1}; bool havedoc{false}; Rcl::Doc doc; if (strurl.size() > 1) { // If an integer follows interpret as doc number const char *bptr = strurl.c_str() + 1; char *eptr; docnum = strtol(bptr, &eptr, 10) - 1; if (docnum >= 0) { if (getDoc(docnum, doc)) { havedoc = true; } else { LOGERR("ResList::onLinkClicked: can't get doc for "<< docnum << "\n"); } } } int what = strurl[0]; switch (what) { // Open abstract/snippets window case 'A': { if (!havedoc) return; emit(showSnippets(doc)); } break; // Show duplicates case 'D': { if (!m_source || !havedoc) return; vector dups; if (m_source->docDups(doc, dups) && m_rclmain) { m_rclmain->newDupsW(doc, dups); } } break; // Open parent folder case 'F': { if (!havedoc) return; emit editRequested(ResultPopup::getFolder(doc)); } break; // Show query details case 'h': case 'H': { showQueryDetails(); break; } // Preview and edit case 'P': case 'E': { if (!havedoc) return; if (what == 'P') { if (m_ismainres) { emit docPreviewClicked(docnum, doc, m_lstClckMod); } else { emit previewRequested(doc); } } else { emit editRequested(doc); } } break; // Next/prev page case 'n': resultPageNext(); break; case 'p': resultPageBack(); break; // Run script. Link format Rnn|Script Name case 'R': { if (!havedoc) return; QString s = qurl.toString(); int bar = s.indexOf("|"); if (bar == -1 || bar >= s.size()-1) break; string cmdname = qs2utf8s(s.right(s.size() - (bar + 1))); DesktopDb ddb(path_cat(theconfig->getConfDir(), "scripts")); DesktopDb::AppDef app; if (ddb.appByName(cmdname, app)) { QAction act(QString::fromUtf8(app.name.c_str()), this); QVariant v(QString::fromUtf8(app.command.c_str())); act.setData(v); m_popDoc = docnum; menuOpenWith(&act); } } break; // Spelling: replacement suggestion clicked case 'S': { string s; if (!strurl.empty()) s = strurl.substr(1); string::size_type bar = s.find_first_of("|"); if (bar != string::npos && bar < s.size() - 1) { string o = s.substr(0, bar); string n = s.substr(bar+1); LOGDEB2("Emitting wordreplace " << o << " -> " << n << std::endl); emit wordReplace(u8s2qs(o), u8s2qs(n)); } } break; default: LOGERR("ResList::onLinkClicked: bad link [" << strurl.substr(0,20) << "]\n"); break; } } void ResList::onPopupJsDone(const QVariant &jr) { QString qs(jr.toString()); LOGDEB("onPopupJsDone: parameter: " << qs2utf8s(qs) << "\n"); #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) auto skipflags = Qt::SkipEmptyParts; #else auto skipflags = QString::SkipEmptyParts; #endif QStringList qsl = qs.split("\n", skipflags); for (int i = 0 ; i < qsl.size(); i++) { int eq = qsl[i].indexOf("="); if (eq > 0) { QString nm = qsl[i].left(eq).trimmed(); QString value = qsl[i].right(qsl[i].size() - (eq+1)).trimmed(); if (!nm.compare("rcldocnum")) { m_popDoc = pageFirstDocNum() + value.toInt(); } else { LOGERR("onPopupJsDone: unknown key: " << qs2utf8s(nm) << "\n"); } } } doCreatePopupMenu(); } void ResList::createPopupMenu(const QPoint& pos) { LOGDEB("ResList::createPopupMenu(" << pos.x() << ", " << pos.y() << ")\n"); m_popDoc = -1; m_popPos = pos; #if defined(USING_WEBKIT) QWebHitTestResult htr = page()->mainFrame()->hitTestContent(pos); if (htr.isNull()) return; QWebElement el = htr.enclosingBlockElement(); while (!el.isNull() && !el.hasAttribute("rcldocnum")) el = el.parent(); if (el.isNull()) return; QString snum = el.attribute("rcldocnum"); m_popDoc = pageFirstDocNum() + snum.toInt(); #elif defined(USING_WEBENGINE) QString js("window.locDetails;"); RclWebPage *mypage = dynamic_cast(page()); mypage->runJavaScript(js, [this](const QVariant &v) {onPopupJsDone(v);}); #else QTextCursor cursor = cursorForPosition(pos); int blocknum = cursor.blockNumber(); LOGDEB("ResList::createPopupMenu(): block " << blocknum << "\n"); m_popDoc = docnumfromparnum(blocknum); #endif doCreatePopupMenu(); } void ResList::doCreatePopupMenu() { if (m_popDoc < 0) return; Rcl::Doc doc; if (!getDoc(m_popDoc, doc)) return; int options = ResultPopup::showSaveOne; if (m_ismainres) options |= ResultPopup::isMain; QMenu *popup = ResultPopup::create(this, options, m_source, doc); popup->popup(mapToGlobal(m_popPos)); } void ResList::menuPreview() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) { if (m_ismainres) { emit docPreviewClicked(m_popDoc, doc, 0); } else { emit previewRequested(doc); } } } void ResList::menuSaveToFile() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) emit docSaveToFileClicked(doc); } void ResList::menuPreviewParent() { Rcl::Doc doc; if (getDoc(m_popDoc, doc) && m_source) { Rcl::Doc pdoc = ResultPopup::getParent(m_source, doc); if (pdoc.mimetype == "inode/directory") { emit editRequested(pdoc); } else { emit previewRequested(pdoc); } } } void ResList::menuOpenParent() { Rcl::Doc doc; if (getDoc(m_popDoc, doc) && m_source) { Rcl::Doc pdoc = ResultPopup::getParent(m_source, doc); if (!pdoc.url.empty()) { emit editRequested(pdoc); } } } void ResList::menuOpenFolder() { Rcl::Doc doc; if (getDoc(m_popDoc, doc) && m_source) { Rcl::Doc pdoc = ResultPopup::getFolder(doc); if (!pdoc.url.empty()) { emit editRequested(pdoc); } } } void ResList::menuShowSnippets() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) emit showSnippets(doc); } void ResList::menuShowSubDocs() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) emit showSubDocs(doc); } void ResList::menuEdit() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) emit editRequested(doc); } void ResList::menuOpenWith(QAction *act) { if (act == 0) return; string cmd = qs2utf8s(act->data().toString()); Rcl::Doc doc; if (getDoc(m_popDoc, doc)) emit openWithRequested(doc, cmd); } void ResList::menuCopyFN() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) ResultPopup::copyFN(doc); } void ResList::menuCopyPath() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) ResultPopup::copyPath(doc); } void ResList::menuCopyText() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) ResultPopup::copyText(doc, m_rclmain); } void ResList::menuCopyURL() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) ResultPopup::copyURL(doc); } void ResList::menuExpand() { Rcl::Doc doc; if (getDoc(m_popDoc, doc)) emit docExpand(doc); } int ResList::pageFirstDocNum() { return m_pager->pageFirstDocNum(); } recoll-1.36.1/qtgui/rclm_wins.cpp0000644000175000017500000003634314473317624013661 00000000000000/* Copyright (C) 2005-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include "log.h" #include "internfile.h" #include "listdialog.h" #include "confgui/confguiindex.h" #include "idxsched.h" #ifdef _WIN32 #include "winschedtool.h" #else #include "crontool.h" #include "rtitool.h" #endif #include "snippets_w.h" #include "fragbuts.h" #include "specialindex.h" #include "rclmain_w.h" #include "webcache.h" #include "restable.h" #include "actsearch_w.h" #include "docseqdocs.h" using namespace std; static const QKeySequence quitKeySeq("Ctrl+q"); static const QKeySequence closeKeySeq("Ctrl+w"); // Open advanced search dialog. void RclMain::showAdvSearchDialog() { if (asearchform == 0) { asearchform = new AdvSearch(0); if (asearchform == 0) { return; } connect(new QShortcut(quitKeySeq, asearchform), SIGNAL (activated()), this, SLOT (fileExit())); connect(asearchform, SIGNAL(startSearch(std::shared_ptr, bool)), this, SLOT(startSearch(std::shared_ptr, bool))); connect(asearchform, SIGNAL(setDescription(QString)), this, SLOT(onSetDescription(QString))); asearchform->show(); } else { // Close and reopen, in hope that makes us visible... asearchform->close(); asearchform->show(); } } void RclMain::showSpellDialog() { if (spellform == 0) { spellform = new SpellW(0); connect(new QShortcut(quitKeySeq, spellform), SIGNAL (activated()), this, SLOT (fileExit())); connect(spellform, SIGNAL(wordSelect(QString)), sSearch, SLOT(addTerm(QString))); spellform->show(); } else { // Close and reopen, in hope that makes us visible... spellform->close(); spellform->show(); } } void RclMain::showWebcacheDialog() { switch (indexerState()) { case RclMain::IXST_UNKNOWN: QMessageBox::warning(0, "Recoll", tr("Unknown indexer state. " "Can't access webcache file.")); return; case RclMain::IXST_RUNNINGMINE: case RclMain::IXST_RUNNINGNOTMINE: QMessageBox::warning(0, "Recoll", tr("Indexer is running. " "Can't access webcache file.")); return; case RclMain::IXST_NOTRUNNING: break; } if (!m_pidfile) { m_pidfile = new Pidfile(theconfig->getPidfile()); if (m_pidfile->open() != 0) { deleteZ(m_pidfile); return; } if (m_pidfile->write_pid() != 0) { deleteZ(m_pidfile); return; } } if (webcache == 0) { webcache = new WebcacheEdit(this); webcache->setAttribute(Qt::WA_DeleteOnClose); connect(new QShortcut(quitKeySeq, webcache), SIGNAL (activated()), this, SLOT (fileExit())); connect(webcache, SIGNAL(destroyed(QObject*)), this, SLOT(onWebcacheDestroyed(QObject*)) ); webcache->show(); } } void RclMain::onWebcacheDestroyed(QObject *) { deleteZ(m_pidfile); webcache = 0; } void RclMain::showIndexStatistics() { showSpellDialog(); if (spellform == 0) return; spellform->setMode(SpellW::TYPECMB_STATS); } void RclMain::showFragButs() { if (fragbuts && fragbuts->isStale(0)) { deleteZ(fragbuts); } if (fragbuts == 0) { fragbuts = new FragButs(0); if (fragbuts->ok()) { fragbuts->show(); connect(new QShortcut(quitKeySeq, fragbuts), SIGNAL (activated()), this, SLOT (fileExit())); connect(new QShortcut(closeKeySeq, fragbuts), SIGNAL (activated()), fragbuts, SLOT(close())); connect(fragbuts, SIGNAL(fragmentsChanged()), this, SLOT(onFragmentsChanged())); } else { deleteZ(fragbuts); } } else { // Close and reopen, in hope that makes us visible... fragbuts->close(); fragbuts->show(); } } void RclMain::showSpecIdx() { if (specidx == 0) { specidx = new SpecIdxW(0); connect(specidx, SIGNAL(accepted()), this, SLOT(specialIndex())); specidx->show(); } else { // Close and reopen, in hope that makes us visible... specidx->close(); specidx->show(); } } void RclMain::showIndexConfig() { showIndexConfig(false); } void RclMain::execIndexConfig() { showIndexConfig(true); } void RclMain::showIndexConfig(bool modal) { LOGDEB("showIndexConfig()\n" ); bool created{false}; if (indexConfig == 0) { created = true; indexConfig = new ConfIndexW(0, theconfig); } indexConfig->showPrefs(modal); if (created) { connect(new QShortcut(quitKeySeq, indexConfig->getDialog()), SIGNAL (activated()), this, SLOT (fileExit())); } } void RclMain::showIndexSched() { showIndexSched(false); } void RclMain::execIndexSched() { showIndexSched(true); } void RclMain::showIndexSched(bool modal) { LOGDEB("showIndexSched()\n" ); if (indexSched == 0) { indexSched = new IdxSchedW(this); connect(new QShortcut(quitKeySeq, indexSched), SIGNAL (activated()), this, SLOT (fileExit())); #ifdef _WIN32 indexSched->cronCLB->setText(tr("Batch scheduling")); indexSched->cronCLB->setDescription( tr("The tool will let you decide at what time indexing should run. " " It uses the Windows task scheduler.")); indexSched->mainExplainLBL->hide(); indexSched->rtidxCLB->hide(); #endif connect(indexSched->cronCLB, SIGNAL(clicked()), this, SLOT(execCronTool())); if (theconfig && theconfig->isDefaultConfig()) { #ifdef RCL_MONITOR connect(indexSched->rtidxCLB, SIGNAL(clicked()), this, SLOT(execRTITool())); #else indexSched->rtidxCLB->setEnabled(false); indexSched->rtidxCLB->setToolTip(tr("Disabled because the real time indexer was not compiled in.")); #endif } else { indexSched->rtidxCLB->setEnabled(false); indexSched->rtidxCLB->setToolTip(tr("This configuration tool only works for the main index.")); } } else { // Close and reopen, in hope that makes us visible... indexSched->close(); } if (modal) { indexSched->exec(); indexSched->setModal(false); } else { indexSched->show(); } } void RclMain::showCronTool() { showCronTool(false); } void RclMain::execCronTool() { showCronTool(true); } void RclMain::showCronTool(bool modal) { LOGDEB("showCronTool()\n" ); if (cronTool == 0) { #ifdef _WIN32 cronTool = new WinSchedToolW(0); #else cronTool = new CronToolW(0); #endif connect(new QShortcut(quitKeySeq, cronTool), SIGNAL (activated()), this, SLOT (fileExit())); } else { // Close and reopen, in hope that makes us visible... cronTool->close(); } if (modal) { cronTool->exec(); cronTool->setModal(false); } else { cronTool->show(); } } void RclMain::showRTITool() { showRTITool(false); } void RclMain::execRTITool() { showRTITool(true); } void RclMain::showRTITool(bool modal) { #ifndef _WIN32 LOGDEB("showRTITool()\n" ); if (rtiTool == 0) { rtiTool = new RTIToolW(0); connect(new QShortcut(quitKeySeq, rtiTool), SIGNAL (activated()), this, SLOT (fileExit())); } else { // Close and reopen, in hope that makes us visible... rtiTool->close(); } if (modal) { rtiTool->exec(); rtiTool->setModal(false); } else { rtiTool->show(); } #else PRETEND_USE(modal); #endif } void RclMain::showUIPrefs() { prefs.useTmpActiveExtraDbs = false; prefs.tmpActiveExtraDbs.clear(); if (uiprefs == 0) { uiprefs = new UIPrefsDialog(this); connect(new QShortcut(quitKeySeq, uiprefs), SIGNAL (activated()), this, SLOT (fileExit())); connect(uiprefs, SIGNAL(uiprefsDone()), this, SLOT(setUIPrefs())); connect(this, SIGNAL(stemLangChanged(const QString&)), uiprefs, SLOT(setStemLang(const QString&))); } else { // Close and reopen, in hope that makes us visible... uiprefs->close(); rwSettings(false); uiprefs->setFromPrefs(); } uiprefs->show(); } void RclMain::showExtIdxDialog() { showUIPrefs(); uiprefs->tabWidget->setCurrentIndex(5); } void RclMain::showAboutDialog() { QString vstring = QString("") + u8s2qs(prefs.htmlHeaderContents()) + "" + u8s2qs(Rcl::version_string()) + "
www.recoll.org" + "
www.xapian.org"; QMessageBox::information(this, tr("About Recoll"), vstring); } void RclMain::showMissingHelpers() { string miss; if (!theconfig->getMissingHelperDesc(miss)) { QMessageBox::information( this, "", tr("No information: initial indexing not yet performed.")); return; } QString msg = QString::fromUtf8("

") + tr("External applications/commands needed for your file types " "and not found, as stored by the last indexing pass in "); msg += ""; msg += path2qs(theconfig->getConfDir()); msg += "/missing:

\n";
    if (!miss.empty()) {
        msg += QString::fromUtf8(miss.c_str());
    } else {
        msg += tr("No helpers found missing");
    }
    msg += "
"; QMessageBox::information(this, tr("Missing helper programs"), msg); } void RclMain::showActiveTypes() { string reason; bool maindberror; if (!maybeOpenDb(reason, true, &maindberror)) { QMessageBox::warning(0, tr("Error"), u8s2qs(reason), QMessageBox::Ok, QMessageBox::NoButton); return; } // All mime types in index. vector vdbtypes; if (!rcldb->getAllDbMimeTypes(vdbtypes)) { QMessageBox::warning(0, tr("Error"), tr("Index query error"), QMessageBox::Ok, QMessageBox::NoButton); return; } set mtypesfromdb; mtypesfromdb.insert(vdbtypes.begin(), vdbtypes.end()); // All types listed in mimeconf: vector mtypesfromconfig = theconfig->getAllMimeTypes(); // Intersect file system types with config types (those not in the // config can be indexed by name, not by content) set mtypesfromdbconf; for (vector::const_iterator it = mtypesfromconfig.begin(); it != mtypesfromconfig.end(); it++) { if (mtypesfromdb.find(*it) != mtypesfromdb.end()) mtypesfromdbconf.insert(*it); } // Substract the types for missing helpers (the docs are indexed // by name only): string miss; if (theconfig->getMissingHelperDesc(miss) && !miss.empty()) { FIMissingStore st(miss); map >::const_iterator it; for (it = st.m_typesForMissing.begin(); it != st.m_typesForMissing.end(); it++) { set::const_iterator it1; for (it1 = it->second.begin(); it1 != it->second.end(); it1++) { set::iterator it2 = mtypesfromdbconf.find(*it1); if (it2 != mtypesfromdbconf.end()) mtypesfromdbconf.erase(it2); } } } ListDialog dialog; dialog.setWindowTitle(tr("Indexed MIME Types")); // Turn the result into a string and display dialog.groupBox->setTitle(tr("Content has been indexed for these MIME types:")); // We replace the list with an editor so that the user can copy/paste deleteZ(dialog.listWidget); QTextEdit *editor = new QTextEdit(dialog.groupBox); editor->setReadOnly(true); dialog.horizontalLayout->addWidget(editor); if (mtypesfromdbconf.empty()) { editor->append(tr("Types list empty: maybe wait for indexing to " "progress?")); } else { for (set::const_iterator it = mtypesfromdbconf.begin(); it != mtypesfromdbconf.end(); it++) { editor->append(QString::fromUtf8(it->c_str())); } } editor->moveCursor(QTextCursor::Start); editor->ensureCursorVisible(); dialog.exec(); } void RclMain::newDupsW(const Rcl::Doc, const vector dups) { if (nullptr == m_dupsw) { m_dupsw = new ResTable(nullptr, {"ipath", "url"}); m_dupsw->setRclMain(this, false); } auto src = std::make_shared(rcldb, dups, qs2utf8s(tr("Duplicates"))); src->setDescription(qs2utf8s(tr("Duplicates"))); auto source = std::make_shared(theconfig, src); m_dupsw->setDocSource(source); m_dupsw->readDocSource(); m_dupsw->show(); } void RclMain::showSnippets(Rcl::Doc doc) { if (!m_source) return; if (!m_snippets) { m_snippets = new SnippetsW(doc, m_source); connect(m_snippets, SIGNAL(startNativeViewer(Rcl::Doc, int, QString, int)), this, SLOT(startNativeViewer(Rcl::Doc, int, QString, int))); connect(m_snippets, SIGNAL(zoomIn()), this, SLOT(zoomIn())); connect(m_snippets, SIGNAL(zoomOut()), this, SLOT(zoomOut())); connect(this, SIGNAL(uiPrefsChanged()), m_snippets, SLOT(onUiPrefsChanged())); connect(new QShortcut(quitKeySeq, m_snippets), SIGNAL (activated()), this, SLOT (fileExit())); connect(new QShortcut(closeKeySeq, m_snippets), SIGNAL (activated()), m_snippets, SLOT (close())); if (restable) { connect( restable, SIGNAL(detailDocChanged(Rcl::Doc, std::shared_ptr)), m_snippets, SLOT(onSetDoc(Rcl::Doc, std::shared_ptr))); } } else { m_snippets->onSetDoc(doc, m_source); } m_snippets->show(); } void RclMain::showActionsSearch() { if (nullptr == actsearchw) { actsearchw = new ActSearchW(this); actsearchw->setActList(findChildren()); connect(actsearchw->actCMB, SIGNAL(editTextChanged(const QString&)), actsearchw, SLOT(onTextChanged(const QString&))); } actsearchw->actCMB->setCurrentIndex(-1); actsearchw->actCMB->clearEditText(); actsearchw->show(); } recoll-1.36.1/qtgui/systray.cpp0000644000175000017500000000417514410615043013363 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include "systray.h" #include "rclmain_w.h" #include "log.h" void RclTrayIcon::init() { QAction *restoreAction = new QAction(tr("Restore"), this); QAction *quitAction = new QAction(tr("Quit"), this); connect(restoreAction, SIGNAL(triggered()), this, SLOT(onRestore())); connect(quitAction, SIGNAL(triggered()), m_mainw, SLOT(fileExit())); QMenu *trayIconMenu = new QMenu(0); trayIconMenu->addAction(restoreAction); trayIconMenu->addAction(quitAction); setContextMenu(trayIconMenu); connect(this, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(onActivated(QSystemTrayIcon::ActivationReason))); } void RclTrayIcon::onRestore() { // Hide and show to restore on current desktop m_mainw->hide(); switch (prefs.showmode) { case PrefsPack::SHOW_NORMAL: m_mainw->show(); break; case PrefsPack::SHOW_MAX: m_mainw->showMaximized(); break; case PrefsPack::SHOW_FULL: m_mainw->showFullScreen(); break; } } void RclTrayIcon::onActivated(QSystemTrayIcon::ActivationReason reason) { LOGDEB("RclTrayIcon::onActivated: reason " << reason << std::endl); switch (reason) { case QSystemTrayIcon::DoubleClick: case QSystemTrayIcon::Trigger: case QSystemTrayIcon::MiddleClick: onRestore(); break; default: return; } } recoll-1.36.1/qtgui/crontool.ui0000644000175000017500000001573714410615043013345 00000000000000 CronToolW 0 0 508 416 Cron Dialog 0 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Recoll</span> batch indexing schedule (cron) </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each field can contain a wildcard (*), a single numeric value, comma-separated lists (1,3,5) and ranges (1-7). More generally, the fields will be used <span style=" font-style:italic;">as is</span> inside the crontab file, and the full crontab syntax can be used, see crontab(5).</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />For example, entering <span style=" font-family:'Courier New,courier';">*</span> in <span style=" font-style:italic;">Days, </span><span style=" font-family:'Courier New,courier';">12,19</span> in <span style=" font-style:italic;">Hours</span> and <span style=" font-family:'Courier New,courier';">15</span> in <span style=" font-style:italic;">Minutes</span> would start recollindex every day at 12:15 AM and 7:15 PM</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A schedule with very frequent activations is probably less efficient than real time indexing.</p></body></html> true Days of week (* or 0-7, 0 or 7 is Sunday) Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Hours (* or 0-23) Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Minutes (0-59) Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Click <span style=" font-style:italic;">Disable</span> to stop automatic batch indexing, <span style=" font-style:italic;">Enable</span> to activate it, <span style=" font-style:italic;">Cancel</span> to change nothing.</p></body></html> true Qt::Horizontal QDialogButtonBox::Cancel buttonBox accepted() CronToolW accept() 248 254 157 274 buttonBox rejected() CronToolW reject() 316 260 286 274 recoll-1.36.1/qtgui/crontool.cpp0000644000175000017500000000765414427373216013524 00000000000000/* Copyright (C) 2005-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include "recoll.h" #include "crontool.h" #include "ecrontab.h" #include "smallut.h" #include "rclutil.h" #include "pathut.h" using std::string; using std::vector; using std::map; using std::list; static string marker; static string idstring(const string& confdir) { // Quote conf dir, there may be spaces and whatelse in there return string("RECOLL_CONFDIR=") + escapeShell(confdir); } void CronToolW::init() { marker = "RCLCRON_RCLINDEX="; enableButton = new QPushButton(tr("Enable")); disableButton = new QPushButton(tr("Disable")); buttonBox->addButton(enableButton, QDialogButtonBox::ActionRole); buttonBox->addButton(disableButton, QDialogButtonBox::ActionRole); connect(enableButton, SIGNAL(clicked()), this, SLOT(enableCron())); connect(disableButton, SIGNAL(clicked()), this, SLOT(disableCron())); // Try to read the current values if (!theconfig) return; if (checkCrontabUnmanaged(marker, "recollindex")) { QMessageBox::warning( 0, "Recoll", tr("It seems that manually edited entries exist for " "recollindex, cannot edit crontab")); QTimer::singleShot(0, this, SLOT(close())); } string id = idstring(theconfig->getConfDir()); vector sched; if (getCrontabSched(marker, id, sched)) { minsLE->setText(QString::fromUtf8(sched[0].c_str())); hoursLE->setText(QString::fromUtf8(sched[1].c_str())); daysLE->setText(QString::fromUtf8(sched[4].c_str())); } } void CronToolW::enableCron() { changeCron(true); } void CronToolW::disableCron() { changeCron(false); } void CronToolW::changeCron(bool enable) { if (!theconfig) return; string id = idstring(theconfig->getConfDir()); string cmd("recollindex"); #ifdef __APPLE__ // The MACPORTS and HOMEBREW flags are set by the resp. portfile // and recipee. Adjust the path for finding recollindex accordingly #if defined(MACPORTS) cmd = string("PATH=/opt/local/bin/:$PATH ") + cmd; #elif defined(HOMEBREW) cmd = string("PATH=/opt/homebrew/bin:/usr/local/bin/:$PATH ") + cmd; #else // Built as a bundle. We add the binary location to the PATH. This is a bit ridiculous because // path_pkgdatadir() actually computes the location from the recoll exe but whatever... auto bindir = path_cat(path_getfather(path_pkgdatadir()), "MacOS"); cmd = string("PATH=") + bindir + string(":$PATH ") + cmd; #endif #endif string reason; if (!enable) { editCrontab(marker, id, "", "", reason); accept(); } else { string mins(qs2utf8s(minsLE->text().remove(QChar(' ')))); string hours(qs2utf8s(hoursLE->text().remove(QChar(' ')))); string days(qs2utf8s(daysLE->text().remove(QChar(' ')))); string sched = mins + " " + hours + " * * " + days; if (editCrontab(marker, id, sched, cmd, reason)) { accept(); } else { QMessageBox::warning( 0, "Recoll", tr("Error installing cron entry. Bad syntax in fields ?")); } } } recoll-1.36.1/qtgui/scbase.h0000644000175000017500000001114314427373216012556 00000000000000/* Copyright (C) 2021 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SCBASE_H_INCLUDED_ #define _SCBASE_H_INCLUDED_ #include #include #include #include /** Shortcuts storage classe. Singleton. * * Manage settings storage for key sequences shortcuts. * Each shortcut is defined by 4 strings: * - Context (e.g. "Main Window"). * - Description (e.g. "Move focus to search entry"). * - Current value, possibly changed by user, e.g. "Ctrl+l". * - Default value. * * The customised values are read from the stored settings by the SCBase * constructor. * The entries with default values are created from the init() method * of each class responsible for a context (e.g. RclMain, SnippetsW), * or from a static method for classes which are not instantiated when * the program starts up. * * Macros are provided for actually creating the shortcuts in the * init() routines, or for just creating the default entries (for use * in the preferences screen). */ class SCBase : public QObject { Q_OBJECT public: ~SCBase(); SCBase(const SCBase&) = delete; SCBase& operator=(const SCBase&) = delete; /* Return a reference to the instantiated singleton */ static SCBase& scBase(); /** Get the current keysequence for the shortcut. If the entry was not * created from the settings, create it with the default * sequence. This is called from the context classes and returns * either the default or the customised sequence. */ QKeySequence get(const QString& id, const QString& context, const QString& description, const QString& defkeyseq); /** Set a customised value for the designated shortcut. Called * from the preference code. */ void set(const QString& id, const QString& context, const QString& description, const QString& keyseq); /** Return a list of all shortcuts. This is used to create the * preferences table. Each entry in the list is a string * tuple: id, context, description, value, default */ QStringList getAll(); /** Return a list of all shortcuts, with only default values (no settings). * Used for resetting the defaults, especially if a lang changed * has messed up the keys */ QStringList getAllDefaults(); /** Store the customised values to the settings storage. Called * from the preferences accept() method. */ void store(); class Internal; signals: /** Preference change has been accepted and client classes should * update their shortcuts */ void shortcutsChanged(); private: Internal *m{nullptr}; SCBase(); }; /** This can be used in the client class init method, to actually * create and connect the shortcuts. */ #define SETSHORTCUT(OBJ, ID, CTXT, DESCR, SEQ, FLD, SLTFUNC) \ do { \ QKeySequence ks = SCBase::scBase().get(ID, CTXT, DESCR, SEQ); \ if (!ks.isEmpty()) { \ delete FLD; \ FLD = new QShortcut(ks, OBJ, SLOT(SLTFUNC())); \ } \ } while (false); /** This can be used from a static method, to be called by the program * initialisation, for classes which are not instantiated at startup, * and so that the shortcuts are available for the preferences * customisation screen. Same param list as SETSHORTCUT to make it * easy to duplicate a list of ones into the other, even if some * parameters are not used here. */ #define LISTSHORTCUT(OBJ, ID, CTXT, DESCR, SEQ, FLD, SLTFUNC) \ do { \ SCBase::scBase().get(ID, CTXT, DESCR, SEQ); \ } while (false); #endif /* _SCBASE_H_INCLUDED_ */ recoll-1.36.1/qtgui/advsearch.ui0000644000175000017500000005131314427373216013447 00000000000000 AdvSearchBase 0 0 544 536 Advanced search true 0 Find 2 0 0 All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. QFrame::NoFrame QFrame::Plain Search for <br>documents<br>satisfying: false 4 8 0 All non empty fields on the right will be combined with AND ("All clauses" choice) or OR ("Any clause" choice) conjunctions. <br>"Any" "All" and "None" field types can accept a mix of simple words, and phrases enclosed in double quotes.<br>Fields with no data are ignored. Qt::Vertical QSizePolicy::Expanding 0 0 0 0 Delete clause false 0 0 Add clause false QFrame::StyledPanel QFrame::Plain 1 0 0 0 0 QFrame::HLine QFrame::Plain Filter 1 0 Check this to enable filtering on dates Filter dates From false To false QFrame::HLine QFrame::Sunken 1 0 Check this to enable filtering on dates Filter birth dates From false To false QFrame::HLine QFrame::Sunken 1 0 Check this to enable filtering on sizes Filter sizes Minimum size. You can use k/K,m/M,g/G as multipliers Min. Size false Maximum size. You can use k/K,m/M,g/G as multipliers Max. Size false QFrame::HLine QFrame::Sunken 1 0 Check this to enable filtering on file types Restrict file types false 1 0 Check this to use file categories instead of raw mime types By categories false Save as default false 0 Searched file types false false 200 20 QAbstractItemView::ExtendedSelection 0 false All ----> false false Sel -----> false false <----- Sel false false <----- All false 0 Ignored file types false false 200 20 QAbstractItemView::ExtendedSelection QFrame::HLine QFrame::Sunken 8 0 300 0 Enter top directory for search true 20 QComboBox::NoInsert false Browse false Restrict results to files in subtree: false Invert Start Search Close false recoll-1.36.1/qtgui/preview_load.h0000644000175000017500000000330514427373216013777 00000000000000/* Copyright (C) 2015 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _PVW_LOAD_H_INCLUDED_ #define _PVW_LOAD_H_INCLUDED_ #include #include #include "rcldoc.h" #include "pathut.h" #include "rclutil.h" #include "rclconfig.h" #include "internfile.h" /* * A thread to perform the file reading / format conversion work for preview */ class LoadThread : public QThread { Q_OBJECT public: LoadThread(RclConfig *conf, const Rcl::Doc& idoc, bool pvhtml, QObject *parent = 0); virtual ~LoadThread() {} LoadThread(const LoadThread&) = delete; LoadThread& operator=(const LoadThread&) = delete; virtual void run(); public: // The results are returned through public members. int status; Rcl::Doc fdoc; TempFile tmpimg; std::string missing; FileInterner::ErrorPossibleCause explain{FileInterner::InternfileOther}; private: Rcl::Doc m_idoc; bool m_previewHtml; RclConfig m_config; }; #endif /* _PVW_LOAD_H_INCLUDED_ */ recoll-1.36.1/qtgui/uiprefs_w.cpp0000644000175000017500000006624114473323007013657 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "safesysstat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "recoll.h" #include "guiutils.h" #include "rclconfig.h" #include "pathut.h" #include "uiprefs_w.h" #include "viewaction_w.h" #include "log.h" #include "editdialog.h" #include "rclmain_w.h" #include "ptrans_w.h" #include "scbase.h" #include "rclhelp.h" using std::string; using std::vector; using std::map; using std::list; void UIPrefsDialog::init() { // See enum above and keep in order ! ssearchTypCMB->addItem(tr("Any term")); ssearchTypCMB->addItem(tr("All terms")); ssearchTypCMB->addItem(tr("File name")); ssearchTypCMB->addItem(tr("Query language")); ssearchTypCMB->addItem(tr("Value from previous program exit")); connect(viewActionPB, SIGNAL(clicked()), this, SLOT(showViewAction())); connect(reslistFontPB, SIGNAL(clicked()), this, SLOT(showFontDialog())); connect(resetFontPB, SIGNAL(clicked()), this, SLOT(resetReslistFont())); connect(stylesheetPB, SIGNAL(clicked()),this, SLOT(showStylesheetDialog())); connect(resetSSPB, SIGNAL(clicked()), this, SLOT(resetStylesheet())); connect(darkSSPB, SIGNAL(clicked()), this, SLOT(setDarkMode())); connect(snipCssPB, SIGNAL(clicked()),this, SLOT(showSnipCssDialog())); connect(synFilePB, SIGNAL(clicked()),this, SLOT(showSynFileDialog())); connect(resetSnipCssPB, SIGNAL(clicked()), this, SLOT(resetSnipCss())); connect(idxLV, SIGNAL(itemSelectionChanged()), this, SLOT(extradDbSelectChanged())); connect(ptransPB, SIGNAL(clicked()), this, SLOT(extraDbEditPtrans())); connect(addExtraDbPB, SIGNAL(clicked()), this, SLOT(addExtraDbPB_clicked())); connect(delExtraDbPB, SIGNAL(clicked()), this, SLOT(delExtraDbPB_clicked())); connect(togExtraDbPB, SIGNAL(clicked()), this, SLOT(togExtraDbPB_clicked())); connect(actAllExtraDbPB, SIGNAL(clicked()), this, SLOT(actAllExtraDbPB_clicked())); connect(unacAllExtraDbPB, SIGNAL(clicked()), this, SLOT(unacAllExtraDbPB_clicked())); connect(CLEditPara, SIGNAL(clicked()), this, SLOT(editParaFormat())); connect(CLEditHeader, SIGNAL(clicked()), this, SLOT(editHeaderText())); connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept())); connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); connect(buildAbsCB, SIGNAL(toggled(bool)), replAbsCB, SLOT(setEnabled(bool))); connect(autoSpellCB, SIGNAL(toggled(bool)), autoSpellMaxDistSB, SLOT(setEnabled(bool))); connect(ssNoCompleteCB, SIGNAL(toggled(bool)), ssSearchOnCompleteCB, SLOT(setDisabled(bool))); connect(ssNoCompleteCB, SIGNAL(toggled(bool)), showcompleterhitcountsCB, SLOT(setDisabled(bool))); connect(ssNoCompleteCB, SIGNAL(toggled(bool)), ssearchCompleterHistCntSB, SLOT(setDisabled(bool))); connect(resetscPB, SIGNAL(clicked()), this, SLOT(resetShortcuts())); (void)new HelpClient(this); HelpClient::installMap("tab_shortcuts", "RCL.SEARCH.GUI.SHORTCUTS"); setFromPrefs(); } // Update dialog state from stored prefs void UIPrefsDialog::setFromPrefs() { // Most values are stored in the prefs struct. Some rarely used // ones go directly through the settings QSettings settings; // Entries per result page spinbox pageLenSB->setValue(prefs.respagesize); idxTreeDepthSB->setValue(prefs.idxFilterTreeDepth); maxHistSizeSB->setValue(prefs.historysize); collapseDupsCB->setChecked(prefs.collapseDuplicates); maxHLTSB->setValue(prefs.maxhltextkbs); if (prefs.ssearchTypSav) { ssearchTypCMB->setCurrentIndex(4); } else { ssearchTypCMB->setCurrentIndex(prefs.ssearchTyp); } switch (prefs.filterCtlStyle) { case PrefsPack::FCS_MN: filterMN_RB->setChecked(1); break; case PrefsPack::FCS_CMB: filterCMB_RB->setChecked(1); break; case PrefsPack::FCS_BT: default: filterBT_RB->setChecked(1); break; } noBeepsCB->setChecked(prefs.noBeeps); ssNoCompleteCB->setChecked(prefs.ssearchNoComplete); showcompleterhitcountsCB->setChecked(prefs.showcompleterhitcounts); ssSearchOnCompleteCB->setChecked(prefs.ssearchStartOnComplete); ssSearchOnCompleteCB->setEnabled(!prefs.ssearchNoComplete); syntlenSB->setValue(prefs.syntAbsLen); syntctxSB->setValue(prefs.syntAbsCtx); initStartAdvCB->setChecked(prefs.startWithAdvSearchOpen); keepSortCB->setChecked(prefs.keepSort); noToolbarsCB->setChecked(prefs.noToolbars); noClearSearchCB->setChecked(prefs.noClearSearch); noStatusBarCB->setChecked(prefs.noStatusBar); noMenuBarCB->setChecked(prefs.noMenuBar); noSSTypCMBCB->setChecked(prefs.noSSTypCMB); restabShowTxtNoShiftRB->setChecked(prefs.resTableTextNoShift); restabShowTxtShiftRB->setChecked(!prefs.resTableTextNoShift); resTableNoHoverMetaCB->setChecked(prefs.resTableNoHoverMeta); noResTableHeaderCB->setChecked(prefs.noResTableHeader); showResTableVHeaderCB->setChecked(prefs.showResTableVHeader); noRowJumpShortcutsCB->setChecked(prefs.noResTableRowJumpSC); showTrayIconCB->setChecked(prefs.showTrayIcon); if (!prefs.showTrayIcon) { prefs.closeToTray = false; prefs.trayMessages = false; } closeToTrayCB->setEnabled(showTrayIconCB->checkState()); trayMessagesCB->setEnabled(showTrayIconCB->checkState()); closeToTrayCB->setChecked(prefs.closeToTray); trayMessagesCB->setChecked(prefs.trayMessages); wholeuiscaleSB->setValue(prefs.wholeuiscale); autoSpellCB->setChecked(prefs.autoSpell); autoSpellMaxDistSB->setValue(prefs.autoSpellMaxDist); autoSpellMaxDistSB->setEnabled(prefs.autoSpell); showcompleterhitcountsCB->setChecked(prefs.showcompleterhitcounts); ssearchCompleterHistCntSB->setValue(prefs.ssearchCompleterHistCnt); /*INSERTHERE_LOAD*/ // See qxtconfirmationmessage. Needs to be -1 for the dialog to show. showTempFileWarningCB->setChecked(prefs.showTempFileWarning == -1); anchorTamilHackCB->setChecked(settings.value("anchorSpcHack", 0).toBool()); previewHtmlCB->setChecked(prefs.previewHtml); previewActiveLinksCB->setChecked(prefs.previewActiveLinks); switch (prefs.previewPlainPre) { case PrefsPack::PP_BR: plainBRRB->setChecked(1); break; case PrefsPack::PP_PRE: plainPRERB->setChecked(1); break; case PrefsPack::PP_PREWRAP: default: plainPREWRAPRB->setChecked(1); break; } // Query terms color qtermStyleCMB->setCurrentText(prefs.qtermstyle); if (qtermStyleCMB->count() <=1) { qtermStyleCMB->addItem(prefs.qtermstyle); qtermStyleCMB->addItem("color: blue"); qtermStyleCMB->addItem("color: red;background: yellow"); qtermStyleCMB->addItem( "color: #dddddd; background: black; font-weight: bold"); } // Abstract snippet separator string abssepLE->setText(prefs.abssep); dateformatLE->setText(u8s2qs(prefs.reslistdateformat)); // Result list font family and size reslistFontFamily = prefs.reslistfontfamily; reslistFontSize = prefs.reslistfontsize; setupReslistFontPB(); // Style sheet qssFile = prefs.qssFile; if (qssFile.isEmpty()) { stylesheetPB->setText(tr("Choose")); } else { string nm = path_getsimple(qs2path(qssFile)); stylesheetPB->setText(path2qs(nm)); } darkMode = prefs.darkMode; snipCssFile = prefs.snipCssFile; if (snipCssFile.isEmpty()) { snipCssPB->setText(tr("Choose")); } else { string nm = path_getsimple(qs2path(snipCssFile)); snipCssPB->setText(path2qs(nm)); } snipwMaxLenSB->setValue(prefs.snipwMaxLength); snipwByPageCB->setChecked(prefs.snipwSortByPage); alwaysSnippetsCB->setChecked(prefs.alwaysSnippets); paraFormat = prefs.reslistformat; headerText = prefs.reslistheadertext; // Stemming language combobox stemLangCMB->clear(); stemLangCMB->addItem(g_stringNoStem); stemLangCMB->addItem(g_stringAllStem); vector langs; if (!getStemLangs(langs)) { QMessageBox::warning(0, "Recoll", tr("error retrieving stemming languages")); } int cur = prefs.queryStemLang == "" ? 0 : 1; for (vector::const_iterator it = langs.begin(); it != langs.end(); it++) { stemLangCMB-> addItem(QString::fromUtf8(it->c_str(), it->length())); if (cur == 0 && !strcmp((const char*)prefs.queryStemLang.toUtf8(), it->c_str())) { cur = stemLangCMB->count(); } } stemLangCMB->setCurrentIndex(cur); autoPhraseCB->setChecked(prefs.ssearchAutoPhrase); autoPThreshSB->setValue(prefs.ssearchAutoPhraseThreshPC); buildAbsCB->setChecked(prefs.queryBuildAbstract); replAbsCB->setEnabled(prefs.queryBuildAbstract); replAbsCB->setChecked(prefs.queryReplaceAbstract); autoSuffsCB->setChecked(prefs.autoSuffsEnable); autoSuffsLE->setText(prefs.autoSuffs); synFile = prefs.synFile; if (synFile.isEmpty()) { synFileCB->setChecked(false); synFileCB->setEnabled(false); synFilePB->setText(tr("Choose")); } else { synFileCB->setChecked(prefs.synFileEnable); synFileCB->setEnabled(true); string nm = path_getsimple(qs2path(synFile)); synFilePB->setText(path2qs(nm)); } // Initialize the extra indexes listboxes idxLV->clear(); for (const auto& dbdir : prefs.allExtraDbs) { QListWidgetItem *item = new QListWidgetItem(path2qs(dbdir), idxLV); if (item) item->setCheckState(Qt::Unchecked); } for (const auto& dbdir : prefs.activeExtraDbs) { auto items = idxLV->findItems(path2qs(dbdir), Qt::MatchFixedString|Qt::MatchCaseSensitive); for (auto& entry : items) { entry->setCheckState(Qt::Checked); } } idxLV->sortItems(); readShortcuts(); setSSButState(); } void UIPrefsDialog::readShortcutsInternal(const QStringList& sl) { shortcutsTB->setRowCount(0); shortcutsTB->setColumnCount(4); shortcutsTB->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Context"))); shortcutsTB->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("Description"))); shortcutsTB->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("Shortcut"))); shortcutsTB->setHorizontalHeaderItem(3, new QTableWidgetItem(tr("Default"))); int row = 0; m_scids.clear(); for (int i = 0; i < sl.size();) { LOGDEB0("UIPrefsDialog::readShortcuts: inserting row " << qs2utf8s(sl.at(i)) << " " << qs2utf8s(sl.at(i+1)) << " " << qs2utf8s(sl.at(i+2)) << " " << qs2utf8s(sl.at(i+3)) << "\n"); shortcutsTB->insertRow(row); m_scids.push_back(sl.at(i++)); shortcutsTB->setItem(row, 0, new QTableWidgetItem(sl.at(i++))); shortcutsTB->setItem(row, 1, new QTableWidgetItem(sl.at(i++))); auto ed = new QKeySequenceEdit(QKeySequence(sl.at(i++))); shortcutsTB->setCellWidget(row, 2, ed); shortcutsTB->setItem(row, 3, new QTableWidgetItem(sl.at(i++))); row++; } shortcutsTB->resizeColumnsToContents(); shortcutsTB->horizontalHeader()->setStretchLastSection(true); } void UIPrefsDialog::readShortcuts() { readShortcutsInternal(SCBase::scBase().getAll()); } void UIPrefsDialog::resetShortcuts() { readShortcutsInternal(SCBase::scBase().getAllDefaults()); } void UIPrefsDialog::storeShortcuts() { SCBase& scbase = SCBase::scBase(); QStringList slout; for (int row = 0; row < shortcutsTB->rowCount(); row++) { QString dflt = shortcutsTB->item(row, 0)->text(); QString ctxt = shortcutsTB->item(row, 1)->text(); auto qsce = (QKeySequenceEdit*)(shortcutsTB->cellWidget(row, 2)); QString val = qsce->keySequence().toString(); scbase.set(m_scids[row], dflt, ctxt, val); } scbase.store(); } void UIPrefsDialog::setupReslistFontPB() { QString s; if (reslistFontFamily.length() == 0) { reslistFontPB->setText(tr("Default QtWebkit font")); } else { reslistFontPB->setText(reslistFontFamily + "-" + s.setNum(reslistFontSize)); } } void UIPrefsDialog::accept() { // Most values are stored in the prefs struct. Some rarely used // ones go directly through the settings QSettings settings; prefs.noBeeps = noBeepsCB->isChecked(); prefs.ssearchNoComplete = ssNoCompleteCB->isChecked(); prefs.showcompleterhitcounts = showcompleterhitcountsCB->isChecked(); prefs.ssearchStartOnComplete = ssSearchOnCompleteCB->isChecked(); if (ssearchTypCMB->currentIndex() == 4) { prefs.ssearchTypSav = true; // prefs.ssearchTyp will be set from the current value when // exiting the program } else { prefs.ssearchTypSav = false; prefs.ssearchTyp = ssearchTypCMB->currentIndex(); } if (filterMN_RB->isChecked()) { prefs.filterCtlStyle = PrefsPack::FCS_MN; } else if (filterCMB_RB->isChecked()) { prefs.filterCtlStyle = PrefsPack::FCS_CMB; } else { prefs.filterCtlStyle = PrefsPack::FCS_BT; } m_mainWindow->setFilterCtlStyle(prefs.filterCtlStyle); prefs.respagesize = pageLenSB->value(); prefs.idxFilterTreeDepth = idxTreeDepthSB->value(); prefs.historysize = maxHistSizeSB->value(); prefs.collapseDuplicates = collapseDupsCB->isChecked(); prefs.maxhltextkbs = maxHLTSB->value(); prefs.qtermstyle = qtermStyleCMB->currentText(); prefs.abssep = abssepLE->text(); prefs.reslistdateformat = qs2utf8s(dateformatLE->text()); prefs.reslistfontfamily = reslistFontFamily; prefs.reslistfontsize = reslistFontSize; prefs.darkMode = darkMode; prefs.setupDarkCSS(); prefs.qssFile = qssFile; prefs.snipCssFile = snipCssFile; prefs.reslistformat = paraFormat; prefs.reslistheadertext = headerText; if (prefs.reslistformat.trimmed().isEmpty()) { prefs.reslistformat = prefs.dfltResListFormat; paraFormat = prefs.reslistformat; } prefs.snipwMaxLength = snipwMaxLenSB->value(); prefs.snipwSortByPage = snipwByPageCB->isChecked(); prefs.alwaysSnippets = alwaysSnippetsCB->isChecked(); prefs.creslistformat = (const char*)prefs.reslistformat.toUtf8(); if (stemLangCMB->currentIndex() == 0) { prefs.queryStemLang = ""; } else if (stemLangCMB->currentIndex() == 1) { prefs.queryStemLang = "ALL"; } else { prefs.queryStemLang = stemLangCMB->currentText(); } prefs.ssearchAutoPhrase = autoPhraseCB->isChecked(); prefs.ssearchAutoPhraseThreshPC = autoPThreshSB->value(); prefs.queryBuildAbstract = buildAbsCB->isChecked(); prefs.queryReplaceAbstract = buildAbsCB->isChecked() && replAbsCB->isChecked(); prefs.startWithAdvSearchOpen = initStartAdvCB->isChecked(); prefs.keepSort = keepSortCB->isChecked(); prefs.noToolbars = noToolbarsCB->isChecked(); m_mainWindow->setupToolbars(); prefs.noMenuBar = noMenuBarCB->isChecked(); m_mainWindow->setupMenus(); prefs.noSSTypCMB = noSSTypCMBCB->isChecked(); prefs.resTableTextNoShift = restabShowTxtNoShiftRB->isChecked(); prefs.resTableNoHoverMeta = resTableNoHoverMetaCB->isChecked(); prefs.noResTableHeader = noResTableHeaderCB->isChecked(); prefs.showResTableVHeader = showResTableVHeaderCB->isChecked(); prefs.noResTableRowJumpSC = noRowJumpShortcutsCB->isChecked(); prefs.noStatusBar = noStatusBarCB->isChecked(); m_mainWindow->setupStatusBar(); prefs.noClearSearch = noClearSearchCB->isChecked(); m_mainWindow->sSearch->setupButtons(); prefs.showTrayIcon = showTrayIconCB->isChecked(); m_mainWindow->enableTrayIcon(prefs.showTrayIcon); prefs.closeToTray = closeToTrayCB->isChecked(); prefs.trayMessages = trayMessagesCB->isChecked(); prefs.wholeuiscale = wholeuiscaleSB->value(); prefs.autoSpell = autoSpellCB->isChecked(); prefs.autoSpellMaxDist = autoSpellMaxDistSB->value(); prefs.showcompleterhitcounts = showcompleterhitcountsCB->isChecked(); prefs.ssearchCompleterHistCnt = ssearchCompleterHistCntSB->value(); /*INSERTHERE_ACCEPT*/ // -1 is the qxtconf... predefined value to show the dialog prefs.showTempFileWarning = showTempFileWarningCB->isChecked() ? -1 : 1; settings.setValue("anchorSpcHack", anchorTamilHackCB->isChecked()); prefs.previewHtml = previewHtmlCB->isChecked(); prefs.previewActiveLinks = previewActiveLinksCB->isChecked(); if (plainBRRB->isChecked()) { prefs.previewPlainPre = PrefsPack::PP_BR; } else if (plainPRERB->isChecked()) { prefs.previewPlainPre = PrefsPack::PP_PRE; } else { prefs.previewPlainPre = PrefsPack::PP_PREWRAP; } prefs.syntAbsLen = syntlenSB->value(); prefs.syntAbsCtx = syntctxSB->value(); prefs.autoSuffsEnable = autoSuffsCB->isChecked(); prefs.autoSuffs = autoSuffsLE->text(); prefs.synFileEnable = synFileCB->isChecked(); prefs.synFile = synFile; prefs.allExtraDbs.clear(); prefs.activeExtraDbs.clear(); for (int i = 0; i < idxLV->count(); i++) { QListWidgetItem *item = idxLV->item(i); if (item) { prefs.allExtraDbs.push_back(qs2path(item->text())); if (item->checkState() == Qt::Checked) { prefs.activeExtraDbs.push_back(qs2path(item->text())); } } } rwSettings(true); storeShortcuts(); string reason; maybeOpenDb(reason, true); emit uiprefsDone(); QDialog::accept(); } void UIPrefsDialog::editParaFormat() { EditDialog dialog(this); dialog.setWindowTitle(tr("Result list paragraph format " "(erase all to reset to default)")); dialog.plainTextEdit->setPlainText(paraFormat); int result = dialog.exec(); if (result == QDialog::Accepted) paraFormat = dialog.plainTextEdit->toPlainText(); } void UIPrefsDialog::editHeaderText() { EditDialog dialog(this); dialog.setWindowTitle(tr("Result list header (default is empty)")); dialog.plainTextEdit->setPlainText(headerText); int result = dialog.exec(); if (result == QDialog::Accepted) headerText = dialog.plainTextEdit->toPlainText(); } void UIPrefsDialog::reject() { setFromPrefs(); QDialog::reject(); } void UIPrefsDialog::setStemLang(const QString& lang) { int cur = 0; if (lang == "") { cur = 0; } else if (lang == "ALL") { cur = 1; } else { for (int i = 1; i < stemLangCMB->count(); i++) { if (lang == stemLangCMB->itemText(i)) { cur = i; break; } } } stemLangCMB->setCurrentIndex(cur); } void UIPrefsDialog::showFontDialog() { bool ok; QFont font; if (prefs.reslistfontfamily.length()) { font.setFamily(prefs.reslistfontfamily); } font.setPointSize(prefs.reslistfontsize); font = QFontDialog::getFont(&ok, font, this); if (ok) { // We used to check if the default font was set, in which case // we erased the preference, but this would result in letting // webkit make a choice of default font which it usually seems // to do wrong. So now always set the font. There is still a // way for the user to let webkit choose the default though: // click reset, then the font name and size will be empty. reslistFontFamily = font.family(); reslistFontSize = font.pointSize(); setupReslistFontPB(); } } void UIPrefsDialog::setSSButState() { darkSSPB->setEnabled(!darkMode); resetSSPB->setEnabled(darkMode || !qssFile.isEmpty()); if (darkMode || qssFile.isEmpty()) { stylesheetPB->setText(tr("Choose QSS File")); } else { stylesheetPB->setText(path2qs(path_getsimple(qs2path(qssFile)))); } } void UIPrefsDialog::showStylesheetDialog() { auto newfn = myGetFileName(false, "Select stylesheet file", true); if (!newfn.isEmpty()) { qssFile = newfn; darkMode = false; } setSSButState(); } void UIPrefsDialog::setDarkMode() { auto fn = path_cat(path_cat(theconfig->getDatadir(), "examples"), "recoll-dark.qss"); qssFile = u8s2qs(fn); darkMode = true; setSSButState(); } void UIPrefsDialog::resetStylesheet() { qssFile.clear(); darkMode = false; setSSButState(); } void UIPrefsDialog::showSnipCssDialog() { snipCssFile = myGetFileName(false, "Select snippets window CSS file", true); string nm = path_getsimple(qs2path(snipCssFile)); snipCssPB->setText(path2qs(nm)); } void UIPrefsDialog::resetSnipCss() { snipCssFile = ""; snipCssPB->setText(tr("Choose")); } void UIPrefsDialog::showSynFileDialog() { synFile = myGetFileName(false, "Select synonyms file", true); if (synFile.isEmpty()) { synFileCB->setChecked(false); synFileCB->setEnabled(false); synFilePB->setText(tr("Choose")); return; } else { synFileCB->setChecked(prefs.synFileEnable); synFileCB->setEnabled(true); string nm = path_getsimple(qs2path(synFile)); synFilePB->setText(path2qs(nm)); } string nm = path_getsimple(qs2path(synFile)); synFilePB->setText(path2qs(nm)); } void UIPrefsDialog::resetReslistFont() { reslistFontFamily = ""; reslistFontSize = QFont().pointSize(); setupReslistFontPB(); } void UIPrefsDialog::showViewAction() { if (m_viewAction == 0) { m_viewAction = new ViewAction(0); } else { // Close and reopen, in hope that makes us visible... m_viewAction->close(); } m_viewAction->show(); } void UIPrefsDialog::showViewAction(const QString& mt) { showViewAction(); m_viewAction->selectMT(mt); } //////////////////////////////////////////// // External / extra search indexes setup void UIPrefsDialog::extradDbSelectChanged() { if (idxLV->selectedItems().size() <= 1) ptransPB->setEnabled(true); else ptransPB->setEnabled(false); } void UIPrefsDialog::extraDbEditPtrans() { string dbdir; if (idxLV->selectedItems().size() == 0) { dbdir = theconfig->getDbDir(); } else if (idxLV->selectedItems().size() == 1) { QListWidgetItem *item = idxLV->selectedItems()[0]; QString qd = item->data(Qt::DisplayRole).toString(); dbdir = qs2path(qd); } else { QMessageBox::warning( 0, "Recoll", tr("At most one index should be selected")); return; } dbdir = path_canon(dbdir); EditTrans *etrans = new EditTrans(dbdir, this); etrans->show(); } void UIPrefsDialog::togExtraDbPB_clicked() { for (int i = 0; i < idxLV->count(); i++) { QListWidgetItem *item = idxLV->item(i); if (item->isSelected()) { if (item->checkState() == Qt::Checked) { item->setCheckState(Qt::Unchecked); } else { item->setCheckState(Qt::Checked); } } } } void UIPrefsDialog::actAllExtraDbPB_clicked() { for (int i = 0; i < idxLV->count(); i++) { QListWidgetItem *item = idxLV->item(i); item->setCheckState(Qt::Checked); } } void UIPrefsDialog::unacAllExtraDbPB_clicked() { for (int i = 0; i < idxLV->count(); i++) { QListWidgetItem *item = idxLV->item(i); item->setCheckState(Qt::Unchecked); } } void UIPrefsDialog::delExtraDbPB_clicked() { QList items = idxLV->selectedItems(); for (QList::iterator it = items.begin(); it != items.end(); it++) { delete *it; } } void UIPrefsDialog::on_showTrayIconCB_clicked() { if (!showTrayIconCB->checkState()) { closeToTrayCB->setChecked(false); trayMessagesCB->setChecked(false); } closeToTrayCB->setEnabled(showTrayIconCB->checkState()); trayMessagesCB->setEnabled(showTrayIconCB->checkState()); } /** * Browse to add another index. * We do a textual comparison to check for duplicates, except for * the main db for which we check inode numbers. */ void UIPrefsDialog::addExtraDbPB_clicked() { QString input = myGetFileName(true, tr("Select recoll config directory or " "xapian index directory " "(e.g.: /home/me/.recoll or " "/home/me/.recoll/xapiandb)")); if (input.isEmpty()) return; string dbdir = qs2path(input); if (path_exists(path_cat(dbdir, "recoll.conf"))) { // Chosen dir is config dir. RclConfig conf(&dbdir); dbdir = conf.getDbDir(); if (dbdir.empty()) { QMessageBox::warning( 0, "Recoll", tr("The selected directory looks like a Recoll " "configuration directory but the configuration " "could not be read")); return; } } LOGDEB("ExtraDbDial: got: [" << (dbdir) << "]\n"); bool stripped; if (!Rcl::Db::testDbDir(dbdir, &stripped)) { QMessageBox::warning(0, "Recoll", tr("The selected directory does not " "appear to be a Xapian index")); return; } if (o_index_stripchars != stripped) { QMessageBox::warning(0, "Recoll", tr("Can't add index with different case/diacritics" " stripping option.")); return; } if (path_samefile(dbdir, theconfig->getDbDir())) { QMessageBox::warning(0, "Recoll", tr("This is the main/local index!")); return; } for (int i = 0; i < idxLV->count(); i++) { QListWidgetItem *item = idxLV->item(i); string existingdir = qs2path(item->text()); if (path_samefile(dbdir, existingdir)) { QMessageBox::warning( 0, "Recoll", tr("The selected directory is already in the " "index list")); return; } } QListWidgetItem *item = new QListWidgetItem(path2qs(dbdir), idxLV); item->setCheckState(Qt::Checked); idxLV->sortItems(); } recoll-1.36.1/qtgui/preview_plaintorich.cpp0000644000175000017500000001433514427373216015734 00000000000000/* Copyright (C) 2014-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include "preview_plaintorich.h" #include "recoll.h" #include "plaintorich.h" #include "log.h" #include "guiutils.h" #include "cancelcheck.h" #include "cstr.h" #include "hldata.h" using namespace std; PlainToRichQtPreview::PlainToRichQtPreview() { clear(); } void PlainToRichQtPreview::clear() { m_curanchor = 1; m_lastanchor = 0; m_groupanchors.clear(); m_groupcuranchors.clear(); QSettings settings; m_spacehack = settings.value("anchorSpcHack", 0).toBool(); } bool PlainToRichQtPreview::haveAnchors() { return m_lastanchor != 0; } string PlainToRichQtPreview::PlainToRichQtPreview::header() { if (m_inputhtml) { return cstr_null; } std::string fontstyle; if (prefs.reslistfontfamily != "") { fontstyle = std::string("font-family: ") + qs2utf8s(prefs.reslistfontfamily); } std::string ret{""}; switch (prefs.previewPlainPre) { case PrefsPack::PP_BR: m_eolbr = true; break; case PrefsPack::PP_PRE: m_eolbr = false; ret += std::string(""; break; case PrefsPack::PP_PREWRAP: default: m_eolbr = false; ret += std::string("
";
    }
    return ret;
}

string PlainToRichQtPreview::startMatch(unsigned int grpidx)
{
    LOGDEB2("startMatch, grpidx " << grpidx << "\n");
    grpidx = m_hdata->index_term_groups[grpidx].grpsugidx;
    LOGDEB2("startMatch, ugrpidx " << grpidx << "\n");
    m_groupanchors[grpidx].push_back(++m_lastanchor);
    m_groupcuranchors[grpidx] = 0;
    // We used to create the region as:
    //     term
    // For some reason, this caused problems with the display of some
    // Tamil text (qt bug?). Just inserting a space character after
    // the opening  section. Also: having  before the match
    // term causes the same problem (so not a possible fix).
    // Space does not seem to work any more (2021-04) ?
    //   Zero Width Non Joiner works but is displayed as ? sometimes on windows.
    //  nbsp seems to now work !
    string hackspace = m_spacehack? " " : "";
    string startmarker{
        "" +
        hackspace +
        "" 
    };
    return startmarker;
}

string  PlainToRichQtPreview::endMatch()
{
    return "";
}

string  PlainToRichQtPreview::termAnchorName(int i) const
{
    static const char *termAnchorNameBase = "TRM";
    char acname[sizeof(termAnchorNameBase) + 20];
    sprintf(acname, "%s%d", termAnchorNameBase, i);
    return string(acname);
}

string  PlainToRichQtPreview::startChunk()
{
    return "
";
}

int  PlainToRichQtPreview::nextAnchorNum(int grpidx)
{
    LOGDEB2("nextAnchorNum: group " << grpidx << "\n");
    auto curit = m_groupcuranchors.find(grpidx);
    auto vecit = m_groupanchors.find(grpidx);
    if (grpidx == -1 || curit == m_groupcuranchors.end() ||
        vecit == m_groupanchors.end()) {
        if (m_curanchor >= m_lastanchor)
            m_curanchor = 1;
        else
            m_curanchor++;
    } else {
        if (curit->second >= vecit->second.size() -1)
            m_groupcuranchors[grpidx] = 0;
        else 
            m_groupcuranchors[grpidx]++;
        m_curanchor = vecit->second[m_groupcuranchors[grpidx]];
        LOGDEB2("nextAnchorNum: curanchor now " << m_curanchor << "\n");
    }
    return m_curanchor;
}

int  PlainToRichQtPreview::prevAnchorNum(int grpidx)
{
    auto curit = m_groupcuranchors.find(grpidx);
    auto vecit = m_groupanchors.find(grpidx);
    if (grpidx == -1 || curit == m_groupcuranchors.end() ||
        vecit == m_groupanchors.end()) {
        if (m_curanchor <= 1)
            m_curanchor = m_lastanchor;
        else
            m_curanchor--;
    } else {
        if (curit->second <= 0)
            m_groupcuranchors[grpidx] = vecit->second.size() -1;
        else 
            m_groupcuranchors[grpidx]--;
        m_curanchor = vecit->second[m_groupcuranchors[grpidx]];
    }
    return m_curanchor;
}

QString  PlainToRichQtPreview::curAnchorName() const
{
    return u8s2qs(termAnchorName(m_curanchor));
}


ToRichThread::ToRichThread(const string &i, const HighlightData& hd,
                           std::shared_ptr ptr,
                           QStringList& qrichlist,
                           QObject *parent)
    : QThread(parent), m_input(i), m_hdata(hd), m_ptr(ptr), m_output(qrichlist)
{
}

// Insert into editor by chunks so that the top becomes visible
// earlier for big texts. This provokes some artifacts (adds empty line),
// so we can't set it too low.
#define CHUNKL 500*1000

void ToRichThread::run()
{
    list out;
    try {
        m_ptr->plaintorich(m_input, out, m_hdata, CHUNKL);
    } catch (CancelExcept) {
        return;
    }

    // Convert C++ string list to QString list
    for (const auto& chunk : out) {
        m_output.push_back(u8s2qs(chunk));
    }
}
recoll-1.36.1/qtgui/idxsched.h0000644000175000017500000000235714410615043013105 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _IDXSCHED_H_INCLUDED_
#define _IDXSCHED_H_INCLUDED_

#include "ui_idxsched.h"
#include "rclhelp.h"

class IdxSchedW : public QDialog, public Ui::IdxSchedW {
    Q_OBJECT
public:
    IdxSchedW(QWidget * parent = 0) 
        : QDialog(parent) {
        setupUi(this);
        (void)new HelpClient(this);
        HelpClient::installMap((const char *)this->objectName().toUtf8(), 
                               "RCL.INDEXING");
    }
};


#endif /* _IDXSCHED_H_INCLUDED_ */
recoll-1.36.1/qtgui/idxmodel.cpp0000644000175000017500000001400714427373216013460 00000000000000/* Copyright (C) 2022 J.F.Dockes
 *
 * License: GPL 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include 
#include 
#include 

#include 

#include "idxmodel.h"

#include "recoll.h"
#include "fstreewalk.h"
#include "log.h"
#include "rcldoc.h"

// Note: we originally used a file system tree walk to populate the tree. This was wrong
// because the file system may have changed since the index was created.
// We now build a directory tree directly from the index data, but still use the previous tree walk
// callback. In case you need an explanation of how we got here, look at the git history.
class WalkerCB : public FsTreeWalkerCB {
public:
    WalkerCB(const std::string& topstring, IdxTreeModel *model, const QModelIndex& index)
        : m_topstring(topstring), m_model(model) {
        LOGDEB1("WalkerCB: topstring [" << topstring << "]\n");
        m_indexes.push(index);
        m_rows.push(0);
    }
    virtual FsTreeWalker::Status processone(const std::string& path, FsTreeWalker::CbFlag flg,
                                            const struct PathStat& = PathStat()) override;

    std::string m_topstring;
    IdxTreeModel *m_model;
    std::stack m_indexes;
    std::stack m_rows;
};

FsTreeWalker::Status WalkerCB::processone(
    const std::string& path, FsTreeWalker::CbFlag flg, const struct PathStat&)
{
    if (flg == FsTreeWalker::FtwDirReturn) {
        m_indexes.pop();
        m_rows.pop();
        return FsTreeWalker::FtwOk;
    }
    if (flg == FsTreeWalker::FtwDirEnter) {
        //std::cerr << "ENTER: " << path << "\n";
        if (m_model->columnCount(m_indexes.top()) == 0) {
            if (!m_model->insertColumn(0, m_indexes.top()))
                return FsTreeWalker::FtwError;
        }
        if (!m_model->insertRow(m_rows.top(), m_indexes.top()))
            return FsTreeWalker::FtwError;
        const QModelIndex child = m_model->index(m_rows.top(), 0, m_indexes.top());
        // Setting the short path in DisplayRole and the real one in EditRole does not seem to work,
        // the treeview shows the EditRole?? So use the ToolTip to store the full value
        std::string disp;
        if (m_topstring.empty()) {
            disp = path_getsimple(path);
        } else {
            disp = m_topstring;
            m_topstring.clear();
        }
        m_model->setData(child, QVariant(path2qs(disp)), Qt::DisplayRole);
        m_model->setData(child, QVariant(path2qs(path)), Qt::ToolTipRole);
        ++m_rows.top();
        m_indexes.push(child);
        m_rows.push(0);
    }
    return FsTreeWalker::FtwOk;
}

// Assemble a path from its components up to lst
std::string toksToPath(std::vector& path, int lst)
{
    if (path.empty()) {
        // ??
#ifdef _WIN32
        return "C:/";
#else
        return "/";
#endif
    }
    std::string out{
#ifdef _WIN32
        path[0]
#else
        "/" + path[0]
#endif
    };
    for (int i = 1; i <= lst; i++) {
        out += "/" + path[i];
    }
    return out;
}

// Process a sorted list of directory paths, generating a sequence of enter/exit calls equivalent to
// what would happen for a recursive tree walk of the original tree.
static void treelist(const std::string& top, const std::vector& lst, WalkerCB &cb)
{
    if (lst.empty()) {
        return;
    }
    std::vector curpath;
    stringToTokens(top, curpath, "/");
    LOGDEB0("treelist: " << "top [" << top << "] TOP depth is " << curpath.size() << "\n");
    for (const auto& dir : lst) {
        LOGDEB1("DIR: " << dir << "\n");
        std::vector npath;
        // Compute the new directory stack
        stringToTokens(dir, npath, "/");
        // Walk the stacks until we find a differing entry, and then unwind the old stack to the new
        // base, and issue enter calls for new entries over the base.
        int i = 0;
        for (; i < int(std::min(curpath.size(), npath.size())); i++) {
            if (npath[i] != curpath[i] && int(curpath.size()) > 0) {
                // Differing at i, unwind old stack and break the main loop
                for (int j = int(curpath.size()) - 1; j >= i; j--) {
                    LOGDEB1("treelist: exiting  " <<  toksToPath(curpath, j) << "\n");
                    cb.processone(toksToPath(curpath, j), FsTreeWalker::FtwDirReturn);
                }
                break;
            }
        }
        // Callbacks for new entries above the base.
        for (int j = i; j < int(npath.size()); j++) {
            LOGDEB1("treelist: entering " << toksToPath(npath, j) << "\n");
            cb.processone(toksToPath(npath, j), FsTreeWalker::FtwDirEnter);
        }
        curpath.swap(npath);
    }
}

void IdxTreeModel::populate()
{
    LOGDEB0("IdxTreeModel::populate\n");
    std::vector thedirs;
    std::string prefix;
    rcldb->dirlist(m_depth, prefix, thedirs);
    LOGDEB1("IdxTreeModel::populate: prefix [" << prefix << "] thedirs: " <<
            stringsToString(thedirs) << "\n");

    QModelIndex index = this->index(0,0);
    if (this->columnCount(index) == 0) {
        if (!this->insertColumn(0, index))
            return;
    }
    const QModelIndex child = this->index(0, 0, index);
    WalkerCB cb(path_isroot(prefix) ? std::string() : prefix, this, child);
    if (!prefix.empty())
        prefix = path_getfather(prefix);
    treelist(prefix, thedirs, cb);
}
recoll-1.36.1/qtgui/viewaction.ui0000644000175000017500000001356714410615043013655 00000000000000

 ViewActionBase
 
  
   
    0
    0
    635
    726
   
  
  
   Native Viewers
  
  
   
    
     
      Select one or several mime types then use the controls in the bottom frame to change how they are processed.
     
     
      false
     
    
   
   
    
     
      Use Desktop preferences by default
     
    
   
   
    
     
      Select one or several file types, then use the controls in the frame below to change how they are processed
     
     
      QFrame::StyledPanel
     
     
      QFrame::Sunken
     
     
      QAbstractItemView::NoEditTriggers
     
     
      QAbstractItemView::ExtendedSelection
     
     
      QAbstractItemView::SelectRows
     
     
      true
     
     
      true
     
     
      2
     
     
      true
     
     
      true
     
     
      150
     
     
      true
     
     
      true
     
     
      false
     
     
     
    
   
   
    
     
      
       
        Recoll action:
       
      
     
     
      
       
        
         1
         0
        
       
       
        QFrame::Box
       
       
        QFrame::Raised
       
       
        current value
       
       
        Qt::PlainText
       
       
        Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
       
      
     
     
       
         
           Select same
         
       
     
    
   
   
    
     
      QFrame::Box
     
     
      QFrame::Plain
     
     
      
       
          
           
            
             <b>New Values:</b>
            
           
          
        
         
          
           Exception to Desktop preferences
          
         
        
        
         
          
           
            
             Action (empty -> recoll default)
            
           
          
          
           
          
         
        
        
         
          
           Apply to current selection
          
         
        
       
      
     
    
   
   
    
     
      
       
        Qt::Horizontal
       
       
        
         40
         20
        
       
      
     
     
      
       
        Close
       
      
     
    
   
  
 
 
 
 

recoll-1.36.1/qtgui/xmltosd.h0000644000175000017500000000603614427373216013015 00000000000000/* Copyright (C) 2014 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef XMLTOSD_H_INCLUDED
#define XMLTOSD_H_INCLUDED
#include "autoconfig.h"

/** Parsing XML from saved queries or advanced search history.
 *
 * Here is how the schemas looks like:
 *
 * For advanced search
 *
 *                          
 *                          
 *    AND|OR         
 *                           
 *     []                 
 *     AND|OR|FN|PH|NE 
 *     [base64data]      
 *     [base64data]      
 *     slack             
 *    
 *
 *    [base64 path]    
 *    [base64 path]    
 *  
 * 
 *  162014 <--! datemin -->
 *  3062014 <--! datemax -->
 *  minsize          
 *  maxsize          
 *  space-sep mtypes   
 *  space-sep mtypes   
 *
 * 
 *
 * For Simple search:
 *
 * 
 *   base64-encoded query text
 *   OR|AND|FN|QL
 *   space-separated lang list
 *                                      
 *   space-separated suffix list    
 *   base64-encoded config path>/EX>     
 * 
 */ 

#include 
#include "searchdata.h"

// Parsing XML from advanced search history or saved advanced search into
// a SearchData structure:
std::shared_ptr xmlToSearchData(const std::string& xml,
                                                 bool complain = true);

// Parsing XML from saved simple search to ssearch parameters
struct SSearchDef {
    SSearchDef() : autophrase(false), mode(0) {}
    std::vector stemlangs;
    std::vector autosuffs;
    std::vector extindexes;
    std::string text;
    bool autophrase;
    int mode;
};
bool xmlToSSearch(const std::string& xml, SSearchDef&);
#endif /* XMLTOSD_H_INCLUDED */
recoll-1.36.1/qtgui/rclhelp.h0000644000175000017500000000235114427373216012750 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef RCLHELP_H
#define RCLHELP_H
#include 

#include 
#include 

class HelpClient : public QObject {
    Q_OBJECT
public:
    HelpClient(QObject *parent, const char *name = 0);

    // Install mapping from widget name to manual section
    static void installMap(std::string wname, std::string section);

protected:
    bool eventFilter(QObject *obj, QEvent *event);
    static std::map helpmap;
};

#endif // RCLHELP_H
recoll-1.36.1/qtgui/webcache.cpp0000644000175000017500000002764614444307651013430 00000000000000/* Copyright (C) 2016-2021 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "recoll.h"
#include "webcache.h"
#include "webstore.h"
#include "circache.h"
#include "conftree.h"
#include "rclmain_w.h"
#include "smallut.h"
#include "log.h"
#include "copyfile.h"

using namespace std;

class CEnt {
public:
    CEnt(const string& ud, const string& ur, const string& mt)
        : udi(ud), url(ur), mimetype(mt) {
    }
    string udi;
    string url;
    string mimetype;
    string date;
    string size;
    // Only useful in the filtered list. Corresponding index in the
    // complete one
    int allidx{-1};
};

class WebcacheModelInternal {
public:
    std::unique_ptr cache;
    // The complete list
    vector all;
    // The entries matching the current filter (displayed)
    vector disp;
};

WebcacheModel::WebcacheModel(QObject *parent)
    : QAbstractTableModel(parent), m(new WebcacheModelInternal())
{
}
WebcacheModel::~WebcacheModel()
{
    delete m;
}

void WebcacheModel::reload()
{
    int idx = 0;
    m->all.clear();
    m->disp.clear();
    if (!(m->cache = std::unique_ptr(new WebStore(theconfig)))) {
        goto out;
    }
    emit headerChanged(m->cache.get());
    bool eof;
    m->cache->cc()->rewind(eof);
    while (!eof) {
        string udi, sdic;
        m->cache->cc()->getCurrent(udi, sdic);
        if (!udi.empty()) {
            ConfSimple dic(sdic);
            string mime, url, mtime, fbytes;
            dic.get("mimetype", mime);
            dic.get("url", url);
            dic.get("fmtime", mtime);
            dic.get("fbytes", fbytes);
            CEnt entry(udi, url, mime);
            entry.allidx = idx++;
            if (!mtime.empty()) {
                time_t clck = atoll(mtime.c_str());
                entry.date = utf8datestring("%c", localtime(&clck));
            }
            if (!fbytes.empty()) {
                entry.size = displayableBytes(atoll(fbytes.c_str()));
            }
            m->all.push_back(entry);
            m->disp.push_back(entry);
        }
        if (!m->cache->cc()->next(eof))
            break;
    }

out:
    emit dataChanged(createIndex(0,0), createIndex(1, m->all.size()));
}

bool WebcacheModel::deleteIdx(unsigned int idx)
{
    if (idx > m->disp.size() || !m->cache)
        return false;
    return m->cache->cc()->erase(m->disp[idx].udi, true);
}

string WebcacheModel::getURL(unsigned int idx)
{
    if (idx > m->disp.size() || !m->cache)
        return string();
    return m->disp[idx].url;
}

string WebcacheModel::getData(unsigned int idx)
{
    LOGDEB0("WebcacheModel::getData: idx " << idx << "\n");
    if (idx > m->disp.size() || !m->cache) {
        LOGERR("WebcacheModel::getData: idx > m->disp.size()" << m->disp.size() << "\n");
        return string();
    }
    // Get index in the "all" list
    auto allidx = m->disp[idx].allidx;
    LOGDEB0("WebcacheModel::getData: allidx " << allidx << "\n");
    if (allidx < 0 || allidx >= int(m->all.size())) {
        LOGERR("WebcacheModel::getData: allidx > m->all.size()" << m->all.size() << "\n");
        return string();
    }
    string udi = m->all[allidx].udi;
    // Compute the instance for this udi (in case we are configured to
    // not erase older instances).  Valid instance values begin at 1
    int instance = 0;
    for (unsigned int i = 0; i <= idx; i++) {
        if (m->all[i].udi == udi) {
            instance++;
        }
    }
    string dic, data;
    m->cache->cc()->get(udi, dic, &data, instance);
    LOGDEB0("WebcacheModel::getData: got " << data.size() << " bytes of data\n");
    return data;
}

int WebcacheModel::rowCount(const QModelIndex&) const
{
    //qDebug() << "WebcacheModel::rowCount(): " << m->disp.size();
    return int(m->disp.size());
}

int WebcacheModel::columnCount(const QModelIndex&) const
{
    //qDebug() << "WebcacheModel::columnCount()";
    return 4;
}

QVariant WebcacheModel::headerData (int col, Qt::Orientation orientation, int role) const
{
    // qDebug() << "WebcacheModel::headerData()";
    if (orientation != Qt::Horizontal || role != Qt::DisplayRole) {
        return QVariant();
    }
    switch (col) {
    case 0: return QVariant(tr("MIME"));
    case 1: return QVariant(tr("Date"));
    case 2: return QVariant(tr("Size"));
    case 3: return QVariant(tr("URL"));
    default: return QVariant();
    }
}

QVariant WebcacheModel::data(const QModelIndex& index, int role) const
{
    //qDebug() << "WebcacheModel::data()";
    Q_UNUSED(index);
    if (role != Qt::DisplayRole) {
        return QVariant();
    }
    int row = index.row();
    if (row < 0 || row >= int(m->disp.size())) {
        return QVariant();
    }
    switch (index.column()) {
    case 0: return QVariant(u8s2qs(m->disp[row].mimetype));
    case 1: return QVariant(u8s2qs(m->disp[row].date));
    case 2: return QVariant(u8s2qs(m->disp[row].size));
    case 3: return QVariant(u8s2qs(m->disp[row].url));
    default: return QVariant();
    }
}

void WebcacheModel::setSearchFilter(const QString& _txt)
{
    SimpleRegexp re(qs2utf8s(_txt), SimpleRegexp::SRE_NOSUB|SimpleRegexp::SRE_ICASE);
    
    m->disp.clear();
    for (unsigned int i = 0; i < m->all.size(); i++) {
        if (re(m->all[i].url)) {
            m->disp.push_back(m->all[i]);
            m->disp.back().allidx = i;
        } else {
            LOGDEB1("WebcacheModel::filter: match failed. exp" <<
                    qs2utf8s(_txt) << "data" << m->all[i].url);
        }
    }
    emit dataChanged(createIndex(0,0), createIndex(1, m->all.size()));
}

static const int ROWHEIGHTPAD = 2;
static const char *cwnm = "/Recoll/prefs/webcachecolw";
static const char *wwnm = "/Recoll/prefs/webcachew";
static const char *whnm = "/Recoll/prefs/webcacheh";
static const QKeySequence closeKS(Qt::ControlModifier | Qt::Key_W);

WebcacheEdit::WebcacheEdit(RclMain *parent)
    : QDialog(parent), m_recoll(parent), m_modified(false)
{
    //qDebug() << "WebcacheEdit::WebcacheEdit()";
    setupUi(this);
    m_model = new WebcacheModel(this);
    tableview->setModel(m_model);
    tableview->setSelectionBehavior(QAbstractItemView::SelectRows);
    tableview->setSelectionMode(QAbstractItemView::ExtendedSelection);
    tableview->setContextMenuPolicy(Qt::CustomContextMenu);
    tableview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    
    QSettings settings;
    QStringList wl;
    wl = settings.value(cwnm).toStringList();
    QHeaderView *header = tableview->horizontalHeader();
    if (header) {
        if (int(wl.size()) == header->count()) {
            for (int i = 0; i < header->count(); i++) {
                header->resizeSection(i, wl[i].toInt());
            }
        }
    }
    connect(header, SIGNAL(sectionResized(int,int,int)), this, SLOT(saveColState()));

    header = tableview->verticalHeader();
    if (header) {
        QFontMetricsF fm(QApplication::font(header));
        header->setDefaultSectionSize(fm.height() + ROWHEIGHTPAD);
    }

    int width = settings.value(wwnm, 0).toInt();
    int height = settings.value(whnm, 0).toInt();
    if (width && height) {
        resize(QSize(width, height));
    }

    connect(searchLE, SIGNAL(textEdited(const QString&)),
            m_model, SLOT(setSearchFilter(const QString&)));
    connect(new QShortcut(closeKS, this), SIGNAL (activated()), this, SLOT (close()));
    connect(tableview, SIGNAL(customContextMenuRequested(const QPoint&)),
            this, SLOT(createPopupMenu(const QPoint&)));
    connect(m_model, SIGNAL(headerChanged(WebStore*)), this, SLOT(onHeaderChanged(WebStore*)));
    m_model->reload();
}

void WebcacheEdit::onHeaderChanged(WebStore *ws)
{
    headerInfoLBL->setText(tr("Maximum size %1 (Index config.). Current size %2. Write position %3.")
                           .arg(u8s2qs(displayableBytes(ws->cc()->maxsize())))
                           .arg(u8s2qs(displayableBytes(ws->cc()->size())))
                           .arg(u8s2qs(displayableBytes(ws->cc()->writepos())))
        );
}

void WebcacheEdit::createPopupMenu(const QPoint& pos)
{
    int selsz = tableview->selectionModel()->selectedRows().size();
    if (selsz <= 0) {
        return;
    }
    QMenu *popup = new QMenu(this);
    if (selsz == 1) {
        popup->addAction(tr("Copy URL"), this, SLOT(copyURL()));
        popup->addAction(tr("Save to File"), this, SLOT(saveToFile()));
    }
    if (m_recoll) {
        RclMain::IndexerState ixstate = m_recoll->indexerState();
        switch (ixstate) {
        case RclMain::IXST_UNKNOWN:
            QMessageBox::warning(0, "Recoll",tr("Unknown indexer state. Can't edit webcache file."));
            break;
        case RclMain::IXST_RUNNINGMINE:
        case RclMain::IXST_RUNNINGNOTMINE:
            QMessageBox::warning(0, "Recoll", tr("Indexer is running. Can't edit webcache file."));
            break;
        case RclMain::IXST_NOTRUNNING:
            popup->addAction(tr("Delete selection"), this, SLOT(deleteSelected()));
            break;
        }
    }
        
    popup->popup(tableview->mapToGlobal(pos));
}

void WebcacheEdit::deleteSelected()
{
    QModelIndexList selection = tableview->selectionModel()->selectedRows();
    for (int i = 0; i < selection.size(); i++) {
        if (m_model->deleteIdx(selection[i].row())) {
            m_modified = true;
        }
    }
    m_model->reload();
    m_model->setSearchFilter(searchLE->text());
    tableview->clearSelection();
}

void WebcacheEdit::copyURL()
{
    QModelIndexList selection = tableview->selectionModel()->selectedRows();
    if (selection.size() != 1)
        return;
    const string& url = m_model->getURL(selection[0].row());
    if (!url.empty()) {
        QString qurl =  path2qs(url);
        QApplication::clipboard()->setText(qurl, QClipboard::Selection);
        QApplication::clipboard()->setText(qurl, QClipboard::Clipboard);
    }
}

void WebcacheEdit::saveToFile()
{
    QModelIndexList selection = tableview->selectionModel()->selectedRows();
    if (selection.size() != 1)
        return;
    string data = m_model->getData(selection[0].row());
    QString qfn  = myGetFileName(false, "Saving webcache data");
    if (qfn.isEmpty())
        return;
    string reason;
    if (!stringtofile(data, qs2utf8s(qfn).c_str(), reason)) {
        QMessageBox::warning(0, "Recoll", tr("File creation failed: ") + u8s2qs(reason));
    }
}

void WebcacheEdit::saveColState()
{
    //qDebug() << "void WebcacheEdit::saveColState()";
    QHeaderView *header = tableview->horizontalHeader();
    QStringList newwidths;
    for (int vi = 0; vi < header->count(); vi++) {
        int li = header->logicalIndex(vi);
        newwidths.push_back(lltodecstr(header->sectionSize(li)).c_str());
    }
    QSettings settings;
    settings.setValue(cwnm, newwidths);
}

void WebcacheEdit::closeEvent(QCloseEvent *event)
{
    if (m_modified) {
        QMessageBox::information(0, "Recoll", tr("Webcache was modified, you will need "
                                    "to run the indexer after closing this window."));
    }
    if (!isFullScreen()) {
        QSettings settings;
        settings.setValue(wwnm, width());
        settings.setValue(whnm, height());
    }
    event->accept();
}
recoll-1.36.1/qtgui/preview.ui0000644000175000017500000001017514410615043013156 00000000000000

 Preview
 
  
   
    0
    0
    777
    432
   
  
  
   Form
  
  
   
    
     
      0
     
     
      true
     
     
      
       Tab 1
      
     
    
   
   
    
     
      
       
        &Search for:
       
       
        searchTextCMB
       
      
     
     
      
       
        true
       
       
        QComboBox::NoInsert
       
      
     
     
      
       
        &Next
       
      
     
     
      
       
        &Previous
       
      
     
     
      
       
        false
       
       
        Clear
       
      
     
     
      
       
        Match &Case
       
      
     
     
      
       
        Qt::Horizontal
       
       
        
         40
         20
        
       
      
     
     
      
       
        Qt::Vertical
       
      
     
     
      
       
        
       
       
         Previous result document
       
       
        
         :/images/prevpage.pngimages/prevpage.png
       
      
     
     
      
       
        
       
       
         Next result document
       
       
        
         :/images/nextpage.pngimages/nextpage.png
       
      
     
     
      
       
        Qt::Vertical
       
      
     
     
      
       
        Qt::Horizontal
       
       
        
         40
         20
        
       
      
     
     
      
       
        Open
       
      
     
    
   
  
 
 
 

recoll-1.36.1/qtgui/preview_plaintorich.h0000644000175000017500000000456714427373216015407 00000000000000/* Copyright (C) 2015 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _PREVIEW_PLAINTORICH_H_INCLUDED_
#define _PREVIEW_PLAINTORICH_H_INCLUDED_
#include "autoconfig.h"

#include 
#include 
#include 
#include 

#include 
#include 

#include "plaintorich.h"

/** Preview text highlighter */
class PlainToRichQtPreview : public PlainToRich {
public:
    PlainToRichQtPreview();
    void clear();
    bool haveAnchors();
    virtual std::string header();
    virtual std::string startMatch(unsigned int grpidx);
    virtual std::string endMatch();
    virtual std::string termAnchorName(int i) const;
    virtual std::string startChunk();
    int nextAnchorNum(int grpidx);
    int prevAnchorNum(int grpidx);
    QString curAnchorName() const;

private:
    int m_curanchor;
    int m_lastanchor;
    // Lists of anchor numbers (match locations) for the term (groups)
    // in the query (the map key is and index into HighlightData.groups).
    std::map > m_groupanchors;
    std::map m_groupcuranchors;
    bool m_spacehack{false};
};

/* A thread to convert to rich text (mark search terms) */
class ToRichThread : public QThread {
    Q_OBJECT
    
public:
    ToRichThread(const std::string &i, const HighlightData& hd,
                 std::shared_ptr ptr,
                 QStringList& qrichlst, // Output
                 QObject *parent = 0);
    virtual void run();

private:
    const std::string &m_input;
    const HighlightData &m_hdata;
    std::shared_ptr m_ptr;
    QStringList &m_output;
};

#endif /* _PREVIEW_PLAINTORICH_H_INCLUDED_ */
recoll-1.36.1/qtgui/actsearch_w.h0000644000175000017500000000331314427373216013601 00000000000000/* Copyright (C) 2021 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _ACTSEARCH_H_INCLUDED_
#define _ACTSEARCH_H_INCLUDED_

// A window for letting the user to search among menu entries (actions) and execute the one found.

#include 
#include 

#include 

#include "ui_actsearch.h"

class QCompleter;
class QAction;
class QString;

class ActSearchW : public QDialog, public Ui::ActSearchDLG {
    Q_OBJECT
public:
    ActSearchW(QWidget* parent = 0) 
        : QDialog(parent) {
        setupUi(this);
        init();
    }
    void setActList(QList);
    virtual bool eventFilter(QObject *target, QEvent *event);

private slots:
    void onActivated(int);
    void onTextChanged(const QString&);

private:
    void init();
    std::vector m_actions;
    QCompleter *m_completer{nullptr};
    // Decides if we match the start only or anywhere. Anywhere seems better. May be made a pref one
    // day.
    bool m_match_contains{true};
};

#endif /* _ACTSEARCH_H_INCLUDED_ */
recoll-1.36.1/qtgui/rclmain.ui0000644000175000017500000004314414427373216013137 00000000000000

 RclMainBase
 
  
   
    0
    0
    800
    600
   
  
  
   
    0
    0
   
  
  
   Recoll
  
  
   
    
     
      
       
        
         
          0
          0
         
        
       
      
      
       
        
         Qt::Horizontal
        
        
         
          QFrame::StyledPanel
         
         
          
           
            
             Query Language Filters
            
           
          
          
           
            
             Qt::Horizontal
            
           
          
          
           
            
             Filter dates
            
            
             false
            
           
          
          
           
            
             
              
               false
              
             
            
            
             
              
               false
              
             
            
           
          
          
           
            
             Filter birth dates
            
            
             false
            
           
          
          
           
            
             
              
               false
              
             
            
            
             
              
               false
              
             
            
           
          
          
           
            
             QFrame::NoFrame
            
           
          
         
        
        
         
          
           
            
             
              2
              0
             
            
           
          
         
        
       
      
     
    
   
  
  
  
   
    
     0
     0
     800
     32
    
   
   
  
  
   
    E&xit
   
   
    Ctrl+Q
   
   
    fileExitAction
   
  
  
   
    Update &index
   
   
    fileToggleIndexingAction
   
  
  
   
    false
   
   
    Trigger incremental pass
   
   
    fileBumpIndexingAction
   
  
  
   
    true
   
   
    Start real time indexer
   
   
    fileStartMonitorAction
   
  
  
   
    &Rebuild index
   
   
    fileRebuildIndexAction
   
  
  
   
    &Erase document history
   
   
    fileEraseDocHistoryAction
   
  
  
   
    &Erase search history
   
   
    fileEraseSearchHistoryAction
   
  
  
   
    E&xport simple search history
   
   
    fileExportSSearchHistoryAction
   
  
  
   
    Missing &helpers
   
   
    showMissingHelpers_Action
   
  
  
   
    Indexed &MIME types
   
   
    showActiveTypes_Action
   
  
  
   
    &About Recoll
   
   
    helpAbout_RecollAction
   
  
  
   
    &User manual
   
   
    userManualAction
   
  
  
   
    
     :/images/history.png:/images/history.png
   
   
    Document &History
   
   
    Document  History
   
   
    toolsDoc_HistoryAction
   
  
  
   
    
     :/images/asearch.png:/images/asearch.png
   
   
    &Advanced Search
   
   
    Assisted complex search
   
   
    toolsAdvanced_SearchAction
   
  
  
   
    &Sort parameters
   
   
    Sort parameters
   
   
    toolsSort_parametersAction
   
  
  
   
    
     :/images/spell.png:/images/spell.png
   
   
    Term &explorer
   
   
    Term explorer tool
   
   
    toolsSpellAction
   
  
  
   
    false
   
   
    
     :/images/nextpage.png:/images/nextpage.png
   
   
    Next page
   
   
    Next page of results
   
   
    PgDown
   
   
    nextPageAction
   
  
  
   
    false
   
   
    
     :/images/firstpage.png:/images/firstpage.png
   
   
    First page
   
   
    Go to first page of results
   
   
    Shift+PgUp
   
   
    firstPageAction
   
  
  
   
    false
   
   
    
     :/images/prevpage.png:/images/prevpage.png
   
   
    Previous page
   
   
    Previous page of results
   
   
    PgUp
   
   
    prevPageAction
   
  
  
   
    &Index configuration
   
   
    indexConfigAction
   
  
  
   
    Indexing &schedule
   
   
    indexScheduleAction
   
  
  
   
    &GUI configuration
   
   
    queryPrefsAction
   
  
  
   
    E&xternal index dialog
   
   
    External index dialog
   
   
    extIdxAction
   
  
  
   
    true
   
   
    Enable synonyms
   
   
    Enable synonyms
   
   
    enbSynAction
   
  
  
   
    &Full Screen
   
   
    Full Screen
   
   
    F11
   
   
    toggleFullScreenAction
   
  
  
   
    Increase results text font size
   
   
    Increase Font Size
   
   
    zoomInAction
   
  
  
   
    Decrease results text font size
   
   
    Decrease Font Size
   
   
    zoomOutAction
   
  
  
   
    true
   
   
    false
   
   
    
     :/images/up.png:/images/up.png
   
   
    Sort by date, oldest first
   
   
    Sort by dates from oldest to newest
   
  
  
   
    true
   
   
    false
   
   
    
     :/images/down.png:/images/down.png
   
   
    Sort by date, newest first
   
   
    Sort by dates from newest to oldest
   
  
  
   
    Show Query Details
   
  
  
   
    true
   
   
    
     :/images/table.png:/images/table.png
   
   
    Show as table
   
   
    Show results in a spreadsheet-like table
   
  
  
   
    Save as CSV (spreadsheet) file
   
   
    Saves the result into a file which you can load in a spreadsheet
   
  
  
   
    Next Page
   
  
  
   
    Previous Page
   
  
  
   
    First Page
   
  
  
   
    
     :/images/code-block.png:/images/code-block.png
   
   
    Query Fragments
   
  
  
   
    true
   
   
        With failed files retrying
   
   
    Next update will retry previously failed files
   
   
    fileToggleIndexingAction
   
  
  
   
    Save last query
   
  
  
   
    Load saved query
   
  
  
   
    Special Indexing
   
   
    Indexing with special options
   
  
  
   
    Index &statistics
   
  
  
   
    Webcache Editor
   
  
 
 
 
  
   SSearch
   QWidget
   
ssearch_w.h
ResList QWidget
reslist.h
ssearch_w.h reslist.h
recoll-1.36.1/qtgui/rclm_menus.cpp0000644000175000017500000002026514427373216014022 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "rclmain_w.h" #include "ssearch_w.h" #include #include #include #include using std::string; using std::vector; using std::map; void RclMain::buildMenus() { fileMenu = new QMenu(); fileMenu->setObjectName(QString::fromUtf8("fileMenu")); fileMenu->setTitle(QApplication::translate("RclMainBase", "&File")); viewMenu = new QMenu(); viewMenu->setObjectName(QString::fromUtf8("viewMenu")); viewMenu->setTitle(QApplication::translate("RclMainBase", "&View")); toolsMenu = new QMenu(); toolsMenu->setObjectName(QString::fromUtf8("toolsMenu")); toolsMenu->setTitle(QApplication::translate("RclMainBase", "&Tools")); preferencesMenu = new QMenu(); preferencesMenu->setObjectName(QString::fromUtf8("preferencesMenu")); preferencesMenu->setTitle(QApplication::translate("RclMainBase", "&Preferences")); helpMenu = new QMenu(); helpMenu->setObjectName(QString::fromUtf8("helpMenu")); helpMenu->setTitle(QApplication::translate("RclMainBase", "&Help")); resultsMenu = new QMenu(); resultsMenu->setObjectName(QString::fromUtf8("resultsMenu")); resultsMenu->setTitle(QApplication::translate("RclMainBase", "&Results")); queryMenu = new QMenu(); queryMenu->setObjectName(QString::fromUtf8("queryMenu")); queryMenu->setTitle(QApplication::translate("RclMainBase", "&Query")); fileMenu->addAction(fileToggleIndexingAction); fileMenu->addAction(fileStartMonitorAction); fileMenu->addAction(fileBumpIndexingAction); fileMenu->addAction(fileRebuildIndexAction); fileMenu->addAction(actionSpecial_Indexing); fileMenu->addSeparator(); fileMenu->addAction(actionSave_last_query); fileMenu->addAction(actionLoad_saved_query); fileMenu->addSeparator(); fileMenu->addAction(fileExportSSearchHistoryAction); fileMenu->addAction(fileEraseSearchHistoryAction); fileMenu->addSeparator(); fileMenu->addAction(fileEraseDocHistoryAction); fileMenu->addSeparator(); fileMenu->addAction(fileExitAction); viewMenu->addSeparator(); viewMenu->addAction(toggleFullScreenAction); viewMenu->addAction(zoomInAction); viewMenu->addAction(zoomOutAction); toolsMenu->addAction(toolsDoc_HistoryAction); toolsMenu->addAction(toolsAdvanced_SearchAction); toolsMenu->addAction(toolsSpellAction); toolsMenu->addAction(actionQuery_Fragments); toolsMenu->addAction(actionWebcache_Editor); toolsMenu->addAction(showMissingHelpers_Action); toolsMenu->addAction(showActiveTypes_Action); toolsMenu->addAction(actionShow_index_statistics); preferencesMenu->addAction(queryPrefsAction); preferencesMenu->addSeparator(); preferencesMenu->addAction(indexConfigAction); preferencesMenu->addAction(indexScheduleAction); queryMenu->addSection(QIcon(), tr("Simple search type")); sstypGroup = new QActionGroup(this); auto actSSAny = new QAction(tr("Any term"), this); actSSAny->setData(QVariant(SSearch::SST_ANY)); actSSAny->setCheckable(true); sstypGroup->addAction(actSSAny); queryMenu->addAction(actSSAny); auto actSSAll = new QAction(tr("All terms"), this); actSSAll->setData(QVariant(SSearch::SST_ALL)); actSSAll->setCheckable(true); sstypGroup->addAction(actSSAll); queryMenu->addAction(actSSAll); auto actSSFile = new QAction(tr("File name"), this); actSSFile->setData(QVariant(SSearch::SST_FNM)); actSSFile->setCheckable(true); sstypGroup->addAction(actSSFile); queryMenu->addAction(actSSFile); auto actSSQuery = new QAction(tr("Query language"), this); actSSQuery->setData(QVariant(SSearch::SST_LANG)); actSSQuery->setCheckable(true); sstypGroup->addAction(actSSQuery); queryMenu->addAction(actSSQuery); queryMenu->addSeparator(); queryMenu->addAction(enbSynAction); queryMenu->addSeparator(); queryMenu->addAction(extIdxAction); connect(queryMenu, SIGNAL(triggered(QAction *)), this, SLOT(onSSTypMenu(QAction *))); connect(sSearch->searchTypCMB, SIGNAL(currentIndexChanged(int)), this, SLOT(onSSTypCMB(int))); queryMenu->addSection(QIcon(), tr("Stemming language")); // Stemming language menu g_stringNoStem = tr("(no stemming)"); g_stringAllStem = tr("(all languages)"); m_idNoStem = queryMenu->addAction(g_stringNoStem); m_idNoStem->setCheckable(true); m_stemLangToId[g_stringNoStem] = m_idNoStem; m_idAllStem = queryMenu->addAction(g_stringAllStem); m_idAllStem->setCheckable(true); m_stemLangToId[g_stringAllStem] = m_idAllStem; // Can't get the stemming languages from the db at this stage as // db not open yet (the case where it does not even exist makes // things complicated). So get the languages from the config // instead vector langs; if (!getStemLangs(langs)) { QMessageBox::warning(0, "Recoll", tr("error retrieving stemming languages")); } QAction *curid = prefs.queryStemLang == "ALL" ? m_idAllStem : m_idNoStem; QAction *id; for (const auto& lang : langs) { QString qlang = u8s2qs(lang); id = queryMenu->addAction(qlang); id->setCheckable(true); m_stemLangToId[qlang] = id; if (prefs.queryStemLang == qlang) { curid = id; } } curid->setChecked(true); helpMenu->addAction(userManualAction); helpMenu->addAction(showMissingHelpers_Action); helpMenu->addAction(showActiveTypes_Action); helpMenu->addSeparator(); helpMenu->addAction(helpAbout_RecollAction); resultsMenu->addAction(nextPageAction); resultsMenu->addAction(prevPageAction); resultsMenu->addAction(firstPageAction); resultsMenu->addSeparator(); resultsMenu->addAction(actionSortByDateAsc); resultsMenu->addAction(actionSortByDateDesc); resultsMenu->addSeparator(); resultsMenu->addAction(actionShowQueryDetails); resultsMenu->addSeparator(); resultsMenu->addAction(actionShowResultsAsTable); resultsMenu->addSeparator(); resultsMenu->addAction(actionSaveResultsAsCSV); MenuBar->addAction(fileMenu->menuAction()); MenuBar->addAction(queryMenu->menuAction()); MenuBar->addAction(resultsMenu->menuAction()); MenuBar->addAction(viewMenu->menuAction()); MenuBar->addAction(toolsMenu->menuAction()); MenuBar->addAction(preferencesMenu->menuAction()); MenuBar->addSeparator(); MenuBar->addAction(helpMenu->menuAction()); buttonTopMenu = new QMenu(); buttonTopMenu->addAction(fileMenu->menuAction()); buttonTopMenu->addAction(queryMenu->menuAction()); buttonTopMenu->addAction(viewMenu->menuAction()); buttonTopMenu->addAction(toolsMenu->menuAction()); buttonTopMenu->addAction(resultsMenu->menuAction()); buttonTopMenu->addAction(preferencesMenu->menuAction()); buttonTopMenu->addSeparator(); buttonTopMenu->addAction(helpMenu->menuAction()); sSearch->menuPB->setMenu(buttonTopMenu); return; } void RclMain::onSSTypMenu(QAction *act) { if (act->actionGroup() != sstypGroup) { return; } int id = act->data().toInt(); sSearch->onSearchTypeChanged(id); } void RclMain::onSSTypCMB(int idx) { QList ssacts = sstypGroup->actions(); for (int i = 0; i < ssacts.size(); ++i) { if (ssacts.at(i)->data().toInt() == idx) ssacts.at(i)->setChecked(true); } } recoll-1.36.1/qtgui/actsearch_w.cpp0000644000175000017500000000746714410615043014137 00000000000000/* Copyright (C) 2020-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include "log.h" #include "actsearch_w.h" // A window for letting the user to search among menu entries (actions) and execute the one found. void ActSearchW::init() { new QShortcut(QKeySequence("Esc"), this, SLOT(hide())); connect(actCMB, SIGNAL(activated(int)), this, SLOT(onActivated(int))); } void ActSearchW::setActList(QList actlist) { for (int i = 0; i < actlist.size(); i++) { if (!actlist[i]->text().isEmpty()) { m_actions.push_back(actlist[i]); } } std::sort(m_actions.begin(), m_actions.end(), [] (const QAction *act1, const QAction *act2) { auto txt1 = act1->text().remove('&'); auto txt2 = act2->text().remove('&'); return txt1.compare(txt2, Qt::CaseInsensitive) < 0;}); QStringList sl; for (const auto act : m_actions) { auto txt = act->text().remove('&'); actCMB->addItem(txt); sl.push_back(txt); } actCMB->setCurrentText(""); m_completer = new QCompleter(sl, this); m_completer->setCaseSensitivity(Qt::CaseInsensitive); if (m_match_contains) { m_completer->setFilterMode(Qt::MatchContains); } actCMB->setCompleter(m_completer); m_completer->popup()->installEventFilter(this); actCMB->installEventFilter(this); } // This is to avoid that if the user types Backspace or Del while we // have inserted / selected the current completion, the lineedit text // goes back to what it was, the completion fires, and it looks like // nothing was typed. Disable the completion after Del or Backspace // is typed. bool ActSearchW::eventFilter(QObject *target, QEvent *event) { Q_UNUSED(target); if (event->type() != QEvent::KeyPress) { return false; } QKeyEvent *keyEvent = (QKeyEvent *)event; if (keyEvent->key() == Qt::Key_Backspace || keyEvent->key()==Qt::Key_Delete) { actCMB->setCompleter(nullptr); return false; } else { if (nullptr == actCMB->completer()) { actCMB->setCompleter(m_completer); } } return false; } void ActSearchW::onTextChanged(const QString&) { if (actCMB->completer() && actCMB->completer()->completionCount() == 1) { // We append the completion part to the end of the current input, line, and select it so // that the user has a clear indication of what will happen if they type Enter. auto le = actCMB->lineEdit(); int pos = le->cursorPosition(); auto text = actCMB->completer()->currentCompletion(); int len = text.size() - actCMB->currentText().size(); le->setText(text); if (!m_match_contains) { le->setCursorPosition(pos); le->setSelection(pos, len); } } } void ActSearchW::onActivated(int index) { if (index < 0 || index >= int(m_actions.size())) return; m_actions[index]->trigger(); hide(); } recoll-1.36.1/qtgui/advsearch_w.h0000644000175000017500000000523614427373216013612 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _ADVSEARCH_W_H_INCLUDED_ #define _ADVSEARCH_W_H_INCLUDED_ #include "autoconfig.h" #include #include #include #include "searchclause_w.h" #include "recoll.h" #include #include "searchdata.h" #include "advshist.h" class QDialog; class QShortcut; #include "ui_advsearch.h" class AdvSearch : public QDialog, public Ui::AdvSearchBase { Q_OBJECT public: AdvSearch(QDialog* parent = 0) : QDialog(parent) { setupUi(this); init(); } static void listShortcuts(); public slots: virtual void delFiltypPB_clicked(); virtual void delAFiltypPB_clicked(); virtual void addFiltypPB_clicked(); virtual void addAFiltypPB_clicked(); virtual void guiListsToIgnTypes(); virtual void filterDatesCB_toggled(bool); virtual void filterBirthDatesCB_toggled(bool); virtual void filterSizesCB_toggled(bool); virtual void restrictFtCB_toggled(bool); virtual void restrictCtCB_toggled(bool); virtual void runSearch(); virtual void fromSearch(std::shared_ptr sdata); virtual void browsePB_clicked(); virtual void saveFileTypes(); virtual void delClause(bool updsaved=true); virtual void addClause(bool updsaved=true); virtual void addClause(int, bool updsaved=true); virtual void slotHistoryNext(); virtual void slotHistoryPrev(); virtual void onNewShortcuts(); signals: void startSearch(std::shared_ptr, bool); void setDescription(QString); private: virtual void init(); std::vector m_clauseWins; QStringList m_ignTypes; bool m_ignByCats; QShortcut *m_histnextsc{nullptr}; QShortcut *m_histprevsc{nullptr}; MyGFNParams m_gfnparams; void saveCnf(); void fillFileTypes(); size_t stringToSize(QString); }; #endif /* _ADVSEARCH_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/xmltosd.cpp0000644000175000017500000002410414427373216013344 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "ssearch_w.h" #include "guiutils.h" #include "log.h" #include "xmltosd.h" #include #include "smallut.h" #include "recoll.h" #include "picoxml.h" #include "base64.h" using namespace std; using namespace Rcl; class SDHXMLHandler : public PicoXMLParser { public: SDHXMLHandler(const std::string& in) : PicoXMLParser(in) { resetTemps(); } void startElement( const std::string& nm, const std::map& attrs) { LOGDEB2("SDHXMLHandler::startElement: name [" << nm << "]\n"); if (nm == "SD") { // Advanced search history entries have no type. So we're good // either if type is absent, or if it's searchdata auto attr = attrs.find("type"); if (attr != attrs.end() && attr->second != "searchdata") { LOGDEB("XMLTOSD: bad type: " << attr->second << endl); contentsOk = false; return; } resetTemps(); // A new search descriptor. Allocate data structure sd = std::make_shared(); if (!sd) { LOGERR("SDHXMLHandler::startElement: out of memory\n"); contentsOk = false; return; } } return; } void endElement(const string & nm) { LOGDEB2("SDHXMLHandler::endElement: name [" << nm << "]\n"); string curtxt{currentText}; trimstring(curtxt, " \t\n\r"); if (nm == "CLT") { if (curtxt == "OR") { sd->setTp(SCLT_OR); } } else if (nm == "CT") { whatclause = curtxt; } else if (nm == "NEG") { exclude = true; } else if (nm == "F") { field = base64_decode(curtxt); } else if (nm == "T") { text = base64_decode(curtxt); } else if (nm == "T2") { text2 = base64_decode(curtxt); } else if (nm == "S") { slack = atoi(curtxt.c_str()); } else if (nm == "C") { SearchDataClause *c; if (whatclause == "AND" || whatclause.empty()) { c = new SearchDataClauseSimple(SCLT_AND, text, field); c->setexclude(exclude); } else if (whatclause == "OR") { c = new SearchDataClauseSimple(SCLT_OR, text, field); c->setexclude(exclude); } else if (whatclause == "RG") { c = new SearchDataClauseRange(text, text2, field); c->setexclude(exclude); } else if (whatclause == "EX") { // Compat with old hist. We don't generete EX // (SCLT_EXCL) anymore it's replaced with OR + exclude // flag c = new SearchDataClauseSimple(SCLT_OR, text, field); c->setexclude(true); } else if (whatclause == "FN") { c = new SearchDataClauseFilename(text); c->setexclude(exclude); } else if (whatclause == "PH") { c = new SearchDataClauseDist(SCLT_PHRASE, text, slack, field); c->setexclude(exclude); } else if (whatclause == "NE") { c = new SearchDataClauseDist(SCLT_NEAR, text, slack, field); c->setexclude(exclude); } else { LOGERR("Bad clause type [" << whatclause << "]\n"); contentsOk = false; return; } sd->addClause(c); whatclause = ""; text.clear(); field.clear(); slack = 0; exclude = false; } else if (nm == "D") { d = atoi(curtxt.c_str()); } else if (nm == "M") { m = atoi(curtxt.c_str()); } else if (nm == "Y") { y = atoi(curtxt.c_str()); } else if (nm == "DMI") { di.d1 = d; di.m1 = m; di.y1 = y; hasdates = true; } else if (nm == "DMA") { di.d2 = d; di.m2 = m; di.y2 = y; hasdates = true; } else if (nm == "MIS") { sd->setMinSize(atoll(curtxt.c_str())); } else if (nm == "MAS") { sd->setMaxSize(atoll(curtxt.c_str())); } else if (nm == "ST") { string types = curtxt.c_str(); vector vt; stringToTokens(types, vt); for (unsigned int i = 0; i < vt.size(); i++) sd->addFiletype(vt[i]); } else if (nm == "IT") { vector vt; stringToTokens(curtxt, vt); for (unsigned int i = 0; i < vt.size(); i++) sd->remFiletype(vt[i]); } else if (nm == "YD") { string d; base64_decode(curtxt, d); sd->addClause(new SearchDataClausePath(d)); } else if (nm == "ND") { string d; base64_decode(curtxt, d); sd->addClause(new SearchDataClausePath(d, true)); } else if (nm == "SD") { // Closing current search descriptor. Finishing touches... if (hasdates) sd->setDateSpan(&di); resetTemps(); isvalid = contentsOk; } currentText.clear(); return; } void characterData(const std::string &str) { currentText += str; } // The object we set up std::shared_ptr sd; bool isvalid{false}; bool contentsOk{true}; private: void resetTemps() { currentText = whatclause = ""; text.clear(); text2.clear(); field.clear(); slack = 0; d = m = y = di.d1 = di.m1 = di.y1 = di.d2 = di.m2 = di.y2 = 0; hasdates = false; exclude = false; } // Temporary data while parsing. std::string currentText; std::string whatclause; std::string field, text, text2; int slack; int d, m, y; DateInterval di; bool hasdates; bool exclude; }; std::shared_ptr xmlToSearchData(const string& xml, bool verbose) { SDHXMLHandler handler(xml); if (!handler.Parse() || !handler.isvalid) { if (verbose) { LOGERR("xmlToSearchData: parse failed for [" << xml << "]\n"); } return std::shared_ptr(); } return handler.sd; } // Handler for parsing saved simple search data class SSHXMLHandler : public PicoXMLParser { public: SSHXMLHandler(const std::string& in) : PicoXMLParser(in) { resetTemps(); } void startElement(const std::string &nm, const std::map& attrs) override { LOGDEB2("SSHXMLHandler::startElement: name [" << nm << "]\n"); if (nm == "SD") { // Simple search saved data has a type='ssearch' attribute. auto attr = attrs.find("type"); if (attr == attrs.end() || attr->second != "ssearch") { if (attr == attrs.end()) { LOGDEB("XMLTOSSS: bad type\n"); } else { LOGDEB("XMLTOSSS: bad type: " << attr->second << endl); } contentsOk = false; } resetTemps(); } } void endElement(const string& nm) override { LOGDEB2("SSHXMLHandler::endElement: name [" << nm << "]\n"); std::string curtxt{currentText}; trimstring(curtxt, " \t\n\r"); if (nm == "SL") { stringToStrings(curtxt, data.stemlangs); } else if (nm == "T") { base64_decode(curtxt, data.text); } else if (nm == "EX") { data.extindexes.push_back(base64_decode(curtxt)); } else if (nm == "SM") { if (curtxt == "QL") { data.mode = SSearch::SST_LANG; } else if (curtxt == "FN") { data.mode = SSearch::SST_FNM; } else if (curtxt == "OR") { data.mode = SSearch::SST_ANY; } else if (curtxt == "AND") { data.mode = SSearch::SST_ALL; } else { LOGERR("BAD SEARCH MODE: [" << curtxt << "]\n"); contentsOk = false; return; } } else if (nm == "AS") { stringToStrings(curtxt, data.autosuffs); } else if (nm == "AP") { data.autophrase = true; } else if (nm == "SD") { // Closing current search descriptor. Finishing touches... resetTemps(); isvalid = contentsOk; } currentText.clear(); return ; } void characterData(const std::string &str) override { currentText += str; } // The object we set up SSearchDef data; bool isvalid{false}; bool contentsOk{true}; private: void resetTemps() { currentText = whatclause = ""; text.clear(); } // Temporary data while parsing. std::string currentText; std::string whatclause; string text; }; bool xmlToSSearch(const string& xml, SSearchDef& data) { SSHXMLHandler handler(xml); if (!handler.Parse() || !handler.isvalid) { LOGERR("xmlToSSearch: parse failed for [" << xml << "]\n"); return false; } data = handler.data; return true; } recoll-1.36.1/qtgui/rclzg.h0000644000175000017500000000210114410615043012416 00000000000000/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RCLZG_H_INCLUDED_ #define _RCLZG_H_INCLUDED_ #include "rcldoc.h" enum ZgSendType {ZGSEND_PREVIEW, ZGSEND_OPEN}; #ifndef USE_ZEITGEIST inline void zg_send_event(ZgSendType, const Rcl::Doc&){} #else extern void zg_send_event(ZgSendType tp, const Rcl::Doc& doc); #endif #endif // _RCLZG_H_INCLUDED_ recoll-1.36.1/qtgui/spell.ui0000644000175000017500000000764114410615043012620 00000000000000 SpellBase 0 0 520 465 0 0 100 100 Term Explorer 100 0 Match Case Accents false Qt::NoFocus &Expand Alt+E true Qt::NoFocus &Close Alt+C 7 No db info. 2 2 false false baseWordLE expandPB dismissPB stemLangCMB recoll-1.36.1/qtgui/idxmodel.h0000644000175000017500000000257414427373216013133 00000000000000/* Copyright (C) 2022 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _IDXMODEL_H #define _IDXMODEL_H #include #include #include class IdxTreeModel : public QStandardItemModel { Q_OBJECT public: IdxTreeModel(int depth, const std::vector& edbs, QWidget *parent = nullptr) : QStandardItemModel(0, 0, (QObject*)parent), m_depth(depth), m_extradbs(edbs) {} ~IdxTreeModel() {} void populate(); int getDepth() {return m_depth;} const std::vector &getEDbs() {return m_extradbs;} private: int m_depth; std::vector m_extradbs; }; #endif // _IDXMODEL_H recoll-1.36.1/qtgui/guiutils.cpp0000644000175000017500000006553214515655616013536 00000000000000/* Copyright (C) 2005-2019 Jean-Francois Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include // Programs built with gcc 4.8.4 (e.g.: Ubuntu Trusty), crash at startup while initializing stdc++ // regular expression objects (and also crash if we make them non-static). #if defined(__clang__) || defined(_WIN32) || __GNUC__ > 4 #define USE_REGEX #include #endif #include "recoll.h" #include "log.h" #include "smallut.h" #include "guiutils.h" #include "pathut.h" #include "readfile.h" #include "dynconf.h" #include #include #ifdef BUILDING_RECOLLGUI #include #include #endif using std::vector; using std::string; RclDynConf *g_dynconf; RclConfig *theconfig; #ifdef _WIN32 static const std::string dirlistsep{";"}; // The web default font family is too ugly on windows, set a different one static const char *defaultfontfamily = "Arial"; #else static const std::string dirlistsep{":"}; static const char *defaultfontfamily = ""; #endif // The table should not be necessary, but I found no css way to get // qt 4.6 qtextedit to clear the margins after the float img without // introducing blank space. const char *PrefsPack::dfltResListFormat = "\n" "\n" "\n" "\n" "
%L  %S   %T
\n" "%M %D    %U %i
\n" "%A %K
\n" ; // The global preferences structure PrefsPack prefs; // Using the same macro to read/write a setting. insurance against typing // mistakes #define SETTING_RW(var, nm, tp, def) \ if (writing) { \ settings.setValue(nm , var); \ } else { \ var = settings.value(nm, def).to##tp \ (); \ } /** * Saving and restoring user preferences. These are stored in a global * structure during program execution and saved to disk using the QT * settings mechanism */ /* Remember if settings were actually read (to avoid writing them if * we stopped before reading them (else some kinds of errors would reset * the qt/recoll settings to defaults) */ static bool havereadsettings; #ifndef _WIN32 static void maybeRenameGUISettings(); #endif /* ! _WIN32 */ void rwSettings(bool writing) { #ifndef _WIN32 maybeRenameGUISettings(); #endif /* !_WIN32 */ QSettings::setDefaultFormat(QSettings::IniFormat); LOGDEB1("rwSettings: write " << writing << "\n"); if (writing && !havereadsettings) return; QSettings settings; SETTING_RW(prefs.showmode, "/Recoll/geometry/showmode", Int, 0); SETTING_RW(prefs.pvwidth, "/Recoll/geometry/pvwidth", Int, 0); SETTING_RW(prefs.pvheight, "/Recoll/geometry/pvheight", Int, 0); SETTING_RW(prefs.ssearchTypSav, "/Recoll/prefs/ssearchTypSav", Bool, 0); SETTING_RW(prefs.ssearchTyp, "/Recoll/prefs/simpleSearchTyp", Int, 3); SETTING_RW(prefs.startWithAdvSearchOpen, "/Recoll/prefs/startWithAdvSearchOpen", Bool, false); SETTING_RW(prefs.previewHtml, "/Recoll/prefs/previewHtml", Bool, true); SETTING_RW(prefs.previewActiveLinks, "/Recoll/prefs/previewActiveLinks", Bool, false); SETTING_RW(prefs.idxFilterTreeDepth, "/Recoll/prefs/ssearch/idxfiltertreedepth", Int, 2); QString advSearchClauses; const int maxclauselistsize = 20; if (writing) { // Limit clause list size to non-absurd size if (prefs.advSearchClauses.size() > maxclauselistsize) { prefs.advSearchClauses.resize(maxclauselistsize); } for (auto clause : prefs.advSearchClauses) { char buf[20]; sprintf(buf, "%d ", clause); advSearchClauses += QString::fromUtf8(buf); } } QString ascdflt; SETTING_RW(advSearchClauses,"/Recoll/prefs/adv/clauseList", String, ascdflt); if (!writing) { vector clauses; stringToStrings(qs2utf8s(advSearchClauses), clauses); // There was a long-lurking bug where the clause list was // growing to absurd sizes. The prefs.advSearchClauses clear() // call was missing (ok with the now false initial assumption // that the prefs were read once per session), which was // causing a doubling of the size each time the prefs were // read. Should be fixed, but in any case, limit the clause // list to a non-absurd size. if (clauses.size() > maxclauselistsize) { clauses.resize(maxclauselistsize); } prefs.advSearchClauses.clear(); prefs.advSearchClauses.reserve(clauses.size()); for (auto clause : clauses) { prefs.advSearchClauses.push_back(atoi(clause.c_str())); } } SETTING_RW(prefs.ssearchNoComplete, "/Recoll/prefs/ssearch/noComplete", Bool, false); SETTING_RW(prefs.ssearchStartOnComplete, "/Recoll/prefs/ssearch/startOnComplete", Bool, true); SETTING_RW(prefs.filterCtlStyle, "/Recoll/prefs/filterCtlStyle", Int, 0); SETTING_RW(prefs.ssearchAutoPhrase, "/Recoll/prefs/ssearchAutoPhrase", Bool, true); SETTING_RW(prefs.ssearchAutoPhraseThreshPC, "/Recoll/prefs/ssearchAutoPhraseThreshPC", Double, 2.0); SETTING_RW(prefs.respagesize, "/Recoll/prefs/reslist/pagelen", Int, 8); SETTING_RW(prefs.historysize, "/Recoll/prefs/historysize", Int, -1); SETTING_RW(prefs.collapseDuplicates, "/Recoll/prefs/reslist/collapseDuplicates", Bool, false); SETTING_RW(prefs.showResultsAsTable, "/Recoll/prefs/showResultsAsTable", Bool, false); SETTING_RW(prefs.maxhltextkbs, "/Recoll/prefs/preview/maxhltextkbs", Int, 3000); // Compat: if maxhltextkbs is not set but old maxhltextmbs is set use it if (!writing && !settings.contains("/Recoll/prefs/preview/maxhltextkbs") && settings.contains("/Recoll/prefs/preview/maxhltextmbs")) { prefs.maxhltextkbs = settings.value( "/Recoll/prefs/preview/maxhltextmbs").toInt() * 1024; } SETTING_RW(prefs.previewPlainPre, "/Recoll/prefs/preview/plainPre", Int, PrefsPack::PP_PREWRAP); // History: used to be able to only set a bare color name. Can now // set any CSS style. Hack on ':' presence to keep compat with old // values SETTING_RW(prefs.qtermstyle, "/Recoll/prefs/qtermcolor", String, "color: blue"); if (!writing && prefs.qtermstyle == "") prefs.qtermstyle = "color: blue"; { // histo compatibility hack int colon = prefs.qtermstyle.indexOf(":"); int semi = prefs.qtermstyle.indexOf(";"); // The 2nd part of the test is to keep compat with the // injection hack of the 1st user who suggested this (had // #ff5000;font-size:110%;... in 'qtermcolor') if (colon == -1 || (colon != -1 && semi != -1 && semi < colon)) { prefs.qtermstyle = QString::fromUtf8("color: ") + prefs.qtermstyle; } } SETTING_RW(u8s2qs(prefs.reslistdateformat), "/Recoll/prefs/reslist/dateformat", String, " %Y-%m-%d %H:%M:%S %z"); if (!writing && prefs.reslistdateformat == "") prefs.reslistdateformat = " %Y-%m-%d %H:%M:%S %z"; SETTING_RW(prefs.reslistfontfamily, "/Recoll/prefs/reslist/fontFamily", String, defaultfontfamily); // While building the kio, we don't really care about QT Gui // defaults and referencing QFont introduces a useless dependency // On Windows, the default font size is 8 as far as I can see, too small #if defined(BUILDING_RECOLLGUI) && !defined(_WIN32) SETTING_RW(prefs.reslistfontsize, "/Recoll/prefs/reslist/fontSize", Int, QFont().pointSize()); #else SETTING_RW(prefs.reslistfontsize, "/Recoll/prefs/reslist/fontSize", Int, 12); #endif LOGDEB("Settings: font family: [" << qs2utf8s(prefs.reslistfontfamily) << "] " << " size " << prefs.reslistfontsize << " points.\n"); QString rlfDflt = QString::fromUtf8(prefs.dfltResListFormat); if (writing) { if (prefs.reslistformat.compare(rlfDflt)) { settings.setValue("/Recoll/prefs/reslist/format", prefs.reslistformat); } else { settings.remove("/Recoll/prefs/reslist/format"); } } else { prefs.reslistformat = settings.value("/Recoll/prefs/reslist/format", rlfDflt).toString(); prefs.creslistformat = qs2utf8s(prefs.reslistformat); } SETTING_RW(prefs.reslistheadertext, "/Recoll/prefs/reslist/headertext", String, ""); SETTING_RW(prefs.darkMode, "/Recoll/prefs/darkMode", Bool, 0); SETTING_RW(prefs.qssFile, "/Recoll/prefs/stylesheet", String, ""); SETTING_RW(prefs.snipCssFile, "/Recoll/prefs/snippets/cssfile", String, ""); SETTING_RW(prefs.queryStemLang, "/Recoll/prefs/query/stemLang", String, "english"); SETTING_RW(prefs.useDesktopOpen, "/Recoll/prefs/useDesktopOpen", Bool, true); SETTING_RW(prefs.keepSort, "/Recoll/prefs/keepSort", Bool, false); SETTING_RW(prefs.sortField, "/Recoll/prefs/sortField", String, ""); SETTING_RW(prefs.sortActive, "/Recoll/prefs/sortActive", Bool, false); SETTING_RW(prefs.sortDesc, "/Recoll/prefs/query/sortDesc", Bool, 0); if (!writing) { // Handle transition from older prefs which did not store sortColumn // (Active always meant sort by date). if (prefs.sortActive && prefs.sortField.isNull()) prefs.sortField = "mtime"; } SETTING_RW(prefs.queryBuildAbstract, "/Recoll/prefs/query/buildAbstract", Bool, true); SETTING_RW(prefs.queryReplaceAbstract, "/Recoll/prefs/query/replaceAbstract", Bool, false); SETTING_RW(prefs.syntAbsLen, "/Recoll/prefs/query/syntAbsLen", Int, 250); SETTING_RW(prefs.syntAbsCtx, "/Recoll/prefs/query/syntAbsCtx", Int, 4); // Abstract snippet separator SETTING_RW(prefs.abssep, "/Recoll/prefs/reslist/abssep", String,"…"); if (!writing && prefs.abssep == "") prefs.abssep = "…"; SETTING_RW(prefs.snipwMaxLength, "/Recoll/prefs/snipwin/maxlen", Int, 1000); SETTING_RW(prefs.snipwSortByPage,"/Recoll/prefs/snipwin/bypage", Bool,false); SETTING_RW(prefs.alwaysSnippets, "/Recoll/prefs/reslist/alwaysSnippets", Bool,false); SETTING_RW(prefs.autoSuffs, "/Recoll/prefs/query/autoSuffs", String, ""); SETTING_RW(prefs.autoSuffsEnable, "/Recoll/prefs/query/autoSuffsEnable", Bool, false); SETTING_RW(prefs.synFileEnable, "/Recoll/prefs/query/synFileEnable", Bool, false); SETTING_RW(prefs.synFile, "/Recoll/prefs/query/synfile", String, ""); SETTING_RW(prefs.termMatchType, "/Recoll/prefs/query/termMatchType", Int, 0); SETTING_RW(prefs.noBeeps, "/Recoll/prefs/query/noBeeps", Bool, false); // This is not really the current program version, just a value to // be used in case we have incompatible changes one day SETTING_RW(prefs.rclVersion, "/Recoll/prefs/rclVersion", Int, 1009); // Ssearch combobox history list if (writing) { settings.setValue("/Recoll/prefs/query/ssearchHistory",prefs.ssearchHistory); } else { prefs.ssearchHistory = settings.value("/Recoll/prefs/query/ssearchHistory").toStringList(); } // Ignored file types (advanced search) if (writing) { settings.setValue("/Recoll/prefs/query/asearchIgnFilTyps", prefs.asearchIgnFilTyps); } else { prefs.asearchIgnFilTyps = settings.value("/Recoll/prefs/query/asearchIgnFilTyps").toStringList(); } SETTING_RW(prefs.fileTypesByCats, "/Recoll/prefs/query/asearchFilTypByCat", Bool, false); SETTING_RW(prefs.noClearSearch, "/Recoll/prefs/noClearSearch", Bool, false); SETTING_RW(prefs.noToolbars, "/Recoll/prefs/noToolbars", Bool, false); SETTING_RW(prefs.noStatusBar, "/Recoll/prefs/noStatusBar", Bool, false); SETTING_RW(prefs.noMenuBar, "/Recoll/prefs/noMenuBar", Bool, false); SETTING_RW(prefs.noSSTypCMB, "/Recoll/prefs/noSSTypCMB", Bool, false); SETTING_RW(prefs.resTableTextNoShift, "/Recoll/prefs/resTableTextNoShift", Bool, false); SETTING_RW(prefs.resTableNoHoverMeta, "/Recoll/prefs/resTableNoHoverMeta", Bool, false); SETTING_RW(prefs.noResTableHeader, "/Recoll/prefs/noResTableHeader", Bool, false); SETTING_RW(prefs.showResTableVHeader, "/Recoll/prefs/showResTableVHeader", Bool, false); SETTING_RW(prefs.noResTableRowJumpSC, "/Recoll/prefs/noResTableRowJumpSC", Bool, false); SETTING_RW(prefs.showTrayIcon, "/Recoll/prefs/showTrayIcon", Bool, false); SETTING_RW(prefs.closeToTray, "/Recoll/prefs/closeToTray", Bool, false); SETTING_RW(prefs.trayMessages, "/Recoll/prefs/trayMessages", Bool, false); SETTING_RW(prefs.wholeuiscale, "/Recoll/ui/wholeuiscale", Double, 1.0); SETTING_RW(prefs.autoSpell, "/Recoll/search/autoSpell", Bool, false) SETTING_RW(prefs.autoSpellMaxDist, "/Recoll/search/autoSpellMaxDist", Int, 1) SETTING_RW(prefs.showcompleterhitcounts, "/Recoll/ui/showcompleterhitcounts", Bool, false) SETTING_RW(prefs.ssearchCompleterHistCnt, "/Recoll/ui/ssearchCompleterHistCnt", Int, 10) /*INSERTHERE*/ // See qxtconfirmationmessage. Needs to be -1 for the dialog to show. SETTING_RW(prefs.showTempFileWarning, "Recoll/prefs/showTempFileWarning", Int, -1); if (g_dynconf == 0) { // Happens return; } // The extra databases settings. These are stored as a list of // xapian directory names, encoded in base64 to avoid any // binary/charset conversion issues. There are 2 lists for all // known dbs and active (searched) ones. // When starting up, we also add from the RECOLL_EXTRA_DBS environment // variable. // These are stored inside the dynamic configuration file (aka: history), // as they are likely to depend on RECOLL_CONFDIR. if (writing) { g_dynconf->eraseAll(allEdbsSk); for (const auto& dbdir : prefs.allExtraDbs) { g_dynconf->enterString(allEdbsSk, dbdir); } g_dynconf->eraseAll(actEdbsSk); for (const auto& dbdir : prefs.activeExtraDbs) { g_dynconf->enterString(actEdbsSk, dbdir); } } else { prefs.allExtraDbs = g_dynconf->getStringEntries(allEdbsSk); const char *cp; if ((cp = getenv("RECOLL_EXTRA_DBS")) != 0) { vector dbl; stringToTokens(cp, dbl, dirlistsep); for (const auto& path : dbl) { string dbdir = path_canon(path); path_catslash(dbdir); if (std::find(prefs.allExtraDbs.begin(), prefs.allExtraDbs.end(), dbdir) != prefs.allExtraDbs.end()) continue; bool stripped; if (!Rcl::Db::testDbDir(dbdir, &stripped)) { LOGERR("Not a xapian index: [" << dbdir << "]\n"); continue; } if (stripped != o_index_stripchars) { LOGERR("Incompatible character stripping: [" << dbdir << "]\n"); continue; } prefs.allExtraDbs.push_back(dbdir); } } // Get the remembered "active external indexes": prefs.activeExtraDbs = g_dynconf->getStringEntries(actEdbsSk); // Clean up the list: remove directories which are not // actually there: useful for removable volumes. for (auto it = prefs.activeExtraDbs.begin(); it != prefs.activeExtraDbs.end();) { bool stripped; if (!Rcl::Db::testDbDir(*it, &stripped) || stripped != o_index_stripchars) { LOGINFO("Not a Xapian index or char stripping differs: [" << *it << "]\n"); it = prefs.activeExtraDbs.erase(it); } else { it++; } } // Get active db directives from the environment. This can only add to // the remembered and cleaned up list const char *cp4Act; if ((cp4Act = getenv("RECOLL_ACTIVE_EXTRA_DBS")) != 0) { vector dbl; stringToTokens(cp4Act, dbl, dirlistsep); for (const auto& path : dbl) { string dbdir = path_canon(path); path_catslash(dbdir); if (std::find(prefs.activeExtraDbs.begin(), prefs.activeExtraDbs.end(), dbdir) != prefs.activeExtraDbs.end()) continue; bool strpd; if (!Rcl::Db::testDbDir(dbdir, &strpd) || strpd != o_index_stripchars) { LOGERR("Not a Xapian dir or diff. char stripping: [" << dbdir << "]\n"); continue; } prefs.activeExtraDbs.push_back(dbdir); } //for } //if } #if 0 std::cerr << "All extra Dbs:\n"; for (const auto& dir : prefs.allExtraDbs) std::cerr << " [" << dir << "]\n"; std::cerr << "Active extra Dbs:\n"; for (const auto& dir : prefs.activeExtraDbs) std::cerr << " [" << dir << "]\n"; #endif const string asbdSk = "asearchSbd"; if (writing) { while (prefs.asearchSubdirHist.size() > 20) prefs.asearchSubdirHist.pop_back(); g_dynconf->eraseAll(asbdSk); for (const auto& qdbd : prefs.asearchSubdirHist) { g_dynconf->enterString(asbdSk, qs2utf8s(qdbd)); } } else { vector tl = g_dynconf->getStringEntries(asbdSk); for (const auto& dbd: tl) { prefs.asearchSubdirHist.push_back(u8s2qs(dbd)); } } if (!writing) { prefs.setupDarkCSS(); } if (!writing) havereadsettings = true; } #ifdef USE_REGEX /* font-size: 10px; */ static const std::string fntsz_exp( R"((\s*font-size\s*:\s*)([0-9]+)(p[tx]\s*;\s*))" ); static std::regex fntsz_regex(fntsz_exp); #endif // USE_REGEX std::string PrefsPack::scaleFonts(const std::string& style, float multiplier) { //cerr << "scale_fonts: multiplier: " << multiplier << "\n"; std::vector lines; stringToTokens(style, lines, "\n"); #ifdef USE_REGEX for (unsigned int ln = 0; ln < lines.size(); ln++) { const string& line = lines[ln]; std::smatch m; //std::cerr << "LINE: " << line << "\n"; if (regex_match(line, m, fntsz_regex) && m.size() == 4) { //std::cerr << "Got match (sz " << m.size() << ") for " << line << "\n"; int fs = atoi(m[2].str().c_str()); int nfs = std::round(fs * multiplier); char buf[20]; snprintf(buf, 20, "%d", nfs); lines[ln] = m[1].str() + buf + m[3].str(); //std::cerr << "New line: [" << lines[ln] << "]\n"; } } #endif // USE_REGEX string nstyle = string(); for (auto& ln : lines) { nstyle += ln + "\n"; } return nstyle; } std::string PrefsPack::htmlHeaderContents() { auto comfn = path_cat(path_cat(theconfig->getDatadir(), "examples"), "recoll-common.css"); std::string comcss; file_to_string(comfn, comcss); std::ostringstream oss; oss << comcss << "\n"; oss << "\n"; oss << qs2utf8s(prefs.darkreslistheadertext) << qs2utf8s(prefs.reslistheadertext); auto css = PrefsPack::scaleFonts(oss.str(), prefs.wholeuiscale); return css; } void PrefsPack::setupDarkCSS() { if (!darkMode) { darkreslistheadertext.clear(); return; } if (nullptr == theconfig) { return; } string fn = path_cat(path_cat(theconfig->getDatadir(), "examples"), "recoll-dark.css"); string data; string reason; if (!file_to_string(fn, data, &reason)) { std::cerr << "Recoll: Could not read: " << fn << "\n"; } darkreslistheadertext = u8s2qs(data); } string PrefsPack::stemlang() { string stemLang(qs2utf8s(prefs.queryStemLang)); if (stemLang == "ALL") { if (theconfig) theconfig->getConfParam("indexstemminglanguages", stemLang); else stemLang = ""; } return stemLang; } #ifndef _WIN32 // The Linux settings name unvontarily changed from // ~/.config/Recoll.org/recoll.conf to ~/.config/Recoll.org/recoll.ini // when the Windows version switched from registry to ini storage. Too // late to really fix as 1.26.6 was released (at least in the // lesbonscomptes repo and Debian unstable). For the lucky guys who // did not run 1.26.6, the following was added in 1.26.7 to rename the // file if the .ini target does not exist. static void maybeRenameGUISettings() { string opath = path_cat(path_home(), ".config/Recoll.org/recoll.conf"); string npath = path_cat(path_home(), ".config/Recoll.org/recoll.ini"); if (path_exists(opath) && !path_exists(npath)) { rename(opath.c_str(), npath.c_str()); } } #endif /* ! _WIN32 */ #ifdef SHOWEVENTS const char *eventTypeToStr(int tp) { switch (tp) { case 0: return "None"; case 1: return "Timer"; case 2: return "MouseButtonPress"; case 3: return "MouseButtonRelease"; case 4: return "MouseButtonDblClick"; case 5: return "MouseMove"; case 6: return "KeyPress"; case 7: return "KeyRelease"; case 8: return "FocusIn"; case 9: return "FocusOut"; case 10: return "Enter"; case 11: return "Leave"; case 12: return "Paint"; case 13: return "Move"; case 14: return "Resize"; case 15: return "Create"; case 16: return "Destroy"; case 17: return "Show"; case 18: return "Hide"; case 19: return "Close"; case 20: return "Quit"; case 21: return "ParentChange"; case 131: return "ParentAboutToChange"; case 22: return "ThreadChange"; case 24: return "WindowActivate"; case 25: return "WindowDeactivate"; case 26: return "ShowToParent"; case 27: return "HideToParent"; case 31: return "Wheel"; case 33: return "WindowTitleChange"; case 34: return "WindowIconChange"; case 35: return "ApplicationWindowIconChange"; case 36: return "ApplicationFontChange"; case 37: return "ApplicationLayoutDirectionChange"; case 38: return "ApplicationPaletteChange"; case 39: return "PaletteChange"; case 40: return "Clipboard"; case 42: return "Speech"; case 43: return "MetaCall"; case 50: return "SockAct"; case 132: return "WinEventAct"; case 52: return "DeferredDelete"; case 60: return "DragEnter"; case 61: return "DragMove"; case 62: return "DragLeave"; case 63: return "Drop"; case 64: return "DragResponse"; case 68: return "ChildAdded"; case 69: return "ChildPolished"; case 70: return "ChildInserted"; case 72: return "LayoutHint"; case 71: return "ChildRemoved"; case 73: return "ShowWindowRequest"; case 74: return "PolishRequest"; case 75: return "Polish"; case 76: return "LayoutRequest"; case 77: return "UpdateRequest"; case 78: return "UpdateLater"; case 79: return "EmbeddingControl"; case 80: return "ActivateControl"; case 81: return "DeactivateControl"; case 82: return "ContextMenu"; case 83: return "InputMethod"; case 86: return "AccessibilityPrepare"; case 87: return "TabletMove"; case 88: return "LocaleChange"; case 89: return "LanguageChange"; case 90: return "LayoutDirectionChange"; case 91: return "Style"; case 92: return "TabletPress"; case 93: return "TabletRelease"; case 94: return "OkRequest"; case 95: return "HelpRequest"; case 96: return "IconDrag"; case 97: return "FontChange"; case 98: return "EnabledChange"; case 99: return "ActivationChange"; case 100: return "StyleChange"; case 101: return "IconTextChange"; case 102: return "ModifiedChange"; case 109: return "MouseTrackingChange"; case 103: return "WindowBlocked"; case 104: return "WindowUnblocked"; case 105: return "WindowStateChange"; case 110: return "ToolTip"; case 111: return "WhatsThis"; case 112: return "StatusTip"; case 113: return "ActionChanged"; case 114: return "ActionAdded"; case 115: return "ActionRemoved"; case 116: return "FileOpen"; case 117: return "Shortcut"; case 51: return "ShortcutOverride"; case 30: return "Accel"; case 32: return "AccelAvailable"; case 118: return "WhatsThisClicked"; case 120: return "ToolBarChange"; case 121: return "ApplicationActivated"; case 122: return "ApplicationDeactivated"; case 123: return "QueryWhatsThis"; case 124: return "EnterWhatsThisMode"; case 125: return "LeaveWhatsThisMode"; case 126: return "ZOrderChange"; case 127: return "HoverEnter"; case 128: return "HoverLeave"; case 129: return "HoverMove"; case 119: return "AccessibilityHelp"; case 130: return "AccessibilityDescription"; case 150: return "EnterEditFocus"; case 151: return "LeaveEditFocus"; case 152: return "AcceptDropsChange"; case 153: return "MenubarUpdated"; case 154: return "ZeroTimerEvent"; case 155: return "GraphicsSceneMouseMove"; case 156: return "GraphicsSceneMousePress"; case 157: return "GraphicsSceneMouseRelease"; case 158: return "GraphicsSceneMouseDoubleClick"; case 159: return "GraphicsSceneContextMenu"; case 160: return "GraphicsSceneHoverEnter"; case 161: return "GraphicsSceneHoverMove"; case 162: return "GraphicsSceneHoverLeave"; case 163: return "GraphicsSceneHelp"; case 164: return "GraphicsSceneDragEnter"; case 165: return "GraphicsSceneDragMove"; case 166: return "GraphicsSceneDragLeave"; case 167: return "GraphicsSceneDrop"; case 168: return "GraphicsSceneWheel"; case 169: return "KeyboardLayoutChange"; case 170: return "DynamicPropertyChange"; case 171: return "TabletEnterProximity"; case 172: return "TabletLeaveProximity"; default: return "UnknownEvent"; } } #endif recoll-1.36.1/qtgui/fragbuts.h0000644000175000017500000000344214427373216013136 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _FRAGBUTS_H_INCLUDED_ #define _FRAGBUTS_H_INCLUDED_ #include #include #include #include class QAbstractButton; /* * Display a series of user-defined buttons which activate query * language fragments to augment the current search */ class FragButs : public QWidget { Q_OBJECT public: FragButs(QWidget* parent = 0); virtual ~FragButs(); FragButs(const FragButs&) = delete; FragButs& operator=(const FragButs&) = delete; struct ButFrag { QAbstractButton *button; std::string fragment; ButFrag(QAbstractButton *but, const std::string& frag) : button(but), fragment(frag) { } }; void getfrags(std::vector&); bool ok() {return m_ok;} bool isStale(time_t *reftime); private slots: void onButtonClicked(bool); signals: void fragmentsChanged(); private: std::vector m_buttons; std::string m_fn; time_t m_reftime; bool m_ok; }; #endif /* _FRAGBUTS_H_INCLUDED_ */ recoll-1.36.1/qtgui/rclm_preview.cpp0000644000175000017500000002267214444307651014357 00000000000000/* Copyright (C) 2005-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include "log.h" #include "internfile.h" #include "rclzg.h" #include "rclmain_w.h" using std::string; using std::vector; using std::map; static const QKeySequence quitKeySeq("Ctrl+q"); // If a preview (toplevel) window gets closed by the user, we need to // clean up because there is no way to reopen it. And check the case // where the current one is closed void RclMain::previewClosed(Preview *w) { LOGDEB("RclMain::previewClosed(" << w << ")\n"); if (w == curPreview) { LOGDEB("Active preview closed\n"); curPreview = 0; } else { LOGDEB("Old preview closed\n"); } } // Document up to date check. The main problem we try to solve is // displaying the wrong message from a compacted mail folder. // // Also we should re-run the query after updating the index because // the ipaths may be wrong in the current result list. For now, the // user does this by clicking search again once the indexing is done // // We only do this for the main index, else jump and prey (cant update // anyway, even the makesig() call might not make sense for our base // config) bool RclMain::containerUpToDate(Rcl::Doc& doc) { static bool ignore_out_of_date_preview = false; // If ipath is empty, we decide we don't care. Also, we need an index, if (ignore_out_of_date_preview || doc.ipath.empty() || rcldb == 0) return true; string udi; doc.getmeta(Rcl::Doc::keyudi, &udi); if (udi.empty()) { // Whatever... return true; } string sig; if (!FileInterner::makesig(theconfig, doc, sig)) { QMessageBox::warning(0, "Recoll", tr("Can't access file: ") + path2qs(doc.url)); // Let's try the preview anyway... return true; } if (!rcldb->needUpdate(udi, sig)) { // Alles ist in ordnung return true; } // Top level (container) document, for checking for indexing error string ctsig = "+"; Rcl::Doc ctdoc; if (rcldb->getContainerDoc(doc, ctdoc)) { ctdoc.getmeta(Rcl::Doc::keysig, &ctsig); } // We can only run indexing on the main index (dbidx 0) bool ismainidx = rcldb->fromMainIndex(doc); // Indexer already running? bool ixnotact = (m_indexerState == IXST_NOTRUNNING); QString msg = tr("Index not up to date for this file.
"); if (ctsig.back() == '+') { msg += tr("Also, it seems that the last index update for the file " "failed.
"); } if (ixnotact && ismainidx) { msg += tr("Click Ok to try to update the " "index for this file. You will need to " "run the query again when indexing is done.
"); } else if (ismainidx) { msg += tr("The indexer is running so things should " "improve when it's done. "); } else if (ixnotact) { // Not main index msg += tr("The document belongs to an external index " "which I can't update. "); } msg += tr("Click Cancel to return to the list.
" "Click Ignore to show the preview anyway (and remember for " "this session). There is a risk of showing the wrong entry.
"); QMessageBox::StandardButtons bts = QMessageBox::Ignore | QMessageBox::Cancel; if (ixnotact &&ismainidx) bts |= QMessageBox::Ok; int rep = QMessageBox::warning(0, tr("Warning"), msg, bts, (ixnotact && ismainidx) ? QMessageBox::Cancel : QMessageBox::NoButton); if (m_indexerState == IXST_NOTRUNNING && rep == QMessageBox::Ok) { LOGDEB("Requesting index update for " << doc.url << "\n"); vector docs(1, doc); updateIdxForDocs(docs); } if (rep == QMessageBox::Ignore) { ignore_out_of_date_preview = true; return true; } else { return false; } } /** * Open a preview window for a given document, or load it into new tab of * existing window. * * @param docnum db query index * @param mod keyboards modifiers like ControlButton, ShiftButton */ void RclMain::startPreview(int docnum, Rcl::Doc doc, int mod) { LOGDEB("startPreview(" << docnum << ", doc, " << mod << ")\n"); if (!containerUpToDate(doc)) return; // Do the zeitgeist thing zg_send_event(ZGSEND_PREVIEW, doc); if (mod & Qt::ShiftModifier) { // User wants new preview window curPreview = 0; } if (curPreview == 0) { HighlightData hdata; m_source->getTerms(hdata); curPreview = new Preview(this, reslist->listId(), hdata); if (curPreview == 0) { QMessageBox::warning(0, tr("Warning"), tr("Can't create preview window"), QMessageBox::Ok, QMessageBox::NoButton); return; } connect(new QShortcut(quitKeySeq, curPreview), SIGNAL (activated()), this, SLOT (fileExit())); connect(curPreview, SIGNAL(previewClosed(Preview *)), this, SLOT(previewClosed(Preview *))); connect(curPreview, SIGNAL(wordSelect(QString)), sSearch, SLOT(addTerm(QString))); connect(curPreview, SIGNAL(showNext(Preview *, int, int)), this, SLOT(previewNextInTab(Preview *, int, int))); connect(curPreview, SIGNAL(showPrev(Preview *, int, int)), this, SLOT(previewPrevInTab(Preview *, int, int))); connect(curPreview, SIGNAL(previewExposed(Preview *, int, int)), this, SLOT(previewExposed(Preview *, int, int))); connect(curPreview, SIGNAL(saveDocToFile(Rcl::Doc)), this, SLOT(saveDocToFile(Rcl::Doc))); connect(curPreview, SIGNAL(editRequested(Rcl::Doc)), this, SLOT(startNativeViewer(Rcl::Doc))); curPreview->setWindowTitle(getQueryDescription()); curPreview->show(); } curPreview->makeDocCurrent(doc, docnum); } /** * Open a preview window for a given document, no linking to result list * * This is used to show ie parent documents, which have no corresponding * entry in the result list. * */ void RclMain::startPreview(Rcl::Doc doc) { Preview *preview = new Preview(this, 0, HighlightData()); if (preview == 0) { QMessageBox::warning(0, tr("Warning"), tr("Can't create preview window"), QMessageBox::Ok, QMessageBox::NoButton); return; } connect(new QShortcut(quitKeySeq, preview), SIGNAL (activated()), this, SLOT (fileExit())); connect(preview, SIGNAL(wordSelect(QString)), sSearch, SLOT(addTerm(QString))); // Do the zeitgeist thing zg_send_event(ZGSEND_PREVIEW, doc); preview->show(); preview->makeDocCurrent(doc, 0); } // Show next document from result list in current preview tab void RclMain::previewNextInTab(Preview * w, int sid, int docnum) { previewPrevOrNextInTab(w, sid, docnum, true); } // Show previous document from result list in current preview tab void RclMain::previewPrevInTab(Preview * w, int sid, int docnum) { previewPrevOrNextInTab(w, sid, docnum, false); } // Combined next/prev from result list in current preview tab void RclMain::previewPrevOrNextInTab(Preview * w, int sid, int docnum, bool nxt) { LOGDEB("RclMain::previewNextInTab sid " << sid << " docnum " << docnum << ", listId " << reslist->listId() << "\n"); if (w == 0) // ?? return; if (sid != reslist->listId()) { QMessageBox::warning(0, "Recoll", tr("This search is not active anymore")); return; } if (nxt) docnum++; else docnum--; if (docnum < 0 || !m_source || docnum >= m_source->getResCnt()) { if (!prefs.noBeeps) { LOGDEB("Beeping\n"); QApplication::beep(); } else { LOGDEB("Not beeping because nobeep is set\n"); } return; } Rcl::Doc doc; if (!reslist->getDoc(docnum, doc)) { QMessageBox::warning(0, "Recoll", tr("Cannot retrieve document info from database")); return; } w->makeDocCurrent(doc, docnum, true); } // Preview tab exposed: if the preview comes from the currently // displayed result list, tell reslist (to color the paragraph) void RclMain::previewExposed(Preview *, int sid, int docnum) { LOGDEB2("RclMain::previewExposed: sid " << sid << " docnum " << docnum << ", m_sid " << reslist->listId() << "\n"); if (sid != reslist->listId()) { return; } reslist->previewExposed(docnum); } recoll-1.36.1/qtgui/rtitool.cpp0000644000175000017500000001300314427373216013342 00000000000000#ifndef _WIN32 /* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include "safesysstat.h" #include "safeunistd.h" #include #include #include #include "recoll.h" #include "rtitool.h" #include "smallut.h" #include "pathut.h" #include "copyfile.h" #include "readfile.h" #include "execmd.h" using std::string; using std::vector; using std::map; using std::list; static const char *rautostartfile = ".config/autostart/recollindex.desktop"; // Just in case we don't find the file in the shared dir, have a // default text ready static const char *desktopfiletext = "[Desktop Entry]\n" "Name=Recoll real time indexer\n" "Comment=Runs in background to extract and index text from modified " "documents\n" "Icon=system-run\n" "Exec=recollindex -w 60 -m\n" "Terminal=false\n" "TerminalOptions=\n" "Type=Application\n" "Categories=Utility;Filesystem;Database;\n" "NoDisplay=true\n" "X-GNOME-Autostart-enabled=true\n" "X-KDE-autostart-after=panel\n" "X-KDE-UniqueApplet=true\n" ; void RTIToolW::init() { connect(this->sesCB, SIGNAL(clicked(bool)), this, SLOT(sesclicked(bool))); string autostartfile = path_cat(path_home(), rautostartfile); if (path_exists(autostartfile)) { sesCB->setChecked(true); } } void RTIToolW::sesclicked(bool on) { nowCB->setEnabled(on); if (!on) nowCB->setChecked(false); } void RTIToolW::accept() { bool exitdial = false; string autostartfile = path_cat(path_home(), rautostartfile); if (sesCB->isChecked()) { // Setting up daemon indexing autostart if (path_exists(autostartfile)) { QString msg = tr("Replacing: ") + path2qs(autostartfile); QMessageBox::Button rep = QMessageBox::question(this, tr("Replacing file"), msg, QMessageBox::Ok | QMessageBox::Cancel); if (rep != QMessageBox::Ok) { goto out; } } string text; if (theconfig) { string sourcefile = path_cat(theconfig->getDatadir(), "examples"); sourcefile = path_cat(sourcefile, "recollindex.desktop"); if (path_exists(sourcefile)) { file_to_string(sourcefile, text); } } if (text.empty()) text = desktopfiletext; // Try to create .config and autostart anyway. If they exists this will // do nothing. An error will be detected when we try to create the file string dir = path_cat(path_home(), ".config"); mkdir(dir.c_str(), 0700); dir = path_cat(dir, "autostart"); mkdir(dir.c_str(), 0700); string reason; if (!stringtofile(text, autostartfile.c_str(), reason)) { QString msg = tr("Can't create: ") + path2qs(autostartfile); QMessageBox::warning(0, tr("Warning"), msg, QMessageBox::Ok); return; } if (nowCB->isChecked()) { ExecCmd cmd; vector args; int status; args.push_back("-m"); args.push_back("-w"); args.push_back("0"); status = cmd.doexec("recollindex", args, 0, 0); if (status) { QMessageBox::warning(0, tr("Warning"), tr("Could not execute recollindex"), QMessageBox::Ok); goto out; } } exitdial = true; } else { // Turning autostart off if (path_exists(autostartfile)) { QString msg = tr("Deleting: ") + path2qs(autostartfile); QMessageBox::Button rep = QMessageBox::question(this, tr("Deleting file"), msg, QMessageBox::Ok | QMessageBox::Cancel); if (rep == QMessageBox::Ok) { exitdial = true; unlink(autostartfile.c_str()); if (theconfig) { Pidfile pidfile(theconfig->getPidfile()); pid_t pid; if ((pid = pidfile.open()) != 0) { QMessageBox::Button rep = QMessageBox::question( this, tr("Removing autostart"), tr("Autostart file deleted. Kill current process too ?"), QMessageBox::Yes | QMessageBox::No); if (rep == QMessageBox::Yes) { kill(pid, SIGTERM); } } } } } else { exitdial = true; } } out: if (exitdial) QDialog::accept(); } #endif recoll-1.36.1/qtgui/crontool.h0000644000175000017500000000250214410615043013141 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _CRONTOOL_W_H_INCLUDED_ #define _CRONTOOL_W_H_INCLUDED_ #include "ui_crontool.h" class QPushButton; class CronToolW : public QDialog, public Ui::CronToolW { Q_OBJECT public: CronToolW(QWidget * parent = 0) : QDialog(parent), enableButton(0), disableButton(0) { setupUi(this); init(); } QPushButton *enableButton; QPushButton *disableButton; private slots: void enableCron(); void disableCron(); private: void init(); void changeCron(bool enable); }; #endif /* _CRONTOOL_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/preview_load.cpp0000644000175000017500000000546414427373216014342 00000000000000/* Copyright (C) 2014 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "log.h" #include "preview_load.h" #include "internfile.h" #include "rcldoc.h" #include "pathut.h" #include "cancelcheck.h" #include "rclconfig.h" using std::string; LoadThread::LoadThread(RclConfig *config, const Rcl::Doc& idc, bool pvhtm, QObject *parent) : QThread(parent), status(1), m_idoc(idc), m_previewHtml(pvhtm), m_config(*config) { } void LoadThread::run() { FileInterner interner(m_idoc, &m_config, FileInterner::FIF_forPreview); FIMissingStore mst; interner.setMissingStore(&mst); // Even when previewHtml is set, we don't set the interner's // target mtype to html because we do want the html filter to // do its work: we won't use the text/plain, but we want the // text/html to be converted to utf-8 (for highlight processing) try { string ipath = m_idoc.ipath; FileInterner::Status ret = interner.internfile(fdoc, ipath); if (ret == FileInterner::FIDone || ret == FileInterner::FIAgain) { // FIAgain is actually not nice here. It means that the record // for the *file* of a multidoc was selected. Actually this // shouldn't have had a preview link at all, but we don't know // how to handle it now. Better to show the first doc than // a mysterious error. Happens when the file name matches a // a search term. status = 0; // If we prefer HTML and it is available, replace the // text/plain document text if (m_previewHtml && !interner.get_html().empty()) { fdoc.text = interner.get_html(); fdoc.mimetype = "text/html"; } tmpimg = interner.get_imgtmp(); } else { fdoc.mimetype = interner.getMimetype(); mst.getMissingExternal(missing); explain = FileInterner::tryGetReason(&m_config, m_idoc); status = -1; } } catch (CancelExcept) { LOGDEB("LoadThread: cancelled\n" ); status = -1; } } recoll-1.36.1/qtgui/rclm_view.cpp0000644000175000017500000004444214502561012013633 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "safeunistd.h" #include #include #include #include #include "qxtconfirmationmessage.h" #include "log.h" #include "fileudi.h" #include "execmd.h" #include "transcode.h" #include "docseqhist.h" #include "docseqdb.h" #include "internfile.h" #include "rclmain_w.h" #include "rclzg.h" #include "pathut.h" #include "unacpp.h" using namespace std; // Browser list used if xdg-open fails for opening the help doc static const vector browser_list{ "opera", "google-chrome", "chromium-browser", "palemoon", "iceweasel", "firefox", "konqueror", "epiphany"}; // Start native viewer or preview for input Doc. This is used to allow using recoll from another app // (e.g. Unity Scope) to view embedded result docs (docs with an ipath). We act as a proxy to // extract the data and start a viewer. The URLs are encoded as file://path#ipath void RclMain::viewUrl() { if (m_urltoview.isEmpty() || !rcldb) return; QUrl qurl(m_urltoview); LOGDEB("RclMain::viewUrl: Path [" << qs2path(qurl.path()) << "] fragment [" << qs2path(qurl.fragment()) << "]\n"); /* In theory, the url might not be for a file managed by the fs indexer so that the make_udi() call here would be wrong(). When/if this happens we'll have to hide this part inside internfile and have some url magic to indicate the appropriate indexer/identification scheme */ string udi; make_udi(qs2path(qurl.path()), qs2path(qurl.fragment()), udi); Rcl::Doc doc; Rcl::Doc idxdoc; // idxdoc.idxi == 0 -> works with base index only if (!rcldb->getDoc(udi, idxdoc, doc) || doc.pc == -1) return; // StartNativeViewer needs a db source to call getEnclosing() on. Rcl::Query *query = new Rcl::Query(rcldb.get()); DocSequenceDb *src = new DocSequenceDb( rcldb, std::shared_ptr(query), "", std::make_shared()); m_source = std::shared_ptr(src); // Start a native viewer if the mimetype has one defined, else a preview. string apptag; doc.getmeta(Rcl::Doc::keyapptg, &apptag); string viewer = theconfig->getMimeViewerDef(doc.mimetype, apptag, prefs.useDesktopOpen); if (viewer.empty()) { startPreview(doc); } else { hide(); startNativeViewer(doc); // We have a problem here because xdg-open will exit // immediately after starting the command instead of waiting // for it, so we can't wait either and we don't know when we // can exit (deleting the temp file). As a bad workaround we // sleep some time then exit. The alternative would be to just // prevent the temp file deletion completely, leaving it // around forever. Better to let the user save a copy if he // wants I think. sleep(60); fileExit(); } } /* Look for HTML browser. We make a special effort for html because it's * used for reading help. This is only used if the normal approach * (xdg-open etc.) failed */ static bool lookForHtmlBrowser(string &exefile) { const char *path = getenv("PATH"); if (path == 0) { path = "/usr/local/bin:/usr/bin:/bin"; } // Look for each browser for (const auto& entry : browser_list) { if (ExecCmd::which(entry, exefile, path)) return true; } exefile.clear(); return false; } void RclMain::openWith(Rcl::Doc doc, string cmdspec) { LOGDEB("RclMain::openWith: " << cmdspec << "\n"); // Split the command line vector lcmd; if (!stringToStrings(cmdspec, lcmd)) { QMessageBox::warning(0, "Recoll", tr("Bad desktop app spec for %1: [%2]\n" "Please check the desktop file") .arg(u8s2qs(doc.mimetype)).arg(path2qs(cmdspec))); return; } // Look for the command to execute in the exec path and the filters // directory string execname = lcmd.front(); lcmd.erase(lcmd.begin()); string url = doc.url; string fn = fileurltolocalpath(doc.url); // Try to keep the letters used more or less consistent with the reslist paragraph format. map subs; #ifdef _WIN32 path_backslashize(fn); #endif subs["F"] = fn; subs["f"] = fn; subs["U"] = url_encode(url); subs["u"] = url; execViewer(subs, false, execname, lcmd, cmdspec, doc); } static bool pagenumNeeded(const std::string& cmd) { return cmd.find("%p") != std::string::npos; } static bool linenumNeeded(const std::string& cmd) { return cmd.find("%l") != std::string::npos; } static bool termNeeded(const std::string& cmd) { return cmd.find("%s") != std::string::npos; } void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString qterm, int linenum) { std::string term = qs2utf8s(qterm); string apptag; doc.getmeta(Rcl::Doc::keyapptg, &apptag); LOGDEB("RclMain::startNativeViewer: mtype [" << doc.mimetype << "] apptag [" << apptag << "] page " << pagenum << " term [" << term << "] url [" << doc.url << "] ipath [" << doc.ipath << "]\n"); // Look for appropriate viewer string cmdplusattr = theconfig->getMimeViewerDef(doc.mimetype, apptag, prefs.useDesktopOpen); if (cmdplusattr.empty()) { QMessageBox::warning(0, "Recoll", tr("No external viewer configured for mime type [") + doc.mimetype.c_str() + "]"); return; } LOGDEB("StartNativeViewer: viewerdef from config: " << cmdplusattr << "\n"); // Separate command string and viewer attributes (if any) ConfSimple viewerattrs; string cmd; theconfig->valueSplitAttributes(cmdplusattr, cmd, viewerattrs); bool ignoreipath = false; int execwflags = 0; if (viewerattrs.get("ignoreipath", cmdplusattr)) ignoreipath = stringToBool(cmdplusattr); if (viewerattrs.get("maximize", cmdplusattr)) { if (stringToBool(cmdplusattr)) { execwflags |= ExecCmd::EXF_MAXIMIZED; } } // Split the command line vector lcmd; if (!stringToStrings(cmd, lcmd)) { QMessageBox::warning(0, "Recoll", tr("Bad viewer command line for %1: [%2]\n" "Please check the mimeview file") .arg(u8s2qs(doc.mimetype)).arg(path2qs(cmd))); return; } // Look for the command to execute in the exec path and the filters // directory string execpath; if (!ExecCmd::which(lcmd.front(), execpath)) { execpath = theconfig->findFilter(lcmd.front()); // findFilter returns its input param if the filter is not in // the normal places. As we already looked in the path, we // have no use for a simple command name here (as opposed to // mimehandler which will just let execvp do its thing). Erase // execpath so that the user dialog will be started further // down. if (!execpath.compare(lcmd.front())) execpath.erase(); // Specialcase text/html because of the help browser need if (execpath.empty() && !doc.mimetype.compare("text/html") && apptag.empty()) { if (lookForHtmlBrowser(execpath)) { lcmd.clear(); lcmd.push_back(execpath); lcmd.push_back("%u"); } } } // Command not found: start the user dialog to help find another one: if (execpath.empty()) { QString mt = QString::fromUtf8(doc.mimetype.c_str()); QString message = tr("The viewer specified in mimeview for %1: %2" " is not found.\nDo you want to start the preferences dialog ?") .arg(mt).arg(path2qs(lcmd.front())); switch(QMessageBox::warning(0, "Recoll", message, QMessageBox::Yes|QMessageBox::No, QMessageBox::No)) { case QMessageBox::Yes: showUIPrefs(); if (uiprefs) uiprefs->showViewAction(mt); break; case QMessageBox::No: default: break; } // The user will have to click on the link again to try the // new command. return; } // Get rid of the command name. lcmd is now argv[1...n] lcmd.erase(lcmd.begin()); // Process the command arguments to determine if we need to create a temporary file. // If the command has a %i parameter it will manage the // un-embedding. Else if ipath is not empty, we need a temp file. // This can be overridden with the "ignoreipath" attribute bool groksipath = (cmd.find("%i") != string::npos) || ignoreipath; // We used to try being clever here, but actually, the only case // where we don't need a local file copy of the document (or // parent document) is the case of ??an HTML page?? with a non-file // URL (http or https). Trying to guess based on %u or %f is // doomed because we pass %u to xdg-open. 2023-01: can't see why the text/html // test any more. The type of URL should be enough ? Can't need a file if it's not file:// ? // If this does not work, we'll need an explicit attribute in the configuration. // Change needed for enabling an external indexer script, with, for example URLs like // joplin://x-callback-url/openNote?id=xxx and a non HTML MIME. bool wantsfile = false; bool wantsparentfile = cmd.find("%F") != string::npos; if (!wantsparentfile && (cmd.find("%f") != string::npos || urlisfileurl(doc.url))) { wantsfile = true; } if (wantsparentfile && !urlisfileurl(doc.url)) { QMessageBox::warning(0, "Recoll", tr("Viewer command line for %1 specifies " "parent file but URL is not file:// : unsupported") .arg(QString::fromUtf8(doc.mimetype.c_str()))); return; } if (wantsfile && wantsparentfile) { QMessageBox::warning(0, "Recoll", tr("Viewer command line for %1 specifies both " "file and parent file value: unsupported") .arg(QString::fromUtf8(doc.mimetype.c_str()))); return; } string url = doc.url; string fn = fileurltolocalpath(doc.url); Rcl::Doc pdoc; if (wantsparentfile) { // We want the path for the parent document. For example to // open the chm file, not the internal page. Note that we just // override the other file name in this case. if (!m_source || !m_source->getEnclosing(doc, pdoc)) { QMessageBox::warning(0, "Recoll", tr("Cannot find parent document")); return; } // Override fn with the parent's : fn = fileurltolocalpath(pdoc.url); // If the parent document has an ipath too, we need to create // a temp file even if the command takes an ipath // parameter. We have no viewer which could handle a double // embedding. Will have to change if such a one appears. if (!pdoc.ipath.empty()) { groksipath = false; } } // Can't remember what enterHistory was actually for. Set it to // true always for now bool enterHistory = true; bool istempfile = false; LOGDEB("StartNativeViewer: groksipath " << groksipath << " wantsf " << wantsfile << " wantsparentf " << wantsparentfile << "\n"); bool wantedfile_doc_has_ipath = (wantsfile && !doc.ipath.empty()) || (wantsparentfile && !pdoc.ipath.empty()); // If the command wants a file but this is not a file url, or // there is an ipath that it won't understand, we need a temp file: theconfig->setKeyDir(fn.empty() ? "" : path_getfather(fn)); if (((wantsfile || wantsparentfile) && fn.empty()) || (!groksipath && wantedfile_doc_has_ipath) ) { TempFile temp; Rcl::Doc& thedoc = wantsparentfile ? pdoc : doc; if (!FileInterner::idocToFile(temp, string(), theconfig, thedoc)) { QMessageBox::warning(0, "Recoll", tr("Cannot extract document or create temporary file")); return; } enterHistory = true; istempfile = true; rememberTempFile(temp); fn = temp.filename(); url = path_pathtofileurl(fn); } // If using an actual file, check that it exists, and if it is // compressed, we may need an uncompressed version if (!fn.empty() && theconfig->mimeViewerNeedsUncomp(doc.mimetype)) { if (!path_readable(fn)) { QMessageBox::warning(0, "Recoll", tr("Can't access file: ") + u8s2qs(fn)); return; } TempFile temp; if (FileInterner::isCompressed(fn, theconfig)) { if (!FileInterner::maybeUncompressToTemp(temp, fn, theconfig,doc)) { QMessageBox::warning(0, "Recoll", tr("Can't uncompress file: ") + path2qs(fn)); return; } } if (temp.ok()) { istempfile = true; rememberTempFile(temp); fn = temp.filename(); url = path_pathtofileurl(fn); } } if (istempfile) { QxtConfirmationMessage confirm( QMessageBox::Warning, "Recoll", tr("Opening a temporary copy. Edits will be lost if you don't save" "
them to a permanent location."), tr("Do not show this warning next time (use GUI preferences to restore).")); confirm.setOverrideSettingsKey("/Recoll/prefs/showTempFileWarning"); confirm.exec(); QSettings settings; prefs.showTempFileWarning = settings.value("/Recoll/prefs/showTempFileWarning").toInt(); } // If we are not called with a page number (which would happen for a call from the snippets // window), see if we can compute a page number anyway. if (m_source && pagenum == -1 && (pagenumNeeded(cmd) || termNeeded(cmd)|| linenumNeeded(cmd))) { pagenum = m_source->getFirstMatchPage(doc, term); if (pagenum == -1) pagenum = 1; } if (linenum < 1 && m_source && !term.empty() && linenumNeeded(cmd)) { if (doc.text.empty()) { rcldb->getDocRawText(doc); } linenum = m_source->getFirstMatchLine(doc, term); } // Substitute %xx inside arguments string efftime; if (!doc.dmtime.empty() || !doc.fmtime.empty()) { efftime = doc.dmtime.empty() ? doc.fmtime : doc.dmtime; } else { efftime = "0"; } // Try to keep the letters used more or less consistent with the reslist // paragraph format. map subs; subs["D"] = efftime; #ifdef _WIN32 path_backslashize(fn); #endif subs["f"] = fn; subs["F"] = fn; subs["i"] = FileInterner::getLastIpathElt(doc.ipath); subs["l"] = ulltodecstr(linenum); subs["M"] = doc.mimetype; subs["p"] = ulltodecstr(pagenum); subs["s"] = term; subs["U"] = url_encode(url); subs["u"] = url; // Let %(xx) access all metadata. for (const auto& ent :doc.meta) { subs[ent.first] = ent.second; } execViewer(subs, enterHistory, execpath, lcmd, cmd, doc, execwflags); } void RclMain::execViewer( const map& subs, bool enterHistory, const string& execpath, const vector& _lcmd, const string& cmd, Rcl::Doc doc, int flags) { vector lcmd; for (const auto& oparm : _lcmd) { string nparm; pcSubst(oparm, nparm, subs); LOGDEB0("" << oparm << "->" << nparm << "\n"); lcmd.push_back(nparm); } // Also substitute inside the unsplit command line for display in status bar string ncmd; pcSubst(cmd, ncmd, subs); #ifndef _WIN32 ncmd += " &"; #endif QStatusBar *stb = statusBar(); if (stb) { string prcmd; #ifdef _WIN32 prcmd = ncmd; #else string fcharset = theconfig->getDefCharset(true); transcode(ncmd, prcmd, fcharset, "UTF-8"); #endif QString msg = tr("Executing: [") + QString::fromUtf8(prcmd.c_str()) + "]"; stb->showMessage(msg, 10000); } if (enterHistory) historyEnterDoc(rcldb.get(), g_dynconf, doc); // Do the zeitgeist thing zg_send_event(ZGSEND_OPEN, doc); // We keep pushing back and never deleting. This can't be good... ExecCmd *ecmd = new ExecCmd(ExecCmd::EXF_SHOWWINDOW | flags); m_viewers.push_back(ecmd); ecmd->startExec(execpath, lcmd, false, false); } void RclMain::startManual() { startManual(string()); } void RclMain::startManual(const string& index) { string docdir = path_cat(theconfig->getDatadir(), "doc"); // The single page user manual is nicer if we have an index. Else // the webhelp one is nicer if it is present string usermanual = path_cat(docdir, "usermanual.html"); string webhelp = path_cat(docdir, "webhelp"); webhelp = path_cat(webhelp, "index.html"); bool has_wh = path_exists(webhelp); LOGDEB("RclMain::startManual: help index is " << (index.empty() ? "(null)" : index) << "\n"); bool indexempty = index.empty(); #ifdef _WIN32 // On Windows I could not find any way to pass the fragment through // rclstartw (tried to set text/html as exception with rclstartw %u). // So always start the webhelp indexempty = true; #endif if (!indexempty) { usermanual += "#"; usermanual += index; } Rcl::Doc doc; if (has_wh && indexempty) { doc.url = path_pathtofileurl(webhelp); } else { doc.url = path_pathtofileurl(usermanual); } doc.mimetype = "text/html"; doc.addmeta(Rcl::Doc::keyapptg, "rclman"); startNativeViewer(doc); } recoll-1.36.1/qtgui/ptrans_w.h0000644000175000017500000000277514410615043013153 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _PTRANS_W_H_INCLUDED_ #define _PTRANS_W_H_INCLUDED_ #include #include #include #include "ui_ptrans.h" class QTableWidgetItem; class EditTrans : public QDialog, public Ui::EditTransBase { Q_OBJECT public: EditTrans(const std::string& dbdir, QWidget* parent = 0) : QDialog(parent) { setupUi(this); init(dbdir); } public slots: virtual void onItemDoubleClicked(QTableWidgetItem *); virtual void on_savePB_clicked(); virtual void on_addPB_clicked(); virtual void on_delPB_clicked(); virtual void on_transTW_itemSelectionChanged(); private: virtual void init(const std::string& dbdir); std::string m_dbdir; }; #endif /* _PTRANS_W_H_INCLUDED_ */ recoll-1.36.1/qtgui/restable.ui0000644000175000017500000000360714410615043013300 00000000000000 ResTable 0 0 640 480 0 0 Qt::Vertical 0 2 QAbstractItemView::NoEditTriggers false true true false false false false 0 0 recoll-1.36.1/qtgui/scbase.cpp0000644000175000017500000001216014410615043013076 00000000000000/* Copyright (C) 2021 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "scbase.h" #include #include #include #include "recoll.h" #include "smallut.h" #include "log.h" struct SCDef { QString id; QString ctxt; QString desc; QKeySequence val; QKeySequence dflt; }; class SCBase::Internal { public: QStringList getAll(const std::map&); std::map scdefs; std::map scvalues; QString scBaseSettingsKey() { return "/Recoll/prefs/sckeys"; } }; SCBase::SCBase() { m = new Internal(); QSettings settings; auto sl = settings.value(m->scBaseSettingsKey()).toStringList(); for (int i = 0; i < sl.size(); ++i) { auto ssc = qs2utf8s(sl.at(i)); std::vector co_des_val; stringToStrings(ssc, co_des_val); if (co_des_val.size() != 4) { LOGERR("Bad shortcut def in prefs: [" << ssc << "]\n"); continue; } QString id = u8s2qs(co_des_val[0]); QString ctxt = u8s2qs(co_des_val[1]); QString desc = u8s2qs(co_des_val[2]); QString val = u8s2qs(co_des_val[3]); auto it = m->scvalues.find(id); if (it == m->scvalues.end()) { m->scvalues[id] = SCDef{id, ctxt, desc, QKeySequence(val), QKeySequence()}; } else { it->second.val = QKeySequence(val); } } } SCBase::~SCBase() { delete m; } QKeySequence SCBase::get(const QString& id, const QString& ctxt, const QString& desc, const QString& defks) { LOGDEB0("SCBase::get: id "<< qs2utf8s(id) << " ["<scdefs[id] = SCDef{id, ctxt, desc, QKeySequence(defks), QKeySequence(defks)}; auto it = m->scvalues.find(id); if (it == m->scvalues.end()) { if (defks.isEmpty()) { return QKeySequence(); } QKeySequence qks(defks); m->scvalues[id] = SCDef{id, ctxt, desc, qks, qks}; LOGDEB0("get(" << qs2utf8s(ctxt) << ", " << qs2utf8s(desc) << ", " << qs2utf8s(defks) << ") -> " << qs2utf8s(qks.toString()) << "\n"); return qks; } LOGDEB0("SCBase::get(" << qs2utf8s(ctxt) << ", " << qs2utf8s(desc) << ", " << qs2utf8s(defks) << ") -> " << qs2utf8s(it->second.val.toString()) << "\n"); it->second.dflt = QKeySequence(defks); return it->second.val; } void SCBase::set(const QString& id, const QString& ctxt, const QString& desc, const QString& newks) { LOGDEB0("SCBase::set: id "<< qs2utf8s(id) << "["<< qs2utf8s(ctxt) << "]/[" << qs2utf8s(desc) << "], [" << qs2utf8s(newks) << "]\n"); auto it = m->scvalues.find(id); if (it == m->scvalues.end()) { QKeySequence qks(newks); m->scvalues[id] = SCDef{id, ctxt, desc, qks, QKeySequence()}; return; } it->second.val = newks; } QStringList SCBase::Internal::getAll(const std::map& mp) { QStringList result; for (const auto& entry : mp) { result.push_back(entry.second.id); result.push_back(entry.second.ctxt); result.push_back(entry.second.desc); result.push_back(entry.second.val.toString()); result.push_back(entry.second.dflt.toString()); } return result; } QStringList SCBase::getAll() { return m->getAll(m->scvalues); } QStringList SCBase::getAllDefaults() { return m->getAll(m->scdefs); } void SCBase::store() { QStringList slout; for (const auto& entry : m->scvalues) { const SCDef& def = entry.second; if (def.val != def.dflt) { std::string e = stringsToString(std::vector{ qs2utf8s(def.id), qs2utf8s(def.ctxt), qs2utf8s(def.desc), qs2utf8s(def.val.toString())}); LOGDEB0("SCBase::store: storing: [" << e << "]\n"); slout.append(u8s2qs(e)); } } // Note: we emit even if the non-default values are not really // changed, just not worth the trouble doing otherwise. emit shortcutsChanged(); QSettings settings; settings.setValue(m->scBaseSettingsKey(), slout); } static SCBase *theBase; SCBase& SCBase::scBase() { if (nullptr == theBase) { theBase = new SCBase(); } return *theBase; } recoll-1.36.1/qtgui/specialindex.ui0000644000175000017500000001263314410615043014146 00000000000000 SpecIdxW Qt::WindowModal 0 0 610 192 Special Indexing Retry previously failed files. Else only modified or failed files will be processed. Erase selected files data before indexing. 8 0 300 0 Directory to recursively index. This must be inside the regular indexed area<br> as defined in the configuration file (topdirs). Browse false Start directory. Must be part of the indexed tree. Use full indexed area if empty. false Leave empty to select all files. You can use multiple space-separated shell-type patterns.<br>Patterns with embedded spaces should be quoted with double quotes.<br>Can only be used if the start target is set. Selection patterns: 8 0 300 0 Browse false Diagnostics output file. Will be truncated and receive indexing diagnostics (reasons for files not being indexed). false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() SpecIdxW accept() 248 254 157 274 buttonBox rejected() SpecIdxW reject() 316 260 286 274 recoll-1.36.1/qtgui/advshist.cpp0000644000175000017500000000515714427373216013506 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "advshist.h" #include "guiutils.h" #include "log.h" #include "xmltosd.h" #include "dynconf.h" using namespace std; using namespace Rcl; AdvSearchHist::AdvSearchHist() { read(); } AdvSearchHist::~AdvSearchHist() { for (auto& entry : m_entries) { entry.reset(); } } std::shared_ptr AdvSearchHist::getnewest() { if (m_entries.empty()) return std::shared_ptr(); return m_entries[0]; } std::shared_ptr AdvSearchHist::getolder() { m_current++; if (m_current >= int(m_entries.size())) { m_current--; return std::shared_ptr(); } return m_entries[m_current]; } std::shared_ptr AdvSearchHist::getnewer() { if (m_current == -1 || m_current == 0 || m_entries.empty()) return std::shared_ptr(); return m_entries[--m_current]; } bool AdvSearchHist::push(std::shared_ptr sd) { m_entries.insert(m_entries.begin(), sd); if (m_current != -1) m_current++; string xml = sd->asXML(); // dynconf interprets <= 0 as unlimited size, but we want 0 to // disable saving history if (prefs.historysize != 0) { g_dynconf->enterString(advSearchHistSk, xml, prefs.historysize); } return true; } bool AdvSearchHist::read() { if (!g_dynconf) return false; // getStringEntries() return the entries in order (lower key // first), but we want most recent first, so revert vector lxml = g_dynconf->getStringEntries(advSearchHistSk); for (auto it = lxml.rbegin(); it != lxml.rend(); it++) { std::shared_ptr sd = xmlToSearchData(*it); if (sd) m_entries.push_back(sd); } return true; } void AdvSearchHist::clear() { g_dynconf->eraseAll(advSearchHistSk); } recoll-1.36.1/qtgui/recoll.h0000644000175000017500000000635114427373216012603 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RECOLL_H_INCLUDED_ #define _RECOLL_H_INCLUDED_ #include #include #include "rclconfig.h" #include "rcldb.h" #include "rclutil.h" #include // Misc declarations in need of sharing between the UI files // Open the database if needed. We now force a close/open by default extern bool maybeOpenDb(std::string &reason, bool force, bool *maindberror = 0); extern const std::vector *getCurrentExtraDbs(); /** Retrieve configured stemming languages */ bool getStemLangs(std::vector& langs); extern RclConfig *theconfig; class RclDynConf; extern RclDynConf *g_dynconf; class AdvSearchHist; extern AdvSearchHist *g_advshistory; extern TempFile *rememberTempFile(TempFile); extern void forgetTempFile(std::string &fn); extern void deleteAllTempFiles(); extern std::shared_ptr rcldb; extern int recollNeedsExit; extern void startManual(const std::string& helpindex); extern void applyStyleSheet(const QString&); inline std::string qs2utf8s(const QString& qs) { auto qb = qs.toUtf8(); return std::string(qb.data(), qb.size()); } inline std::string qs2u8s(const QString& qs) { auto qb = qs.toUtf8(); return std::string(qb.data(), qb.size()); } inline QString u8s2qs(const std::string& us) { return QString::fromUtf8(us.c_str(), us.size()); } inline QString path2qs(const std::string& us) { #ifdef _WIN32 return QString::fromUtf8(us.c_str()); #else return QString::fromLocal8Bit(us.c_str()); #endif } inline std::string qs2path(const QString& qs) { #ifdef _WIN32 return qs2utf8s(qs); #else return (const char*)qs.toLocal8Bit(); #endif } /** Specialized version of the qt file dialog. Can't use getOpenFile() etc. cause they hide dot files... Need something more adaptable than the static functions but less complex than the full dialog */ // Also : can't keep adding parms with default values, we now use an object as parameter. class MyGFNParams { public: QString caption; bool filenosave{false}; QString dirlocation; // Note: this holds the new location on return QString dfltnm; std::vector sidedirs; bool readonly{false}; }; extern QString myGetFileName(bool isdir, QString caption = QString(), bool filenosave = false, QString dirlocation = QString(), QString dlftnm = QString() ); extern QString myGetFileName(bool isdir, MyGFNParams &parms); #endif /* _RECOLL_H_INCLUDED_ */ recoll-1.36.1/qtgui/fragbuts.cpp0000644000175000017500000001431514467416451013475 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "safesysstat.h" #include #include #include #include #include #include #include #include #include #include "fragbuts.h" #include "pathut.h" #include "smallut.h" #include "recoll.h" #include "log.h" #include "readfile.h" #include "copyfile.h" #include "picoxml.h" using namespace std; class FragButsParser : public PicoXMLParser { public: FragButsParser( const std::string& in, FragButs *_p, vector& _bts) : PicoXMLParser(in), parent(_p), vlw(new QVBoxLayout(parent)), vl(new QVBoxLayout()), buttons(_bts) {} void startElement(const std::string &nm, const std::map&) override { //std::cerr << "startElement [" << nm << "]\n"; currentText.clear(); if (nm == "buttons") { radio = false; hl = new QHBoxLayout(); } else if (nm == "radiobuttons") { radio = true; bg = new QButtonGroup(parent); hl = new QHBoxLayout(); } else if (nm == "label" || nm == "frag" || nm == "fragbuts" || nm == "fragbuttons" || nm == "fragbut" || nm == "fragbutton" || nm == "message") { } else { QMessageBox::warning(0, "Recoll", QString("Bad element name: [%1]").arg(nm.c_str())); } } void endElement(const std::string& nm) override { //std::cerr << "endElement [" << nm << "]\n"; if (nm == "label") { label = u8s2qs(currentText); } else if (nm == "frag") { frag = currentText; } else if (nm == "fragbut" || nm == "fragbutton") { string slab = qs2utf8s(label); trimstring(slab, " \t\n\t"); label = u8s2qs(slab.c_str()); QAbstractButton *abut; if (radio) { QRadioButton *but = new QRadioButton(label, parent); bg->addButton(but); if (bg->buttons().length() == 1) but->setChecked(true); abut = but; } else { QCheckBox *but = new QCheckBox(label, parent); abut = but; } abut->setToolTip(u8s2qs(currentText)); buttons.push_back(FragButs::ButFrag(abut, frag)); hl->addWidget(abut); } else if (nm == "message") { string slab = qs2utf8s(label); trimstring(slab, " \t\n\t"); label = u8s2qs(slab.c_str()); QLabel *lbl = new QLabel(label, parent); hl->addWidget(lbl); } else if (nm == "buttons" || nm == "radiobuttons") { vl->addLayout(hl); hl = 0; } else if (nm == "fragbuts" || nm == "fragbuttons") { vlw->addLayout(vl); } else { QMessageBox::warning( 0, "Recoll", QString("Bad element name: [%1]").arg(nm.c_str())); } } void characterData(const std::string &str) override { //std::cerr << "characterData [" << str << "]\n"; currentText += str; } private: QWidget *parent; QVBoxLayout *vlw; QVBoxLayout *vl; vector& buttons; // Temporary data while parsing. QHBoxLayout *hl{nullptr}; QButtonGroup *bg{nullptr}; QString label; std::string currentText; std::string frag; bool radio{false}; }; FragButs::FragButs(QWidget* parent) : QWidget(parent), m_reftime(0), m_ok(false) { m_fn = path_cat(theconfig->getConfDir(), "fragment-buttons.xml"); string data, reason; if (!path_exists(m_fn)) { // Try the older name m_fn = path_cat(theconfig->getConfDir(), "fragbuts.xml"); if (!path_exists(m_fn)) { // No configuration file yet: create it from the sample file string src = path_cat(theconfig->getDatadir(), "examples"); src = path_cat(src, "fragment-buttons.xml"); m_fn = path_cat(theconfig->getConfDir(), "fragment-buttons.xml"); copyfile(src.c_str(), m_fn.c_str(), reason); } } if (!file_to_string(m_fn, data, &reason)) { QMessageBox::warning(0, "Recoll", tr("%1 not found.").arg(path2qs(m_fn))); LOGERR("Fragbuts:: can't read [" << m_fn << "]\n"); return; } FragButsParser parser(data, this, m_buttons); if (!parser.Parse()) { QMessageBox::warning(0, "Recoll", tr("%1:\n %2").arg(path2qs(m_fn)) .arg(u8s2qs(parser.getLastErrorMessage()))); return; } for (auto& entry : m_buttons) { connect(entry.button, SIGNAL(clicked(bool)), this, SLOT(onButtonClicked(bool))); } setWindowTitle(tr("Query Fragments")); isStale(&m_reftime); m_ok = true; } FragButs::~FragButs() { } bool FragButs::isStale(time_t *reftime) { struct stat st; stat(m_fn.c_str(), &st); bool ret = st.st_mtime != m_reftime; if (reftime) *reftime = st.st_mtime; return ret; } void FragButs::onButtonClicked(bool on) { LOGDEB("FragButs::onButtonClicked: [" << (int(on)) << "]\n" ); emit fragmentsChanged(); } void FragButs::getfrags(std::vector& frags) { for (auto &entry : m_buttons) { if (entry.button->isChecked() && !entry.fragment.empty()) { LOGDEB("FragButs: fragment [" << entry.fragment << "]\n" ); frags.push_back(entry.fragment); } } } recoll-1.36.1/qtgui/spell_w.cpp0000644000175000017500000003725014444307651013324 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "recoll.h" #include "spell_w.h" #include "guiutils.h" #include "rcldb.h" #include "searchdata.h" #include "rclquery.h" #include "rclhelp.h" #include "wasatorcl.h" #include "execmd.h" #include "indexer.h" #include "fstreewalk.h" using std::list; using std::multimap; using std::string; using std::vector; using std::pair; inline bool wordlessMode(SpellW::comboboxchoice v) { return (v == SpellW::TYPECMB_STATS || v == SpellW::TYPECMB_FAILED); } void SpellW::init() { m_c2t.clear(); expTypeCMB->addItem(tr("Wildcards")); m_c2t.push_back(TYPECMB_WILD); expTypeCMB->addItem(tr("Regexp")); m_c2t.push_back(TYPECMB_REG); expTypeCMB->addItem(tr("Stem expansion")); m_c2t.push_back(TYPECMB_STEM); expTypeCMB->addItem(tr("Spelling/Phonetic")); m_c2t.push_back(TYPECMB_SPELL); expTypeCMB->addItem(tr("Show index statistics")); m_c2t.push_back(TYPECMB_STATS); expTypeCMB->addItem(tr("List files which could not be indexed (slow)")); m_c2t.push_back(TYPECMB_FAILED); // Stemming language combobox stemLangCMB->clear(); vector langs; if (!getStemLangs(langs)) { QMessageBox::warning(0, "Recoll", tr("error retrieving stemming languages")); } for (vector::const_iterator it = langs.begin(); it != langs.end(); it++) { stemLangCMB->addItem(u8s2qs(*it)); } (void)new HelpClient(this); HelpClient::installMap((const char *)this->objectName().toUtf8(), "RCL.SEARCH.GUI.TERMEXPLORER"); // signals and slots connections connect(baseWordLE, SIGNAL(textChanged(const QString&)), this, SLOT(wordChanged(const QString&))); connect(baseWordLE, SIGNAL(returnPressed()), this, SLOT(doExpand())); connect(expandPB, SIGNAL(clicked()), this, SLOT(doExpand())); connect(dismissPB, SIGNAL(clicked()), this, SLOT(close())); connect(expTypeCMB, SIGNAL(activated(int)), this, SLOT(onModeChanged(int))); resTW->setShowGrid(0); #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) resTW->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); #else resTW->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); #endif resTW->verticalHeader()->setDefaultSectionSize(20); connect(resTW, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(textDoubleClicked(int, int))); resTW->setColumnWidth(0, 200); resTW->setColumnWidth(1, 150); resTW->installEventFilter(this); int idx = cmbIdx((comboboxchoice)prefs.termMatchType); expTypeCMB->setCurrentIndex(idx); onModeChanged(idx); } int SpellW::cmbIdx(comboboxchoice mode) { vector::const_iterator it = std::find(m_c2t.begin(), m_c2t.end(), mode); if (it == m_c2t.end()) it = m_c2t.begin(); return it - m_c2t.begin(); } static const int maxexpand = 10000; /* Expand term according to current mode */ void SpellW::doExpand() { int idx = expTypeCMB->currentIndex(); if (idx < 0 || idx >= int(m_c2t.size())) idx = 0; comboboxchoice mode = m_c2t[idx]; // Can't clear qt4 table widget: resets column headers too resTW->setRowCount(0); if (baseWordLE->text().isEmpty() && !wordlessMode(mode)) return; string reason; if (!maybeOpenDb(reason, false)) { QMessageBox::critical(0, "Recoll", QString(reason.c_str())); LOGDEB("SpellW::doExpand: db error: " << (reason) << "\n" ); return; } int mt; switch(mode) { case TYPECMB_WILD: mt = Rcl::Db::ET_WILD; break; case TYPECMB_REG: mt = Rcl::Db::ET_REGEXP; break; case TYPECMB_STEM: mt = Rcl::Db::ET_STEM; break; default: mt = Rcl::Db::ET_WILD; } if (caseSensCB->isChecked()) { mt |= Rcl::Db::ET_CASESENS; } if (diacSensCB->isChecked()) { mt |= Rcl::Db::ET_DIACSENS; } Rcl::TermMatchResult res; string expr = string((const char *)baseWordLE->text().toUtf8()); Rcl::DbStats dbs; rcldb->dbStats(dbs, false); switch (mode) { case TYPECMB_WILD: default: case TYPECMB_REG: case TYPECMB_STEM: { string l_stemlang = qs2utf8s(stemLangCMB->currentText()); if (!rcldb->termMatch(mt, l_stemlang, expr, res, maxexpand)) { LOGERR("SpellW::doExpand:rcldb::termMatch failed\n" ); return; } statsLBL->setText(tr("Index: %1 documents, average length %2 terms." "%3 results") .arg(dbs.dbdoccount).arg(dbs.dbavgdoclen, 0, 'f', 0) .arg(res.entries.size())); } break; case TYPECMB_SPELL: { LOGDEB("SpellW::doExpand: spelling [" << expr << "]\n" ); vector suggs; if (!rcldb->getSpellingSuggestions(expr, suggs)) { QMessageBox::warning(0, "Recoll", tr("Spell expansion error.")); } for (const auto& it : suggs) { res.entries.push_back(Rcl::TermMatchEntry(it)); } statsLBL->setText(tr("%1 results").arg(res.entries.size())); } break; case TYPECMB_STATS: { showStats(); return; } break; case TYPECMB_FAILED: { showFailed(); return; } break; } if (res.entries.empty()) { resTW->setItem(0, 0, new QTableWidgetItem(tr("No expansion found"))); } else { int row = 0; // maxexpand is static const, thus guaranteed to be >0 if (int(res.entries.size()) >= maxexpand) { resTW->setRowCount(row + 1); resTW->setSpan(row, 0, 1, 2); resTW->setItem(row++, 0, new QTableWidgetItem( tr("List was truncated alphabetically, " "some frequent "))); resTW->setRowCount(row + 1); resTW->setSpan(row, 0, 1, 2); resTW->setItem(row++, 0, new QTableWidgetItem( tr("terms may be missing. " "Try using a longer root."))); resTW->setRowCount(row + 1); resTW->setItem(row++, 0, new QTableWidgetItem("")); } for (vector::iterator it = res.entries.begin(); it != res.entries.end(); it++) { LOGDEB2("SpellW::expand: " << it->wcf << " [" << it->term << "]\n"); char num[30]; if (it->wcf) sprintf(num, "%d / %d", it->docs, it->wcf); else num[0] = 0; resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(u8s2qs(it->term))); resTW->setItem(row++, 1, new QTableWidgetItem(QString::fromUtf8(num))); } } } void SpellW::showStats() { statsLBL->setText(""); int row = 0; Rcl::DbStats res; if (!rcldb->dbStats(res, false)) { LOGERR("SpellW::doExpand:rcldb::dbStats failed\n" ); return; } resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr("Number of documents"))); resTW->setItem(row++, 1, new QTableWidgetItem( QString::number(res.dbdoccount))); resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr("Average terms per document"))); resTW->setItem(row++, 1, new QTableWidgetItem( QString::number(res.dbavgdoclen, 'f', 0))); resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr("Smallest document length (terms)"))); resTW->setItem(row++, 1, new QTableWidgetItem( QString::number(res.mindoclen))); resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr("Longest document length (terms)"))); resTW->setItem(row++, 1, new QTableWidgetItem( QString::number(res.maxdoclen))); if (!theconfig) return; DbIxStatus st; readIdxStatus(theconfig, st); resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr("Results from last indexing:"))); resTW->setItem(row++, 1, new QTableWidgetItem("")); resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr(" Documents created/updated"))); resTW->setItem(row++, 1, new QTableWidgetItem(QString::number(st.docsdone))); resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr(" Files tested"))); resTW->setItem(row++, 1, new QTableWidgetItem(QString::number(st.filesdone))); resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr(" Unindexed files"))); resTW->setItem(row++, 1, new QTableWidgetItem(QString::number(st.fileerrors))); baseWordLE->setText(path2qs(theconfig->getDbDir())); int64_t dbkbytes = fsTreeBytes(theconfig->getDbDir()) / 1024; if (dbkbytes < 0) { dbkbytes = 0; } resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr("Database directory size"))); resTW->setItem(row++, 1, new QTableWidgetItem( u8s2qs(displayableBytes(dbkbytes*1024)))); vector allmimetypes = theconfig->getAllMimeTypes(); multimap mtbycnt; for (vector::const_iterator it = allmimetypes.begin(); it != allmimetypes.end(); it++) { string reason; string q = string("mime:") + *it; std::shared_ptr sd = wasaStringToRcl(theconfig, "", q, reason); Rcl::Query query(rcldb.get()); if (!query.setQuery(sd)) { LOGERR("Query setup failed: " << (query.getReason()) << "" ); return; } int cnt = query.getResCnt(); mtbycnt.insert(pair(cnt,*it)); } resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(tr("MIME types:"))); resTW->setItem(row++, 1, new QTableWidgetItem("")); for (multimap::const_reverse_iterator it = mtbycnt.rbegin(); it != mtbycnt.rend(); it++) { resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(QString(" ") + u8s2qs(it->second))); resTW->setItem(row++, 1, new QTableWidgetItem( QString::number(it->first))); } } void SpellW::showFailed() { statsLBL->setText(""); int row = 0; Rcl::DbStats res; if (!rcldb->dbStats(res, true)) { LOGERR("SpellW::doExpand:rcldb::dbStats failed\n" ); return; } for (auto entry : res.failedurls) { resTW->setRowCount(row+1); resTW->setItem(row, 0, new QTableWidgetItem(u8s2qs(entry))); resTW->setItem(row++, 1, new QTableWidgetItem("")); } } void SpellW::wordChanged(const QString &text) { if (text.isEmpty()) { expandPB->setEnabled(false); resTW->setRowCount(0); } else { expandPB->setEnabled(true); } } void SpellW::textDoubleClicked() {} void SpellW::textDoubleClicked(int row, int) { QTableWidgetItem *item = resTW->item(row, 0); if (item) emit(wordSelect(item->text())); } void SpellW::onModeChanged(int idx) { if (idx < 0 || idx > int(m_c2t.size())) return; comboboxchoice mode = m_c2t[idx]; setModeCommon(mode); } void SpellW::setMode(comboboxchoice mode) { expTypeCMB->setCurrentIndex(cmbIdx(mode)); setModeCommon(mode); } void SpellW::setModeCommon(comboboxchoice mode) { if (wordlessMode(m_prevmode)) { baseWordLE->setText(""); } m_prevmode = mode; resTW->setRowCount(0); if (o_index_stripchars) { caseSensCB->setEnabled(false); diacSensCB->setEnabled(false); } else { caseSensCB->setEnabled(true); diacSensCB->setEnabled(true); } if (mode == TYPECMB_STEM) { stemLangCMB->setEnabled(true); diacSensCB->setChecked(false); diacSensCB->setEnabled(false); caseSensCB->setChecked(false); caseSensCB->setEnabled(false); } else { stemLangCMB->setEnabled(false); } if (wordlessMode(mode)) { baseWordLE->setEnabled(false); QStringList labels(tr("Item")); labels.push_back(tr("Value")); resTW->setHorizontalHeaderLabels(labels); diacSensCB->setEnabled(false); caseSensCB->setEnabled(false); doExpand(); } else { baseWordLE->setEnabled(true); QStringList labels(tr("Term")); labels.push_back(tr("Doc. / Tot.")); resTW->setHorizontalHeaderLabels(labels); prefs.termMatchType = mode; } } void SpellW::copy() { QItemSelectionModel * selection = resTW->selectionModel(); QModelIndexList indexes = selection->selectedIndexes(); if(indexes.size() < 1) return; // QModelIndex::operator < sorts first by row, then by column. // this is what we need std::sort(indexes.begin(), indexes.end()); // You need a pair of indexes to find the row changes QModelIndex previous = indexes.first(); indexes.removeFirst(); QString selected_text; QModelIndex current; Q_FOREACH(current, indexes) { QVariant data = resTW->model()->data(previous); QString text = data.toString(); // At this point `text` contains the text in one cell selected_text.append(text); // If you are at the start of the row the row number of the previous index // isn't the same. Text is followed by a row separator, which is a newline. if (current.row() != previous.row()) { selected_text.append(QLatin1Char('\n')); } // Otherwise it's the same row, so append a column separator, which is a tab. else { selected_text.append(QLatin1Char('\t')); } previous = current; } // add last element selected_text.append(resTW->model()->data(current).toString()); selected_text.append(QLatin1Char('\n')); qApp->clipboard()->setText(selected_text, QClipboard::Selection); qApp->clipboard()->setText(selected_text, QClipboard::Clipboard); } bool SpellW::eventFilter(QObject *target, QEvent *event) { if (event->type() != QEvent::KeyPress || (target != resTW && target != resTW->viewport())) return false; QKeyEvent *keyEvent = (QKeyEvent *)event; if(keyEvent->matches(QKeySequence::Copy) ) { copy(); return true; } return false; } recoll-1.36.1/qtgui/ssearch_w.cpp0000644000175000017500000006360414444307651013637 00000000000000/* Copyright (C) 2006-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "guiutils.h" #include "searchdata.h" #include "ssearch_w.h" #include "textsplit.h" #include "wasatorcl.h" #include "rclhelp.h" #include "xmltosd.h" #include "smallut.h" #include "rcldb.h" #include "recoll.h" #include "scbase.h" #include "base64.h" using namespace std; // Max db term matches fetched from the index static const int maxdbtermmatch = 20; // Visible rows for the completer listview static const int completervisibleitems = 20; void RclCompleterModel::init() { if (!clockPixmap.load(":/images/clock.png") || !interroPixmap.load(":/images/interro.png")) { LOGERR("SSearch: pixmap loading failed\n"); } } int RclCompleterModel::rowCount(const QModelIndex &) const { LOGDEB1("RclCompleterModel::rowCount: " << currentlist.size() << "\n"); return currentlist.size(); } int RclCompleterModel::columnCount(const QModelIndex &) const { return 2; } QVariant RclCompleterModel::data(const QModelIndex &index, int role) const { LOGDEB1("RclCompleterModel::data: row: " << index.row() << " role " << role << "\n"); if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::DecorationRole) { return QVariant(); } if (index.row() < 0 || index.row() >= int(currentlist.size())) { return QVariant(); } if (index.column() == 0) { if (role == Qt::DecorationRole) { LOGDEB1("RclCompleterModel::data: returning pixmap\n"); return index.row() < firstfromindex ? QVariant(clockPixmap) : QVariant(interroPixmap); } else { LOGDEB1("RclCompleterModel::data: return: " << qs2u8s(currentlist[index.row()]) << "\n"); return QVariant(currentlist[index.row()].first); } } else if (index.column() == 1 && prefs.showcompleterhitcounts) { if (currentlist[index.row()].second > 0) { return QVariant(QString("%1").arg(currentlist[index.row()].second) + tr(" Hits")); } } return QVariant(); } void RclCompleterModel::onPartialWord(int tp, const QString& _qtext, const QString& qpartial) { string partial = qs2u8s(qpartial); QString qtext = _qtext.trimmed(); bool onlyspace = qtext.isEmpty(); LOGDEB1("RclCompleterModel::onPartialWord: [" << partial << "] onlyspace "<< onlyspace << "\n"); currentlist.clear(); beginResetModel(); if ((prefs.ssearchNoComplete && !onlyspace) || tp == SSearch::SST_FNM) { // Nocomplete: only look at history by entering space // Filename: no completion for now. We'd need to termatch with // the right prefix? endResetModel(); return; } int maxhistmatch = prefs.ssearchCompleterHistCnt; int histmatch = 0; // Look for matches between the full entry and the search history // (anywhere in the string) for (int i = 0; i < prefs.ssearchHistory.count(); i++) { LOGDEB1("[" << qs2u8s(prefs.ssearchHistory[i]) << "] contains ["<= maxhistmatch) break; currentlist.push_back({prefs.ssearchHistory[i], -1}); } } firstfromindex = currentlist.size(); // Look for Recoll terms beginning with the partial word. If the index is not stripped, only do // this after the partial has at least 2 characters, else the syn/diac/case expansion is too // expensive int mintermsizeforexpand = o_index_stripchars ? 1 : 2; if (qpartial.trimmed().size() >= mintermsizeforexpand) { Rcl::TermMatchResult rclmatches; if (!rcldb->termMatch(Rcl::Db::ET_WILD, string(), partial + "*", rclmatches, maxdbtermmatch)) { LOGDEB1("RclCompleterModel: termMatch failed: [" << partial + "*" << "]\n"); } else { LOGDEB1("RclCompleterModel: termMatch cnt: " << rclmatches.entries.size() << endl); } for (const auto& entry : rclmatches.entries) { LOGDEB1("RclCompleterModel: match " << entry.term << endl); currentlist.push_back({u8s2qs(entry.term), entry.wcf}); } } endResetModel(); QTimer::singleShot(0, m_parent, SLOT(onCompleterShown())); } void SSearch::init() { // See enum in .h and keep in order ! searchTypCMB->addItem(tr("Any term")); searchTypCMB->addItem(tr("All terms")); searchTypCMB->addItem(tr("File name")); searchTypCMB->addItem(tr("Query language")); connect(queryText, SIGNAL(returnPressed()), this, SLOT(startSimpleSearch())); connect(queryText, SIGNAL(textChanged(const QString&)), this, SLOT(searchTextChanged(const QString&))); connect(queryText, SIGNAL(textEdited(const QString&)), this, SLOT(searchTextEdited(const QString&))); connect(clearqPB, SIGNAL(clicked()), queryText, SLOT(clear())); connect(searchPB, SIGNAL(clicked()), this, SLOT(startSimpleSearch())); connect(searchTypCMB, SIGNAL(activated(int)), this, SLOT(onSearchTypeChanged(int))); m_completermodel = new RclCompleterModel(this); m_completer = new QCompleter(m_completermodel, this); auto popup = new QTableView(); popup->setShowGrid(false); popup->setWordWrap(false); popup->horizontalHeader()->hide(); popup->verticalHeader()->hide(); m_completer->setPopup(popup); m_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); m_completer->setFilterMode(Qt::MatchContains); m_completer->setCaseSensitivity(Qt::CaseInsensitive); m_completer->setMaxVisibleItems(completervisibleitems); queryText->setCompleter(m_completer); m_completer->popup()->installEventFilter(this); queryText->installEventFilter(this); connect(this, SIGNAL(partialWord(int, const QString&, const QString&)), m_completermodel, SLOT(onPartialWord(int,const QString&,const QString&))); connect(m_completer, SIGNAL(activated(const QString&)), this, SLOT(onCompletionActivated(const QString&))); connect(historyPB, SIGNAL(clicked()), this, SLOT(onHistoryClicked())); setupButtons(); onNewShortcuts(); connect(&SCBase::scBase(), SIGNAL(shortcutsChanged()),this, SLOT(onNewShortcuts())); } void SSearch::onNewShortcuts() { SETSHORTCUT(this, "ssearch:197", tr("Simple search"), tr("History"), "Ctrl+H", m_histsc, onHistoryClicked); } void SSearch::setupButtons() { if (prefs.noClearSearch) { clearqPB->hide(); searchPB->hide(); queryText->setClearButtonEnabled(true); } else { clearqPB->show(); searchPB->show(); queryText->setClearButtonEnabled(false); } if (prefs.noSSTypCMB) { searchTypCMB->hide(); } else { searchTypCMB->show(); } } void SSearch::takeFocus() { LOGDEB1("SSearch: take focus\n"); queryText->setFocus(Qt::ShortcutFocusReason); // If the focus was already in the search entry, the text is not selected. // Do it for consistency queryText->selectAll(); } QString SSearch::currentText() { return queryText->text(); } void SSearch::clearAll() { queryText->clear(); } void SSearch::onCompleterShown() { LOGDEB("SSearch::onCompleterShown\n"); QCompleter *completer = queryText->completer(); if (!completer) { LOGDEB0("SSearch::onCompleterShown: no completer\n"); return; } QAbstractItemView *popup = completer->popup(); if (!popup) { LOGDEB0("SSearch::onCompleterShown: no popup\n"); return; } auto tb = (QTableView*)popup; tb->resizeColumnToContents(0); tb->resizeRowsToContents(); QVariant data = popup->model()->data(popup->currentIndex()); if (!data.isValid()) { LOGDEB0("SSearch::onCompleterShown: data not valid\n"); return; } // Test if the completer text begins with the current input. QString text = data.toString(); if (text.lastIndexOf(queryText->text()) != 0) { return; } LOGDEB0("SSearch::onCompleterShown:" << " current [" << qs2utf8s(currentText()) << "] saved [" << qs2utf8s(m_savedEditText) << "] popup [" << qs2utf8s(text) << "]\n"); // We append the completion part to the end of the current input, // line, and select it so that the user has a clear indication of // what will happen if they type Enter. int pos = queryText->cursorPosition(); int len = text.size() - currentText().size(); queryText->setText(text); queryText->setCursorPosition(pos); queryText->setSelection(pos, len); } // This is to avoid that if the user types Backspace or Del while we // have inserted / selected the current completion, the lineedit text // goes back to what it was, the completion fires, and it looks like // nothing was typed. Disable the completionn after Del or Backspace // is typed. bool SSearch::eventFilter(QObject *target, QEvent *event) { Q_UNUSED(target); LOGDEB1("SSearch::eventFilter: event\n"); if (event->type() != QEvent::KeyPress) { return false; } LOGDEB1("SSearch::eventFilter: KeyPress event. Target " << target << " popup "<popup() << " lineedit "<key() == Qt::Key_Backspace || keyEvent->key()==Qt::Key_Delete) { LOGDEB("SSearch::eventFilter: backspace/delete\n"); queryText->setCompleter(nullptr); return false; } else { if (nullptr == queryText->completer()) { queryText->setCompleter(m_completer); } } return false; } // onCompletionActivated() is called when an entry is selected in the // popup, but the edit text is going to be replaced in any case if // there is a current match (we can't prevent it in the signal). If // there is no match (e.g. the user clicked the history button and // selected an entry), the query text will not be set. // So: // - We set the query text to the popup activation value in all cases // - We schedule a callback to set the text to what we want (which is the // concatenation of the user entry before the current partial word and the // pop up data. // - Note that a history click will replace a current partial word, // so that the effect is different if there is a space at the end // of the entry or not: pure concatenation vs replacement of the // last (partial) word. void SSearch::restoreText() { LOGDEB("SSearch::restoreText: savedEdit: " << qs2u8s(m_savedEditText) << endl); if (!m_savedEditText.trimmed().isEmpty()) { // If the popup text begins with the saved text, just let it replace if (currentText().lastIndexOf(m_savedEditText) != 0) { queryText->setText(m_savedEditText.trimmed() + " " + currentText()); } m_savedEditText = ""; } queryText->setFocus(); if (prefs.ssearchStartOnComplete) { QTimer::singleShot(0, this, SLOT(startSimpleSearch())); } } void SSearch::onCompletionActivated(const QString& text) { LOGDEB("SSearch::onCompletionActivated: queryText [" << qs2u8s(currentText()) << "] text [" << qs2u8s(text) << "]\n"); queryText->setText(text); QTimer::singleShot(0, this, SLOT(restoreText())); } void SSearch::onHistoryClicked() { if (m_completermodel) { queryText->setCompleter(m_completer); m_completermodel->onPartialWord(SST_LANG, "", ""); queryText->completer()->complete(); } } void SSearch::searchTextEdited(const QString& text) { LOGDEB1("SSearch::searchTextEdited: text [" << qs2u8s(text) << "]\n"); QString pword; int cs = getPartialWord(pword); int tp = searchTypCMB->currentIndex(); m_savedEditText = text.left(cs); LOGDEB1("SSearch::searchTextEdited: cs " <= 0) { emit partialWord(tp, currentText(), pword); } else { emit partialWord(tp, currentText(), " "); } } void SSearch::searchTextChanged(const QString& text) { LOGDEB1("SSearch::searchTextChanged: text [" << qs2u8s(text) << "]\n"); if (text.isEmpty()) { searchPB->setEnabled(false); clearqPB->setEnabled(false); queryText->setFocus(); emit clearSearch(); } else { searchPB->setEnabled(true); clearqPB->setEnabled(true); } } void SSearch::onSearchTypeChanged(int typ) { LOGDEB1("Search type now " << typ << "\n"); // This may come from the menus or the combobox. Ensure that // things are in sync. No loop because we are connected to // combobox or menu activated(), not currentIndexChanged() searchTypCMB->setCurrentIndex(typ); // Adjust context help if (typ == SST_LANG) { HelpClient::installMap((const char *)this->objectName().toUtf8(), "RCL.SEARCH.LANG"); } else { HelpClient::installMap((const char *)this->objectName().toUtf8(), "RCL.SEARCH.GUI.SIMPLE"); } // Also fix tooltips switch (typ) { case SST_LANG: queryText->setToolTip( // Do not modify the text here, test with the // sshelp/qhelp.html file and a browser, then use // sshelp/helphtmltoc.sh to turn to code and insert here tr("") + tr("

Query language cheat-sheet. In doubt: click Show Query Details. ") + tr("You should really look at the manual (F1)

") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("") + tr("
WhatExamples
Andone two   one AND two   one && two
Orone OR two   one || two
Complex boolean. OR has priority, use parentheses ") + tr("where needed(one AND two) OR three
Not-term
Phrase\"pride and prejudice\"
Ordered proximity (slack=1)\"pride prejudice\"o1
Unordered proximity (slack=1)\"prejudice pride\"po1
Unordered prox. (default slack=10)\"prejudice pride\"p
Capitalize to suppress stem expansionFloor
Field-specificauthor:austen  title:prejudice
AND inside field (no order)author:jane,austen
OR inside fieldauthor:austen/bronte
Field namestitle/subject/caption  author/from
recipient/to  filename  ext
Directory path filterdir:/home/me  dir:doc
MIME type filtermime:text/plain mime:video/*
Date intervalsdate:2018-01-01/2018-31-12
") + tr("date:2018  date:2018-01-01/P12M
Sizesize>100k size<1M
") ); break; case SST_FNM: queryText->setToolTip(tr("Enter file name wildcard expression.")); break; case SST_ANY: case SST_ALL: default: queryText->setToolTip(tr("Enter search terms here.")); } emit ssearchTypeChanged(typ); } void SSearch::startSimpleSearch() { // Avoid a double search if we are fired on CR and the completer is active if (queryText->completer() && queryText->completer()->popup()->isVisible() && !queryText->completer()->currentCompletion().isEmpty()) { return; } string u8 = qs2u8s(queryText->text()); trimstring(u8); if (u8.length() == 0) return; if (!startSimpleSearch(u8)) return; // Search terms history. // New text at the front and erase any older identical entry QString txt = currentText().trimmed(); if (txt.isEmpty()) return; if (prefs.historysize) { prefs.ssearchHistory.insert(0, txt); prefs.ssearchHistory.removeDuplicates(); } if (prefs.historysize >= 0) { for (int i = (int)prefs.ssearchHistory.count(); i > prefs.historysize; i--) { prefs.ssearchHistory.removeLast(); } } } void SSearch::setPrefs() { } string SSearch::asXML() { return m_xml; } bool SSearch::startSimpleSearch(const string& u8, int maxexp) { LOGDEB("SSearch::startSimpleSearch(" << u8 << ")\n"); string stemlang = prefs.stemlang(); ostringstream xml; xml << "\n"; xml << " " << stemlang << "\n"; xml << " " << base64_encode(u8) << "\n"; SSearchType tp = (SSearchType)searchTypCMB->currentIndex(); std::shared_ptr sdata; if (tp == SST_LANG) { xml << " QL\n"; string reason; if (prefs.autoSuffsEnable) { sdata = wasaStringToRcl(theconfig, stemlang, u8, reason, (const char *)prefs.autoSuffs.toUtf8()); if (!prefs.autoSuffs.isEmpty()) { xml << " " << qs2u8s(prefs.autoSuffs) << "\n"; } } else { sdata = wasaStringToRcl(theconfig, stemlang, u8, reason); } if (!sdata) { QMessageBox::warning(0, "Recoll", tr("Bad query string") + ": " + QString::fromUtf8(reason.c_str())); return false; } } else { sdata = std::make_shared(Rcl::SCLT_OR, stemlang); if (!sdata) { QMessageBox::warning(0, "Recoll", tr("Out of memory")); return false; } Rcl::SearchDataClause *clp = 0; if (tp == SST_FNM) { xml << " FN\n"; clp = new Rcl::SearchDataClauseFilename(u8); } else { // ANY or ALL, several words. if (tp == SST_ANY) { xml << " OR\n"; clp = new Rcl::SearchDataClauseSimple(Rcl::SCLT_OR, u8); } else { xml << " AND\n"; clp = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND, u8); } } sdata->addClause(clp); } if (prefs.ssearchAutoPhrase && rcldb) { xml << " \n"; sdata->maybeAddAutoPhrase(*rcldb, prefs.ssearchAutoPhraseThreshPC / 100.0); } if (maxexp != -1) { sdata->setMaxExpand(maxexp); } for (const auto& dbdir : prefs.activeExtraDbs) { xml << " " << base64_encode(dbdir) << ""; } xml << "\n"; m_xml = xml.str(); LOGDEB("SSearch::startSimpleSearch:xml:[" << m_xml << "]\n"); emit setDescription(u8s2qs(u8)); emit startSearch(sdata, true); return true; } bool SSearch::checkExtIndexes(const std::vector& dbs) { std::string reason; if (!maybeOpenDb(reason, false)) { QMessageBox::critical(0, "Recoll", tr("Can't open index") + u8s2qs(reason)); return false; } if (!rcldb->setExtraQueryDbs(dbs)) { return false; } return true; } bool SSearch::fromXML(const SSearchDef& fxml) { string asString; set cur; set stored; // Retrieve current list of stemlangs. prefs returns a // space-separated list Warn if stored differs from current, // but don't change the latter. stringToStrings(prefs.stemlang(), cur); stored = set(fxml.stemlangs.begin(), fxml.stemlangs.end()); stringsToString(fxml.stemlangs, asString); if (cur != stored) { QMessageBox::warning( 0, "Recoll", tr("Stemming languages for stored query: ") + QString::fromUtf8(asString.c_str()) + tr(" differ from current preferences (kept)")); } // Same for autosuffs stringToStrings(qs2u8s(prefs.autoSuffs), cur); stored = set(fxml.autosuffs.begin(), fxml.autosuffs.end()); stringsToString(fxml.stemlangs, asString); if (cur != stored) { QMessageBox::warning( 0, "Recoll", tr("Auto suffixes for stored query: ") + QString::fromUtf8(asString.c_str()) + tr(" differ from current preferences (kept)")); } if (!checkExtIndexes(fxml.extindexes)) { stringsToString(fxml.extindexes, asString); QMessageBox::warning( 0, "Recoll", tr("Could not restore external indexes for stored query:
") + (rcldb ? u8s2qs(rcldb->getReason()) : tr("???")) + QString("
") + tr("Using current preferences.")); string s; maybeOpenDb(s, true); } else { prefs.useTmpActiveExtraDbs = true; prefs.tmpActiveExtraDbs = fxml.extindexes; } if (prefs.ssearchAutoPhrase && !fxml.autophrase) { QMessageBox::warning( 0, "Recoll", tr("Autophrase is set but it was unset for stored query")); } else if (!prefs.ssearchAutoPhrase && fxml.autophrase) { QMessageBox::warning( 0, "Recoll", tr("Autophrase is unset but it was set for stored query")); } setSearchString(QString::fromUtf8(fxml.text.c_str())); // We used to use prefs.ssearchTyp here. Not too sure why? // Minimize user surprise factor ? Anyway it seems cleaner to // restore the saved search type searchTypCMB->setCurrentIndex(fxml.mode); return true; } void SSearch::setSearchString(const QString& txt) { queryText->setText(txt); } bool SSearch::hasSearchString() { return !currentText().isEmpty(); } // Add term to simple search. Term comes out of double-click in // reslist or preview. // It would probably be better to cleanup in preview.ui.h and // reslist.cpp and do the proper html stuff in the latter case // (which is different because it format is explicit richtext // instead of auto as for preview, needed because it's built by // fragments?). static const char* punct = " \t()<>\"'[]{}!^*.,:;\n\r"; void SSearch::addTerm(QString term) { LOGDEB("SSearch::AddTerm: [" << qs2u8s(term) << "]\n"); string t = (const char *)term.toUtf8(); string::size_type pos = t.find_last_not_of(punct); if (pos == string::npos) return; t = t.substr(0, pos+1); pos = t.find_first_not_of(punct); if (pos != string::npos) t = t.substr(pos); if (t.empty()) return; term = QString::fromUtf8(t.c_str()); QString text = currentText(); text += QString::fromLatin1(" ") + term; queryText->setText(text); } void SSearch::onWordReplace(const QString& o, const QString& n) { LOGDEB("SSearch::onWordReplace: o [" << qs2u8s(o) << "] n [" << qs2u8s(n) << "]\n"); QString txt = currentText(); QRegularExpression exp(QString("\\b") + o + QString("\\b"), QRegularExpression::CaseInsensitiveOption); txt.replace(exp, n); queryText->setText(txt); Qt::KeyboardModifiers mods = QApplication::keyboardModifiers(); if (mods == Qt::NoModifier) startSimpleSearch(); } void SSearch::setAnyTermMode() { searchTypCMB->setCurrentIndex(SST_ANY); } // If text does not end with space, return last (partial) word and >0 // else return -1 int SSearch::getPartialWord(QString& word) { // Extract last word in text QString txt = currentText(); if (txt.isEmpty()) { return -1; } int lstidx = txt.size()-1; // If the input ends with a space or dquote (phrase input), or // dquote+qualifiers, no partial word. if (txt[lstidx] == ' ') { return -1; } int cs = txt.lastIndexOf("\""); if (cs > 0) { bool dquoteToEndNoSpace{true}; for (int i = cs; i <= lstidx; i++) { if (txt[i] == ' ') { dquoteToEndNoSpace = false; break; } } if (dquoteToEndNoSpace) { return -1; } } cs = txt.lastIndexOf(" "); if (cs < 0) cs = 0; else cs++; word = txt.right(txt.size() - cs); return cs; } recoll-1.36.1/qtgui/confgui/0000755000175000017500000000000014521161751012651 500000000000000recoll-1.36.1/qtgui/confgui/confgui.cpp0000644000175000017500000007740514473600016014742 00000000000000/* Copyright (C) 2005-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "confgui.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smallut.h" #ifdef ENABLE_XMLCONF #include "picoxml.h" #endif namespace confgui { // Main layout spacing static const int spacing = 3; // left,top,right, bottom static QMargins margin(4,3,4,3); // Margin around text to explicitely set pushbutton sizes lower than // the default min (80?). Different on Mac OS for some reason #ifdef __APPLE__ static const int pbTextMargin = 30; #else static const int pbTextMargin = 15; #endif ConfTabsW::ConfTabsW(QWidget *parent, const QString& title, ConfLinkFact *fact) : QDialog(parent), m_makelink(fact) { setWindowTitle(title); tabWidget = new QTabWidget; buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->setSpacing(spacing); mainLayout->setContentsMargins(margin); mainLayout->addWidget(tabWidget); mainLayout->addWidget(buttonBox); setLayout(mainLayout); resize(QSize(500, 400).expandedTo(minimumSizeHint())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(acceptChanges())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(rejectChanges())); } void ConfTabsW::hideButtons() { if (buttonBox) buttonBox->hide(); } void ConfTabsW::acceptChanges() { for (auto& entry : m_panels) { entry->storeValues(); } for (auto& entry : m_widgets) { entry->storeValues(); } emit sig_prefsChanged(); if (!buttonBox->isHidden()) close(); } void ConfTabsW::rejectChanges() { reloadPanels(); if (!buttonBox->isHidden()) close(); } void ConfTabsW::reloadPanels() { for (auto& entry : m_panels) { entry->loadValues(); } for (auto& entry : m_widgets) { entry->loadValues(); } } int ConfTabsW::addPanel(const QString& title) { ConfPanelW *w = new ConfPanelW(this); m_panels.push_back(w); return tabWidget->addTab(w, title); } int ConfTabsW::addForeignPanel(ConfPanelWIF* w, const QString& title) { m_widgets.push_back(w); QWidget *qw = dynamic_cast(w); if (qw == 0) { qDebug() << "addForeignPanel: can't cast panel to QWidget"; abort(); } return tabWidget->addTab(qw, title); } void ConfTabsW::setCurrentIndex(int idx) { if (tabWidget) { tabWidget->setCurrentIndex(idx); } } QWidget *ConfTabsW::addBlurb(int tabindex, const QString& txt) { ConfPanelW *panel = (ConfPanelW*)tabWidget->widget(tabindex); if (panel == 0) { return 0; } QFrame *line = new QFrame(panel); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); panel->addWidget(line); QLabel *explain = new QLabel(panel); explain->setWordWrap(true); explain->setText(txt); panel->addWidget(explain); line = new QFrame(panel); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); panel->addWidget(line); return explain; } ConfParamW *ConfTabsW::addParam( int tabindex, ParamType tp, const QString& varname, const QString& label, const QString& tooltip, int ival, int maxval, const QStringList* sl) { ConfLink lnk = (*m_makelink)(varname); ConfPanelW *panel = (ConfPanelW*)tabWidget->widget(tabindex); if (panel == 0) { return 0; } ConfParamW *cp = 0; switch (tp) { case CFPT_BOOL: cp = new ConfParamBoolW(varname, this, lnk, label, tooltip, ival); break; case CFPT_INT: { size_t v = (size_t)sl; int v1 = (v & 0xffffffff); cp = new ConfParamIntW(varname, this, lnk, label, tooltip, ival, maxval, v1); break; } case CFPT_STR: cp = new ConfParamStrW(varname, this, lnk, label, tooltip); break; case CFPT_CSTR: cp = new ConfParamCStrW(varname, this, lnk, label, tooltip, *sl); break; case CFPT_FN: cp = new ConfParamFNW(varname, this, lnk, label, tooltip, ival); break; case CFPT_STRL: cp = new ConfParamSLW(varname, this, lnk, label, tooltip); break; case CFPT_DNL: cp = new ConfParamDNLW(varname, this, lnk, label, tooltip); break; case CFPT_CSTRL: cp = new ConfParamCSLW(varname, this, lnk, label, tooltip, *sl); break; } cp->setToolTip(tooltip); panel->addParam(cp); return cp; } ConfParamW *ConfTabsW::findParamW(const QString& varname) { for (const auto& panel : m_panels) { ConfParamW *w = panel->findParamW(varname); if (w) return w; } return nullptr; } void ConfTabsW::endOfList(int tabindex) { ConfPanelW *panel = dynamic_cast(tabWidget->widget(tabindex)); // panel may be null if this is a foreign panel (not a conftabsw) if (nullptr == panel) { return; } panel->endOfList(); } bool ConfTabsW::enableLink(ConfParamW* boolw, ConfParamW* otherw, bool revert) { ConfParamBoolW *bw = dynamic_cast(boolw); if (bw == 0) { std::cerr << "ConfTabsW::enableLink: not a boolw\n"; return false; } otherw->setEnabled(revert ? !bw->m_cb->isChecked() : bw->m_cb->isChecked()); if (revert) { connect(bw->m_cb, SIGNAL(toggled(bool)), otherw, SLOT(setDisabled(bool))); } else { connect(bw->m_cb, SIGNAL(toggled(bool)), otherw, SLOT(setEnabled(bool))); } return true; } ConfPanelW::ConfPanelW(QWidget *parent) : QWidget(parent) { m_vboxlayout = new QVBoxLayout(this); m_vboxlayout->setSpacing(spacing); m_vboxlayout->setAlignment(Qt::AlignTop); m_vboxlayout->setContentsMargins(margin); } void ConfPanelW::addParam(ConfParamW *w) { m_vboxlayout->addWidget(w); m_params.push_back(w); } void ConfPanelW::addWidget(QWidget *w) { m_vboxlayout->addWidget(w); } ConfParamW *ConfPanelW::findParamW(const QString& varname) { for (const auto& param : m_params) { if (!varname.compare(param->getVarName())) { return param; } } return nullptr; } void ConfPanelW::endOfList() { m_vboxlayout->addStretch(2); } void ConfPanelW::storeValues() { for (auto& widgetp : m_params) { widgetp->storeValue(); } } void ConfPanelW::loadValues() { for (auto& widgetp : m_params) { widgetp->loadValue(); } } static QString myGetFileName(bool isdir, QString caption = QString(), bool filenosave = false); static QString myGetFileName(bool isdir, QString caption, bool filenosave) { QFileDialog dialog(0, caption); if (isdir) { dialog.setFileMode(QFileDialog::Directory); dialog.setOptions(QFileDialog::ShowDirsOnly); } else { dialog.setFileMode(QFileDialog::AnyFile); if (filenosave) { dialog.setAcceptMode(QFileDialog::AcceptOpen); } else { dialog.setAcceptMode(QFileDialog::AcceptSave); } } dialog.setViewMode(QFileDialog::List); QFlags flags = QDir::NoDotAndDotDot | QDir::Hidden; if (isdir) { flags |= QDir::Dirs; } else { flags |= QDir::Dirs | QDir::Files; } dialog.setFilter(flags); if (dialog.exec() == QDialog::Accepted) { return dialog.selectedFiles().value(0); } return QString(); } void ConfParamW::setValue(const QString& value) { if (m_fsencoding) { #ifdef _WIN32 m_cflink->set(std::string((const char *)value.toUtf8())); #else m_cflink->set(std::string((const char *)value.toLocal8Bit())); #endif } else { m_cflink->set(std::string((const char *)value.toUtf8())); } } void ConfParamW::setValue(int value) { char buf[30]; sprintf(buf, "%d", value); m_cflink->set(std::string(buf)); } void ConfParamW::setValue(bool value) { char buf[30]; sprintf(buf, "%d", value); m_cflink->set(std::string(buf)); } extern void setSzPol(QWidget *w, QSizePolicy::Policy hpol, QSizePolicy::Policy vpol, int hstretch, int vstretch); void setSzPol(QWidget *w, QSizePolicy::Policy hpol, QSizePolicy::Policy vpol, int hstretch, int vstretch) { QSizePolicy policy(hpol, vpol); policy.setHorizontalStretch(hstretch); policy.setVerticalStretch(vstretch); policy.setHeightForWidth(w->sizePolicy().hasHeightForWidth()); w->setSizePolicy(policy); } bool ConfParamW::createCommon(const QString& lbltxt, const QString&) { m_hl = new QHBoxLayout(this); m_hl->setSpacing(spacing); m_hl->setContentsMargins(margin); QLabel *tl = new QLabel(this); setSzPol(tl, QSizePolicy::Preferred, QSizePolicy::Fixed, 0, 0); tl->setText(lbltxt); m_hl->addWidget(tl); return true; } ConfParamIntW::ConfParamIntW( const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt, int minvalue, int maxvalue, int defaultvalue) : ConfParamW(varnm, parent, cflink), m_defaultvalue(defaultvalue) { if (!createCommon(lbltxt, tltptxt)) { return; } m_sb = new QSpinBox(this); m_sb->setMinimum(minvalue); m_sb->setMaximum(maxvalue); setSzPol(m_sb, QSizePolicy::Fixed, QSizePolicy::Fixed, 0, 0); m_hl->addWidget(m_sb); QFrame *fr = new QFrame(this); setSzPol(fr, QSizePolicy::Preferred, QSizePolicy::Fixed, 0, 0); m_hl->addWidget(fr); loadValue(); } void ConfParamIntW::storeValue() { if (m_origvalue != m_sb->value()) { setValue(m_sb->value()); } } void ConfParamIntW::loadValue() { std::string s; if (m_cflink->get(s)) { m_sb->setValue(m_origvalue = atoi(s.c_str())); } else { m_sb->setValue(m_origvalue = m_defaultvalue); } } void ConfParamIntW::setImmediate() { connect(m_sb, SIGNAL(valueChanged(int)), this, SLOT(setValue(int))); } ConfParamStrW::ConfParamStrW( const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt) : ConfParamW(varnm, parent, cflink) { if (!createCommon(lbltxt, tltptxt)) { return; } m_le = new QLineEdit(this); setSzPol(m_le, QSizePolicy::Preferred, QSizePolicy::Fixed, 1, 0); m_hl->addWidget(m_le); loadValue(); } void ConfParamStrW::storeValue() { if (m_origvalue.compare(m_le->text())) { setValue(m_le->text()); } } void ConfParamStrW::loadValue() { std::string s; if (!m_cflink->get(s)) { s = m_strdefault; } if (m_fsencoding) { #ifdef _WIN32 m_le->setText(m_origvalue = QString::fromUtf8(s.c_str())); #else m_le->setText(m_origvalue = QString::fromLocal8Bit(s.c_str())); #endif } else { m_le->setText(m_origvalue = QString::fromUtf8(s.c_str())); } } void ConfParamStrW::setImmediate() { connect(m_le, SIGNAL(textChanged(const QString&)), this, SLOT(setValue(const QString&))); } ConfParamCStrW::ConfParamCStrW( const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt, const QStringList& sl) : ConfParamW(varnm, parent, cflink) { if (!createCommon(lbltxt, tltptxt)) { return; } m_cmb = new QComboBox(this); m_cmb->setEditable(false); m_cmb->insertItems(0, sl); setSzPol(m_cmb, QSizePolicy::Preferred, QSizePolicy::Fixed, 1, 0); m_hl->addWidget(m_cmb); loadValue(); } void ConfParamCStrW::setList(const QStringList& sl) { m_cmb->clear(); m_cmb->insertItems(0, sl); loadValue(); } void ConfParamCStrW::storeValue() { if (m_origvalue.compare(m_cmb->currentText())) { setValue(m_cmb->currentText()); } } void ConfParamCStrW::loadValue() { std::string s; if (!m_cflink->get(s)) { s = m_strdefault; } QString cs; if (m_fsencoding) { #ifdef _WIN32 cs = QString::fromUtf8(s.c_str()); #else cs = QString::fromLocal8Bit(s.c_str()); #endif } else { cs = QString::fromUtf8(s.c_str()); } for (int i = 0; i < m_cmb->count(); i++) { if (!cs.compare(m_cmb->itemText(i))) { m_cmb->setCurrentIndex(i); break; } } m_origvalue = cs; } void ConfParamCStrW::setImmediate() { connect(m_cmb, SIGNAL(activated(const QString&)), this, SLOT(setValue(const QString&))); } ConfParamBoolW::ConfParamBoolW( const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString&, bool deflt) : ConfParamW(varnm, parent, cflink), m_dflt(deflt) { // No createCommon because the checkbox has a label m_hl = new QHBoxLayout(this); m_hl->setSpacing(spacing); m_hl->setContentsMargins(margin); m_cb = new QCheckBox(lbltxt, this); setSzPol(m_cb, QSizePolicy::Fixed, QSizePolicy::Fixed, 0, 0); m_hl->addWidget(m_cb); QFrame *fr = new QFrame(this); setSzPol(fr, QSizePolicy::Preferred, QSizePolicy::Fixed, 1, 0); m_hl->addWidget(fr); loadValue(); } void ConfParamBoolW::storeValue() { if (m_origvalue != m_cb->isChecked()) { setValue(m_cb->isChecked()); } } void ConfParamBoolW::loadValue() { std::string s; if (!m_cflink->get(s)) { m_origvalue = m_dflt; } else { m_origvalue = stringToBool(s); } m_cb->setChecked(m_origvalue); } void ConfParamBoolW::setImmediate() { connect(m_cb, SIGNAL(toggled(bool)), this, SLOT(setValue(bool))); } ConfParamFNW::ConfParamFNW( const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt, bool isdir) : ConfParamW(varnm, parent, cflink), m_isdir(isdir) { if (!createCommon(lbltxt, tltptxt)) { return; } m_fsencoding = true; m_le = new QLineEdit(this); m_le->setMinimumSize(QSize(150, 0)); setSzPol(m_le, QSizePolicy::Preferred, QSizePolicy::Fixed, 1, 0); m_hl->addWidget(m_le); m_pb = new QPushButton(this); QString text = tr("Choose"); m_pb->setText(text); int width = m_pb->fontMetrics().boundingRect(text).width() + pbTextMargin; m_pb->setMaximumWidth(width); setSzPol(m_pb, QSizePolicy::Minimum, QSizePolicy::Fixed, 0, 0); m_hl->addWidget(m_pb); loadValue(); QObject::connect(m_pb, SIGNAL(clicked()), this, SLOT(showBrowserDialog())); } void ConfParamFNW::storeValue() { if (m_origvalue.compare(m_le->text())) { setValue(m_le->text()); } } void ConfParamFNW::loadValue() { std::string s; if (!m_cflink->get(s)) { s = m_strdefault; } #ifdef _WIN32 m_le->setText(m_origvalue = QString::fromUtf8(s.c_str())); #else m_le->setText(m_origvalue = QString::fromLocal8Bit(s.c_str())); #endif } void ConfParamFNW::showBrowserDialog() { QString s = myGetFileName(m_isdir); if (!s.isEmpty()) { m_le->setText(s); } } void ConfParamFNW::setImmediate() { connect(m_le, SIGNAL(textChanged(const QString&)), this, SLOT(setValue(const QString&))); } class SmallerListWidget: public QListWidget { public: SmallerListWidget(QWidget *parent) : QListWidget(parent) {} virtual QSize sizeHint() const { return QSize(150, 40); } }; ConfParamSLW::ConfParamSLW( const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString&) : ConfParamW(varnm, parent, cflink) { // Can't use createCommon here cause we want the buttons below the label m_hl = new QHBoxLayout(this); m_hl->setSpacing(spacing); m_hl->setContentsMargins(margin); QVBoxLayout *vl1 = new QVBoxLayout(); vl1->setSpacing(spacing); vl1->setContentsMargins(margin); QHBoxLayout *hl1 = new QHBoxLayout(); hl1->setSpacing(spacing); hl1->setContentsMargins(margin); QLabel *tl = new QLabel(this); setSzPol(tl, QSizePolicy::Preferred, QSizePolicy::Fixed, 0, 0); tl->setText(lbltxt); vl1->addWidget(tl); QPushButton *pbA = new QPushButton(this); QString text = tr("+"); pbA->setText(text); pbA->setToolTip(tr("Add entry")); int width = pbA->fontMetrics().boundingRect(text).width() + pbTextMargin; pbA->setMaximumWidth(width); setSzPol(pbA, QSizePolicy::Minimum, QSizePolicy::Fixed, 0, 0); hl1->addWidget(pbA); QObject::connect(pbA, SIGNAL(clicked()), this, SLOT(showInputDialog())); QPushButton *pbD = new QPushButton(this); text = tr("-"); pbD->setText(text); pbD->setToolTip(tr("Delete selected entries")); width = pbD->fontMetrics().boundingRect(text).width() + pbTextMargin; pbD->setMaximumWidth(width); setSzPol(pbD, QSizePolicy::Minimum, QSizePolicy::Fixed, 0, 0); hl1->addWidget(pbD); QObject::connect(pbD, SIGNAL(clicked()), this, SLOT(deleteSelected())); m_pbE = new QPushButton(this); text = tr("~"); m_pbE->setText(text); m_pbE->setToolTip(tr("Edit selected entries")); width = m_pbE->fontMetrics().boundingRect(text).width() + pbTextMargin; m_pbE->setMaximumWidth(width); setSzPol(m_pbE, QSizePolicy::Minimum, QSizePolicy::Fixed, 0, 0); hl1->addWidget(m_pbE); QObject::connect(m_pbE, SIGNAL(clicked()), this, SLOT(editSelected())); m_pbE->hide(); vl1->addLayout(hl1); m_hl->addLayout(vl1); m_lb = new SmallerListWidget(this); m_lb->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_lb, SIGNAL(currentTextChanged(const QString&)), this, SIGNAL(currentTextChanged(const QString&))); setSzPol(m_lb, QSizePolicy::Preferred, QSizePolicy::Preferred, 1, 1); m_hl->addWidget(m_lb); setSzPol(this, QSizePolicy::Preferred, QSizePolicy::Preferred, 1, 1); loadValue(); } void ConfParamSLW::setEditable(bool onoff) { if (onoff) { m_pbE->show(); } else { m_pbE->hide(); } } std::string ConfParamSLW::listToString() { std::vector ls; for (int i = 0; i < m_lb->count(); i++) { // General parameters are encoded as utf-8. // Linux file names as local8bit There is no hope for 8bit // file names anyway except for luck: the original encoding is // unknown. In most modern configs, local8Bits will be UTF-8. // Except on Windows: we store file names as UTF-8 QString text = m_lb->item(i)->text(); if (m_fsencoding) { #ifdef _WIN32 ls.push_back((const char *)(text.toUtf8())); #else ls.push_back((const char *)(text.toLocal8Bit())); #endif } else { ls.push_back((const char *)(text.toUtf8())); } } std::string s; stringsToString(ls, s); return s; } void ConfParamSLW::storeValue() { std::string s = listToString(); if (s.compare(m_origvalue)) { m_cflink->set(s); } } void ConfParamSLW::loadValue() { m_origvalue.clear(); if (!m_cflink->get(m_origvalue)) { m_origvalue = m_strdefault; } std::vector ls; stringToStrings(m_origvalue, ls); QStringList qls; for (const auto& str : ls) { if (m_fsencoding) { #ifdef _WIN32 qls.push_back(QString::fromUtf8(str.c_str())); #else qls.push_back(QString::fromLocal8Bit(str.c_str())); #endif } else { qls.push_back(QString::fromUtf8(str.c_str())); } } m_lb->clear(); m_lb->insertItems(0, qls); } void ConfParamSLW::showInputDialog() { bool ok; QString s = QInputDialog::getText(this, "", "", QLineEdit::Normal, "", &ok); if (!ok || s.isEmpty()) { return; } performInsert(s); } void ConfParamSLW::performInsert(const QString& s) { QList existing = m_lb->findItems(s, Qt::MatchFixedString | Qt::MatchCaseSensitive); if (!existing.empty()) { m_lb->setCurrentItem(existing[0]); return; } m_lb->insertItem(0, s); m_lb->sortItems(); existing = m_lb->findItems(s, Qt::MatchFixedString | Qt::MatchCaseSensitive); if (existing.empty()) { std::cerr << "Item not found after insertion!" << "\n"; return; } m_lb->setCurrentItem(existing[0], QItemSelectionModel::ClearAndSelect); if (m_immediate) { std::string nv = listToString(); m_cflink->set(nv); } } void ConfParamSLW::deleteSelected() { // We used to repeatedly go through the list and delete the first // found selected item (then restart from the beginning). But it // seems (probably depends on the qt version), that, when deleting // a selected item, qt will keep the selection active at the same // index (now containing the next item), so that we'd end up // deleting the whole list. // // Instead, we now build a list of indices, and delete it starting // from the top so as not to invalidate lower indices std::vector idxes; for (int i = 0; i < m_lb->count(); i++) { if (m_lb->item(i)->isSelected()) { idxes.push_back(i); } } for (std::vector::reverse_iterator it = idxes.rbegin(); it != idxes.rend(); it++) { QListWidgetItem *item = m_lb->takeItem(*it); emit entryDeleted(item->text()); delete item; } if (m_immediate) { std::string nv = listToString(); m_cflink->set(nv); } if (m_lb->count()) { m_lb->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); } } void ConfParamSLW::editSelected() { for (int i = 0; i < m_lb->count(); i++) { if (m_lb->item(i)->isSelected()) { bool ok; QString s = QInputDialog::getText( this, "", "", QLineEdit::Normal, m_lb->item(i)->text(), &ok); if (ok && !s.isEmpty()) { m_lb->item(i)->setText(s); if (m_immediate) { std::string nv = listToString(); m_cflink->set(nv); } } } } } // "Add entry" dialog for a file name list void ConfParamDNLW::showInputDialog() { QString s = myGetFileName(true); if (s.isEmpty()) { return; } performInsert(s); } // "Add entry" dialog for a constrained string list void ConfParamCSLW::showInputDialog() { bool ok; QString s = QInputDialog::getItem(this, "", "", m_sl, 0, false, &ok); if (!ok || s.isEmpty()) { return; } performInsert(s); } #ifdef ENABLE_XMLCONF static QString u8s2qs(const std::string us) { return QString::fromUtf8(us.c_str()); } static const std::string& mapfind(const std::string& nm, const std::map& mp) { static std::string strnull; std::map::const_iterator it; it = mp.find(nm); if (it == mp.end()) { return strnull; } return it->second; } static std::string looksLikeAssign(const std::string& data) { //LOGDEB("looksLikeAssign. data: [" << data << "]"); std::vector toks; stringToTokens(data, toks, "\n\r\t "); if (toks.size() >= 2 && !toks[1].compare("=")) { return toks[0]; } return std::string(); } ConfTabsW *xmlToConfGUI(const std::string& xml, std::string& toptext, ConfLinkFact* lnkf, QWidget *parent) { //LOGDEB("xmlToConfGUI: [" << xml << "]"); class XMLToConfGUI : public PicoXMLParser { public: XMLToConfGUI(const std::string& x, ConfLinkFact *lnkf, QWidget *parent) : PicoXMLParser(x), m_lnkfact(lnkf), m_parent(parent), m_idx(0), m_hadTitle(false), m_hadGroup(false) { } virtual ~XMLToConfGUI() {} virtual void startElement(const std::string& tagname, const std::map& attrs) { if (!tagname.compare("var")) { m_curvar = mapfind("name", attrs); m_curvartp = mapfind("type", attrs); m_curvarvals = mapfind("values", attrs); //LOGDEB("Curvar: " << m_curvar); if (m_curvar.empty() || m_curvartp.empty()) { throw std::runtime_error( " with no name attribute or no type ! nm [" + m_curvar + "] tp [" + m_curvartp + "]"); } else { m_brief.clear(); m_descr.clear(); } } else if (!tagname.compare("filetitle") || !tagname.compare("grouptitle")) { m_other.clear(); } } virtual void endElement(const std::string& tagname) { if (!tagname.compare("var")) { if (!m_hadTitle) { m_w = new ConfTabsW(m_parent, "Teh title", m_lnkfact); m_hadTitle = true; } if (!m_hadGroup) { m_idx = m_w->addPanel("Group title"); m_hadGroup = true; } ConfTabsW::ParamType paramtype; if (!m_curvartp.compare("bool")) { paramtype = ConfTabsW::CFPT_BOOL; } else if (!m_curvartp.compare("int")) { paramtype = ConfTabsW::CFPT_INT; } else if (!m_curvartp.compare("string")) { paramtype = ConfTabsW::CFPT_STR; } else if (!m_curvartp.compare("cstr")) { paramtype = ConfTabsW::CFPT_CSTR; } else if (!m_curvartp.compare("cstrl")) { paramtype = ConfTabsW::CFPT_CSTRL; } else if (!m_curvartp.compare("fn")) { paramtype = ConfTabsW::CFPT_FN; } else if (!m_curvartp.compare("dfn")) { paramtype = ConfTabsW::CFPT_FN; } else if (!m_curvartp.compare("strl")) { paramtype = ConfTabsW::CFPT_STRL; } else if (!m_curvartp.compare("dnl")) { paramtype = ConfTabsW::CFPT_DNL; } else { throw std::runtime_error("Bad type " + m_curvartp + " for " + m_curvar); } rtrimstring(m_brief, " ."); switch (paramtype) { case ConfTabsW::CFPT_BOOL: { int def = atoi(m_curvarvals.c_str()); m_w->addParam(m_idx, paramtype, u8s2qs(m_curvar), u8s2qs(m_brief), u8s2qs(m_descr), def); break; } case ConfTabsW::CFPT_INT: { std::vector vals; stringToTokens(m_curvarvals, vals); int min = 0, max = 0, def = 0; if (vals.size() >= 3) { min = atoi(vals[0].c_str()); max = atoi(vals[1].c_str()); def = atoi(vals[2].c_str()); } QStringList *sldef = 0; sldef = (QStringList*)(((char*)sldef) + def); m_w->addParam(m_idx, paramtype, u8s2qs(m_curvar), u8s2qs(m_brief), u8s2qs(m_descr), min, max, sldef); break; } case ConfTabsW::CFPT_CSTR: case ConfTabsW::CFPT_CSTRL: { std::vector cstrl; stringToTokens(neutchars(m_curvarvals, "\n\r"), cstrl); QStringList qstrl; for (unsigned int i = 0; i < cstrl.size(); i++) { qstrl.push_back(u8s2qs(cstrl[i])); } m_w->addParam(m_idx, paramtype, u8s2qs(m_curvar), u8s2qs(m_brief), u8s2qs(m_descr), 0, 0, &qstrl); break; } default: m_w->addParam(m_idx, paramtype, u8s2qs(m_curvar), u8s2qs(m_brief), u8s2qs(m_descr)); } } else if (!tagname.compare("filetitle")) { m_w = new ConfTabsW(m_parent, u8s2qs(m_other), m_lnkfact); m_hadTitle = true; m_other.clear(); } else if (!tagname.compare("grouptitle")) { if (!m_hadTitle) { m_w = new ConfTabsW(m_parent, "Teh title", m_lnkfact); m_hadTitle = true; } // Get rid of "parameters" in the title, it's not interesting // and this makes our tab headers smaller. std::string ps{"parameters"}; std::string::size_type pos = m_other.find(ps); if (pos != std::string::npos) { m_other = m_other.replace(pos, ps.size(), ""); } m_idx = m_w->addPanel(u8s2qs(m_other)); m_hadGroup = true; m_other.clear(); } else if (!tagname.compare("descr")) { } else if (!tagname.compare("brief")) { m_brief = neutchars(m_brief, "\n\r"); } } virtual void characterData(const std::string& data) { if (!tagStack().back().compare("brief")) { m_brief += data; } else if (!tagStack().back().compare("descr")) { m_descr += data; } else if (!tagStack().back().compare("filetitle") || !tagStack().back().compare("grouptitle")) { // We don't want \n in there m_other += neutchars(data, "\n\r"); m_other += " "; } else if (!tagStack().back().compare("confcomments")) { std::string nvarname = looksLikeAssign(data); if (!nvarname.empty() && nvarname.compare(m_curvar)) { std::cerr << "Var assigned [" << nvarname << "] mismatch " "with current variable [" << m_curvar << "]\n"; } m_toptext += data; } } ConfTabsW *m_w; ConfLinkFact *m_lnkfact; QWidget *m_parent; int m_idx; std::string m_curvar; std::string m_curvartp; std::string m_curvarvals; std::string m_brief; std::string m_descr; std::string m_other; std::string m_toptext; bool m_hadTitle; bool m_hadGroup; }; XMLToConfGUI parser(xml, lnkf, parent); try { if (!parser.parse()) { std::cerr << "Parse failed: " << parser.getLastErrorMessage() << "\n"; return 0; } } catch (const std::runtime_error& e) { std::cerr << e.what() << "\n"; return 0; } toptext = parser.m_toptext; return parser.m_w; } #endif /* ENABLE_XMLCONF */ } // Namespace confgui recoll-1.36.1/qtgui/confgui/confguiindex.h0000644000175000017500000000501514427373216015433 00000000000000/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _confguiindex_h_included_ #define _confguiindex_h_included_ /** * Classes to handle the gui for the indexing configuration. These group * confgui elements, linked to configuration parameters, into panels. */ #include #include #include #include #include #include #include #include #include #include #include "confgui.h" class ConfNull; class RclConfig; class ConfIndexW : public QWidget { Q_OBJECT public: ConfIndexW(QWidget *parent, RclConfig *config) : m_parent(parent), m_rclconf(config) {} public slots: void showPrefs(bool modal); void acceptChanges(); QWidget *getDialog() {return m_w;} signals: void idxConfigPossiblyChanged(); private: void initPanels(); bool setupTopPanel(int idx); bool setupWebHistoryPanel(int idx); bool setupSearchPanel(int idx); QWidget *m_parent; RclConfig *m_rclconf; ConfNull *m_conf{nullptr}; confgui::ConfTabsW *m_w{nullptr}; QStringList m_stemlangs; }; /** A special panel for parameters which may change in subdirectories: */ class ConfSubPanelW : public QWidget, public confgui::ConfPanelWIF { Q_OBJECT public: ConfSubPanelW(QWidget *parent, ConfNull **config, RclConfig *rclconf); virtual void storeValues(); virtual void loadValues(); private slots: void subDirChanged(QListWidgetItem *, QListWidgetItem *); void subDirDeleted(QString); void restoreEmpty(); private: std::string m_sk; ConfNull **m_config; confgui::ConfParamDNLW *m_subdirs; std::vector m_widgets; QGroupBox *m_groupbox; }; #endif /* _confguiindex_h_included_ */ recoll-1.36.1/qtgui/confgui/confgui.h0000644000175000017500000003757214427373216014420 00000000000000/* Copyright (C) 2007-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _confgui_h_included_ #define _confgui_h_included_ /** * Utilities for a configuration/preferences settings user interface. * * This file declares a number of data input Qt widgets (virtual base: * ConfParamW), with a well defined virtual interface to configuration storage, * which may be QSettings or something else (e.g. conftree). * * Subclasses are defined for entering different kind of data, e.g. a string, * a file name, an integer, etc. * * Each GUI object is linked to the configuration data through * a "link" object which knows the details of interacting with the actual * configuration data, like the parameter name, the actual * configuration interface, etc. * * The link object is set when the input widget is created and cannot be * changed. * * The link object get() methods are called for reading the initial data. * * The set() methods for all the objects are normally called when the * user clicks "Accept", only if the current value() differs from the * value obtained by get() when the object was initialized. This can * be used to avoid cluttering the output with values which are * unmodified from the defaults. * * The setImmediate() method can be called on the individial controls * to ensure that set() is inconditionnaly called whenever the user * changes the related value. This can be especially useful if the * configuration state can't be fully represented in the GUI (for * example if the same parameter name can exist in different sections * depending on the value of another parameter). It allows using local * storage for the values, and flushing, for example, when * sig_prefsChanged() is emitted after the user clicks accept. * * The file also defines a multi-tabbed dialog container for the * parameter objects, with simple interface methods to add tabs and add * configuration elements to them. * * Some of the tab widgets can be defined as "foreign", with specific internals. * They just need to implement a loadValues() to be called at * initialisation and a storeValues(), called when the user commits * the changes. */ #include #include #include #include #include #include #include #include class QCheckBox; class QComboBox; class QDialogButtonBox; class QHBoxLayout; class QLineEdit; class QListWidget; class QPushButton; class QSpinBox; class QTabWidget; class QVBoxLayout; namespace confgui { /** Interface between the GUI widget and the config storage mechanism: */ class ConfLinkRep { public: virtual ~ConfLinkRep() {} virtual bool set(const std::string& val) = 0; virtual bool get(std::string& val) = 0; }; typedef std::shared_ptr ConfLink; // May be used to store/manage data which has no direct representation // in the stored configuration. class ConfLinkNullRep : public ConfLinkRep { public: virtual ~ConfLinkNullRep() {} virtual bool set(const std::string&) { return true; } virtual bool get(std::string& val) {val = ""; return true;} }; /** Link maker class. Will be called back by addParam() to create the link */ class ConfLinkFact { public: virtual ~ConfLinkFact() {} virtual ConfLink operator()(const QString& nm) = 0; }; /** Interface for "foreign" panels. The object must also be a QWidget, which * we don't express by inheriting here to avoid qt issues */ class ConfPanelWIF { public: virtual ~ConfPanelWIF() {} virtual void storeValues() = 0; virtual void loadValues() = 0; }; class ConfPanelW; class ConfParamW; /** The top level widget has tabs, each tab/panel has multiple widgets * for setting parameter values */ class ConfTabsW : public QDialog { Q_OBJECT public: ConfTabsW(QWidget *parent, const QString& title, ConfLinkFact *linkfact); enum ParamType {CFPT_BOOL, CFPT_INT, CFPT_STR, CFPT_CSTR, // Constrained string: from list CFPT_FN, // File/directory CFPT_STRL, CFPT_DNL, CFPT_CSTRL // lists of the same }; /** Add tab and return its identifier / index */ int addPanel(const QString& title); /** Add foreign tab where we only know to call loadvalues/storevalues. * The object has to derive from QWidget */ int addForeignPanel(ConfPanelWIF* w, const QString& title); /** Add parameter setter to specified tab */ ConfParamW *addParam( int tabindex, ParamType tp, const QString& varname, const QString& label, const QString& tooltip, int isdirorminval = 0, /* Dep. on type: directory flag or min value */ int maxval = 0, const QStringList* sl = 0); /** Add explanatory text between 2 horizontal lines */ QWidget *addBlurb(int tabindex, const QString& txt); /** Enable link between bool value and another parameter: the control will * be enabled depending on the boolean value (with possible inversion). Can * be called multiple times for the same bool to enable/disable * several controls */ bool enableLink(ConfParamW* boolw, ConfParamW* otherw, bool revert = false); /** Call this when you are done filling up a tab */ void endOfList(int tabindex); /** Find param widget associated with given variable name */ ConfParamW *findParamW(const QString& varname); void hideButtons(); public slots: void acceptChanges(); void rejectChanges(); void reloadPanels(); void setCurrentIndex(int); signals: /** This is emitted when acceptChanges() is called, after the * values have been stored */ void sig_prefsChanged(); private: ConfLinkFact *m_makelink{nullptr}; // All ConfPanelW managed panels. Each has a load/store interface // and an internal list of controls std::vector m_panels; // "Foreign" panels. Just implement load/store std::vector m_widgets; QTabWidget *tabWidget{nullptr}; QDialogButtonBox *buttonBox{nullptr}; }; ///////////////////////////////////////////////// // The rest of the class definitions are only useful if you need to // access a specific element for customisation (use findParamW() and a // dynamic cast). /** A panel/tab contains multiple controls for parameters */ class ConfPanelW : public QWidget { Q_OBJECT public: ConfPanelW(QWidget *parent); void addParam(ConfParamW *w); void addWidget(QWidget *w); void storeValues(); void loadValues(); void endOfList(); /** Find param widget associated with given variable name */ ConfParamW *findParamW(const QString& varname); private: QVBoxLayout *m_vboxlayout; std::vector m_params; }; /** Config panel element: manages one configuration * parameter. Subclassed for specific parameter types. */ class ConfParamW : public QWidget { Q_OBJECT public: ConfParamW(const QString& varnm, QWidget *parent, ConfLink cflink) : QWidget(parent), m_varname(varnm), m_cflink(cflink), m_fsencoding(false) { } virtual void loadValue() = 0; // Call setValue() each time the control changes, instead of on accept. virtual void setImmediate() = 0; virtual void setFsEncoding(bool onoff) { m_fsencoding = onoff; } const QString& getVarName() { return m_varname; } void setStrDefault(const std::string& value) { m_strdefault = value; } public slots: virtual void setEnabled(bool) = 0; virtual void storeValue() = 0; protected slots: void setValue(const QString& newvalue); void setValue(int newvalue); void setValue(bool newvalue); protected: QString m_varname; ConfLink m_cflink; QHBoxLayout *m_hl; // File names are encoded as local8bit in the config files. Other // are encoded as utf-8 bool m_fsencoding; // Bool and Int have constructor parameters for the default value. Others may use this std::string m_strdefault; virtual bool createCommon(const QString& lbltxt, const QString& tltptxt); }; //////// Widgets for setting the different types of configuration parameters: /** Boolean */ class ConfParamBoolW : public ConfParamW { Q_OBJECT public: ConfParamBoolW(const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt, bool deflt = false); virtual void loadValue(); virtual void storeValue(); virtual void setImmediate(); public slots: virtual void setEnabled(bool i) { if (m_cb) { ((QWidget*)m_cb)->setEnabled(i); } } public: QCheckBox *m_cb; bool m_dflt; bool m_origvalue; }; // Int class ConfParamIntW : public ConfParamW { Q_OBJECT public: // The default value is only used if none exists in the sample // configuration file. Defaults are normally set in there. ConfParamIntW(const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt, int minvalue = INT_MIN, int maxvalue = INT_MAX, int defaultvalue = 0); virtual void loadValue(); virtual void storeValue(); virtual void setImmediate(); public slots: virtual void setEnabled(bool i) { if (m_sb) { ((QWidget*)m_sb)->setEnabled(i); } } protected: QSpinBox *m_sb; int m_defaultvalue; int m_origvalue; }; // Arbitrary string class ConfParamStrW : public ConfParamW { Q_OBJECT public: ConfParamStrW(const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt); virtual void loadValue(); virtual void storeValue(); virtual void setImmediate(); public slots: virtual void setEnabled(bool i) { if (m_le) { ((QWidget*)m_le)->setEnabled(i); } } protected: QLineEdit *m_le; QString m_origvalue; }; // Constrained string: choose from list class ConfParamCStrW : public ConfParamW { Q_OBJECT public: ConfParamCStrW(const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt, const QStringList& sl); virtual void loadValue(); virtual void storeValue(); virtual void setList(const QStringList& sl); virtual void setImmediate(); public slots: virtual void setEnabled(bool i) { if (m_cmb) { ((QWidget*)m_cmb)->setEnabled(i); } } protected: QComboBox *m_cmb; QString m_origvalue; }; // File name class ConfParamFNW : public ConfParamW { Q_OBJECT public: ConfParamFNW(const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt, bool isdir = false); virtual void loadValue(); virtual void storeValue(); virtual void setImmediate(); protected slots: void showBrowserDialog(); public slots: virtual void setEnabled(bool i) { if (m_le) { ((QWidget*)m_le)->setEnabled(i); } if (m_pb) { ((QWidget*)m_pb)->setEnabled(i); } } protected: QLineEdit *m_le; QPushButton *m_pb; bool m_isdir; QString m_origvalue; }; // String list class ConfParamSLW : public ConfParamW { Q_OBJECT public: ConfParamSLW(const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt); virtual void loadValue(); virtual void storeValue(); QListWidget *getListBox() { return m_lb; } virtual void setEditable(bool onoff); virtual void setImmediate() { m_immediate = true; } public slots: virtual void setEnabled(bool i) { if (m_lb) { ((QWidget*)m_lb)->setEnabled(i); } } protected slots: virtual void showInputDialog(); void deleteSelected(); void editSelected(); void performInsert(const QString&); signals: void entryDeleted(QString); void currentTextChanged(const QString&); protected: QListWidget *m_lb; std::string listToString(); std::string m_origvalue; QPushButton *m_pbE; bool m_immediate{false}; }; // Dir name list class ConfParamDNLW : public ConfParamSLW { Q_OBJECT public: ConfParamDNLW(const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt) : ConfParamSLW(varnm, parent, cflink, lbltxt, tltptxt) { m_fsencoding = true; } protected slots: virtual void showInputDialog(); }; // Constrained string list (chose from predefined) class ConfParamCSLW : public ConfParamSLW { Q_OBJECT public: ConfParamCSLW(const QString& varnm, QWidget *parent, ConfLink cflink, const QString& lbltxt, const QString& tltptxt, const QStringList& sl) : ConfParamSLW(varnm, parent, cflink, lbltxt, tltptxt), m_sl(sl) { } protected slots: virtual void showInputDialog(); protected: const QStringList m_sl; }; extern void setSzPol(QWidget *w, QSizePolicy::Policy hpol, QSizePolicy::Policy vpol, int hstretch, int vstretch); #ifdef ENABLE_XMLCONF /** * Interpret an XML string and create a configuration interface. XML sample: * * * Configuration file parameters for upmpdcli * MPD parameters * * Host MPD runs on. * Defaults to localhost. This can also be specified as -h * * mpdhost = default-host * * IP port used by MPD. * Can also be specified as -p port. Defaults to the... * * mpdport = defport * * Set if we own the MPD queue. * If this is set (on by default), we own the MPD... * * ownqueue = * * * creates a panel in which the following are set. * The attributes should be self-explanatory. "values" * is used for different things depending on the var type * (min/max, default, str list). Check the code about this. * type values: "bool" "int" "string" "cstr" "cstrl" "fn" "dfn" "strl" "dnl" * * The XML would typically exist as comments inside a reference configuration * file (ConfSimple can extract such comments). * * This means that the reference configuration file can generate both * the documentation and the GUI interface. * * @param xml the input xml * @param[output] toptxt the top level XML text (text not inside , * normally commented variable assignments). This will be evaluated * as a config for default values. * @lnkf factory to create the objects which link the GUI to the * storage mechanism. */ extern ConfTabsW *xmlToConfGUI(const std::string& xml, std::string& toptxt, ConfLinkFact* lnkf, QWidget *parent); #endif } #endif /* _confgui_h_included_ */ recoll-1.36.1/qtgui/confgui/confguiindex.cpp0000644000175000017500000006410414444307651015771 00000000000000/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using std::vector; using std::set; using std::string; #include "recoll.h" #include "confguiindex.h" #include "smallut.h" #include "log.h" #include "rcldb.h" #include "execmd.h" #include "rclconfig.h" #include "webstore.h" #include "circache.h" #include "conftree.h" static const int spacing = 3; static const int margin = 3; using namespace confgui; /* Link class for ConfTree. Has a subkey pointer member which makes it easy * to change the current subkey for multiple instances. */ class ConfLinkRclRep : public ConfLinkRep { public: ConfLinkRclRep(ConfNull **conf, const string& nm, string *sk = 0) : m_conf(conf), m_nm(nm), m_sk(sk) /* KEEP THE POINTER, shared data */ {} virtual ~ConfLinkRclRep() {} virtual bool set(const string& val) { if (!m_conf || !*m_conf) return false; LOGDEB("ConfLinkRclRep: set " << m_nm << " -> " << val << " sk " << getSk() << std::endl); bool ret = (*m_conf)->set(m_nm, val, getSk()); if (!ret) LOGERR("Value set failed\n" ); return ret; } virtual bool get(string& val) { if (!m_conf || !*m_conf) return false; bool ret = (*m_conf)->get(m_nm, val, getSk()); LOGDEB("ConfLinkRcl::get: [" << m_nm << "] sk [" << getSk() << "] -> [" << (ret ? val : "no value") << "]\n"); return ret; } private: string getSk() { return m_sk ? *m_sk : string(); } ConfNull **m_conf; const string m_nm; const string *m_sk; }; /* Special link for skippedNames and noContentSuffixes which are computed as set differences */ typedef std::function()> RclConfVecValueGetter; class ConfLinkPlusMinus : public ConfLinkRep { public: ConfLinkPlusMinus(RclConfig *rclconf, ConfNull **conf, const string& basename, RclConfVecValueGetter getter, string *sk = 0) : m_rclconf(rclconf), m_conf(conf), m_basename(basename), m_getter(getter), m_sk(sk) /* KEEP THE POINTER, shared data */ { } virtual ~ConfLinkPlusMinus() {} virtual bool set(const string& snval) { if (!m_conf || !*m_conf || !m_rclconf) return false; string sbase; (*m_conf)->get(m_basename, sbase, getSk()); std::set nval; stringToStrings(snval, nval); string splus, sminus; RclConfig::setPlusMinus(sbase, nval, splus, sminus); LOGDEB1("ConfLinkPlusMinus: base [" << sbase << "] nvalue [" << snval << "] splus [" << splus << "] sminus [" << sminus << "]\n"); if (!(*m_conf)->set(m_basename + "-", sminus, getSk())) { return false; } if (!(*m_conf)->set(m_basename + "+", splus, getSk())) { return false; } return true; } virtual bool get(string& val) { LOGDEB("ConfLinPlusMinus::get [" << m_basename << "]\n"); if (!m_conf || !*m_conf || !m_rclconf) return false; m_rclconf->setKeyDir(getSk()); vector vval = m_getter(); val = stringsToString(vval); LOGDEB1("ConfLinkPlusMinus: " << m_basename << " -> " << val << "\n"); return true; } private: string getSk() { return m_sk ? *m_sk : string(); } RclConfig *m_rclconf; ConfNull **m_conf; string m_basename; RclConfVecValueGetter m_getter; const string *m_sk; }; class MyConfLinkFactRCL : public ConfLinkFact { public: MyConfLinkFactRCL() {} MyConfLinkFactRCL(ConfNull **conf, string *sk = 0) : m_conf(conf), m_sk(sk) /* KEEP THE POINTER, shared data */ {} virtual ConfLink operator()(const QString& nm) { ConfLinkRep *lnk = new ConfLinkRclRep(m_conf, qs2utf8s(nm), m_sk); return ConfLink(lnk); } ConfNull **m_conf{nullptr}; string *m_sk{nullptr}; }; string sknull; static MyConfLinkFactRCL conflinkfactory; void ConfIndexW::showPrefs(bool modal) { delete m_conf; if ((m_conf = m_rclconf->cloneMainConfig()) == 0) { return; } m_conf->holdWrites(true); if (nullptr == m_w) { QString title = u8s2qs("Recoll - Index Settings: "); title += path2qs(m_rclconf->getConfDir()); conflinkfactory = MyConfLinkFactRCL(&m_conf, &sknull); if (nullptr == (m_w = new ConfTabsW(this, title, &conflinkfactory))) { return; } connect(m_w, SIGNAL(sig_prefsChanged()), this, SLOT(acceptChanges())); initPanels(); } else { m_w->hide(); } m_w->reloadPanels(); if (modal) { m_w->exec(); m_w->setModal(false); } else { m_w->show(); } } void ConfIndexW::acceptChanges() { LOGDEB("ConfIndexW::acceptChanges()\n" ); if (!m_conf) { LOGERR("ConfIndexW::acceptChanges: no config\n" ); return; } if (!m_conf->holdWrites(false)) { QMessageBox::critical(0, "Recoll", tr("Can't write configuration file")); } // Delete local copy and update the main one from the file delete m_conf; m_conf = 0; m_rclconf->updateMainConfig(); emit idxConfigPossiblyChanged(); } void ConfIndexW::initPanels() { int idx = m_w->addPanel(tr("Global parameters")); setupTopPanel(idx); idx = m_w->addForeignPanel( new ConfSubPanelW(m_w, &m_conf, m_rclconf), tr("Local parameters")); idx = m_w->addPanel(tr("Web history")); setupWebHistoryPanel(idx); idx = m_w->addPanel(tr("Search parameters")); setupSearchPanel(idx); } bool ConfIndexW::setupTopPanel(int idx) { m_w->addParam(idx, ConfTabsW::CFPT_DNL, "topdirs", tr("Top directories"), tr("The list of directories where recursive " "indexing starts. Default: your home.")); ConfParamW *cparam = m_w->addParam( idx, ConfTabsW::CFPT_DNL, "skippedPaths", tr("Skipped paths"), tr("These are pathnames of directories which indexing " "will not enter.
Path elements may contain wildcards. " "The entries must match the paths seen by the indexer " "(e.g.: if topdirs includes '/home/me' and '/home' is " "actually a link to '/usr/home', a correct skippedPath entry " "would be '/home/me/tmp*', not '/usr/home/me/tmp*')")); cparam->setFsEncoding(true); ((confgui::ConfParamSLW*)cparam)->setEditable(true); if (m_stemlangs.empty()) { vector cstemlangs = Rcl::Db::getStemmerNames(); for (const auto &clang : cstemlangs) { m_stemlangs.push_back(u8s2qs(clang)); } } m_w->addParam(idx, ConfTabsW::CFPT_CSTRL, "indexstemminglanguages", tr("Stemming languages"), tr("The languages for which stemming expansion " "dictionaries will be built.
See the Xapian stemmer " "documentation for possible values. E.g. english, " "french, german..."), 0, 0, &m_stemlangs); m_w->addParam(idx, ConfTabsW::CFPT_FN, "logfilename", tr("Log file name"), tr("The file where the messages will be written.
" "Use 'stderr' for terminal output"), 0); m_w->addParam( idx, ConfTabsW::CFPT_INT, "loglevel", tr("Log verbosity level"), tr("This value adjusts the amount of messages,
from only " "errors to a lot of debugging data."), 0, 6); m_w->addParam(idx, ConfTabsW::CFPT_FN, "idxlogfilename", tr("Indexer log file name"), tr("If empty, the above log file name value will be used. " "It may useful to have a separate log for diagnostic " "purposes because the common log will be erased when
" "the GUI starts up."), 0); m_w->addParam(idx, ConfTabsW::CFPT_INT, "idxflushmb", tr("Index flush megabytes interval"), tr("This value adjust the amount of " "data which is indexed between flushes to disk.
" "This helps control the indexer memory usage. " "Default 10MB "), 0, 1000); m_w->addParam(idx, ConfTabsW::CFPT_INT, "maxfsoccuppc", tr("Disk full threshold percentage at which we stop indexing
" "(E.g. 90% to stop at 90% full, 0 or 100 means no limit)"), tr("This is the percentage of disk usage " "- total disk usage, not index size - at which " "indexing will fail and stop.
" "The default value of 0 removes any limit."), 0, 100); ConfParamW *bparam = m_w->addParam( idx, ConfTabsW::CFPT_BOOL, "noaspell", tr("No aspell usage") + tr(" (by default, aspell suggests mispellings when a query has no results)."), tr("Disables use of aspell to generate spelling " "approximation in the term explorer tool.
" "Useful if aspell is absent or does not work. ")); cparam = m_w->addParam( idx, ConfTabsW::CFPT_STR, "aspellLanguage", tr("Aspell language"), tr("The language for the aspell dictionary. " "The values are are 2-letter " "language codes, e.g. 'en', 'fr' ...
" "If this value is not set, the NLS environment " "will be used to compute it, which usually works. " "To get an idea of what is installed on your system, " "type 'aspell config' and look for .dat files inside " "the 'data-dir' directory.")); m_w->enableLink(bparam, cparam, true); m_w->addParam( idx, ConfTabsW::CFPT_FN, "dbdir", tr("Database directory name"), tr("The name for a directory where to store the index
" "A non-absolute path is taken relative to the " "configuration directory. The default is 'xapiandb'."), true); m_w->addParam(idx, ConfTabsW::CFPT_STR, "unac_except_trans", tr("Unac exceptions"), tr("

These are exceptions to the unac mechanism " "which, by default, removes all diacritics, " "and performs canonic decomposition. You can override " "unaccenting for some characters, depending on your " "language, and specify additional decompositions, " "e.g. for ligatures. In each space-separated entry, " "the first character is the source one, and the rest " "is the translation." )); m_w->endOfList(idx); return true; } bool ConfIndexW::setupWebHistoryPanel(int idx) { ConfParamW *bparam = m_w->addParam( idx, ConfTabsW::CFPT_BOOL, "processwebqueue", tr("Process the Web history queue"), tr("Enables indexing Firefox visited pages.
" "(you need also install the Firefox Recoll plugin)")); ConfParamW *cparam = m_w->addParam( idx, ConfTabsW::CFPT_FN, "webcachedir", tr("Web page store directory name"), tr("The name for a directory where to store the copies " "of visited web pages.
" "A non-absolute path is taken relative to the " "configuration directory."), 1); m_w->enableLink(bparam, cparam); cparam = m_w->addParam( idx, ConfTabsW::CFPT_INT, "webcachemaxmbs", tr("Max. size for the web store (MB)"), tr("Entries will be recycled once the size is reached." "
" "Only increasing the size really makes sense because " "reducing the value will not truncate an existing " "file (only waste space at the end)." ), -1, 1000*1000); // Max 1TB... m_w->enableLink(bparam, cparam); QStringList intervals{"", "day", "week", "month", "year"}; cparam = m_w->addParam( idx, ConfTabsW::CFPT_CSTR, "webcachekeepinterval", tr("Page recycle interval"), tr("

By default, only one instance of an URL is kept in the cache. This " "can be changed by setting this to a value determining at what frequency " "we keep multiple instances ('day', 'week', 'month', 'year'). " "Note that increasing the interval will not erase existing entries."), 0, 0, &intervals); m_w->enableLink(bparam, cparam); int64_t sz = 0; auto ws = std::unique_ptr(new WebStore(m_rclconf)); if (ws && ws->cc()) { sz = ws->cc()->size(); } m_w->addBlurb(idx, tr("Note: old pages will be erased to make space for " "new ones when the maximum size is reached. " "Current size: %1").arg(u8s2qs(displayableBytes(sz)))); m_w->endOfList(idx); return true; } bool ConfIndexW::setupSearchPanel(int idx) { if (!o_index_stripchars) { m_w->addParam(idx, ConfTabsW::CFPT_BOOL, "autodiacsens", tr("Automatic diacritics sensitivity"), tr("

Automatically trigger diacritics sensitivity " "if the search term has accented characters " "(not in unac_except_trans). Else you need to " "use the query language and the D " "modifier to specify diacritics sensitivity.")); m_w->addParam(idx, ConfTabsW::CFPT_BOOL, "autocasesens", tr("Automatic character case sensitivity"), tr("

Automatically trigger character case " "sensitivity if the entry has upper-case " "characters in any but the first position. " "Else you need to use the query language and " "the C modifier to specify character-case " "sensitivity.")); } m_w->addParam(idx, ConfTabsW::CFPT_INT, "maxTermExpand", tr("Maximum term expansion count"), tr("

Maximum expansion count for a single term " "(e.g.: when using wildcards). The default " "of 10 000 is reasonable and will avoid " "queries that appear frozen while the engine is " "walking the term list."), 0, 100000); m_w->addParam(idx, ConfTabsW::CFPT_INT, "maxXapianClauses", tr("Maximum Xapian clauses count"), tr("

Maximum number of elementary clauses we " "add to a single Xapian query. In some cases, " "the result of term expansion can be " "multiplicative, and we want to avoid using " "excessive memory. The default of 100 000 " "should be both high enough in most cases " "and compatible with current typical hardware " "configurations."), 0, 1000000); m_w->endOfList(idx); return true; } ConfSubPanelW::ConfSubPanelW(QWidget *parent, ConfNull **config, RclConfig *rclconf) : QWidget(parent), m_config(config) { QVBoxLayout *vboxLayout = new QVBoxLayout(this); vboxLayout->setSpacing(spacing); vboxLayout->setContentsMargins(QMargins(margin,margin,margin,margin)); m_subdirs = new ConfParamDNLW( "bogus00", this, ConfLink(new confgui::ConfLinkNullRep()), QObject::tr("Customised subtrees"), QObject::tr("The list of subdirectories in the indexed " "hierarchy
where some parameters need " "to be redefined. Default: empty.")); m_subdirs->getListBox()->setSelectionMode( QAbstractItemView::SingleSelection); connect(m_subdirs->getListBox(), SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(subDirChanged(QListWidgetItem *, QListWidgetItem *))); connect(m_subdirs, SIGNAL(entryDeleted(QString)), this, SLOT(subDirDeleted(QString))); // We only retrieve the subkeys from the user's config (shallow), // no use to confuse the user by showing the subtrees which are // customized in the system config like .thunderbird or // .purple. This doesn't prevent them to add and customize them // further. vector allkeydirs = (*config)->getSubKeys(true); QStringList qls; for (const auto& dir: allkeydirs) { qls.push_back(u8s2qs(dir)); } m_subdirs->getListBox()->insertItems(0, qls); vboxLayout->addWidget(m_subdirs); QFrame *line2 = new QFrame(this); line2->setFrameShape(QFrame::HLine); line2->setFrameShadow(QFrame::Sunken); vboxLayout->addWidget(line2); QLabel *explain = new QLabel(this); explain->setWordWrap(true); explain->setText( QObject::tr( "The parameters that follow are set either at the " "top level, if nothing " "or an empty line is selected in the listbox above, " "or for the selected subdirectory. " "You can add or remove directories by clicking " "the +/- buttons.")); vboxLayout->addWidget(explain); m_groupbox = new QGroupBox(this); setSzPol(m_groupbox, QSizePolicy::Preferred, QSizePolicy::Preferred, 1, 3); QGridLayout *gl1 = new QGridLayout(m_groupbox); gl1->setSpacing(spacing); gl1->setContentsMargins(QMargins(margin,margin,margin,margin)); int gridy = 0; ConfParamSLW *eskn = new ConfParamSLW( "skippedNames", m_groupbox, ConfLink(new ConfLinkPlusMinus( rclconf, config, "skippedNames", std::bind(&RclConfig::getSkippedNames, rclconf), &m_sk)), QObject::tr("Skipped names"), QObject::tr("These are patterns for file or directory " " names which should not be indexed.")); eskn->setFsEncoding(true); eskn->setImmediate(); m_widgets.push_back(eskn); gl1->addWidget(eskn, gridy, 0); vector amimes = rclconf->getAllMimeTypes(); QStringList amimesq; for (const auto& mime: amimes) { amimesq.push_back(u8s2qs(mime)); } ConfParamCSLW *eincm = new ConfParamCSLW( "indexedmimetypes", m_groupbox, ConfLink(new ConfLinkRclRep(config, "indexedmimetypes", &m_sk)), tr("Only mime types"), tr("An exclusive list of indexed mime types.
Nothing " "else will be indexed. Normally empty and inactive"), amimesq); eincm->setImmediate(); m_widgets.push_back(eincm); gl1->addWidget(eincm, gridy++, 1); ConfParamCSLW *eexcm = new ConfParamCSLW( "excludedmimetypes", m_groupbox, ConfLink(new ConfLinkRclRep(config, "excludedmimetypes", &m_sk)), tr("Exclude mime types"), tr("Mime types not to be indexed"), amimesq); eexcm->setImmediate(); m_widgets.push_back(eexcm); gl1->addWidget(eexcm, gridy, 0); ConfParamSLW *encs = new ConfParamSLW( "noContentSuffixes", m_groupbox, ConfLink(new ConfLinkPlusMinus( rclconf, config, "noContentSuffixes", std::bind(&RclConfig::getStopSuffixes, rclconf), &m_sk)), QObject::tr("Ignored endings"), QObject::tr("These are file name endings for files which will be " "indexed by name only \n(no MIME type identification " "attempt, no decompression, no content indexing).")); encs->setImmediate(); encs->setFsEncoding(true); m_widgets.push_back(encs); gl1->addWidget(encs, gridy++, 1); vector args; args.push_back("-l"); ExecCmd ex; string icout; string cmd = "iconv"; int status = ex.doexec(cmd, args, 0, &icout); if (status) { LOGERR("Can't get list of charsets from 'iconv -l'"); } icout = neutchars(icout, ","); vector ccsets; stringToStrings(icout, ccsets); QStringList charsets; charsets.push_back(""); for (const auto& charset : ccsets) { charsets.push_back(u8s2qs(charset)); } ConfParamCStrW *e21 = new ConfParamCStrW( "defaultcharset", m_groupbox, ConfLink(new ConfLinkRclRep(config, "defaultcharset", &m_sk)), QObject::tr("Default
character set"), QObject::tr("Character set used for reading files " "which do not identify the character set " "internally, for example pure text files.
" "The default value is empty, " "and the value from the NLS environnement is used." ), charsets); e21->setImmediate(); m_widgets.push_back(e21); gl1->addWidget(e21, gridy++, 0); ConfParamBoolW *e3 = new ConfParamBoolW( "followLinks", m_groupbox, ConfLink(new ConfLinkRclRep(config, "followLinks", &m_sk)), QObject::tr("Follow symbolic links"), QObject::tr("Follow symbolic links while " "indexing. The default is no, " "to avoid duplicate indexing")); e3->setImmediate(); m_widgets.push_back(e3); gl1->addWidget(e3, gridy, 0); ConfParamBoolW *eafln = new ConfParamBoolW( "indexallfilenames", m_groupbox, ConfLink(new ConfLinkRclRep(config, "indexallfilenames", &m_sk)), QObject::tr("Index all file names"), QObject::tr("Index the names of files for which the contents " "cannot be identified or processed (no or " "unsupported mime type). Default true")); eafln->setImmediate(); m_widgets.push_back(eafln); gl1->addWidget(eafln, gridy++, 1); ConfParamIntW *ezfmaxkbs = new ConfParamIntW( "compressedfilemaxkbs", m_groupbox, ConfLink(new ConfLinkRclRep(config, "compressedfilemaxkbs", &m_sk)), tr("Max. compressed file size (KB)"), tr("This value sets a threshold beyond which compressed" "files will not be processed. Set to -1 for no " "limit, to 0 for no decompression ever."), -1, 1000000, -1); ezfmaxkbs->setImmediate(); m_widgets.push_back(ezfmaxkbs); gl1->addWidget(ezfmaxkbs, gridy, 0); ConfParamIntW *etxtmaxmbs = new ConfParamIntW( "textfilemaxmbs", m_groupbox, ConfLink(new ConfLinkRclRep(config, "textfilemaxmbs", &m_sk)), tr("Max. text file size (MB)"), tr("This value sets a threshold beyond which text " "files will not be processed. Set to -1 for no " "limit. \nThis is for excluding monster " "log files from the index."), -1, 1000000); etxtmaxmbs->setImmediate(); m_widgets.push_back(etxtmaxmbs); gl1->addWidget(etxtmaxmbs, gridy++, 1); ConfParamIntW *etxtpagekbs = new ConfParamIntW( "textfilepagekbs", m_groupbox, ConfLink(new ConfLinkRclRep(config, "textfilepagekbs", &m_sk)), tr("Text file page size (KB)"), tr("If this value is set (not equal to -1), text " "files will be split in chunks of this size for " "indexing.\nThis will help searching very big text " " files (ie: log files)."), -1, 1000000); etxtpagekbs->setImmediate(); m_widgets.push_back(etxtpagekbs); gl1->addWidget(etxtpagekbs, gridy, 0); ConfParamIntW *efiltmaxsecs = new ConfParamIntW( "filtermaxseconds", m_groupbox, ConfLink(new ConfLinkRclRep(config, "filtermaxseconds", &m_sk)), tr("Max. filter exec. time (s)"), tr("External filters working longer than this will be " "aborted. This is for the rare case (ie: postscript) " "where a document could cause a filter to loop. " "Set to -1 for no limit.\n"), -1, 10000); efiltmaxsecs->setImmediate(); m_widgets.push_back(efiltmaxsecs); gl1->addWidget(efiltmaxsecs, gridy++, 1); vboxLayout->addWidget(m_groupbox); subDirChanged(0, 0); LOGDEB("ConfSubPanelW::ConfSubPanelW: done\n"); } void ConfSubPanelW::loadValues() { LOGDEB("ConfSubPanelW::loadValues\n"); for (auto widget : m_widgets) { widget->loadValue(); } LOGDEB("ConfSubPanelW::loadValues done\n"); } void ConfSubPanelW::storeValues() { for (auto widget : m_widgets) { widget->storeValue(); } } void ConfSubPanelW::subDirChanged(QListWidgetItem *current, QListWidgetItem *) { LOGDEB("ConfSubPanelW::subDirChanged\n"); if (current == 0 || current->text() == "") { m_sk = ""; m_groupbox->setTitle(tr("Global")); } else { m_sk = qs2utf8s(current->text()); m_groupbox->setTitle(current->text()); } LOGDEB("ConfSubPanelW::subDirChanged: now [" << m_sk << "]\n"); loadValues(); LOGDEB("ConfSubPanelW::subDirChanged: done\n"); } void ConfSubPanelW::subDirDeleted(QString sbd) { LOGDEB("ConfSubPanelW::subDirDeleted(" << qs2utf8s(sbd) << ")\n"); if (sbd == "") { // Can't do this, have to reinsert it QTimer::singleShot(0, this, SLOT(restoreEmpty())); return; } // Have to delete all entries for submap (*m_config)->eraseKey(qs2utf8s(sbd)); } void ConfSubPanelW::restoreEmpty() { LOGDEB("ConfSubPanelW::restoreEmpty()\n"); m_subdirs->getListBox()->insertItem(0, ""); } recoll-1.36.1/qtgui/rclhelp.cpp0000644000175000017500000000543414427373216013310 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include "recoll.h" #include "rclhelp.h" #include "log.h" using std::string; using std::map; map HelpClient::helpmap; void HelpClient::installMap(string wname, string section) { helpmap[wname] = section; } HelpClient::HelpClient(QObject *parent, const char *) : QObject(parent) { parent->installEventFilter(this); } bool HelpClient::eventFilter(QObject *obj, QEvent *event) { static time_t last_start; if (event->type() != QEvent::KeyPress && event->type() != QEvent::ShortcutOverride) { return false; } QKeyEvent *ke = static_cast(event); if (ke->key() != Qt::Key_F1 && ke->key() != Qt::Key_Help) { return false; } if (!obj->isWidgetType()) { return false; } QWidget *widget = dynamic_cast(obj); if (nullptr == widget) { // ?? return false; } widget = widget->focusWidget(); auto it = helpmap.end(); std::string onm; while (widget) { QTabWidget *tw = dynamic_cast(widget); if (nullptr != tw) { onm = qs2utf8s(tw->currentWidget()->objectName()); LOGDEB1("HelpClient: tab name " << onm << "\n"); it = helpmap.find(onm); if (it != helpmap.end()) break; } onm = qs2utf8s(widget->objectName()); LOGDEB1("HelpClient: object name " << onm << "\n"); it = helpmap.find(onm); if (it != helpmap.end()) break; widget = widget->parentWidget(); } if (time(0) - last_start > 5) { last_start = time(0); if (it != helpmap.end()) { LOGDEB("HelpClient::eventFilter: " << it->first << "->" << it->second << "\n"); startManual(it->second); } else { LOGDEB("HelpClient::eventFilter: no help section\n"); startManual(""); } } return true; } recoll-1.36.1/qtgui/advsearch_w.cpp0000644000175000017500000005346114427373216014150 00000000000000/* Copyright (C) 2005-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "advsearch_w.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "recoll.h" #include "rclconfig.h" #include "log.h" #include "searchdata.h" #include "guiutils.h" #include "rclhelp.h" #include "scbase.h" #include "advshist.h" #include "pathut.h" using namespace std; static const int initclausetypes[] = {1, 3, 0, 2, 5}; static const unsigned int iclausescnt = sizeof(initclausetypes) / sizeof(int); static map cat_translations; static map cat_rtranslations; void AdvSearch::init() { (void)new HelpClient(this); HelpClient::installMap((const char *)objectName().toUtf8(), "RCL.SEARCH.GUI.COMPLEX"); // signals and slots connections connect(delFiltypPB, SIGNAL(clicked()), this, SLOT(delFiltypPB_clicked())); connect(searchPB, SIGNAL(clicked()), this, SLOT(runSearch())); connect(filterDatesCB, SIGNAL(toggled(bool)), this, SLOT(filterDatesCB_toggled(bool))); connect(filterSizesCB, SIGNAL(toggled(bool)), this, SLOT(filterSizesCB_toggled(bool))); connect(restrictFtCB, SIGNAL(toggled(bool)), this, SLOT(restrictFtCB_toggled(bool))); connect(restrictCtCB, SIGNAL(toggled(bool)), this, SLOT(restrictCtCB_toggled(bool))); connect(dismissPB, SIGNAL(clicked()), this, SLOT(close())); connect(browsePB, SIGNAL(clicked()), this, SLOT(browsePB_clicked())); connect(addFiltypPB, SIGNAL(clicked()), this, SLOT(addFiltypPB_clicked())); connect(delAFiltypPB, SIGNAL(clicked()), this, SLOT(delAFiltypPB_clicked())); connect(addAFiltypPB, SIGNAL(clicked()), this, SLOT(addAFiltypPB_clicked())); connect(saveFileTypesPB, SIGNAL(clicked()), this, SLOT(saveFileTypes())); connect(addClausePB, SIGNAL(clicked()), this, SLOT(addClause())); connect(delClausePB, SIGNAL(clicked()), this, SLOT(delClause())); onNewShortcuts(); connect(&SCBase::scBase(), SIGNAL(shortcutsChanged()), this, SLOT(onNewShortcuts())); #ifdef EXT4_BIRTH_TIME filterBirthDatesCB->show(); minBirthDateDTE->show(); maxBirthDateDTE->show(); label_birth_from->show(); label_birth_to->show(); line_birth->show(); connect(filterBirthDatesCB, SIGNAL(toggled(bool)), this, SLOT(filterBirthDatesCB_toggled(bool))); #else filterBirthDatesCB->hide(); minBirthDateDTE->hide(); maxBirthDateDTE->hide(); label_birth_from->hide(); label_birth_to->hide(); line_birth->hide(); #endif conjunctCMB->insertItem(1, tr("All clauses")); conjunctCMB->insertItem(2, tr("Any clause")); // Create preconfigured clauses for (unsigned int i = 0; i < iclausescnt; i++) { addClause(initclausetypes[i], false); } // Tune initial state according to last saved { vector::iterator cit = m_clauseWins.begin(); unsigned int existing = m_clauseWins.size(); for (unsigned int i = 0; i < prefs.advSearchClauses.size(); i++) { if (i < existing) { (*cit)->tpChange(prefs.advSearchClauses[i]); cit++; } else { addClause(prefs.advSearchClauses[i], false); } } } (*m_clauseWins.begin())->wordsLE->setFocus(); // Initialize min/max mtime from extrem values in the index int minyear, maxyear; if (rcldb) { rcldb->maxYearSpan(&minyear, &maxyear); minDateDTE->setDisplayFormat("yyyy-MM-dd"); maxDateDTE->setDisplayFormat("yyyy-MM-dd"); minDateDTE->setDate(QDate(minyear, 1, 1)); maxDateDTE->setDate(QDate(maxyear, 12, 31)); } #ifdef EXT4_BIRTH_TIME int birthminyear, birthmaxyear; if (rcldb) { rcldb->maxYearSpan(&birthminyear, &birthmaxyear); minBirthDateDTE->setDisplayFormat("yyyy-MM-dd"); maxBirthDateDTE->setDisplayFormat("yyyy-MM-dd"); minBirthDateDTE->setDate(QDate(birthminyear, 1, 1)); maxBirthDateDTE->setDate(QDate(birthmaxyear, 12, 31)); } #endif // Initialize lists of accepted and ignored mime types from config // and settings m_ignTypes = prefs.asearchIgnFilTyps; m_ignByCats = prefs.fileTypesByCats; restrictCtCB->setEnabled(false); restrictCtCB->setChecked(m_ignByCats); fillFileTypes(); subtreeCMB->insertItems(0, prefs.asearchSubdirHist); subtreeCMB->setEditText(""); // The clauseline frame is needed to force designer to accept a // vbox to englobe the base clauses grid and 'something else' (the // vbox is so that we can then insert SearchClauseWs), but we // don't want to see it. clauseline->close(); bool calpop = 0; minDateDTE->setCalendarPopup(calpop); maxDateDTE->setCalendarPopup(calpop); #ifdef EXT4_BIRTH_TIME bool birthcalpop = 0; minBirthDateDTE->setCalendarPopup(birthcalpop); maxBirthDateDTE->setCalendarPopup(birthcalpop); #endif // Translations for known categories cat_translations[QString::fromUtf8("texts")] = tr("text"); cat_rtranslations[tr("texts")] = QString::fromUtf8("text"); cat_translations[QString::fromUtf8("spreadsheet")] = tr("spreadsheet"); cat_rtranslations[tr("spreadsheets")] = QString::fromUtf8("spreadsheet"); cat_translations[QString::fromUtf8("presentation")] = tr("presentation"); cat_rtranslations[tr("presentation")] =QString::fromUtf8("presentation"); cat_translations[QString::fromUtf8("media")] = tr("media"); cat_rtranslations[tr("media")] = QString::fromUtf8("media"); cat_translations[QString::fromUtf8("message")] = tr("message"); cat_rtranslations[tr("message")] = QString::fromUtf8("message"); cat_translations[QString::fromUtf8("other")] = tr("other"); cat_rtranslations[tr("other")] = QString::fromUtf8("other"); } void AdvSearch::saveCnf() { // Save my state prefs.advSearchClauses.clear(); for (const auto& clause : m_clauseWins) { prefs.advSearchClauses.push_back(clause->sTpCMB->currentIndex()); } } void AdvSearch::onNewShortcuts() { SETSHORTCUT(this, "advsearch:171", tr("Advanced Search"), tr("Load next stored search"), "Up", m_histnextsc, slotHistoryNext); SETSHORTCUT(this, "advsearch:174", tr("Advanced Search"), tr("Load previous stored search"), "Down", m_histprevsc, slotHistoryPrev); } void AdvSearch::listShortcuts() { LISTSHORTCUT(this, "advsearch:171", tr("Advanced Search"), tr("Load next stored search"), "Up", m_histnextsc, slotHistoryNext); LISTSHORTCUT(this, "advsearch:174", tr("Advanced Search"), tr("Load previous stored search"), "Down", m_histprevsc, slotHistoryPrev); } void AdvSearch::addClause(bool updsaved) { addClause(0, updsaved); } void AdvSearch::addClause(int tp, bool updsaved) { SearchClauseW *w = new SearchClauseW(clauseFRM); m_clauseWins.push_back(w); ((QVBoxLayout *)(clauseFRM->layout()))->addWidget(w); w->show(); w->tpChange(tp); if (m_clauseWins.size() > iclausescnt) { delClausePB->setEnabled(true); } else { delClausePB->setEnabled(false); } if (updsaved) { saveCnf(); } } void AdvSearch::delClause(bool updsaved) { if (m_clauseWins.size() <= iclausescnt) return; delete m_clauseWins.back(); m_clauseWins.pop_back(); if (m_clauseWins.size() > iclausescnt) { delClausePB->setEnabled(true); } else { delClausePB->setEnabled(false); } if (updsaved) { saveCnf(); } } void AdvSearch::delAFiltypPB_clicked() { yesFiltypsLB->selectAll(); delFiltypPB_clicked(); } // Move selected file types from the searched to the ignored box void AdvSearch::delFiltypPB_clicked() { QList items = yesFiltypsLB->selectedItems(); for (QList::iterator it = items.begin(); it != items.end(); it++) { int row = yesFiltypsLB->row(*it); QListWidgetItem *item = yesFiltypsLB->takeItem(row); noFiltypsLB->insertItem(0, item); } guiListsToIgnTypes(); } // Move selected file types from the ignored to the searched box void AdvSearch::addFiltypPB_clicked() { QList items = noFiltypsLB->selectedItems(); for (QList::iterator it = items.begin(); it != items.end(); it++) { int row = noFiltypsLB->row(*it); QListWidgetItem *item = noFiltypsLB->takeItem(row); yesFiltypsLB->insertItem(0, item); } guiListsToIgnTypes(); } // Compute list of ignored mime type from widget lists void AdvSearch::guiListsToIgnTypes() { yesFiltypsLB->sortItems(); noFiltypsLB->sortItems(); m_ignTypes.clear(); for (int i = 0; i < noFiltypsLB->count();i++) { QListWidgetItem *item = noFiltypsLB->item(i); m_ignTypes.append(item->text()); } } void AdvSearch::addAFiltypPB_clicked() { noFiltypsLB->selectAll(); addFiltypPB_clicked(); } // Activate file type selection void AdvSearch::restrictFtCB_toggled(bool on) { restrictCtCB->setEnabled(on); yesFiltypsLB->setEnabled(on); delFiltypPB->setEnabled(on); addFiltypPB->setEnabled(on); delAFiltypPB->setEnabled(on); addAFiltypPB->setEnabled(on); noFiltypsLB->setEnabled(on); saveFileTypesPB->setEnabled(on); } // Activate file type selection void AdvSearch::filterSizesCB_toggled(bool on) { minSizeLE->setEnabled(on); maxSizeLE->setEnabled(on); } // Activate file type selection void AdvSearch::filterDatesCB_toggled(bool on) { minDateDTE->setEnabled(on); maxDateDTE->setEnabled(on); } void AdvSearch::filterBirthDatesCB_toggled(bool on) { minBirthDateDTE->setEnabled(on); maxBirthDateDTE->setEnabled(on); } void AdvSearch::restrictCtCB_toggled(bool on) { m_ignByCats = on; // Only reset the list if we're enabled. Else this is init from prefs if (restrictCtCB->isEnabled()) m_ignTypes.clear(); fillFileTypes(); } void AdvSearch::fillFileTypes() { noFiltypsLB->clear(); yesFiltypsLB->clear(); noFiltypsLB->insertItems(0, m_ignTypes); QStringList ql; if (m_ignByCats == false) { vector types = theconfig->getAllMimeTypes(); rcldb->getAllDbMimeTypes(types); sort(types.begin(), types.end()); types.erase(unique(types.begin(), types.end()), types.end()); for (vector::iterator it = types.begin(); it != types.end(); it++) { QString qs = QString::fromUtf8(it->c_str()); if (m_ignTypes.indexOf(qs) < 0) ql.append(qs); } } else { vector cats; theconfig->getMimeCategories(cats); for (vector::const_iterator it = cats.begin(); it != cats.end(); it++) { map::const_iterator it1; QString cat; if ((it1 = cat_translations.find(QString::fromUtf8(it->c_str()))) != cat_translations.end()) { cat = it1->second; } else { cat = QString::fromUtf8(it->c_str()); } if (m_ignTypes.indexOf(cat) < 0) ql.append(cat); } } yesFiltypsLB->insertItems(0, ql); } // Save current set of ignored file types to prefs void AdvSearch::saveFileTypes() { prefs.asearchIgnFilTyps = m_ignTypes; prefs.fileTypesByCats = m_ignByCats; rwSettings(true); } void AdvSearch::browsePB_clicked() { auto topdirs = theconfig->getTopdirs(); // if dirlocation is not empty, it's the last location set by the user, leave it alone if (m_gfnparams.dirlocation.isEmpty() && !topdirs.empty()) { m_gfnparams.dirlocation = u8s2qs(topdirs[0]); } m_gfnparams.sidedirs = topdirs; m_gfnparams.readonly = true; m_gfnparams.caption = "Select directory"; QString dir = myGetFileName(true, m_gfnparams); #ifdef _WIN32 string s = qs2utf8s(dir); path_slashize(s); dir = u8s2qs(path_slashdrive(s)); #endif subtreeCMB->setEditText(dir); } size_t AdvSearch::stringToSize(QString qsize) { size_t size = size_t(-1); qsize.replace(QRegularExpression("[\\s]+"), ""); if (!qsize.isEmpty()) { string csize(qs2utf8s(qsize)); char *cp; size = strtoll(csize.c_str(), &cp, 10); if (*cp != 0) { switch (*cp) { case 'k': case 'K': size *= 1E3;break; case 'm': case 'M': size *= 1E6;break; case 'g': case 'G': size *= 1E9;break; case 't': case 'T': size *= 1E12;break; default: QMessageBox::warning(0, "Recoll", tr("Bad multiplier suffix in size filter")); size = size_t(-1); } } } return size; } using namespace Rcl; void AdvSearch::runSearch() { string stemLang = prefs.stemlang(); std::shared_ptr sdata(new SearchData(conjunctCMB->currentIndex() == 0 ? SCLT_AND : SCLT_OR, stemLang)); bool hasclause = false; for (vector::iterator it = m_clauseWins.begin(); it != m_clauseWins.end(); it++) { SearchDataClause *cl; if ((cl = (*it)->getClause())) { sdata->addClause(cl); hasclause = true; } } if (!hasclause) return; if (restrictFtCB->isChecked() && noFiltypsLB->count() > 0) { for (int i = 0; i < yesFiltypsLB->count(); i++) { if (restrictCtCB->isChecked()) { QString qcat = yesFiltypsLB->item(i)->text(); map::const_iterator qit; string cat; if ((qit = cat_rtranslations.find(qcat)) != cat_rtranslations.end()) { cat = qs2utf8s(qit->second); } else { cat = qs2utf8s(qcat); } vector types; theconfig->getMimeCatTypes(cat, types); for (vector::const_iterator it = types.begin(); it != types.end(); it++) { sdata->addFiletype(*it); } } else { sdata->addFiletype(qs2utf8s(yesFiltypsLB->item(i)->text())); } } } if (filterDatesCB->isChecked()) { QDate mindate = minDateDTE->date(); QDate maxdate = maxDateDTE->date(); DateInterval di; di.y1 = mindate.year(); di.m1 = mindate.month(); di.d1 = mindate.day(); di.y2 = maxdate.year(); di.m2 = maxdate.month(); di.d2 = maxdate.day(); sdata->setDateSpan(&di); } #ifdef EXT4_BIRTH_TIME if (filterBirthDatesCB->isChecked()) { QDate mindate = minBirthDateDTE->date(); QDate maxdate = maxBirthDateDTE->date(); DateInterval di; di.y1 = mindate.year(); di.m1 = mindate.month(); di.d1 = mindate.day(); di.y2 = maxdate.year(); di.m2 = maxdate.month(); di.d2 = maxdate.day(); sdata->setBrDateSpan(&di); } #endif if (filterSizesCB->isChecked()) { size_t size = stringToSize(minSizeLE->text()); sdata->setMinSize(size); size = stringToSize(maxSizeLE->text()); sdata->setMaxSize(size); } if (!subtreeCMB->currentText().isEmpty()) { QString current = subtreeCMB->currentText(); Rcl::SearchDataClausePath *pathclause = new Rcl::SearchDataClausePath( qs2path(current), direxclCB->isChecked()); if (sdata->getTp() == SCLT_AND) { sdata->addClause(pathclause); } else { std::shared_ptr nsdata(new SearchData(SCLT_AND, stemLang)); nsdata->addClause(new Rcl::SearchDataClauseSub(sdata)); nsdata->addClause(pathclause); sdata = nsdata; } // Keep history clean and sorted. Maybe there would be a // simpler way to do this list entries; for (int i = 0; i < subtreeCMB->count(); i++) { entries.push_back(subtreeCMB->itemText(i)); } entries.push_back(subtreeCMB->currentText()); entries.sort(); entries.unique(); LOGDEB("Subtree list now has " << (entries.size()) << " entries\n" ); subtreeCMB->clear(); for (list::iterator it = entries.begin(); it != entries.end(); it++) { subtreeCMB->addItem(*it); } subtreeCMB->setCurrentIndex(subtreeCMB->findText(current)); prefs.asearchSubdirHist.clear(); for (int index = 0; index < subtreeCMB->count(); index++) prefs.asearchSubdirHist.push_back(subtreeCMB->itemText(index)); } saveCnf(); g_advshistory && g_advshistory->push(sdata); emit setDescription(""); emit startSearch(sdata, false); } // Set up fields from existing search data, which must be compatible // with what we can do... void AdvSearch::fromSearch(std::shared_ptr sdata) { if (sdata->m_tp == SCLT_OR) conjunctCMB->setCurrentIndex(1); else conjunctCMB->setCurrentIndex(0); while (sdata->m_query.size() > m_clauseWins.size()) { addClause(); } subtreeCMB->setEditText(""); direxclCB->setChecked(0); for (unsigned int i = 0; i < sdata->m_query.size(); i++) { // Set fields from clause if (sdata->m_query[i]->getTp() == SCLT_SUB) { LOGERR("AdvSearch::fromSearch: SUB clause found !\n" ); continue; } if (sdata->m_query[i]->getTp() == SCLT_PATH) { SearchDataClausePath *cs = dynamic_cast(sdata->m_query[i]); // We can only use one such clause. There should be only one too // if this is sfrom aved search data. QString qdir = path2qs(cs->gettext()); subtreeCMB->setEditText(qdir); direxclCB->setChecked(cs->getexclude()); continue; } SearchDataClauseSimple *cs = dynamic_cast(sdata->m_query[i]); m_clauseWins[i]->setFromClause(cs); } for (unsigned int i = sdata->m_query.size(); i < m_clauseWins.size(); i++) { m_clauseWins[i]->clear(); } restrictCtCB->setChecked(0); if (!sdata->m_filetypes.empty()) { restrictFtCB_toggled(1); delAFiltypPB_clicked(); for (unsigned int i = 0; i < sdata->m_filetypes.size(); i++) { QString ft = QString::fromUtf8(sdata->m_filetypes[i].c_str()); QList lst = noFiltypsLB->findItems(ft, Qt::MatchExactly); if (!lst.isEmpty()) { int row = noFiltypsLB->row(lst[0]); QListWidgetItem *item = noFiltypsLB->takeItem(row); yesFiltypsLB->insertItem(0, item); } } yesFiltypsLB->sortItems(); } else { addAFiltypPB_clicked(); restrictFtCB_toggled(0); } if (sdata->m_haveDates) { filterDatesCB->setChecked(1); DateInterval &di(sdata->m_dates); QDate mindate(di.y1, di.m1, di.d1); QDate maxdate(di.y2, di.m2, di.d2); minDateDTE->setDate(mindate); maxDateDTE->setDate(maxdate); } else { filterDatesCB->setChecked(0); QDate date; minDateDTE->setDate(date); maxDateDTE->setDate(date); } #ifdef EXT4_BIRTH_TIME if (sdata->m_haveBrDates) { filterBirthDatesCB->setChecked(1); DateInterval &di(sdata->m_brdates); QDate mindate(di.y1, di.m1, di.d1); QDate maxdate(di.y2, di.m2, di.d2); minBirthDateDTE->setDate(mindate); maxBirthDateDTE->setDate(maxdate); } else { filterBirthDatesCB->setChecked(0); QDate date; minBirthDateDTE->setDate(date); maxBirthDateDTE->setDate(date); } #endif if (sdata->m_maxSize != -1 || sdata->m_minSize != -1) { filterSizesCB->setChecked(1); QString sz; if (sdata->m_minSize != -1) { sz.setNum(sdata->m_minSize); minSizeLE->setText(sz); } else { minSizeLE->setText(""); } if (sdata->m_maxSize != -1) { sz.setNum(sdata->m_maxSize); maxSizeLE->setText(sz); } else { maxSizeLE->setText(""); } } else { filterSizesCB->setChecked(0); minSizeLE->setText(""); maxSizeLE->setText(""); } } void AdvSearch::slotHistoryNext() { if (g_advshistory == 0) return; std::shared_ptr sd = g_advshistory->getnewer(); if (!sd) return; fromSearch(sd); } void AdvSearch::slotHistoryPrev() { if (g_advshistory == 0) return; std::shared_ptr sd = g_advshistory->getolder(); if (!sd) return; fromSearch(sd); } recoll-1.36.1/qtgui/respopup.cpp0000644000175000017500000001647414502563125013534 00000000000000/* Copyright (C) 2005-2022 J.F.Dockes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include "log.h" #include "smallut.h" #include "recoll.h" #include "docseq.h" #include "respopup.h" #include "appformime.h" #include "rclmain_w.h" #include "utf8iter.h" #include "rclutil.h" using std::string; using std::vector; using std::map; using std::list; namespace ResultPopup { QMenu *create(QWidget *me, int opts, std::shared_ptr source, Rcl::Doc& doc) { QMenu *popup = new QMenu(me); LOGDEB("ResultPopup::create: opts " << opts << " haspages " << doc.haspages << " " << (source ? "Source not null" : "Source is Null") << " " << (source ? (source->snippetsCapable() ? "snippetsCapable":"not snippetsCapable") : "") << "\n"); string apptag; doc.getmeta(Rcl::Doc::keyapptg, &apptag); // Is this a top level file system file (accessible by regular utilities)? bool isFsTop = doc.ipath.empty() && doc.isFsFile(); popup->addAction(QWidget::tr("&Preview"), me, SLOT(menuPreview())); if (!theconfig->getMimeViewerDef(doc.mimetype, apptag, 0).empty()) { popup->addAction(QWidget::tr("&Open"), me, SLOT(menuEdit())); } if (isFsTop) { // Openable by regular program. Add "open with" entry. vector apps; DesktopDb *ddb = DesktopDb::getDb(); if (ddb && ddb->appForMime(doc.mimetype, &apps) && !apps.empty()) { QMenu *sub = popup->addMenu(QWidget::tr("Open With")); if (sub) { for (const auto& app : apps) { QAction *act = new QAction(u8s2qs(app.name), me); QVariant v(u8s2qs(app.command)); act->setData(v); sub->addAction(act); } sub->connect(sub, SIGNAL(triggered(QAction *)), me, SLOT(menuOpenWith(QAction *))); } } // See if there are any desktop files in $RECOLL_CONFDIR/scripts // and possibly create a "run script" menu. apps.clear(); ddb = new DesktopDb(path_cat(theconfig->getConfDir(), "scripts")); if (ddb && ddb->allApps(&apps) && !apps.empty()) { QMenu *sub = popup->addMenu(QWidget::tr("Run Script")); if (sub) { for (const auto& app : apps) { QAction *act = new QAction(u8s2qs(app.name), me); QVariant v(u8s2qs(app.command)); act->setData(v); sub->addAction(act); } sub->connect(sub, SIGNAL(triggered(QAction *)), me, SLOT(menuOpenWith(QAction *))); } } delete ddb; } if (doc.isFsFile()) { popup->addAction(QWidget::tr("Copy &File Path"), me, SLOT(menuCopyPath())); } popup->addAction(QWidget::tr("Copy &URL"), me, SLOT(menuCopyURL())); popup->addAction(QWidget::tr("Copy File Name"), me, SLOT(menuCopyFN())); popup->addAction(QWidget::tr("Copy Text"), me, SLOT(menuCopyText())); if ((opts&showSaveOne) && !(isFsTop)) popup->addAction(QWidget::tr("&Write to File"), me, SLOT(menuSaveToFile())); if ((opts&showSaveSel)) popup->addAction(QWidget::tr("Save selection to files"), me, SLOT(menuSaveSelection())); // We now separate preview/open parent, which only makes sense for // an embedded doc, and open folder (which was previously done if // the doc was a top level file and was not accessible else). Rcl::Doc pdoc; bool isEnclosed = source && source->getEnclosing(doc, pdoc); if (isEnclosed) { popup->addAction(QWidget::tr("Preview P&arent document/folder"), me, SLOT(menuPreviewParent())); popup->addAction(QWidget::tr("&Open Parent document"), me, SLOT(menuOpenParent())); } if (doc.isFsFile()) popup->addAction(QWidget::tr("&Open Parent Folder"), me, SLOT(menuOpenFolder())); if (opts & showExpand) popup->addAction(QWidget::tr("Find &similar documents"), me, SLOT(menuExpand())); if (doc.haspages && source && source->snippetsCapable()) popup->addAction(QWidget::tr("Open &Snippets window"), me, SLOT(menuShowSnippets())); if ((opts & showSubs) && rcldb && rcldb->hasSubDocs(doc)) popup->addAction(QWidget::tr("Show subdocuments / attachments"), me, SLOT(menuShowSubDocs())); return popup; } Rcl::Doc getParent(std::shared_ptr source, Rcl::Doc& doc) { Rcl::Doc pdoc; if (source) { source->getEnclosing(doc, pdoc); } return pdoc; } Rcl::Doc getFolder(Rcl::Doc& doc) { Rcl::Doc pdoc; pdoc.url = url_parentfolder(doc.url); pdoc.meta[Rcl::Doc::keychildurl] = doc.url; pdoc.meta[Rcl::Doc::keyapptg] = "parentopen"; pdoc.mimetype = "inode/directory"; return pdoc; } static const std::string twoslash{"//"}; void copyPath(const Rcl::Doc &doc) { auto path = fileurltolocalpath(doc.url); #ifdef _WIN32 path_backslashize(path); #endif // Problem: setText expects a QString. Passing a (const char*) as // we used to do causes an implicit conversion from latin1. File // are binary and the right approach would be no conversion, but // it's probably better (less worse...) to make a "best effort" // tentative and try to convert from the locale's charset than // accept the default conversion. This is unlikely to yield a // usable path for binary paths in non-utf8 locales though. QString qpath = path2qs(path); QApplication::clipboard()->setText(qpath, QClipboard::Selection); QApplication::clipboard()->setText(qpath, QClipboard::Clipboard); } // This is typically used to add the file name to the query string, so // we want the values as search terms here, not the binary from the // url. void copyFN(const Rcl::Doc &doc) { std::string fn; doc.getmeta(Rcl::Doc::keyfn, &fn); QString qpath = u8s2qs(fn); QApplication::clipboard()->setText(qpath, QClipboard::Selection); QApplication::clipboard()->setText(qpath, QClipboard::Clipboard); } void copyURL(const Rcl::Doc &doc) { QString url; #ifdef _WIN32 url = u8s2qs(doc.url); #else if (utf8check(doc.url)) { url = u8s2qs(doc.url); } else { url = u8s2qs(url_encode(doc.url)); } #endif QApplication::clipboard()->setText(url, QClipboard::Selection); QApplication::clipboard()->setText(url, QClipboard::Clipboard); } void copyText(Rcl::Doc &doc, RclMain *) { if (rcldb->getDocRawText(doc)) { QApplication::clipboard()->setText(u8s2qs(doc.text)); } } } recoll-1.36.1/qtgui/multisave.h0000644000175000017500000000174614427373216013337 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _MULTISAVE_W_H_INCLUDED_ #define _MULTISAVE_W_H_INCLUDED_ #include #include extern void multiSave(QWidget *parent, std::vector& docs); #endif /* _MULTISAVE_W_H_INCLUDED_ */ recoll-1.36.1/python/0000755000175000017500000000000014521161751011407 500000000000000recoll-1.36.1/python/samples/0000755000175000017500000000000014521161751013053 500000000000000recoll-1.36.1/python/samples/rcldlkp.py0000755000175000017500000000532414410615043015002 00000000000000#!/usr/bin/env python __doc__ = """ An example indexer for an arbitrary multi-document file format. Not supposed to run ''as-is'' or be really useful. ''Lookup'' notes file indexing The file format has text notes separated by lines with a single '%' character If the script is called with just the file name as an argument, it will (re)index the contents. If the script is called with second numeric argument, it will retrieve the specified record and output it in html """ import os import stat import sys import re rclconf = "/Users/dockes/.recoll-dlkp" def udi(docfile, numrec): return docfile + "#" + str(numrec) ############################################################### def index_rec(db, numrec, rec): doc = recoll.Doc() # url doc.url = "file://" + docfile # utf8fn # ipath doc.ipath = str(numrec) # mimetype doc.mimetype = "text/plain" # mtime # origcharset # title lines = rec.split("\n") if len(lines) >= 2: doc.title = unicode(lines[1], "iso-8859-1") if len(doc.title.strip()) == 0 and len(lines) >= 3: doc.title = unicode(lines[2], "iso-8859-1") # keywords # abstract # author # fbytes doc.fbytes = str(fbytes) # text doc.text = unicode(rec, "iso-8859-1") # dbytes doc.dbytes = str(len(rec)) # sig if numrec == 0: doc.sig = str(fmtime) db.addOrUpdate(udi(docfile, numrec), doc) def output_rec(rec): # Escape html rec = unicode(rec, "iso-8859-1").encode("utf-8") rec = rec.replace("<", "<"); rec = rec.replace("&", "&"); rec = rec.replace('"', "&dquot;"); print '' print '' print '

'
    print rec
    print '
' ################################################################ def usage(): sys.stderr.write("Usage: rcldlkp.py []\n") exit(1) if len(sys.argv) < 2: usage() docfile = sys.argv[1] if len(sys.argv) > 2: targetnum = int(sys.argv[2]) else: targetnum = None #print docfile, targetnum stdata = os.stat(docfile) fmtime = stdata[stat.ST_MTIME] fbytes = stdata[stat.ST_SIZE] f = open(docfile, 'r') if targetnum == None: import recoll db = recoll.connect(confdir=rclconf, writable=1) if not db.needUpdate(udi(docfile, 0), str(fmtime)): exit(0) rec = "" numrec = 1 for line in f: if re.compile("^%[ \t]*").match(line): if targetnum == None: index_rec(db, numrec, rec) elif targetnum == numrec: output_rec(rec) exit(0) numrec += 1 rec = "" else: rec += line if targetnum == None: index_rec(db, 0, "") recoll-1.36.1/python/samples/rclmbox.py0000755000175000017500000001715514471606020015024 00000000000000#!/usr/bin/python3 """This sample uses the Recoll Python API to index a directory containing mbox files. This is not particularly useful as Recoll itself can do this better (e.g. this script does not process attachments), but it shows the use of most of the Recoll interface features, except 'parent_udi' (we do not create a 'self' document to act as the parent).""" import sys import glob import os import stat import signal import mailbox import email.header import email.utils from recoll import recoll def logmsg(s): print(f"rclmbox.py: {s}", file=sys.stderr) # EDIT # Change this for some directory with mbox files, such as a # Thunderbird/Icedove mail storage directory. mbdir = os.path.expanduser("~/mail") #mbdir = os.path.expanduser("~/.icedove/n8n19644.default/Mail/Local Folders/") # Utility: extract text for named header def header_value(msg, nm, to_utf = False): value = msg.get(nm) if value == None: return "" #value = value.replace("\n", "") #value = value.replace("\r", "") parts = email.header.decode_header(value) univalue = u"" for part in parts: try: if part[1] != None: univalue += part[0].decode(part[1]) + u" " else: if isinstance(part[0], bytes): univalue += part[0].decode("cp1252") + u" " else: univalue += part[0] + u" " except Exception as err: logmsg("Failed decoding header: %s" % err) pass if to_utf: return univalue.encode('utf-8') else: return univalue # Utility: extract text parts from body def extract_text(msg): """Extract and decode all text/plain parts from the message""" text = "" # We only output the headers for previewing, else they're already # output/indexed as fields. if "RECOLL_FILTER_FORPREVIEW" in os.environ and os.environ["RECOLL_FILTER_FORPREVIEW"] == "yes": text += "From: " + header_value(msg, "From") + "\n" text += "To: " + header_value(msg, "To") + "\n" text += "Subject: " + header_value(msg, "Subject") + "\n" text += "\n" for part in msg.walk(): if part.is_multipart(): pass else: ct = part.get_content_type() if ct.lower() == "text/plain": charset = part.get_content_charset("cp1252") try: ntxt = part.get_payload(None, True).decode(charset) text += ntxt except Exception as err: logmsg("Failed decoding payload: %s" % err) pass return text class mbox_indexer: """The indexer classs. An object is created for indexing one mbox folder""" def __init__(self, db, mbfile): """Initialize for writable db recoll.Db object and mbfile mbox file. We retrieve the the file size and mtime.""" self.db = db self.mbfile = mbfile stdata = os.stat(mbfile) self.fmtime = stdata[stat.ST_MTIME] self.fbytes = stdata[stat.ST_SIZE] self.msgnum = 1 self.parent_udi = self.udi(0) def sig(self): """Create an update verification value for an mbox file: we use the modification time concatenated with the size""" return str(self.fmtime) + ":" + str(self.fbytes) def udi(self, msgnum): """Create a unique document identifier for a given message. This should be shorter than 150 bytes, which we optimistically don't check here, as we just concatenate the mbox file name and message number""" return self.mbfile + ":" + str(msgnum) def index(self): if not self.db.needUpdate(self.udi(0), self.sig()): logmsg("Index is up to date for %s"%self.mbfile) return None mb = mailbox.mbox(self.mbfile) for msg in mb.values(): logmsg("Indexing message %d" % self.msgnum) self.index_message(msg) self.msgnum += 1 # Finally create and add the top doc (must be done after indexing the subdocs because, else, # the up to date tests would succeed... doc = recoll.Doc() doc.mimetype = "text/x-mail" doc.rclbes = "MBOX" doc.url = "file://" + self.mbfile doc.sig = self.sig() self.db.addOrUpdate(self.parent_udi, doc) def getdata(self, ipath): """Implements the 'fetch' data access interface (called at query time from the command line).""" #logmsg("mbox::getdata: ipath: %s" % ipath) imsgnum = int(ipath) mb = mailbox.mbox(self.mbfile) msgnum = 0; for msg in mb.values(): msgnum += 1 if msgnum == imsgnum: return extract_text(msg) return "" def index_message(self, msg): doc = recoll.Doc() # Misc standard recoll fields doc.author = header_value(msg, "From") doc.recipient = header_value(msg, "To") + " " + header_value(msg, "Cc") dte = header_value(msg, "Date") tm = email.utils.parsedate_tz(dte) if tm == None: doc.mtime = str(self.fmtime) else: doc.mtime = str(email.utils.mktime_tz(tm)) doc.title = header_value(msg, "Subject") doc.fbytes = str(self.fbytes) # Custom field doc.myfield = "some value" # Main document text and MIME type doc.text = extract_text(msg) doc.dbytes = str(len(doc.text.encode('UTF-8'))) doc.mimetype = "text/plain" # Store data for later "up to date" checks doc.sig = self.sig() # The rclbes field is the link between the index data and this # script when used at query time doc.rclbes = "MBOX" # These get stored inside the index, and returned at query # time, but the main identifier is the condensed 'udi' doc.url = "file://" + self.mbfile doc.ipath = str(self.msgnum) # The udi is the unique document identifier, later used if we # want to e.g. delete the document index data (and other ops). udi = self.udi(self.msgnum) self.db.addOrUpdate(udi, doc, parent_udi = self.parent_udi) db = None def handler(signum, frame): logmsg(f"Got signal") if db: db.close() logmsg(f"Exiting") sys.exit(1) signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGTERM, handler) # Index a directory containing mbox files def index_mboxdir(dir): global db db = recoll.connect(writable=1) db.preparePurge("MBOX") entries = glob.glob(dir + "/*") for dirent in entries: if os.path.basename(dirent)[0] == "." or not os.path.isfile(dirent): continue logmsg(f"Processing {dirent}") mbidx = mbox_indexer(db, dirent) mbidx.index() db.purge() usage_string='''Usage: rclmbox.py index Index the directory (the path is hard-coded inside the script) rclmbox.py udi url ipath fetch subdoc data or make signature (query time) ''' def usage(): print("%s" % usage_string, file=sys.stderr) sys.exit(1) if len(sys.argv) < 2: usage() elif len(sys.argv) == 2 and sys.argv[1] == "index": index_mboxdir(mbdir) elif len(sys.argv) == 5: cmd = sys.argv[1] udi = sys.argv[2] url = sys.argv[3] ipath = sys.argv[4] mbfile = url.replace('file://', '') # No need for a db for getdata or makesig. mbidx = mbox_indexer(None, mbfile) if cmd == 'fetch': print(f"{mbidx.getdata(ipath)}", end="") elif cmd == 'makesig': print(mbidx.sig(), end="") else: usage() else: usage() sys.exit(0) recoll-1.36.1/python/samples/recollqsd.py0000755000175000017500000000241714410615043015337 00000000000000#!/usr/bin/env python """Example for using the ''searchdata''' structured query interface. Not good for anything except showing/trying the code.""" import sys from recoll import recoll def dotest(db, q): query = db.query() query.sortby("title", 1) nres = query.executesd(q) print "Result count: ", nres print "Query: ", query.getxquery().encode('utf-8') if nres > 10: nres = 10 for i in range(nres): doc = query.fetchone() print query.next if type(query.next) == int else query.rownumber for k in ("url", "mtime", "title", "author", "abstract"): if getattr(doc, k): print k, ":", getattr(doc, k).encode('utf-8') else: print k, ": None" print # End dotest # addclause(type='and'|'or'|'filename'|'phrase'|'near'|'path'|'sub' # qstring=string, slack=int, field=string, stemming=1|0, # subSearch=SearchData, exclude=0|1, anchorstart=0|1, anchorend=0|1 # casesens=0|1, diacsens=0|1) #sd.addclause("and", "dockes", field="author") #sd.addclause("phrase", "jean francois", 1) db = recoll.connect(confdir="/home/dockes/.recoll-prod") sd = recoll.SearchData(stemlang="english") sd.addclause('filename', "recollqsd*") dotest(db, sd) sys.exit(0) recoll-1.36.1/python/samples/recollq.py0000755000175000017500000000675714427355416015040 00000000000000#!/usr/bin/python3 # -*- coding: utf-8 -*- """A Python simplified equivalent of the command line query tool recollq The input string is always interpreted as a query language string. This could actually be useful for something after some customization """ import sys import locale from getopt import getopt from recoll import recoll, rclextract allmeta = ("title", "keywords", "abstract", "url", "mimetype", "mtime", "ipath", "fbytes", "dbytes", "relevancyrating") def Usage(): print("Usage: recollq.py [-c conf] [-i extra_index] ") sys.exit(1); class ptrmeths: def __init__(self, groups): self.groups = groups def startMatch(self, idx): ugroup = " ".join(self.groups[idx][1]) return '' % (idx, ugroup) def endMatch(self): return '' def extract(doc): extractor = rclextract.Extractor(doc) newdoc = extractor.textextract(doc.ipath) return newdoc def extractofile(doc, outfilename=""): extractor = rclextract.Extractor(doc) outfilename = extractor.idoctofile(doc.ipath, doc.mimetype, \ ofilename=outfilename) return outfilename def doquery(db, q): # Get query object query = db.query() #query.sortby("dmtime", ascending=True) # Parse/run input query string nres = query.execute(q, stemming = 0, stemlang="english") qs = "Xapian query: [%s]" % query.getxquery() print(f"{qs}") groups = query.getgroups() m = ptrmeths(groups) # Print results: print("Result count: %d %d" % (nres, query.rowcount)) if nres > 20: nres = 20 #results = query.fetchmany(nres) #for doc in results: for i in range(nres): doc = query.fetchone() rownum = query.next if type(query.next) == int else \ query.rownumber print("%d:"%(rownum,)) #for k,v in doc.items().items(): # print(f"KEY: {k} VALUE: {v}") #continue #outfile = extractofile(doc) ; print(f"outfile: {outfile} url: {doc.url}") for k in ("title", "mtime", "author"): value = getattr(doc, k) #value = doc.get(k) if value is None: print(f"{k}: (None)") else: print(f"{k} : {value}") #doc.setbinurl(bytearray("toto")) #burl = doc.getbinurl(); print("Bin URL : [%s]"%(doc.getbinurl(),)) abs = query.makedocabstract(doc, methods=m) print(f"{abs}\n") # fulldoc = extract(doc) # print("FULLDOC MIMETYPE %s TEXT: %s" % (fulldoc.mimetype,fulldoc.text)) ########################################### MAIN if len(sys.argv) < 2: Usage() language, localecharset = locale.getdefaultlocale() confdir="" extra_dbs = [] # Snippet params maxchars = 120 contextwords = 4 # Process options: [-c confdir] [-i extra_db [-i extra_db] ...] try: options, args = getopt(sys.argv[1:], "c:i:") except Exception as ex: print(f"{ex}") sys.exit(1) for opt,val in options: if opt == "-c": confdir = val elif opt == "-i": extra_dbs.append(val) else: print("Bad opt: %s"%(opt,)) Usage() # The query should be in the remaining arg(s) if len(args) == 0: print("No query found in command line") Usage() q = '' for word in args: q += word + ' ' print(f"QUERY: [{q}]") db = recoll.connect(confdir=confdir, extra_dbs=extra_dbs) db.setAbstractParams(maxchars=maxchars, contextwords=contextwords) doquery(db, q) recoll-1.36.1/python/samples/mutt-recoll.py0000755000175000017500000000550014410615043015612 00000000000000#!/usr/bin/env python """ Modified from github:honza/mutt-notmuch-py This is a recoll version of the original mutt-notmuch script. It will interactively ask you for a search query and then symlink the matching messages to $HOME/.cache/mutt_results. Add this to your .muttrc. macro index / "unset wait_keymutt-recoll.py~/.cache/mutt_results" \ "search mail (using recoll)" This script overrides the $HOME/.cache/mutt_results each time you run a query. Install this by adding this file somewhere on your PATH. (c) 2012 - Honza Pokorny (c) 2014 - Jean-Francois Dockes Licensed under BSD """ import os import hashlib from commands import getoutput from mailbox import Maildir from optparse import OptionParser from collections import defaultdict def digest(filename): with open(filename) as f: return hashlib.sha1(f.read()).hexdigest() def pick_all_mail(messages): for m in messages: if 'All Mail' in m: return m def empty_dir(directory): box = Maildir(directory) box.clear() def command(cmd): return getoutput(cmd) def main(dest_box, is_gmail): query = raw_input('Query: ') command('mkdir -p %s/cur' % dest_box) command('mkdir -p %s/new' % dest_box) empty_dir(dest_box) files = command('recoll -t -b -q %s' % query).split('\n') data = defaultdict(list) messages = [] for f in files: # Recoll outputs file:// urls f = f[7:] if not f: continue try: sha = digest(f) data[sha].append(f) except IOError: print('File %s does not exist' % f) for sha in data: if is_gmail and len(data[sha]) > 1: messages.append(pick_all_mail(data[sha])) else: messages.append(data[sha][0]) for m in messages: if not m: continue target = os.path.join(dest_box, 'cur', os.path.basename(m)) if not os.path.exists(target): print "symlink [%s] -> [%s]" % (m, target) os.symlink(m, target) if __name__ == '__main__': p = OptionParser("usage: %prog [OPTIONS] [RESULTDIR]") p.add_option('-g', '--gmail', dest='gmail', action='store_true', default=True, help='gmail-specific behavior') p.add_option('-G', '--not-gmail', dest='gmail', action='store_false', help='gmail-specific behavior') (options, args) = p.parse_args() if args: dest = args[0] else: dest = os.path.expanduser('~/.cache/mutt_results') if not os.path.exists(dest): os.makedirs(dest) # Use expanduser() so that os.symlink() won't get weirded out by tildes. main(os.path.expanduser(dest).rstrip('/'), options.gmail) recoll-1.36.1/python/samples/docdups.py0000755000175000017500000000641014410615043015005 00000000000000#!/usr/bin/env python3 import sys import xapian o_index_stripchars = True md5wpref = "XM" # Handle caps/diac-stripping option. If the db is raw the prefixes are # wrapped with ":" def wrap_prefix(prefix): if o_index_stripchars: return prefix else: return b":" + prefix + b":" def init_stripchars(xdb): global o_index_stripchars global md5wpref t = xdb.allterms() t.skip_to(b":") for term in t: if term.term.find(b":") == 0: o_index_stripchars = False break md5wpref = wrap_prefix(b"XM") # Retrieve named value from document data record. # The record format is a sequence of nm=value lines def get_attributes(xdb, docid, flds, decode=True): doc = xdb.get_document(docid) data = doc.get_data() res = [] for fld in flds: s = data.find(fld + b"=") if s == -1: res.append(None) else: e = data.find(b"\n", s) if decode: res.append(data[s+len(fld)+1:e].decode('UTF-8')) else: res.append(data[s+len(fld)+1:e]) return res # Convenience: retrieve postings as Python list def get_postlist(xdb, term): ret = list() for posting in xdb.postlist(term): ret.append(posting.docid) return ret # Return list of docids having same md5 including self def get_dups(xdb, docid): doc = xdb.get_document(int(docid)) # It would be more efficient to retrieve the value, but it's # binary so we'd have to decode it md5term = doc.termlist().skip_to(md5wpref).term if not md5term.startswith(md5wpref): return posts = get_postlist(xdb, md5term) return posts # Retrieve all sets of duplicates: # walk the list of all MD5 terms, look up their posting lists, and # store the docids where the list is longer than one. def find_all_dups(xdb): alldups = list() # Walk the MD5 terms t = xdb.allterms() t.skip_to(md5wpref) for term in t: if not term.term.startswith(md5wpref): break # Check postlist for term, if it's not of length 1, we have a dup dups = get_postlist(xdb, term.term) if len(dups) != 1: alldups.append(dups) return alldups # Print docid url ipath for list of docids def print_urlipath(xdb, doclist): for docid in doclist: url,ipath = get_attributes(xdb, docid, [b"url", b"ipath"]) print("%s %s %s" % (docid, url, ipath)) def msg(s): print("%s" % s, file = sys.stderr) ########## Main program if len(sys.argv) < 2: msg("Usage: %s /path/to/db [docid [docid ...]]" % \ sys.argv[0]) msg(" will print all sets of dups if no docid is given") msg(" else only the duplicates for the given docids") sys.exit(1) xdbpath = sys.argv[1] xdb = xapian.Database(xdbpath) init_stripchars(xdb) try: if len(sys.argv) == 2: # No docid args, alldups = find_all_dups(xdb) for dups in alldups: print_urlipath(xdb, dups) print("") else: for docid in sys.argv[2:]: dups = get_dups(xdb, docid) if dups is not None and len(dups) > 1: print_urlipath(xdb, dups) except Exception as e: msg("Error: %s" % str(e)) sys.exit(1) recoll-1.36.1/python/samples/recollgui/0000755000175000017500000000000014521161751015040 500000000000000recoll-1.36.1/python/samples/recollgui/Makefile0000644000175000017500000000020014410615043016403 00000000000000PYUIC = pyuic5 all: rclmain.py rclmain.py: rclmain.ui $(PYUIC) -o rclmain.py rclmain.ui clean: rm -f rclmain.py rclmain.pyc recoll-1.36.1/python/samples/recollgui/qrecoll.py0000755000175000017500000002050014410615043016766 00000000000000#!/usr/bin/env python from __future__ import print_function import sys import datetime try: from recoll import recoll from recoll import rclextract hasextract = True except: import recoll hasextract = False import rclmain from getopt import getopt from PyQt5 import QtCore from PyQt5.QtCore import pyqtSlot from PyQt5.QtGui import QKeySequence from PyQt5.QtWidgets import * #################### # Highlighting methods. Just for showing the groups usage, we add the # original string for the match to the highlighted text. I don't think # you'd want to do this in a real app, but maybe some kind of tooltip? class HlMeths: def __init__(self, groups): self.groups = groups def startMatch(self, idx): ugroup = " ".join(self.groups[idx][0]) return ''+ugroup+'' def endMatch(self): return '' ############ # Data extraction. The 2 following methods use the extractor module # and get the data from the original document # # Extract and return document text (in text or html format, indicated # by newdoc.mimetype) def textextract(doc): extractor = rclextract.Extractor(doc) newdoc = extractor.textextract(doc.ipath) return newdoc # Extract document in original format (ie: application/msword) and # save it to a file. This only works if ipath is not null (else just # use the url !) def extractofile(doc, outfilename=""): extractor = rclextract.Extractor(doc) outfilename = extractor.idoctofile(doc.ipath, doc.mimetype, \ ofilename=outfilename) return outfilename ######### # RecollQuery wraps a recoll.query object in a Qt model class RecollQuery(QtCore.QAbstractTableModel): def __init__(self): QtCore.QAbstractTableModel.__init__(self) self.totres = -1 self.db = None self.query = None self.qtext = "" self.docs = [] self.pagelen = 10 self.attrs = ("filename", "title", "mtime", "url", "ipath") def rowCount(self, parent): ret = len(self.docs) #print("RecollQuery.rowCount(): %d"% ret) return ret def columnCount(self, parent): #print("RecollQuery.columnCount()") if parent.isValid(): return 0 else: return len(self.attrs) def setquery(self, db, q, sortfield="", ascending=True): """Parse and execute query on open db""" #print("RecollQuery.setquery():") # Get query object self.query = db.query() if sortfield: self.query.sortby(sortfield, ascending) # Parse/run input query string self.totres = self.query.execute(q) self.qtext = q self.db = db self.docs = [] self.fetchMore(None) def getdoc(self, index): if index.row() < len(self.docs): return self.docs[index.row()] else: return None def sort(self, col, order): #print("sort %s %s", (col, order)) self.setquery(self.db, self.qtext, sortfield=self.attrs[col], ascending = order) def headerData(self, idx, orient, role): if orient == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: return self.attrs[idx] return None def data(self, index, role): #print("RecollQuery.data: row %d, role: %s" % (index.row(),role)) if not index.isValid(): return QtCore.QVariant() if index.row() >= len(self.docs): return QtCore.QVariant() if role == QtCore.Qt.DisplayRole: #print("RecollQuery.data: row %d, col %d role: %s" % \ # (index.row(), index.column() role)) attr = self.attrs[index.column()] value = getattr(self.docs[index.row()], attr) if attr == "mtime": dte = datetime.datetime.fromtimestamp(int(value)) value = str(dte) return value else: return QtCore.QVariant() def canFetchMore(self, parent): #print("RecollQuery.canFetchMore:") if len(self.docs) < self.totres: return True else: return False def fetchMore(self, parent): #print("RecollQuery.fetchMore:") self.beginInsertRows(QtCore.QModelIndex(), len(self.docs), \ len(self.docs) + self.pagelen) for count in range(self.pagelen): try: self.docs.append(self.query.fetchone()) except: break self.endInsertRows() ### # UI interaction code class RclGui_Main(QMainWindow): def __init__(self, db, parent=None): QMainWindow.__init__(self, parent) self.ui = rclmain.Ui_MainWindow() self.ui.setupUi(self) self.db = db self.qmodel = RecollQuery() scq = QShortcut(QKeySequence("Ctrl+Q"), self) scq.activated.connect(self.onexit) header = self.ui.resTable.horizontalHeader() header.setSortIndicatorShown(True) header.setSortIndicator(-1, QtCore.Qt.AscendingOrder) self.ui.resTable.setSortingEnabled(True) self.currentindex = -1 self.currentdoc = None def on_searchEntry_returnPressed(self): self.startQuery() def on_resTable_clicked(self, index): doc = self.qmodel.getdoc(index) self.currentindex = index self.currentdoc = doc if doc is None: print("NO DoC") return query = self.qmodel.query groups = query.getgroups() meths = HlMeths(groups) abs = query.makedocabstract(doc, methods=meths) self.ui.resDetail.setText(abs) if hasextract: ipath = doc.get('ipath') #print("ipath[%s]" % ipath) self.ui.previewPB.setEnabled(True) if ipath: self.ui.savePB.setEnabled(True) else: self.ui.savePB.setEnabled(False) @pyqtSlot() def on_previewPB_clicked(self): print("on_previewPB_clicked(self)") newdoc = textextract(self.currentdoc) query = self.qmodel.query; groups = query.getgroups() meths = HlMeths(groups) #print("newdoc.mimetype:", newdoc.mimetype) if newdoc.mimetype == 'text/html': ishtml = True else: ishtml = False text = '' + \ query.highlight(newdoc.text, methods=meths, ishtml=ishtml, eolbr=True) text += '' self.ui.resDetail.setText(text) @pyqtSlot() def on_savePB_clicked(self): print("on_savePB_clicked(self)") doc = self.currentdoc ipath = doc.ipath if not ipath: return fn = QFileDialog.getSaveFileName(self) if fn: docitems = doc.items() fn = extractofile(doc, str(fn.toLocal8Bit())) print("Saved as %s" % fn) else: print("Canceled", file=sys.stderr) def startQuery(self): self.qmodel.setquery(self.db, self.ui.searchEntry.text()) self.ui.resTable.setModel(self.qmodel) def onexit(self): self.close() def Usage(): print('''Usage: qt.py [ [ ...]]''', file=sys.stderr) sys.exit(1) def main(args): app = QApplication(args) confdir="" extra_dbs = [] # Snippet params maxchars = 300 contextwords = 6 # Process options: [-c confdir] [-i extra_db [-i extra_db] ...] options, args = getopt(args[1:], "c:i:") for opt,val in options: if opt == "-c": confdir = val elif opt == "-i": extra_dbs.append(val) else: print("Bad opt: %s"% opt, file=sys.stderr) Usage() # The query should be in the remaining arg(s) q = None if len(args) > 0: q = "" for word in args: q += word + " " db = recoll.connect(confdir=confdir, extra_dbs=extra_dbs) db.setAbstractParams(maxchars=maxchars, contextwords=contextwords) topwindow = RclGui_Main(db) topwindow.show() if q is not None: topwindow.ui.searchEntry.setText(q) topwindow.startQuery() sys.exit(app.exec_()) if __name__=="__main__": main(sys.argv) recoll-1.36.1/python/samples/recollgui/rclmain.ui0000644000175000017500000000615614410615043016747 00000000000000 MainWindow 0 0 800 600 MainWindow Search string + CR 0 4 QAbstractItemView::NoEditTriggers false 0 0 false Preview false Save 0 0 800 23 File Exit actionExit triggered() MainWindow close() -1 -1 399 299 recoll-1.36.1/python/pychm/0000755000175000017500000000000014521161751012527 500000000000000recoll-1.36.1/python/pychm/README-RECOLL.txt0000644000175000017500000000071114426500174015122 00000000000000= Recoll and PyCHM Up to 2019, PyCHM (https://github.com/dottedmag/pychm) had no Python3 version. The pull request I submitted for the port in 2018 was sitting there, and so was the Debian bug. https://github.com/dottedmag/pychm/pull/5 Which is why Recoll forked and bundled pychm, enhanced for Python3. The source repo is here: https://github.com/medoc92/pychm PyCHM now has Python3 support, maybe we will go back to the official version at some point. recoll-1.36.1/python/pychm/AUTHORS0000644000175000017500000000125614410615043013516 00000000000000Author ------ Rubens Ramos Maintainer ---------- Mikhail Gusarov Python3 port minor changes -------------------------- Jean-Francois Dockes Acknowledgements ---------------- This work would not have been possible without the existence of chmlib, developed by Jed Wing, and a lot of the python code used to parse the contents tree and to decode the index files was heavily based on the code implemented by Razvan Cojocaru for the xCHM viewer. Bug reports ----------- can3p, Chang (changshu), Hristo Iliev, Carlos Liu, Torsten Marek, Dmitri (nebraskin), Fredrik de Vibe, Glenn Washburn recoll-1.36.1/python/pychm/setup.cfg.in0000644000175000017500000000031314515753104014674 00000000000000 # There is nothing to configure here, we exist just to be copied into the build dir, and so that # The egg stuff gets created there and not in the source tree. [egg_info] egg_base = recollchm.egg-info recoll-1.36.1/python/pychm/COPYING0000644000175000017500000003542714410615043013510 00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS recoll-1.36.1/python/pychm/MANIFEST.in0000644000175000017500000000004714410615043014201 00000000000000include COPYING include chm/swig_chm.i recoll-1.36.1/python/pychm/recollchm/0000755000175000017500000000000014517521132014475 500000000000000recoll-1.36.1/python/pychm/recollchm/chmlib.py0000644000175000017500000001010614517521130016221 00000000000000# This file was automatically generated by SWIG (http://www.swig.org). # Version 4.0.2 # # Do not make changes to this file unless you know what you are doing--modify # the SWIG interface file instead. from sys import version_info as _swig_python_version_info if _swig_python_version_info < (2, 7, 0): raise RuntimeError("Python 2.7 or later required") # Import the low-level C/C++ module if __package__ or "." in __name__: from . import _chmlib else: import _chmlib try: import builtins as __builtin__ except ImportError: import __builtin__ def _swig_repr(self): try: strthis = "proxy of " + self.this.__repr__() except __builtin__.Exception: strthis = "" return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) def _swig_setattr_nondynamic_instance_variable(set): def set_instance_attr(self, name, value): if name == "thisown": self.this.own(value) elif name == "this": set(self, name, value) elif hasattr(self, name) and isinstance(getattr(type(self), name), property): set(self, name, value) else: raise AttributeError("You cannot add instance attributes to %s" % self) return set_instance_attr def _swig_setattr_nondynamic_class_variable(set): def set_class_attr(cls, name, value): if hasattr(cls, name) and not isinstance(getattr(cls, name), property): set(cls, name, value) else: raise AttributeError("You cannot add class attributes to %s" % cls) return set_class_attr def _swig_add_metaclass(metaclass): """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass""" def wrapper(cls): return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy()) return wrapper class _SwigNonDynamicMeta(type): """Meta class to enforce nondynamic attributes (no new attributes) for a class""" __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__) CHM_UNCOMPRESSED = _chmlib.CHM_UNCOMPRESSED CHM_COMPRESSED = _chmlib.CHM_COMPRESSED CHM_MAX_PATHLEN = _chmlib.CHM_MAX_PATHLEN class chmUnitInfo(object): thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") __repr__ = _swig_repr start = property(_chmlib.chmUnitInfo_start_get, _chmlib.chmUnitInfo_start_set) length = property(_chmlib.chmUnitInfo_length_get, _chmlib.chmUnitInfo_length_set) space = property(_chmlib.chmUnitInfo_space_get, _chmlib.chmUnitInfo_space_set) path = property(_chmlib.chmUnitInfo_path_get, _chmlib.chmUnitInfo_path_set) def __init__(self): _chmlib.chmUnitInfo_swiginit(self, _chmlib.new_chmUnitInfo()) __swig_destroy__ = _chmlib.delete_chmUnitInfo # Register chmUnitInfo in _chmlib: _chmlib.chmUnitInfo_swigregister(chmUnitInfo) def chm_open(filename): return _chmlib.chm_open(filename) def chm_close(h): return _chmlib.chm_close(h) CHM_PARAM_MAX_BLOCKS_CACHED = _chmlib.CHM_PARAM_MAX_BLOCKS_CACHED def chm_set_param(h, paramType, paramVal): return _chmlib.chm_set_param(h, paramType, paramVal) CHM_RESOLVE_SUCCESS = _chmlib.CHM_RESOLVE_SUCCESS CHM_RESOLVE_FAILURE = _chmlib.CHM_RESOLVE_FAILURE def chm_resolve_object(h, objPath): return _chmlib.chm_resolve_object(h, objPath) def chm_retrieve_object(h, ui, addr, len): return _chmlib.chm_retrieve_object(h, ui, addr, len) CHM_ENUMERATE_NORMAL = _chmlib.CHM_ENUMERATE_NORMAL CHM_ENUMERATE_META = _chmlib.CHM_ENUMERATE_META CHM_ENUMERATE_SPECIAL = _chmlib.CHM_ENUMERATE_SPECIAL CHM_ENUMERATE_FILES = _chmlib.CHM_ENUMERATE_FILES CHM_ENUMERATE_DIRS = _chmlib.CHM_ENUMERATE_DIRS CHM_ENUMERATE_ALL = _chmlib.CHM_ENUMERATE_ALL CHM_ENUMERATOR_FAILURE = _chmlib.CHM_ENUMERATOR_FAILURE CHM_ENUMERATOR_CONTINUE = _chmlib.CHM_ENUMERATOR_CONTINUE CHM_ENUMERATOR_SUCCESS = _chmlib.CHM_ENUMERATOR_SUCCESS def chm_enumerate(h, what, e, context): return _chmlib.chm_enumerate(h, what, e, context) def chm_enumerate_dir(h, prefix, what, e, context): return _chmlib.chm_enumerate_dir(h, prefix, what, e, context) recoll-1.36.1/python/pychm/recollchm/__init__.py0000644000175000017500000000250114410615043016521 00000000000000# Copyright (C) 2003-2006 Rubens Ramos # # pychm is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; see the file COPYING. If not, # write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA # ''' chm - A package to manipulate CHM files The chm package provides four modules: chm, chmlib, extra and _chmlib. _chmlib and chmlib are very low level libraries generated from SWIG interface files, and are simple wrappers around the API defined by the C library chmlib. The extra module adds full-text search support. the chm module provides some higher level classes to simplify access to the CHM files information. ''' __all__ = ["chm", "chmlib", "_chmlib", "extra"] __version__ = "0.8.4.1+git" __revision__ = "$Id$" recoll-1.36.1/python/pychm/recollchm/chm.py0000644000175000017500000005027614410615043015545 00000000000000# Copyright (C) 2003-2006 Rubens Ramos # # Based on code by: # Copyright (C) 2003 Razvan Cojocaru # # pychm is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; see the file COPYING. If not, # write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA ''' chm - A high-level front end for the chmlib python module. The chm module provides high level access to the functionality included in chmlib. It encapsulates functions in the CHMFile class, and provides some additional features, such as the ability to obtain the contents tree of a CHM archive. ''' from . import chmlib from . import extra import array import os.path import sys charset_table = { 0: 'iso8859_1', # ANSI_CHARSET 238: 'iso8859_2', # EASTEUROPE_CHARSET 178: 'iso8859_6', # ARABIC_CHARSET 161: 'iso8859_7', # GREEK_CHARSET 177: 'iso8859_8', # HEBREW_CHARSET 162: 'iso8859_9', # TURKISH_CHARSET 222: 'iso8859_11', # THAI_CHARSET - hmm not in python 2.2... 186: 'iso8859_13', # BALTIC_CHARSET 204: 'cp1251', # RUSSIAN_CHARSET 255: 'cp437', # OEM_CHARSET 128: 'cp932', # SHIFTJIS_CHARSET 134: 'cp936', # GB2312_CHARSET 129: 'cp949', # HANGUL_CHARSET 136: 'cp950', # CHINESEBIG5_CHARSET 1: None, # DEFAULT_CHARSET 2: None, # SYMBOL_CHARSET 130: None, # JOHAB_CHARSET 163: None, # VIETNAMESE_CHARSET 77: None, # MAC_CHARSET } locale_table = { 0x0436: ('iso8859_1', "Afrikaans", "Western Europe & US"), 0x041c: ('iso8859_2', "Albanian", "Central Europe"), 0x0401: ('iso8859_6', "Arabic_Saudi_Arabia", "Arabic"), 0x0801: ('iso8859_6', "Arabic_Iraq", "Arabic"), 0x0c01: ('iso8859_6', "Arabic_Egypt", "Arabic"), 0x1001: ('iso8859_6', "Arabic_Libya", "Arabic"), 0x1401: ('iso8859_6', "Arabic_Algeria", "Arabic"), 0x1801: ('iso8859_6', "Arabic_Morocco", "Arabic"), 0x1c01: ('iso8859_6', "Arabic_Tunisia", "Arabic"), 0x2001: ('iso8859_6', "Arabic_Oman", "Arabic"), 0x2401: ('iso8859_6', "Arabic_Yemen", "Arabic"), 0x2801: ('iso8859_6', "Arabic_Syria", "Arabic"), 0x2c01: ('iso8859_6', "Arabic_Jordan", "Arabic"), 0x3001: ('iso8859_6', "Arabic_Lebanon", "Arabic"), 0x3401: ('iso8859_6', "Arabic_Kuwait", "Arabic"), 0x3801: ('iso8859_6', "Arabic_UAE", "Arabic"), 0x3c01: ('iso8859_6', "Arabic_Bahrain", "Arabic"), 0x4001: ('iso8859_6', "Arabic_Qatar", "Arabic"), 0x042b: (None, "Armenian", "Armenian"), 0x042c: ('iso8859_9', "Azeri_Latin", "Turkish"), 0x082c: ('cp1251', "Azeri_Cyrillic", "Cyrillic"), 0x042d: ('iso8859_1', "Basque", "Western Europe & US"), 0x0423: ('cp1251', "Belarusian", "Cyrillic"), 0x0402: ('cp1251', "Bulgarian", "Cyrillic"), 0x0403: ('iso8859_1', "Catalan", "Western Europe & US"), 0x0404: ('cp950', "Chinese_Taiwan", "Traditional Chinese"), 0x0804: ('cp936', "Chinese_PRC", "Simplified Chinese"), 0x0c04: ('cp950', "Chinese_Hong_Kong", "Traditional Chinese"), 0x1004: ('cp936', "Chinese_Singapore", "Simplified Chinese"), 0x1404: ('cp950', "Chinese_Macau", "Traditional Chinese"), 0x041a: ('iso8859_2', "Croatian", "Central Europe"), 0x0405: ('iso8859_2', "Czech", "Central Europe"), 0x0406: ('iso8859_1', "Danish", "Western Europe & US"), 0x0413: ('iso8859_1', "Dutch_Standard", "Western Europe & US"), 0x0813: ('iso8859_1', "Dutch_Belgian", "Western Europe & US"), 0x0409: ('iso8859_1', "English_United_States", "Western Europe & US"), 0x0809: ('iso8859_1', "English_United_Kingdom", "Western Europe & US"), 0x0c09: ('iso8859_1', "English_Australian", "Western Europe & US"), 0x1009: ('iso8859_1', "English_Canadian", "Western Europe & US"), 0x1409: ('iso8859_1', "English_New_Zealand", "Western Europe & US"), 0x1809: ('iso8859_1', "English_Irish", "Western Europe & US"), 0x1c09: ('iso8859_1', "English_South_Africa", "Western Europe & US"), 0x2009: ('iso8859_1', "English_Jamaica", "Western Europe & US"), 0x2409: ('iso8859_1', "English_Caribbean", "Western Europe & US"), 0x2809: ('iso8859_1', "English_Belize", "Western Europe & US"), 0x2c09: ('iso8859_1', "English_Trinidad", "Western Europe & US"), 0x3009: ('iso8859_1', "English_Zimbabwe", "Western Europe & US"), 0x3409: ('iso8859_1', "English_Philippines", "Western Europe & US"), 0x0425: ('iso8859_13', "Estonian", "Baltic",), 0x0438: ('iso8859_1', "Faeroese", "Western Europe & US"), 0x0429: ('iso8859_6', "Farsi", "Arabic"), 0x040b: ('iso8859_1', "Finnish", "Western Europe & US"), 0x040c: ('iso8859_1', "French_Standard", "Western Europe & US"), 0x080c: ('iso8859_1', "French_Belgian", "Western Europe & US"), 0x0c0c: ('iso8859_1', "French_Canadian", "Western Europe & US"), 0x100c: ('iso8859_1', "French_Swiss", "Western Europe & US"), 0x140c: ('iso8859_1', "French_Luxembourg", "Western Europe & US"), 0x180c: ('iso8859_1', "French_Monaco", "Western Europe & US"), 0x0437: (None, "Georgian", "Georgian"), 0x0407: ('iso8859_1', "German_Standard", "Western Europe & US"), 0x0807: ('iso8859_1', "German_Swiss", "Western Europe & US"), 0x0c07: ('iso8859_1', "German_Austrian", "Western Europe & US"), 0x1007: ('iso8859_1', "German_Luxembourg", "Western Europe & US"), 0x1407: ('iso8859_1', "German_Liechtenstein", "Western Europe & US"), 0x0408: ('iso8859_7', "Greek", "Greek"), 0x040d: ('iso8859_8', "Hebrew", "Hebrew"), 0x0439: (None, "Hindi", "Indic"), 0x040e: ('iso8859_2', "Hungarian", "Central Europe"), 0x040f: ('iso8859_1', "Icelandic", "Western Europe & US"), 0x0421: ('iso8859_1', "Indonesian", "Western Europe & US"), 0x0410: ('iso8859_1', "Italian_Standard", "Western Europe & US"), 0x0810: ('iso8859_1', "Italian_Swiss", "Western Europe & US"), 0x0411: ('cp932', "Japanese", "Japanese"), 0x043f: ('cp1251', "Kazakh", "Cyrillic"), 0x0457: (None, "Konkani", "Indic"), 0x0412: ('cp949', "Korean", "Korean"), 0x0426: ('iso8859_13', "Latvian", "Baltic",), 0x0427: ('iso8859_13', "Lithuanian", "Baltic",), 0x042f: ('cp1251', "Macedonian", "Cyrillic"), 0x043e: ('iso8859_1', "Malay_Malaysia", "Western Europe & US"), 0x083e: ('iso8859_1', "Malay_Brunei_Darussalam", "Western Europe & US"), 0x044e: (None, "Marathi", "Indic"), 0x0414: ('iso8859_1', "Norwegian_Bokmal", "Western Europe & US"), 0x0814: ('iso8859_1', "Norwegian_Nynorsk", "Western Europe & US"), 0x0415: ('iso8859_2', "Polish", "Central Europe"), 0x0416: ('iso8859_1', "Portuguese_Brazilian", "Western Europe & US"), 0x0816: ('iso8859_1', "Portuguese_Standard", "Western Europe & US"), 0x0418: ('iso8859_2', "Romanian", "Central Europe"), 0x0419: ('cp1251', "Russian", "Cyrillic"), 0x044f: (None, "Sanskrit", "Indic"), 0x081a: ('iso8859_2', "Serbian_Latin", "Central Europe"), 0x0c1a: ('cp1251', "Serbian_Cyrillic", "Cyrillic"), 0x041b: ('iso8859_2', "Slovak", "Central Europe"), 0x0424: ('iso8859_2', "Slovenian", "Central Europe"), 0x040a: ('iso8859_1', "Spanish_Trad_Sort", "Western Europe & US"), 0x080a: ('iso8859_1', "Spanish_Mexican", "Western Europe & US"), 0x0c0a: ('iso8859_1', "Spanish_Modern_Sort", "Western Europe & US"), 0x100a: ('iso8859_1', "Spanish_Guatemala", "Western Europe & US"), 0x140a: ('iso8859_1', "Spanish_Costa_Rica", "Western Europe & US"), 0x180a: ('iso8859_1', "Spanish_Panama", "Western Europe & US"), 0x1c0a: ('iso8859_1', "Spanish_Dominican_Repub", "Western Europe & US"), 0x200a: ('iso8859_1', "Spanish_Venezuela", "Western Europe & US"), 0x240a: ('iso8859_1', "Spanish_Colombia", "Western Europe & US"), 0x280a: ('iso8859_1', "Spanish_Peru", "Western Europe & US"), 0x2c0a: ('iso8859_1', "Spanish_Argentina", "Western Europe & US"), 0x300a: ('iso8859_1', "Spanish_Ecuador", "Western Europe & US"), 0x340a: ('iso8859_1', "Spanish_Chile", "Western Europe & US"), 0x380a: ('iso8859_1', "Spanish_Uruguay", "Western Europe & US"), 0x3c0a: ('iso8859_1', "Spanish_Paraguay", "Western Europe & US"), 0x400a: ('iso8859_1', "Spanish_Bolivia", "Western Europe & US"), 0x440a: ('iso8859_1', "Spanish_El_Salvador", "Western Europe & US"), 0x480a: ('iso8859_1', "Spanish_Honduras", "Western Europe & US"), 0x4c0a: ('iso8859_1', "Spanish_Nicaragua", "Western Europe & US"), 0x500a: ('iso8859_1', "Spanish_Puerto_Rico", "Western Europe & US"), 0x0441: ('iso8859_1', "Swahili", "Western Europe & US"), 0x041d: ('iso8859_1', "Swedish", "Western Europe & US"), 0x081d: ('iso8859_1', "Swedish_Finland", "Western Europe & US"), 0x0449: (None, "Tamil", "Indic"), 0x0444: ('cp1251', "Tatar", "Cyrillic"), 0x041e: ('iso8859_11', "Thai", "Thai"), 0x041f: ('iso8859_9', "Turkish", "Turkish"), 0x0422: ('cp1251', "Ukrainian", "Cyrillic"), 0x0420: ('iso8859_6', "Urdu", "Arabic"), 0x0443: ('iso8859_9', "Uzbek_Latin", "Turkish"), 0x0843: ('cp1251', "Uzbek_Cyrillic", "Cyrillic"), 0x042a: (None, "Vietnamese", "Vietnamese") } class CHMFile: "A class to manage access to CHM files." filename = "" file = None title = "" home = "/" index = None topics = None encoding = None lcid = None binaryindex = None def __init__(self): self.searchable = 0 def LoadCHM(self, archiveName): '''Loads a CHM archive. This function will also call GetArchiveInfo to obtain information such as the index file name and the topics file. It returns 1 on success, and 0 if it fails. ''' if self.filename is not None: self.CloseCHM() self.file = chmlib.chm_open(archiveName) if self.file is None: return 0 self.filename = archiveName self.GetArchiveInfo() return 1 def CloseCHM(self): '''Closes the CHM archive. This function will close the CHM file, if it is open. All variables are also reset. ''' if self.filename is not None: chmlib.chm_close(self.file) self.file = None self.filename = '' self.title = "" self.home = "/" self.index = None self.topics = None self.encoding = None def GetArchiveInfo(self): '''Obtains information on CHM archive. This function checks the /#SYSTEM file inside the CHM archive to obtain the index, home page, topics, encoding and title. It is called from LoadCHM. ''' self.searchable = extra.is_searchable(self.file) self.lcid = None result, ui = chmlib.chm_resolve_object(self.file, b'/#SYSTEM') if (result != chmlib.CHM_RESOLVE_SUCCESS): sys.stderr.write('GetArchiveInfo: #SYSTEM does not exist\n') return 0 size, text = chmlib.chm_retrieve_object(self.file, ui, 4, ui.length) if (size == 0): sys.stderr.write('GetArchiveInfo: file size = 0\n') return 0 buff = array.array('B', text) index = 0 while (index < size): cursor = buff[index] + (buff[index+1] * 256) if (cursor == 0): index += 2 cursor = buff[index] + (buff[index+1] * 256) index += 2 self.topics = b'/' + text[index:index+cursor-1] elif (cursor == 1): index += 2 cursor = buff[index] + (buff[index+1] * 256) index += 2 self.index = b'/' + text[index:index+cursor-1] elif (cursor == 2): index += 2 cursor = buff[index] + (buff[index+1] * 256) index += 2 self.home = b'/' + text[index:index+cursor-1] elif (cursor == 3): index += 2 cursor = buff[index] + (buff[index+1] * 256) index += 2 self.title = text[index:index+cursor-1] elif (cursor == 4): index += 2 cursor = buff[index] + (buff[index+1] * 256) index += 2 self.lcid = buff[index] + (buff[index+1] * 256) elif (cursor == 6): index += 2 cursor = buff[index] + (buff[index+1] * 256) index += 2 tmp = text[index:index+cursor-1] if not self.topics: tmp1 = b'/' + tmp + b'.hhc' tmp2 = b'/' + tmp + b'.hhk' res1, ui1 = chmlib.chm_resolve_object(self.file, tmp1) res2, ui2 = chmlib.chm_resolve_object(self.file, tmp2) if not self.topics and res1 == chmlib.CHM_RESOLVE_SUCCESS: self.topics = b'/' + tmp + b'.hhc' if not self.index and res2 == chmlib.CHM_RESOLVE_SUCCESS: self.index = b'/' + tmp + b'.hhk' elif (cursor == 16): index += 2 cursor = buff[index] + (buff[index+1] * 256) index += 2 self.encoding = text[index:index+cursor-1] else: index += 2 cursor = buff[index] + (buff[index+1] * 256) index += 2 index += cursor self.GetWindowsInfo() if not self.lcid: self.lcid = extra.get_lcid(self.file) return 1 def GetTopicsTree(self): '''Reads and returns the topics tree. This auxiliary function reads and returns the topics tree file contents for the CHM archive. ''' if self.topics is None: return None if self.topics: res, ui = chmlib.chm_resolve_object(self.file, self.topics) if (res != chmlib.CHM_RESOLVE_SUCCESS): return None size, text = chmlib.chm_retrieve_object(self.file, ui, 0, ui.length) if (size == 0): sys.stderr.write('GetTopicsTree: file size = 0\n') return None return text def GetIndex(self): '''Reads and returns the index tree. This auxiliary function reads and returns the index tree file contents for the CHM archive. ''' if self.index is None: return None if self.index: res, ui = chmlib.chm_resolve_object(self.file, self.index) if (res != chmlib.CHM_RESOLVE_SUCCESS): return None size, text = chmlib.chm_retrieve_object(self.file, ui, 0, ui.length) if (size == 0): sys.stderr.write('GetIndex: file size = 0\n') return None return text def ResolveObject(self, document): '''Tries to locate a document in the archive. This function tries to locate the document inside the archive. It returns a tuple where the first element is zero if the function was successful, and the second is the UnitInfo for that document. The UnitInfo is used to retrieve the document contents ''' if self.file: # path = os.path.abspath(document) # wtf?? the index contents # are independent of the os ! path = document return chmlib.chm_resolve_object(self.file, path) else: return (1, None) def RetrieveObject(self, ui, start=-1, length=-1): '''Retrieves the contents of a document. This function takes a UnitInfo and two optional arguments, the first being the start address and the second is the length. These define the amount of data to be read from the archive. ''' if self.file and ui: if length == -1: len = ui.length else: len = length if start == -1: st = 0 else: st = long(start) return chmlib.chm_retrieve_object(self.file, ui, st, len) else: return (0, '') def Search(self, text, wholewords=0, titleonly=0): '''Performs full-text search on the archive. The first parameter is the word to look for, the second indicates if the search should be for whole words only, and the third parameter indicates if the search should be restricted to page titles. This method will return a tuple, the first item indicating if the search results were partial, and the second item being a dictionary containing the results.''' if text and text != '' and self.file: return extra.search(self.file, text, wholewords, titleonly) else: return None def IsSearchable(self): '''Indicates if the full-text search is available for this archive - this flag is updated when GetArchiveInfo is called''' return self.searchable def GetEncoding(self): '''Returns a string that can be used with the codecs python package to encode or decode the files in the chm archive. If an error is found, or if it is not possible to find the encoding, None is returned.''' if self.encoding: vals = self.encoding.split(b',') if len(vals) > 2: try: return charset_table[int(vals[2])] except KeyError: pass return None def GetLCID(self): '''Returns the archive Locale ID''' if self.lcid in locale_table: return locale_table[self.lcid] else: return None def GetDWORD(self, buff, idx=0): '''Internal method. Reads a double word (4 bytes) from a buffer. ''' result = buff[idx] + (buff[idx+1] << 8) + (buff[idx+2] << 16) + \ (buff[idx+3] << 24) if result == 0xFFFFFFFF: result = 0 return result def GetString(self, text, idx): '''Internal method. Retrieves a string from the #STRINGS buffer. ''' next = text.find(b'\x00', idx) chunk = text[idx:next] return chunk def GetWindowsInfo(self): '''Gets information from the #WINDOWS file. Checks the #WINDOWS file to see if it has any info that was not found in #SYSTEM (topics, index or default page. ''' result, ui = chmlib.chm_resolve_object(self.file, b'/#WINDOWS') if (result != chmlib.CHM_RESOLVE_SUCCESS): return -1 size, text = chmlib.chm_retrieve_object(self.file, ui, 0, 8) if (size < 8): return -2 buff = array.array('B', text) num_entries = self.GetDWORD(buff, 0) entry_size = self.GetDWORD(buff, 4) if num_entries < 1: return -3 size, text = chmlib.chm_retrieve_object(self.file, ui, 8, entry_size) if (size < entry_size): return -4 buff = array.array('B', text) toc_index = self.GetDWORD(buff, 0x60) idx_index = self.GetDWORD(buff, 0x64) dft_index = self.GetDWORD(buff, 0x68) result, ui = chmlib.chm_resolve_object(self.file, b'/#STRINGS') if (result != chmlib.CHM_RESOLVE_SUCCESS): return -5 size, text = chmlib.chm_retrieve_object(self.file, ui, 0, ui.length) if (size == 0): return -6 if (not self.topics): self.topics = self.GetString(text, toc_index) if not self.topics.startswith(b"/"): self.topics = b"/" + self.topics if (not self.index): self.index = self.GetString(text, idx_index) if not self.index.startswith(b"/"): self.index = b"/" + self.index if (dft_index != 0): self.home = self.GetString(text, dft_index) if not self.home.startswith(b"/"): self.home = b"/" + self.home recoll-1.36.1/python/pychm/recollchm/swig_chm.i0000644000175000017500000001474414517521041016400 00000000000000%module chmlib %begin %{ #define SWIG_PYTHON_STRICT_BYTE_CHAR %} %include "typemaps.i" %include "cstring.i" %{ /* Copyright (C) 2003 Rubens Ramos Based on code by: Copyright (C) 2003 Razvan Cojocaru pychm is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA $Id$ */ #include "chm_lib.h" #include static PyObject *my_callback = NULL; static PyObject * my_set_callback(PyObject *dummy, PyObject *arg) { PyObject *result = NULL; if (!PyCallable_Check(arg)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } Py_XINCREF(arg); /* Add a reference to new callback */ Py_XDECREF(my_callback); /* Dispose of previous callback */ my_callback = arg; /* Remember new callback */ /* Boilerplate to return "None" */ Py_INCREF(Py_None); result = Py_None; return result; } int dummy_enumerator (struct chmFile *h, struct chmUnitInfo *ui, void *context) { PyObject *arglist; PyObject *result; PyObject *py_h; PyObject *py_ui; PyObject *py_c; py_h = SWIG_NewPointerObj((void *) h, SWIGTYPE_p_chmFile, 0); py_ui = SWIG_NewPointerObj((void *) ui, SWIGTYPE_p_chmUnitInfo, 0); /* The following was: py_c = PyCObject_AsVoidPtr(context); which did not make sense because the function takes a PyObject * and returns a void *, not the reverse. This was probably never used?? In doubt, replace with a call which makes sense and hope for the best... */ py_c = PyCapsule_New(context, "context", NULL); /* Time to call the callback */ arglist = Py_BuildValue("(OOO)", py_h, py_ui, py_c); if (arglist) { result = PyObject_CallObject(my_callback, arglist); Py_DECREF(arglist); Py_DECREF(result); Py_DECREF(py_h); Py_DECREF(py_ui); Py_DECREF(py_c); if (result == NULL) { return 0; /* Pass error back */ } else { return 1; } } else return 0; } %} %typemap(in) CHM_ENUMERATOR { if (!my_set_callback(self, $input)) goto fail; $1 = dummy_enumerator; } %typemap(in) void *context { if (!($1 = PyCapsule_New($input, "context", NULL))) goto fail; } %typemap(in, numinputs=0) struct chmUnitInfo *OutValue (struct chmUnitInfo *temp = (struct chmUnitInfo *) calloc(1, sizeof(struct chmUnitInfo))) { $1 = temp; } %typemap(argout) struct chmUnitInfo *OutValue { PyObject *o, *o2, *o3; o = SWIG_NewPointerObj((void *) $1, SWIGTYPE_p_chmUnitInfo, 1); if ((!$result) || ($result == Py_None)) { $result = o; } else { if (!PyTuple_Check($result)) { PyObject *o2 = $result; $result = PyTuple_New(1); PyTuple_SetItem($result,0,o2); } o3 = PyTuple_New(1); PyTuple_SetItem(o3,0,o); o2 = $result; $result = PySequence_Concat(o2,o3); Py_DECREF(o2); Py_DECREF(o3); } } %typemap(check) unsigned char *OUTPUT { /* nasty hack */ #ifdef __cplusplus $1 = ($1_ltype) new char[arg5]; #else $1 = ($1_ltype) malloc(arg5); #endif if ($1 == NULL) SWIG_fail; } %typemap(argout,fragment="t_output_helper") unsigned char *OUTPUT { PyObject *o; o = SWIG_FromCharPtrAndSize((const char*)$1, arg5); /* o = PyString_FromStringAndSize($1, arg5);*/ $result = t_output_helper($result,o); #ifdef __cplusplus delete [] $1; #else free($1); #endif } #ifdef WIN32 typedef unsigned __int64 LONGUINT64; typedef __int64 LONGINT64; #else typedef unsigned long long LONGUINT64; typedef long long LONGINT64; #endif /* the two available spaces in a CHM file */ /* N.B.: The format supports arbitrarily many spaces, but only */ /* two appear to be used at present. */ #define CHM_UNCOMPRESSED (0) #define CHM_COMPRESSED (1) /* structure representing an ITS (CHM) file stream */ struct chmFile; /* structure representing an element from an ITS file stream */ #define CHM_MAX_PATHLEN 256 struct chmUnitInfo { LONGUINT64 start; LONGUINT64 length; int space; char path[CHM_MAX_PATHLEN+1]; }; /* open an ITS archive */ struct chmFile* chm_open(const char *filename); /* close an ITS archive */ void chm_close(struct chmFile *h); /* methods for ssetting tuning parameters for particular file */ #define CHM_PARAM_MAX_BLOCKS_CACHED 0 void chm_set_param(struct chmFile *h, int paramType, int paramVal); /* resolve a particular object from the archive */ #define CHM_RESOLVE_SUCCESS (0) #define CHM_RESOLVE_FAILURE (1) int chm_resolve_object(struct chmFile *h, const char *objPath, struct chmUnitInfo *OutValue); /* retrieve part of an object from the archive */ LONGINT64 chm_retrieve_object(struct chmFile *h, struct chmUnitInfo *ui, unsigned char *OUTPUT, LONGUINT64 addr, LONGINT64 len); /* enumerate the objects in the .chm archive */ typedef int (*CHM_ENUMERATOR)(struct chmFile *h, struct chmUnitInfo *ui, void *context); #define CHM_ENUMERATE_NORMAL (1) #define CHM_ENUMERATE_META (2) #define CHM_ENUMERATE_SPECIAL (4) #define CHM_ENUMERATE_FILES (8) #define CHM_ENUMERATE_DIRS (16) #define CHM_ENUMERATE_ALL (31) #define CHM_ENUMERATOR_FAILURE (0) #define CHM_ENUMERATOR_CONTINUE (1) #define CHM_ENUMERATOR_SUCCESS (2) int chm_enumerate(struct chmFile *h, int what, CHM_ENUMERATOR e, void *context); int chm_enumerate_dir(struct chmFile *h, const char *prefix, int what, CHM_ENUMERATOR e, void *context); recoll-1.36.1/python/pychm/recollchm/extra.c0000644000175000017500000004421614410615043015710 00000000000000/* * extra.c - full-text search support for pychm * * Copyright (C) 2004 Rubens Ramos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * Author: Rubens Ramos * * Heavily based on work done by: * Pabs - chmdeco * Razvan Cojocaru - xCHM * */ #include "chm_lib.h" #ifdef __PYTHON__ #include "Python.h" #else #include #define PyObject void #endif typedef struct { PyObject_HEAD void *ptr; void *ty; int own; PyObject *next; #ifdef SWIGPYTHON_BUILTIN PyObject *dict; #endif } SwigPyObject; #include #if defined(_WIN32) || defined(__WIN32__) # if defined(_MSC_VER) # if defined(STATIC_LINKED) # define MODEXPORT(a) a # define MODIMPORT(a) extern a # else # define MODEXPORT(a) __declspec(dllexport) a # define MODIMPORT(a) extern a # endif #define uint64_t unsigned long long #define uint32_t unsigned int #define uint16_t unsigned short #define uint8_t unsigned char #define size_t int #define strcasecmp _stricmp #define strncasecmp _strnicmp # else # if defined(__BORLANDC__) # define MODEXPORT(a) a _export # define MODIMPORT(a) a _export # else # define MODEXPORT(a) a # define MODIMPORT(a) a # endif # endif #else # define MODEXPORT(a) a # define MODIMPORT(a) a #include #include #endif #define false 0 #define true 1 #define FTS_HEADER_LEN 0x32 #define TOPICS_ENTRY_LEN 16 #define COMMON_BUF_LEN 1025 #define FREE(x) free (x); x = NULL static uint16_t get_uint16 (uint8_t* b) { return b[0] | b[1]<<8; } static uint32_t get_uint32 (uint8_t* b) { return b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24; } static uint64_t be_encint (unsigned char *buffer, size_t *length) { uint64_t result = 0; int shift=0; *length = 0; do { result |= ((*buffer) & 0x7f) << shift; shift += 7; *length = *length + 1; } while (*(buffer++) & 0x80); return result; } /* Finds the first unset bit in memory. Returns the number of set bits found. Returns -1 if the buffer runs out before we find an unset bit. */ static int ffus (unsigned char* byte, int* bit, size_t *length) { int bits = 0; *length = 0; while(*byte & (1 << *bit)){ if(*bit) --(*bit); else { ++byte; ++(*length); *bit = 7; } ++bits; } if(*bit) --(*bit); else { ++(*length); *bit = 7; } return bits; } static uint64_t sr_int(unsigned char* byte, int* bit, unsigned char s, unsigned char r, size_t *length) { uint64_t ret; unsigned char mask; int n, n_bits, num_bits, base, count; size_t fflen; *length = 0; if(!bit || *bit > 7 || s != 2) return ~(uint64_t)0; ret = 0; count = ffus(byte, bit, &fflen); *length += fflen; byte += *length; n_bits = n = r + (count ? count-1 : 0) ; while (n > 0) { num_bits = n > *bit ? *bit : n-1; base = n > *bit ? 0 : *bit - (n-1); switch (num_bits){ case 0: mask = 1; break; case 1: mask = 3; break; case 2: mask = 7; break; case 3: mask = 0xf; break; case 4: mask = 0x1f; break; case 5: mask = 0x3f; break; case 6: mask = 0x7f; break; case 7: mask = 0xff; break; default: mask = 0xff; break; } mask <<= base; ret = (ret << (num_bits+1)) | (uint64_t)((*byte & mask) >> base); if( n > *bit ){ ++byte; ++(*length); n -= *bit+1; *bit = 7; } else { *bit -= n; n = 0; } } if(count) ret |= (uint64_t)1 << n_bits; return ret; } static uint32_t get_leaf_node_offset(struct chmFile *chmfile, const char *text, uint32_t initial_offset, uint32_t buff_size, uint16_t tree_depth, struct chmUnitInfo *ui) { unsigned char word_len; unsigned char pos; uint16_t free_space; char *wrd_buf; char *word = NULL; uint32_t test_offset = 0; uint32_t i = sizeof(uint16_t); unsigned char *buffer = malloc (buff_size); if (NULL == buffer) return 0; while (--tree_depth) { if (initial_offset == test_offset) { FREE(buffer); return 0; } test_offset = initial_offset; if (chm_retrieve_object (chmfile, ui, buffer, initial_offset, buff_size) == 0) { FREE(buffer); return 0; } free_space = get_uint16 (buffer); while (i < buff_size - free_space) { word_len = *(buffer + i); pos = *(buffer + i + 1); wrd_buf = malloc (word_len); memcpy (wrd_buf, buffer + i + 2, word_len - 1); wrd_buf[word_len - 1] = 0; if (pos == 0) { FREE (word); word = (char *) strdup (wrd_buf); } else { word = realloc (word, word_len + pos + 1); strcpy (word + pos, wrd_buf); } FREE(wrd_buf); if (strcasecmp (text, word) <= 0) { initial_offset = get_uint32 (buffer + i + word_len + 1); break; } i += word_len + sizeof (unsigned char) + sizeof(uint32_t) + sizeof(uint16_t); } } if(initial_offset == test_offset) initial_offset = 0; FREE(word); FREE(buffer); return initial_offset; } static int pychm_process_wlc (struct chmFile *chmfile, uint64_t wlc_count, uint64_t wlc_size, uint32_t wlc_offset, unsigned char ds, unsigned char dr, unsigned char cs, unsigned char cr, unsigned char ls, unsigned char lr, struct chmUnitInfo *uimain, struct chmUnitInfo* uitbl, struct chmUnitInfo *uistrings, struct chmUnitInfo* topics, struct chmUnitInfo *urlstr, PyObject *dict) { uint32_t stroff, urloff; uint64_t i, j, count; size_t length; int wlc_bit = 7; size_t off = 0; uint64_t index = 0; unsigned char entry[TOPICS_ENTRY_LEN]; unsigned char combuf[COMMON_BUF_LEN]; unsigned char *buffer = malloc (wlc_size); char *url = NULL; char *topic = NULL; if (chm_retrieve_object(chmfile, uimain, buffer, wlc_offset, wlc_size) == 0) { FREE(buffer); return false; } for (i = 0; i < wlc_count; ++i) { if(wlc_bit != 7) { ++off; wlc_bit = 7; } index += sr_int(buffer + off, &wlc_bit, ds, dr, &length); off += length; if(chm_retrieve_object(chmfile, topics, entry, index * 16, TOPICS_ENTRY_LEN) == 0) { FREE(topic); FREE(url); FREE(buffer); return false; } combuf[COMMON_BUF_LEN - 1] = 0; stroff = get_uint32 (entry + 4); FREE (topic); if (chm_retrieve_object (chmfile, uistrings, combuf, stroff, COMMON_BUF_LEN - 1) == 0) { topic = strdup ("Untitled in index"); } else { combuf[COMMON_BUF_LEN - 1] = 0; topic = strdup ((char *)combuf); } urloff = get_uint32 (entry + 8); if(chm_retrieve_object (chmfile, uitbl, combuf, urloff, 12) == 0) { FREE(buffer); return false; } urloff = get_uint32 (combuf + 8); if (chm_retrieve_object (chmfile, urlstr, combuf, urloff + 8, COMMON_BUF_LEN - 1) == 0) { FREE(topic); FREE(url); FREE(buffer); return false; } combuf[COMMON_BUF_LEN - 1] = 0; FREE (url); url = strdup ((char *)combuf); if (url && topic) { #ifdef __PYTHON__ PyDict_SetItem(dict, #if PY_MAJOR_VERSION >= 3 PyBytes_FromStringAndSize(topic, strlen(topic)), PyBytes_FromStringAndSize(url, strlen(url)) #else PyString_FromString (topic), PyString_FromString (url) #endif ); #else printf ("%s ==> %s\n", url, topic); #endif } count = sr_int (buffer + off, &wlc_bit, cs, cr, &length); off += length; for (j = 0; j < count; ++j) { sr_int (buffer + off, &wlc_bit, ls, lr, &length); off += length; } } FREE(topic); FREE(url); FREE(buffer); return true; } static int chm_search (struct chmFile *chmfile, const char *text, int whole_words, int titles_only, PyObject *dict) { unsigned char header[FTS_HEADER_LEN]; unsigned char doc_index_s; unsigned char doc_index_r; unsigned char code_count_s; unsigned char code_count_r; unsigned char loc_codes_s; unsigned char loc_codes_r; unsigned char word_len, pos; unsigned char *buffer; char *word = NULL; uint32_t node_offset; uint32_t node_len; uint16_t tree_depth; uint32_t i; uint16_t free_space; uint64_t wlc_count, wlc_size; uint32_t wlc_offset; char *wrd_buf; unsigned char title; size_t encsz; struct chmUnitInfo ui, uitopics, uiurltbl, uistrings, uiurlstr; int partial = false; if (NULL == text) return -1; if (chm_resolve_object (chmfile, "/$FIftiMain", &ui) != CHM_RESOLVE_SUCCESS || chm_resolve_object (chmfile, "/#TOPICS", &uitopics) != CHM_RESOLVE_SUCCESS || chm_resolve_object (chmfile, "/#STRINGS", &uistrings) != CHM_RESOLVE_SUCCESS || chm_resolve_object (chmfile, "/#URLTBL", &uiurltbl) != CHM_RESOLVE_SUCCESS || chm_resolve_object (chmfile, "/#URLSTR", &uiurlstr) != CHM_RESOLVE_SUCCESS) return false; if(chm_retrieve_object(chmfile, &ui, header, 0, FTS_HEADER_LEN) == 0) return false; doc_index_s = header[0x1E]; doc_index_r = header[0x1F]; code_count_s = header[0x20]; code_count_r = header[0x21]; loc_codes_s = header[0x22]; loc_codes_r = header[0x23]; if(doc_index_s != 2 || code_count_s != 2 || loc_codes_s != 2) { return false; } node_offset = get_uint32 (header + 0x14); node_len = get_uint32 (header + 0x2e); tree_depth = get_uint16 (header + 0x18); i = sizeof(uint16_t); buffer = malloc (node_len); node_offset = get_leaf_node_offset (chmfile, text, node_offset, node_len, tree_depth, &ui); if (!node_offset) { FREE(buffer); return false; } do { if (chm_retrieve_object (chmfile, &ui, buffer, node_offset, node_len) == 0) { FREE(word); FREE(buffer); return false; } free_space = get_uint16 (buffer + 6); i = sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t); encsz = 0; while (i < node_len - free_space) { word_len = *(buffer + i); pos = *(buffer + i + 1); wrd_buf = malloc (word_len); memcpy (wrd_buf, buffer + i + 2, word_len - 1); wrd_buf[word_len - 1] = 0; if (pos == 0) { FREE(word); word = (char *) strdup (wrd_buf); } else { word = realloc (word, word_len + pos + 1); strcpy (word + pos, wrd_buf); } FREE(wrd_buf); i += 2 + word_len; title = *(buffer + i - 1); wlc_count = be_encint (buffer + i, &encsz); i += encsz; wlc_offset = get_uint32 (buffer + i); i += sizeof(uint32_t) + sizeof(uint16_t); wlc_size = be_encint (buffer + i, &encsz); i += encsz; node_offset = get_uint32 (buffer); if (!title && titles_only) continue; if (whole_words && !strcasecmp(text, word)) { partial = pychm_process_wlc (chmfile, wlc_count, wlc_size, wlc_offset, doc_index_s, doc_index_r,code_count_s, code_count_r, loc_codes_s, loc_codes_r, &ui, &uiurltbl, &uistrings, &uitopics, &uiurlstr, dict); FREE(word); FREE(buffer); return partial; } if (!whole_words) { if (!strncasecmp (word, text, strlen(text))) { partial = true; pychm_process_wlc (chmfile, wlc_count, wlc_size, wlc_offset, doc_index_s, doc_index_r,code_count_s, code_count_r, loc_codes_s, loc_codes_r, &ui, &uiurltbl, &uistrings, &uitopics, &uiurlstr, dict); } else if (strncasecmp (text, word, strlen(text)) < -1) break; } } } while (!whole_words && !strncmp (word, text, strlen(text)) && node_offset); FREE(word); FREE(buffer); return partial; } typedef struct { const char *file; int offset; } Langrec; static Langrec lang_files[] = { {"/$FIftiMain", 0x7E}, {"$WWKeywordLinks/BTree", 0x34}, {"$WWAssociativeLinks/BTree", 0x34} }; #define LANG_FILES_SIZE (sizeof(lang_files)/sizeof(Langrec)) static int chm_get_lcid (struct chmFile *chmfile) { struct chmUnitInfo ui; uint32_t lang; int i; for (i=0; i<(int)LANG_FILES_SIZE; i++) { if (chm_resolve_object (chmfile, lang_files[i].file, &ui) == CHM_RESOLVE_SUCCESS) { if (chm_retrieve_object (chmfile, &ui, (unsigned char *) &lang, lang_files[i].offset, sizeof(uint32_t)) != 0) return lang; } } return -1; } #ifdef __PYTHON__ static PyObject * is_searchable (PyObject *self, PyObject *args) { struct chmFile *file; PyObject *obj0; struct chmUnitInfo ui; if (PyArg_ParseTuple (args, "O:is_searchable", &obj0)) { file = (struct chmFile *) ((SwigPyObject*)(obj0))->ptr; if (chm_resolve_object (file, "/$FIftiMain", &ui) != CHM_RESOLVE_SUCCESS || chm_resolve_object (file, "/#TOPICS", &ui) != CHM_RESOLVE_SUCCESS || chm_resolve_object (file, "/#STRINGS", &ui) != CHM_RESOLVE_SUCCESS || chm_resolve_object (file, "/#URLTBL", &ui) != CHM_RESOLVE_SUCCESS || chm_resolve_object (file, "/#URLSTR", &ui) != CHM_RESOLVE_SUCCESS) return Py_BuildValue ("i", 0); else return Py_BuildValue ("i", 1); } else { PyErr_SetString(PyExc_TypeError, "Expected chmfile (not CHMFile!)"); return NULL; } } static PyObject * search (PyObject *self, PyObject *args) { char *text; int whole_words = 0; int titles_only = 0; int partial; struct chmFile *file; PyObject *obj0; PyObject *dict; #if PY_MAJOR_VERSION >= 3 PyObject *obj1; if (PyArg_ParseTuple (args, "OSii:search", &obj0, &obj1, #else if (PyArg_ParseTuple (args, "Osii:search", &obj0, &text, #endif &whole_words, &titles_only)) { #if PY_MAJOR_VERSION >= 3 text = PyBytes_AsString(obj1); #endif dict = PyDict_New(); if (dict) { file = (struct chmFile *) ((SwigPyObject*)(obj0))->ptr; partial = chm_search (file, text, whole_words, titles_only, dict); return Py_BuildValue ("(iO)", partial, dict); } else { PyErr_NoMemory(); return NULL; } } else { PyErr_SetString(PyExc_TypeError, "Expected chmfile (not CHMFile!), string, int, int"); return NULL; } } static PyObject * get_lcid (PyObject *self, PyObject *args) { int code; struct chmFile *file; PyObject *obj0; if (PyArg_ParseTuple (args, "O:get_lcid", &obj0)) { file = (struct chmFile *) ((SwigPyObject*)(obj0))->ptr; code = chm_get_lcid (file); if (code != -1) return Py_BuildValue ("i", code); else Py_INCREF(Py_None); return Py_None; } else { PyErr_SetString(PyExc_TypeError,"Expected a chmfile (not a CHMFile!)"); return NULL; } } static PyMethodDef IndexMethods[] = { {"get_lcid", get_lcid, METH_VARARGS, "Returns LCID (Locale ID) for archive."}, {"search", search, METH_VARARGS, "Perform Full-Text search."}, {"is_searchable", is_searchable, METH_VARARGS, "Return 1 if it is possible to search the archive, 0 otherwise."}, {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "extra", NULL, -1, IndexMethods, NULL, NULL, NULL, NULL }; #define INITERROR return NULL #else /* python < 3 */ #define INITERROR return #endif /* python 3/2 */ #if PY_MAJOR_VERSION >= 3 PyObject* PyInit_extra(void) #else void initextra (void) #endif { PyObject *module; #if PY_MAJOR_VERSION >= 3 module = PyModule_Create(&moduledef); #else module = Py_InitModule ("extra", IndexMethods); #endif if (module == NULL) INITERROR; #if PY_MAJOR_VERSION >= 3 return module; #endif } #else int main (int argc, char **argv) { struct chmFile *file; char text[255]; int whole_words, titles_only; int partial; if (argc == 2) { file = chm_open (argv[1]); if (file) { printf ("\nLCID= %d (%08X)\n", chm_get_lcid(file), chm_get_lcid(file)); while (1) { printf ("\n \n"); printf ("> "); if (scanf ("%d %d %s", &whole_words, &titles_only, text)) partial = chm_search (file, text, whole_words, titles_only, NULL); else break; printf ("Partial = %d\n", partial); } chm_close (file); return 0; } return -1; } else { printf ("\n%s \n", argv[0]); return 0; } } #endif recoll-1.36.1/python/pychm/recollchm/swig_chm.c0000644000175000017500000042662214517521130016373 00000000000000/* ---------------------------------------------------------------------------- * This file was automatically generated by SWIG (http://www.swig.org). * Version 4.0.2 * * This file is not intended to be easily readable and contains a number of * coding conventions designed to improve portability and efficiency. Do not make * changes to this file unless you know what you are doing--modify the SWIG * interface file instead. * ----------------------------------------------------------------------------- */ #define SWIG_PYTHON_STRICT_BYTE_CHAR #ifndef SWIGPYTHON #define SWIGPYTHON #endif #define SWIG_PYTHON_DIRECTOR_NO_VTABLE /* ----------------------------------------------------------------------------- * This section contains generic SWIG labels for method/variable * declarations/attributes, and other compiler dependent labels. * ----------------------------------------------------------------------------- */ /* template workaround for compilers that cannot correctly implement the C++ standard */ #ifndef SWIGTEMPLATEDISAMBIGUATOR # if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) # define SWIGTEMPLATEDISAMBIGUATOR template # elif defined(__HP_aCC) /* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ /* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ # define SWIGTEMPLATEDISAMBIGUATOR template # else # define SWIGTEMPLATEDISAMBIGUATOR # endif #endif /* inline attribute */ #ifndef SWIGINLINE # if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) # define SWIGINLINE inline # else # define SWIGINLINE # endif #endif /* attribute recognised by some compilers to avoid 'unused' warnings */ #ifndef SWIGUNUSED # if defined(__GNUC__) # if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) # define SWIGUNUSED __attribute__ ((__unused__)) # else # define SWIGUNUSED # endif # elif defined(__ICC) # define SWIGUNUSED __attribute__ ((__unused__)) # else # define SWIGUNUSED # endif #endif #ifndef SWIG_MSC_UNSUPPRESS_4505 # if defined(_MSC_VER) # pragma warning(disable : 4505) /* unreferenced local function has been removed */ # endif #endif #ifndef SWIGUNUSEDPARM # ifdef __cplusplus # define SWIGUNUSEDPARM(p) # else # define SWIGUNUSEDPARM(p) p SWIGUNUSED # endif #endif /* internal SWIG method */ #ifndef SWIGINTERN # define SWIGINTERN static SWIGUNUSED #endif /* internal inline SWIG method */ #ifndef SWIGINTERNINLINE # define SWIGINTERNINLINE SWIGINTERN SWIGINLINE #endif /* exporting methods */ #if defined(__GNUC__) # if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) # ifndef GCC_HASCLASSVISIBILITY # define GCC_HASCLASSVISIBILITY # endif # endif #endif #ifndef SWIGEXPORT # if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) # if defined(STATIC_LINKED) # define SWIGEXPORT # else # define SWIGEXPORT __declspec(dllexport) # endif # else # if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) # define SWIGEXPORT __attribute__ ((visibility("default"))) # else # define SWIGEXPORT # endif # endif #endif /* calling conventions for Windows */ #ifndef SWIGSTDCALL # if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) # define SWIGSTDCALL __stdcall # else # define SWIGSTDCALL # endif #endif /* Deal with Microsoft's attempt at deprecating C standard runtime functions */ #if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) # define _CRT_SECURE_NO_DEPRECATE #endif /* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ #if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) # define _SCL_SECURE_NO_DEPRECATE #endif /* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ #if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) # define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 #endif /* Intel's compiler complains if a variable which was never initialised is * cast to void, which is a common idiom which we use to indicate that we * are aware a variable isn't used. So we just silence that warning. * See: https://github.com/swig/swig/issues/192 for more discussion. */ #ifdef __INTEL_COMPILER # pragma warning disable 592 #endif #if defined(__GNUC__) && defined(_WIN32) && !defined(SWIG_PYTHON_NO_HYPOT_WORKAROUND) /* Workaround for '::hypot' has not been declared', see https://bugs.python.org/issue11566 */ # include #endif #if defined(_DEBUG) && defined(SWIG_PYTHON_INTERPRETER_NO_DEBUG) /* Use debug wrappers with the Python release dll */ # undef _DEBUG # include # define _DEBUG 1 #else # include #endif /* ----------------------------------------------------------------------------- * swigrun.swg * * This file contains generic C API SWIG runtime support for pointer * type checking. * ----------------------------------------------------------------------------- */ /* This should only be incremented when either the layout of swig_type_info changes, or for whatever reason, the runtime changes incompatibly */ #define SWIG_RUNTIME_VERSION "4" /* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ #ifdef SWIG_TYPE_TABLE # define SWIG_QUOTE_STRING(x) #x # define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) # define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) #else # define SWIG_TYPE_TABLE_NAME #endif /* You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for creating a static or dynamic library from the SWIG runtime code. In 99.9% of the cases, SWIG just needs to declare them as 'static'. But only do this if strictly necessary, ie, if you have problems with your compiler or suchlike. */ #ifndef SWIGRUNTIME # define SWIGRUNTIME SWIGINTERN #endif #ifndef SWIGRUNTIMEINLINE # define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE #endif /* Generic buffer size */ #ifndef SWIG_BUFFER_SIZE # define SWIG_BUFFER_SIZE 1024 #endif /* Flags for pointer conversions */ #define SWIG_POINTER_DISOWN 0x1 #define SWIG_CAST_NEW_MEMORY 0x2 #define SWIG_POINTER_NO_NULL 0x4 /* Flags for new pointer objects */ #define SWIG_POINTER_OWN 0x1 /* Flags/methods for returning states. The SWIG conversion methods, as ConvertPtr, return an integer that tells if the conversion was successful or not. And if not, an error code can be returned (see swigerrors.swg for the codes). Use the following macros/flags to set or process the returning states. In old versions of SWIG, code such as the following was usually written: if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { // success code } else { //fail code } Now you can be more explicit: int res = SWIG_ConvertPtr(obj,vptr,ty.flags); if (SWIG_IsOK(res)) { // success code } else { // fail code } which is the same really, but now you can also do Type *ptr; int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); if (SWIG_IsOK(res)) { // success code if (SWIG_IsNewObj(res) { ... delete *ptr; } else { ... } } else { // fail code } I.e., now SWIG_ConvertPtr can return new objects and you can identify the case and take care of the deallocation. Of course that also requires SWIG_ConvertPtr to return new result values, such as int SWIG_ConvertPtr(obj, ptr,...) { if () { if () { *ptr = ; return SWIG_NEWOBJ; } else { *ptr = ; return SWIG_OLDOBJ; } } else { return SWIG_BADOBJ; } } Of course, returning the plain '0(success)/-1(fail)' still works, but you can be more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the SWIG errors code. Finally, if the SWIG_CASTRANK_MODE is enabled, the result code allows to return the 'cast rank', for example, if you have this int food(double) int fooi(int); and you call food(1) // cast rank '1' (1 -> 1.0) fooi(1) // cast rank '0' just use the SWIG_AddCast()/SWIG_CheckState() */ #define SWIG_OK (0) #define SWIG_ERROR (-1) #define SWIG_IsOK(r) (r >= 0) #define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) /* The CastRankLimit says how many bits are used for the cast rank */ #define SWIG_CASTRANKLIMIT (1 << 8) /* The NewMask denotes the object was created (using new/malloc) */ #define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) /* The TmpMask is for in/out typemaps that use temporal objects */ #define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) /* Simple returning values */ #define SWIG_BADOBJ (SWIG_ERROR) #define SWIG_OLDOBJ (SWIG_OK) #define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) #define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) /* Check, add and del mask methods */ #define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) #define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) #define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) #define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) #define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) #define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) /* Cast-Rank Mode */ #if defined(SWIG_CASTRANK_MODE) # ifndef SWIG_TypeRank # define SWIG_TypeRank unsigned long # endif # ifndef SWIG_MAXCASTRANK /* Default cast allowed */ # define SWIG_MAXCASTRANK (2) # endif # define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) # define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) SWIGINTERNINLINE int SWIG_AddCast(int r) { return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; } SWIGINTERNINLINE int SWIG_CheckState(int r) { return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; } #else /* no cast-rank mode */ # define SWIG_AddCast(r) (r) # define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) #endif #include #ifdef __cplusplus extern "C" { #endif typedef void *(*swig_converter_func)(void *, int *); typedef struct swig_type_info *(*swig_dycast_func)(void **); /* Structure to store information on one type */ typedef struct swig_type_info { const char *name; /* mangled name of this type */ const char *str; /* human readable name of this type */ swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ struct swig_cast_info *cast; /* linked list of types that can cast into this type */ void *clientdata; /* language specific type data */ int owndata; /* flag if the structure owns the clientdata */ } swig_type_info; /* Structure to store a type and conversion function used for casting */ typedef struct swig_cast_info { swig_type_info *type; /* pointer to type that is equivalent to this type */ swig_converter_func converter; /* function to cast the void pointers */ struct swig_cast_info *next; /* pointer to next cast in linked list */ struct swig_cast_info *prev; /* pointer to the previous cast */ } swig_cast_info; /* Structure used to store module information * Each module generates one structure like this, and the runtime collects * all of these structures and stores them in a circularly linked list.*/ typedef struct swig_module_info { swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ size_t size; /* Number of types in this module */ struct swig_module_info *next; /* Pointer to next element in circularly linked list */ swig_type_info **type_initial; /* Array of initially generated type structures */ swig_cast_info **cast_initial; /* Array of initially generated casting structures */ void *clientdata; /* Language specific module data */ } swig_module_info; /* Compare two type names skipping the space characters, therefore "char*" == "char *" and "Class" == "Class", etc. Return 0 when the two name types are equivalent, as in strncmp, but skipping ' '. */ SWIGRUNTIME int SWIG_TypeNameComp(const char *f1, const char *l1, const char *f2, const char *l2) { for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { while ((*f1 == ' ') && (f1 != l1)) ++f1; while ((*f2 == ' ') && (f2 != l2)) ++f2; if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; } return (int)((l1 - f1) - (l2 - f2)); } /* Check type equivalence in a name list like ||... Return 0 if equal, -1 if nb < tb, 1 if nb > tb */ SWIGRUNTIME int SWIG_TypeCmp(const char *nb, const char *tb) { int equiv = 1; const char* te = tb + strlen(tb); const char* ne = nb; while (equiv != 0 && *ne) { for (nb = ne; *ne; ++ne) { if (*ne == '|') break; } equiv = SWIG_TypeNameComp(nb, ne, tb, te); if (*ne) ++ne; } return equiv; } /* Check type equivalence in a name list like ||... Return 0 if not equal, 1 if equal */ SWIGRUNTIME int SWIG_TypeEquiv(const char *nb, const char *tb) { return SWIG_TypeCmp(nb, tb) == 0 ? 1 : 0; } /* Check the typename */ SWIGRUNTIME swig_cast_info * SWIG_TypeCheck(const char *c, swig_type_info *ty) { if (ty) { swig_cast_info *iter = ty->cast; while (iter) { if (strcmp(iter->type->name, c) == 0) { if (iter == ty->cast) return iter; /* Move iter to the top of the linked list */ iter->prev->next = iter->next; if (iter->next) iter->next->prev = iter->prev; iter->next = ty->cast; iter->prev = 0; if (ty->cast) ty->cast->prev = iter; ty->cast = iter; return iter; } iter = iter->next; } } return 0; } /* Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison */ SWIGRUNTIME swig_cast_info * SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *ty) { if (ty) { swig_cast_info *iter = ty->cast; while (iter) { if (iter->type == from) { if (iter == ty->cast) return iter; /* Move iter to the top of the linked list */ iter->prev->next = iter->next; if (iter->next) iter->next->prev = iter->prev; iter->next = ty->cast; iter->prev = 0; if (ty->cast) ty->cast->prev = iter; ty->cast = iter; return iter; } iter = iter->next; } } return 0; } /* Cast a pointer up an inheritance hierarchy */ SWIGRUNTIMEINLINE void * SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) { return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory); } /* Dynamic pointer casting. Down an inheritance hierarchy */ SWIGRUNTIME swig_type_info * SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { swig_type_info *lastty = ty; if (!ty || !ty->dcast) return ty; while (ty && (ty->dcast)) { ty = (*ty->dcast)(ptr); if (ty) lastty = ty; } return lastty; } /* Return the name associated with this type */ SWIGRUNTIMEINLINE const char * SWIG_TypeName(const swig_type_info *ty) { return ty->name; } /* Return the pretty name associated with this type, that is an unmangled type name in a form presentable to the user. */ SWIGRUNTIME const char * SWIG_TypePrettyName(const swig_type_info *type) { /* The "str" field contains the equivalent pretty names of the type, separated by vertical-bar characters. We choose to print the last name, as it is often (?) the most specific. */ if (!type) return NULL; if (type->str != NULL) { const char *last_name = type->str; const char *s; for (s = type->str; *s; s++) if (*s == '|') last_name = s+1; return last_name; } else return type->name; } /* Set the clientdata field for a type */ SWIGRUNTIME void SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { swig_cast_info *cast = ti->cast; /* if (ti->clientdata == clientdata) return; */ ti->clientdata = clientdata; while (cast) { if (!cast->converter) { swig_type_info *tc = cast->type; if (!tc->clientdata) { SWIG_TypeClientData(tc, clientdata); } } cast = cast->next; } } SWIGRUNTIME void SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { SWIG_TypeClientData(ti, clientdata); ti->owndata = 1; } /* Search for a swig_type_info structure only by mangled name Search is a O(log #types) We start searching at module start, and finish searching when start == end. Note: if start == end at the beginning of the function, we go all the way around the circular list. */ SWIGRUNTIME swig_type_info * SWIG_MangledTypeQueryModule(swig_module_info *start, swig_module_info *end, const char *name) { swig_module_info *iter = start; do { if (iter->size) { size_t l = 0; size_t r = iter->size - 1; do { /* since l+r >= 0, we can (>> 1) instead (/ 2) */ size_t i = (l + r) >> 1; const char *iname = iter->types[i]->name; if (iname) { int compare = strcmp(name, iname); if (compare == 0) { return iter->types[i]; } else if (compare < 0) { if (i) { r = i - 1; } else { break; } } else if (compare > 0) { l = i + 1; } } else { break; /* should never happen */ } } while (l <= r); } iter = iter->next; } while (iter != end); return 0; } /* Search for a swig_type_info structure for either a mangled name or a human readable name. It first searches the mangled names of the types, which is a O(log #types) If a type is not found it then searches the human readable names, which is O(#types). We start searching at module start, and finish searching when start == end. Note: if start == end at the beginning of the function, we go all the way around the circular list. */ SWIGRUNTIME swig_type_info * SWIG_TypeQueryModule(swig_module_info *start, swig_module_info *end, const char *name) { /* STEP 1: Search the name field using binary search */ swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); if (ret) { return ret; } else { /* STEP 2: If the type hasn't been found, do a complete search of the str field (the human readable name) */ swig_module_info *iter = start; do { size_t i = 0; for (; i < iter->size; ++i) { if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) return iter->types[i]; } iter = iter->next; } while (iter != end); } /* neither found a match */ return 0; } /* Pack binary data into a string */ SWIGRUNTIME char * SWIG_PackData(char *c, void *ptr, size_t sz) { static const char hex[17] = "0123456789abcdef"; const unsigned char *u = (unsigned char *) ptr; const unsigned char *eu = u + sz; for (; u != eu; ++u) { unsigned char uu = *u; *(c++) = hex[(uu & 0xf0) >> 4]; *(c++) = hex[uu & 0xf]; } return c; } /* Unpack binary data from a string */ SWIGRUNTIME const char * SWIG_UnpackData(const char *c, void *ptr, size_t sz) { unsigned char *u = (unsigned char *) ptr; const unsigned char *eu = u + sz; for (; u != eu; ++u) { char d = *(c++); unsigned char uu; if ((d >= '0') && (d <= '9')) uu = (unsigned char)((d - '0') << 4); else if ((d >= 'a') && (d <= 'f')) uu = (unsigned char)((d - ('a'-10)) << 4); else return (char *) 0; d = *(c++); if ((d >= '0') && (d <= '9')) uu |= (unsigned char)(d - '0'); else if ((d >= 'a') && (d <= 'f')) uu |= (unsigned char)(d - ('a'-10)); else return (char *) 0; *u = uu; } return c; } /* Pack 'void *' into a string buffer. */ SWIGRUNTIME char * SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { char *r = buff; if ((2*sizeof(void *) + 2) > bsz) return 0; *(r++) = '_'; r = SWIG_PackData(r,&ptr,sizeof(void *)); if (strlen(name) + 1 > (bsz - (r - buff))) return 0; strcpy(r,name); return buff; } SWIGRUNTIME const char * SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { if (*c != '_') { if (strcmp(c,"NULL") == 0) { *ptr = (void *) 0; return name; } else { return 0; } } return SWIG_UnpackData(++c,ptr,sizeof(void *)); } SWIGRUNTIME char * SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { char *r = buff; size_t lname = (name ? strlen(name) : 0); if ((2*sz + 2 + lname) > bsz) return 0; *(r++) = '_'; r = SWIG_PackData(r,ptr,sz); if (lname) { strncpy(r,name,lname+1); } else { *r = 0; } return buff; } SWIGRUNTIME const char * SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { if (*c != '_') { if (strcmp(c,"NULL") == 0) { memset(ptr,0,sz); return name; } else { return 0; } } return SWIG_UnpackData(++c,ptr,sz); } #ifdef __cplusplus } #endif /* Errors in SWIG */ #define SWIG_UnknownError -1 #define SWIG_IOError -2 #define SWIG_RuntimeError -3 #define SWIG_IndexError -4 #define SWIG_TypeError -5 #define SWIG_DivisionByZero -6 #define SWIG_OverflowError -7 #define SWIG_SyntaxError -8 #define SWIG_ValueError -9 #define SWIG_SystemError -10 #define SWIG_AttributeError -11 #define SWIG_MemoryError -12 #define SWIG_NullReferenceError -13 /* Compatibility macros for Python 3 */ #if PY_VERSION_HEX >= 0x03000000 #define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type) #define PyInt_Check(x) PyLong_Check(x) #define PyInt_AsLong(x) PyLong_AsLong(x) #define PyInt_FromLong(x) PyLong_FromLong(x) #define PyInt_FromSize_t(x) PyLong_FromSize_t(x) #define PyString_Check(name) PyBytes_Check(name) #define PyString_FromString(x) PyUnicode_FromString(x) #define PyString_Format(fmt, args) PyUnicode_Format(fmt, args) #define PyString_AsString(str) PyBytes_AsString(str) #define PyString_Size(str) PyBytes_Size(str) #define PyString_InternFromString(key) PyUnicode_InternFromString(key) #define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE #define PyString_AS_STRING(x) PyUnicode_AS_STRING(x) #define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x) #endif #ifndef Py_TYPE # define Py_TYPE(op) ((op)->ob_type) #endif /* SWIG APIs for compatibility of both Python 2 & 3 */ #if PY_VERSION_HEX >= 0x03000000 # define SWIG_Python_str_FromFormat PyUnicode_FromFormat #else # define SWIG_Python_str_FromFormat PyString_FromFormat #endif /* Warning: This function will allocate a new string in Python 3, * so please call SWIG_Python_str_DelForPy3(x) to free the space. */ SWIGINTERN char* SWIG_Python_str_AsChar(PyObject *str) { #if PY_VERSION_HEX >= 0x03030000 return (char *)PyUnicode_AsUTF8(str); #elif PY_VERSION_HEX >= 0x03000000 char *newstr = 0; str = PyUnicode_AsUTF8String(str); if (str) { char *cstr; Py_ssize_t len; if (PyBytes_AsStringAndSize(str, &cstr, &len) != -1) { newstr = (char *) malloc(len+1); if (newstr) memcpy(newstr, cstr, len+1); } Py_XDECREF(str); } return newstr; #else return PyString_AsString(str); #endif } #if PY_VERSION_HEX >= 0x03030000 || PY_VERSION_HEX < 0x03000000 # define SWIG_Python_str_DelForPy3(x) #else # define SWIG_Python_str_DelForPy3(x) free( (void*) (x) ) #endif SWIGINTERN PyObject* SWIG_Python_str_FromChar(const char *c) { #if PY_VERSION_HEX >= 0x03000000 return PyUnicode_FromString(c); #else return PyString_FromString(c); #endif } #ifndef PyObject_DEL # define PyObject_DEL PyObject_Del #endif // SWIGPY_USE_CAPSULE is no longer used within SWIG itself, but some user // interface files check for it. # define SWIGPY_USE_CAPSULE # define SWIGPY_CAPSULE_NAME ("swig_runtime_data" SWIG_RUNTIME_VERSION ".type_pointer_capsule" SWIG_TYPE_TABLE_NAME) #if PY_VERSION_HEX < 0x03020000 #define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) #define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) #define Py_hash_t long #endif /* ----------------------------------------------------------------------------- * error manipulation * ----------------------------------------------------------------------------- */ SWIGRUNTIME PyObject* SWIG_Python_ErrorType(int code) { PyObject* type = 0; switch(code) { case SWIG_MemoryError: type = PyExc_MemoryError; break; case SWIG_IOError: type = PyExc_IOError; break; case SWIG_RuntimeError: type = PyExc_RuntimeError; break; case SWIG_IndexError: type = PyExc_IndexError; break; case SWIG_TypeError: type = PyExc_TypeError; break; case SWIG_DivisionByZero: type = PyExc_ZeroDivisionError; break; case SWIG_OverflowError: type = PyExc_OverflowError; break; case SWIG_SyntaxError: type = PyExc_SyntaxError; break; case SWIG_ValueError: type = PyExc_ValueError; break; case SWIG_SystemError: type = PyExc_SystemError; break; case SWIG_AttributeError: type = PyExc_AttributeError; break; default: type = PyExc_RuntimeError; } return type; } SWIGRUNTIME void SWIG_Python_AddErrorMsg(const char* mesg) { PyObject *type = 0; PyObject *value = 0; PyObject *traceback = 0; if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback); if (value) { PyObject *old_str = PyObject_Str(value); const char *tmp = SWIG_Python_str_AsChar(old_str); PyErr_Clear(); Py_XINCREF(type); if (tmp) PyErr_Format(type, "%s %s", tmp, mesg); else PyErr_Format(type, "%s", mesg); SWIG_Python_str_DelForPy3(tmp); Py_DECREF(old_str); Py_DECREF(value); } else { PyErr_SetString(PyExc_RuntimeError, mesg); } } SWIGRUNTIME int SWIG_Python_TypeErrorOccurred(PyObject *obj) { PyObject *error; if (obj) return 0; error = PyErr_Occurred(); return error && PyErr_GivenExceptionMatches(error, PyExc_TypeError); } SWIGRUNTIME void SWIG_Python_RaiseOrModifyTypeError(const char *message) { if (SWIG_Python_TypeErrorOccurred(NULL)) { /* Use existing TypeError to preserve stacktrace and enhance with given message */ PyObject *newvalue; PyObject *type = NULL, *value = NULL, *traceback = NULL; PyErr_Fetch(&type, &value, &traceback); #if PY_VERSION_HEX >= 0x03000000 newvalue = PyUnicode_FromFormat("%S\nAdditional information:\n%s", value, message); #else newvalue = PyString_FromFormat("%s\nAdditional information:\n%s", PyString_AsString(value), message); #endif Py_XDECREF(value); PyErr_Restore(type, newvalue, traceback); } else { /* Raise TypeError using given message */ PyErr_SetString(PyExc_TypeError, message); } } #if defined(SWIG_PYTHON_NO_THREADS) # if defined(SWIG_PYTHON_THREADS) # undef SWIG_PYTHON_THREADS # endif #endif #if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */ # if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL) # define SWIG_PYTHON_USE_GIL # endif # if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */ # ifndef SWIG_PYTHON_INITIALIZE_THREADS # define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads() # endif # ifdef __cplusplus /* C++ code */ class SWIG_Python_Thread_Block { bool status; PyGILState_STATE state; public: void end() { if (status) { PyGILState_Release(state); status = false;} } SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {} ~SWIG_Python_Thread_Block() { end(); } }; class SWIG_Python_Thread_Allow { bool status; PyThreadState *save; public: void end() { if (status) { PyEval_RestoreThread(save); status = false; }} SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {} ~SWIG_Python_Thread_Allow() { end(); } }; # define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block # define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end() # define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow # define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end() # else /* C code */ # define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure() # define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block) # define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread() # define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow) # endif # else /* Old thread way, not implemented, user must provide it */ # if !defined(SWIG_PYTHON_INITIALIZE_THREADS) # define SWIG_PYTHON_INITIALIZE_THREADS # endif # if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK) # define SWIG_PYTHON_THREAD_BEGIN_BLOCK # endif # if !defined(SWIG_PYTHON_THREAD_END_BLOCK) # define SWIG_PYTHON_THREAD_END_BLOCK # endif # if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW) # define SWIG_PYTHON_THREAD_BEGIN_ALLOW # endif # if !defined(SWIG_PYTHON_THREAD_END_ALLOW) # define SWIG_PYTHON_THREAD_END_ALLOW # endif # endif #else /* No thread support */ # define SWIG_PYTHON_INITIALIZE_THREADS # define SWIG_PYTHON_THREAD_BEGIN_BLOCK # define SWIG_PYTHON_THREAD_END_BLOCK # define SWIG_PYTHON_THREAD_BEGIN_ALLOW # define SWIG_PYTHON_THREAD_END_ALLOW #endif /* ----------------------------------------------------------------------------- * Python API portion that goes into the runtime * ----------------------------------------------------------------------------- */ #ifdef __cplusplus extern "C" { #endif /* ----------------------------------------------------------------------------- * Constant declarations * ----------------------------------------------------------------------------- */ /* Constant Types */ #define SWIG_PY_POINTER 4 #define SWIG_PY_BINARY 5 /* Constant information structure */ typedef struct swig_const_info { int type; const char *name; long lvalue; double dvalue; void *pvalue; swig_type_info **ptype; } swig_const_info; #ifdef __cplusplus } #endif /* ----------------------------------------------------------------------------- * pyrun.swg * * This file contains the runtime support for Python modules * and includes code for managing global variables and pointer * type checking. * * ----------------------------------------------------------------------------- */ #if PY_VERSION_HEX < 0x02070000 /* 2.7.0 */ # error "This version of SWIG only supports Python >= 2.7" #endif #if PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03020000 # error "This version of SWIG only supports Python 3 >= 3.2" #endif /* Common SWIG API */ /* for raw pointers */ #define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0) #define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags) #define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own) #ifdef SWIGPYTHON_BUILTIN #define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(self, ptr, type, flags) #else #define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) #endif #define SWIG_InternalNewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) #define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty) #define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src) #define swig_owntype int /* for raw packed data */ #define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) #define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) /* for class or struct pointers */ #define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags) #define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags) /* for C or C++ function pointers */ #define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type) #define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(NULL, ptr, type, 0) /* for C++ member pointers, ie, member methods */ #define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) #define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) /* Runtime API */ #define SWIG_GetModule(clientdata) SWIG_Python_GetModule(clientdata) #define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer) #define SWIG_NewClientData(obj) SwigPyClientData_New(obj) #define SWIG_SetErrorObj SWIG_Python_SetErrorObj #define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg #define SWIG_ErrorType(code) SWIG_Python_ErrorType(code) #define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg) #define SWIG_fail goto fail /* Runtime API implementation */ /* Error manipulation */ SWIGINTERN void SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) { SWIG_PYTHON_THREAD_BEGIN_BLOCK; PyErr_SetObject(errtype, obj); Py_DECREF(obj); SWIG_PYTHON_THREAD_END_BLOCK; } SWIGINTERN void SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) { SWIG_PYTHON_THREAD_BEGIN_BLOCK; PyErr_SetString(errtype, msg); SWIG_PYTHON_THREAD_END_BLOCK; } #define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj) /* Set a constant value */ #if defined(SWIGPYTHON_BUILTIN) SWIGINTERN void SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) { PyObject *s = PyString_InternFromString(key); PyList_Append(seq, s); Py_DECREF(s); } SWIGINTERN void SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) { PyDict_SetItemString(d, name, obj); Py_DECREF(obj); if (public_interface) SwigPyBuiltin_AddPublicSymbol(public_interface, name); } #else SWIGINTERN void SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) { PyDict_SetItemString(d, name, obj); Py_DECREF(obj); } #endif /* Append a value to the result obj */ SWIGINTERN PyObject* SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) { if (!result) { result = obj; } else if (result == Py_None) { Py_DECREF(result); result = obj; } else { if (!PyList_Check(result)) { PyObject *o2 = result; result = PyList_New(1); PyList_SetItem(result, 0, o2); } PyList_Append(result,obj); Py_DECREF(obj); } return result; } /* Unpack the argument tuple */ SWIGINTERN Py_ssize_t SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs) { if (!args) { if (!min && !max) { return 1; } else { PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none", name, (min == max ? "" : "at least "), (int)min); return 0; } } if (!PyTuple_Check(args)) { if (min <= 1 && max >= 1) { Py_ssize_t i; objs[0] = args; for (i = 1; i < max; ++i) { objs[i] = 0; } return 2; } PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple"); return 0; } else { Py_ssize_t l = PyTuple_GET_SIZE(args); if (l < min) { PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", name, (min == max ? "" : "at least "), (int)min, (int)l); return 0; } else if (l > max) { PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", name, (min == max ? "" : "at most "), (int)max, (int)l); return 0; } else { Py_ssize_t i; for (i = 0; i < l; ++i) { objs[i] = PyTuple_GET_ITEM(args, i); } for (; l < max; ++l) { objs[l] = 0; } return i + 1; } } } SWIGINTERN int SWIG_Python_CheckNoKeywords(PyObject *kwargs, const char *name) { int no_kwargs = 1; if (kwargs) { assert(PyDict_Check(kwargs)); if (PyDict_Size(kwargs) > 0) { PyErr_Format(PyExc_TypeError, "%s() does not take keyword arguments", name); no_kwargs = 0; } } return no_kwargs; } /* A functor is a function object with one single object argument */ #define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL); /* Helper for static pointer initialization for both C and C++ code, for example static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...); */ #ifdef __cplusplus #define SWIG_STATIC_POINTER(var) var #else #define SWIG_STATIC_POINTER(var) var = 0; if (!var) var #endif /* ----------------------------------------------------------------------------- * Pointer declarations * ----------------------------------------------------------------------------- */ /* Flags for new pointer objects */ #define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1) #define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN) #define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1) #define SWIG_BUILTIN_TP_INIT (SWIG_POINTER_OWN << 2) #define SWIG_BUILTIN_INIT (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN) #ifdef __cplusplus extern "C" { #endif /* The python void return value */ SWIGRUNTIMEINLINE PyObject * SWIG_Py_Void(void) { PyObject *none = Py_None; Py_INCREF(none); return none; } /* SwigPyClientData */ typedef struct { PyObject *klass; PyObject *newraw; PyObject *newargs; PyObject *destroy; int delargs; int implicitconv; PyTypeObject *pytype; } SwigPyClientData; SWIGRUNTIMEINLINE int SWIG_Python_CheckImplicit(swig_type_info *ty) { SwigPyClientData *data = (SwigPyClientData *)ty->clientdata; int fail = data ? data->implicitconv : 0; if (fail) PyErr_SetString(PyExc_TypeError, "Implicit conversion is prohibited for explicit constructors."); return fail; } SWIGRUNTIMEINLINE PyObject * SWIG_Python_ExceptionType(swig_type_info *desc) { SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0; PyObject *klass = data ? data->klass : 0; return (klass ? klass : PyExc_RuntimeError); } SWIGRUNTIME SwigPyClientData * SwigPyClientData_New(PyObject* obj) { if (!obj) { return 0; } else { SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData)); /* the klass element */ data->klass = obj; Py_INCREF(data->klass); /* the newraw method and newargs arguments used to create a new raw instance */ if (PyClass_Check(obj)) { data->newraw = 0; data->newargs = obj; Py_INCREF(obj); } else { data->newraw = PyObject_GetAttrString(data->klass, "__new__"); if (data->newraw) { Py_INCREF(data->newraw); data->newargs = PyTuple_New(1); PyTuple_SetItem(data->newargs, 0, obj); } else { data->newargs = obj; } Py_INCREF(data->newargs); } /* the destroy method, aka as the C++ delete method */ data->destroy = PyObject_GetAttrString(data->klass, "__swig_destroy__"); if (PyErr_Occurred()) { PyErr_Clear(); data->destroy = 0; } if (data->destroy) { int flags; Py_INCREF(data->destroy); flags = PyCFunction_GET_FLAGS(data->destroy); data->delargs = !(flags & (METH_O)); } else { data->delargs = 0; } data->implicitconv = 0; data->pytype = 0; return data; } } SWIGRUNTIME void SwigPyClientData_Del(SwigPyClientData *data) { Py_XDECREF(data->newraw); Py_XDECREF(data->newargs); Py_XDECREF(data->destroy); } /* =============== SwigPyObject =====================*/ typedef struct { PyObject_HEAD void *ptr; swig_type_info *ty; int own; PyObject *next; #ifdef SWIGPYTHON_BUILTIN PyObject *dict; #endif } SwigPyObject; #ifdef SWIGPYTHON_BUILTIN SWIGRUNTIME PyObject * SwigPyObject_get___dict__(PyObject *v, PyObject *SWIGUNUSEDPARM(args)) { SwigPyObject *sobj = (SwigPyObject *)v; if (!sobj->dict) sobj->dict = PyDict_New(); Py_INCREF(sobj->dict); return sobj->dict; } #endif SWIGRUNTIME PyObject * SwigPyObject_long(SwigPyObject *v) { return PyLong_FromVoidPtr(v->ptr); } SWIGRUNTIME PyObject * SwigPyObject_format(const char* fmt, SwigPyObject *v) { PyObject *res = NULL; PyObject *args = PyTuple_New(1); if (args) { if (PyTuple_SetItem(args, 0, SwigPyObject_long(v)) == 0) { PyObject *ofmt = SWIG_Python_str_FromChar(fmt); if (ofmt) { #if PY_VERSION_HEX >= 0x03000000 res = PyUnicode_Format(ofmt,args); #else res = PyString_Format(ofmt,args); #endif Py_DECREF(ofmt); } Py_DECREF(args); } } return res; } SWIGRUNTIME PyObject * SwigPyObject_oct(SwigPyObject *v) { return SwigPyObject_format("%o",v); } SWIGRUNTIME PyObject * SwigPyObject_hex(SwigPyObject *v) { return SwigPyObject_format("%x",v); } SWIGRUNTIME PyObject * SwigPyObject_repr(SwigPyObject *v) { const char *name = SWIG_TypePrettyName(v->ty); PyObject *repr = SWIG_Python_str_FromFormat("", (name ? name : "unknown"), (void *)v); if (v->next) { PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next); # if PY_VERSION_HEX >= 0x03000000 PyObject *joined = PyUnicode_Concat(repr, nrep); Py_DecRef(repr); Py_DecRef(nrep); repr = joined; # else PyString_ConcatAndDel(&repr,nrep); # endif } return repr; } /* We need a version taking two PyObject* parameters so it's a valid * PyCFunction to use in swigobject_methods[]. */ SWIGRUNTIME PyObject * SwigPyObject_repr2(PyObject *v, PyObject *SWIGUNUSEDPARM(args)) { return SwigPyObject_repr((SwigPyObject*)v); } SWIGRUNTIME int SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w) { void *i = v->ptr; void *j = w->ptr; return (i < j) ? -1 : ((i > j) ? 1 : 0); } /* Added for Python 3.x, would it also be useful for Python 2.x? */ SWIGRUNTIME PyObject* SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op) { PyObject* res; if( op != Py_EQ && op != Py_NE ) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0); return res; } SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void); #ifdef SWIGPYTHON_BUILTIN static swig_type_info *SwigPyObject_stype = 0; SWIGRUNTIME PyTypeObject* SwigPyObject_type(void) { SwigPyClientData *cd; assert(SwigPyObject_stype); cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; assert(cd); assert(cd->pytype); return cd->pytype; } #else SWIGRUNTIME PyTypeObject* SwigPyObject_type(void) { static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce(); return type; } #endif SWIGRUNTIMEINLINE int SwigPyObject_Check(PyObject *op) { #ifdef SWIGPYTHON_BUILTIN PyTypeObject *target_tp = SwigPyObject_type(); if (PyType_IsSubtype(op->ob_type, target_tp)) return 1; return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0); #else return (Py_TYPE(op) == SwigPyObject_type()) || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0); #endif } SWIGRUNTIME PyObject * SwigPyObject_New(void *ptr, swig_type_info *ty, int own); SWIGRUNTIME void SwigPyObject_dealloc(PyObject *v) { SwigPyObject *sobj = (SwigPyObject *) v; PyObject *next = sobj->next; if (sobj->own == SWIG_POINTER_OWN) { swig_type_info *ty = sobj->ty; SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; PyObject *destroy = data ? data->destroy : 0; if (destroy) { /* destroy is always a VARARGS method */ PyObject *res; /* PyObject_CallFunction() has the potential to silently drop the active exception. In cases of unnamed temporary variable or where we just finished iterating over a generator StopIteration will be active right now, and this needs to remain true upon return from SwigPyObject_dealloc. So save and restore. */ PyObject *type = NULL, *value = NULL, *traceback = NULL; PyErr_Fetch(&type, &value, &traceback); if (data->delargs) { /* we need to create a temporary object to carry the destroy operation */ PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); res = SWIG_Python_CallFunctor(destroy, tmp); Py_DECREF(tmp); } else { PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); PyObject *mself = PyCFunction_GET_SELF(destroy); res = ((*meth)(mself, v)); } if (!res) PyErr_WriteUnraisable(destroy); PyErr_Restore(type, value, traceback); Py_XDECREF(res); } #if !defined(SWIG_PYTHON_SILENT_MEMLEAK) else { const char *name = SWIG_TypePrettyName(ty); printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown")); } #endif } Py_XDECREF(next); PyObject_DEL(v); } SWIGRUNTIME PyObject* SwigPyObject_append(PyObject* v, PyObject* next) { SwigPyObject *sobj = (SwigPyObject *) v; if (!SwigPyObject_Check(next)) { PyErr_SetString(PyExc_TypeError, "Attempt to append a non SwigPyObject"); return NULL; } sobj->next = next; Py_INCREF(next); return SWIG_Py_Void(); } SWIGRUNTIME PyObject* SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) { SwigPyObject *sobj = (SwigPyObject *) v; if (sobj->next) { Py_INCREF(sobj->next); return sobj->next; } else { return SWIG_Py_Void(); } } SWIGINTERN PyObject* SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) { SwigPyObject *sobj = (SwigPyObject *)v; sobj->own = 0; return SWIG_Py_Void(); } SWIGINTERN PyObject* SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) { SwigPyObject *sobj = (SwigPyObject *)v; sobj->own = SWIG_POINTER_OWN; return SWIG_Py_Void(); } SWIGINTERN PyObject* SwigPyObject_own(PyObject *v, PyObject *args) { PyObject *val = 0; if (!PyArg_UnpackTuple(args, "own", 0, 1, &val)) { return NULL; } else { SwigPyObject *sobj = (SwigPyObject *)v; PyObject *obj = PyBool_FromLong(sobj->own); if (val) { if (PyObject_IsTrue(val)) { SwigPyObject_acquire(v,args); } else { SwigPyObject_disown(v,args); } } return obj; } } static PyMethodDef swigobject_methods[] = { {"disown", SwigPyObject_disown, METH_NOARGS, "releases ownership of the pointer"}, {"acquire", SwigPyObject_acquire, METH_NOARGS, "acquires ownership of the pointer"}, {"own", SwigPyObject_own, METH_VARARGS, "returns/sets ownership of the pointer"}, {"append", SwigPyObject_append, METH_O, "appends another 'this' object"}, {"next", SwigPyObject_next, METH_NOARGS, "returns the next 'this' object"}, {"__repr__",SwigPyObject_repr2, METH_NOARGS, "returns object representation"}, {0, 0, 0, 0} }; SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void) { static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer"; static PyNumberMethods SwigPyObject_as_number = { (binaryfunc)0, /*nb_add*/ (binaryfunc)0, /*nb_subtract*/ (binaryfunc)0, /*nb_multiply*/ /* nb_divide removed in Python 3 */ #if PY_VERSION_HEX < 0x03000000 (binaryfunc)0, /*nb_divide*/ #endif (binaryfunc)0, /*nb_remainder*/ (binaryfunc)0, /*nb_divmod*/ (ternaryfunc)0,/*nb_power*/ (unaryfunc)0, /*nb_negative*/ (unaryfunc)0, /*nb_positive*/ (unaryfunc)0, /*nb_absolute*/ (inquiry)0, /*nb_nonzero*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ #if PY_VERSION_HEX < 0x03000000 0, /*nb_coerce*/ #endif (unaryfunc)SwigPyObject_long, /*nb_int*/ #if PY_VERSION_HEX < 0x03000000 (unaryfunc)SwigPyObject_long, /*nb_long*/ #else 0, /*nb_reserved*/ #endif (unaryfunc)0, /*nb_float*/ #if PY_VERSION_HEX < 0x03000000 (unaryfunc)SwigPyObject_oct, /*nb_oct*/ (unaryfunc)SwigPyObject_hex, /*nb_hex*/ #endif #if PY_VERSION_HEX >= 0x03050000 /* 3.5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_matrix_multiply */ #elif PY_VERSION_HEX >= 0x03000000 /* 3.0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */ #else 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */ #endif }; static PyTypeObject swigpyobject_type; static int type_init = 0; if (!type_init) { const PyTypeObject tmp = { #if PY_VERSION_HEX >= 0x03000000 PyVarObject_HEAD_INIT(NULL, 0) #else PyObject_HEAD_INIT(NULL) 0, /* ob_size */ #endif "SwigPyObject", /* tp_name */ sizeof(SwigPyObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)SwigPyObject_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ #if PY_VERSION_HEX >= 0x03000000 0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */ #else (cmpfunc)SwigPyObject_compare, /* tp_compare */ #endif (reprfunc)SwigPyObject_repr, /* tp_repr */ &SwigPyObject_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)0, /* tp_hash */ (ternaryfunc)0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ swigobject_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ swigobject_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ 0, /* tp_version_tag */ #if PY_VERSION_HEX >= 0x03040000 0, /* tp_finalize */ #endif #if PY_VERSION_HEX >= 0x03080000 0, /* tp_vectorcall */ #endif #if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000) 0, /* tp_print */ #endif #ifdef COUNT_ALLOCS 0, /* tp_allocs */ 0, /* tp_frees */ 0, /* tp_maxalloc */ 0, /* tp_prev */ 0 /* tp_next */ #endif }; swigpyobject_type = tmp; type_init = 1; if (PyType_Ready(&swigpyobject_type) < 0) return NULL; } return &swigpyobject_type; } SWIGRUNTIME PyObject * SwigPyObject_New(void *ptr, swig_type_info *ty, int own) { SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type()); if (sobj) { sobj->ptr = ptr; sobj->ty = ty; sobj->own = own; sobj->next = 0; } return (PyObject *)sobj; } /* ----------------------------------------------------------------------------- * Implements a simple Swig Packed type, and use it instead of string * ----------------------------------------------------------------------------- */ typedef struct { PyObject_HEAD void *pack; swig_type_info *ty; size_t size; } SwigPyPacked; SWIGRUNTIME PyObject * SwigPyPacked_repr(SwigPyPacked *v) { char result[SWIG_BUFFER_SIZE]; if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) { return SWIG_Python_str_FromFormat("", result, v->ty->name); } else { return SWIG_Python_str_FromFormat("", v->ty->name); } } SWIGRUNTIME PyObject * SwigPyPacked_str(SwigPyPacked *v) { char result[SWIG_BUFFER_SIZE]; if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){ return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name); } else { return SWIG_Python_str_FromChar(v->ty->name); } } SWIGRUNTIME int SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w) { size_t i = v->size; size_t j = w->size; int s = (i < j) ? -1 : ((i > j) ? 1 : 0); return s ? s : strncmp((const char *)v->pack, (const char *)w->pack, 2*v->size); } SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void); SWIGRUNTIME PyTypeObject* SwigPyPacked_type(void) { static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce(); return type; } SWIGRUNTIMEINLINE int SwigPyPacked_Check(PyObject *op) { return ((op)->ob_type == SwigPyPacked_TypeOnce()) || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0); } SWIGRUNTIME void SwigPyPacked_dealloc(PyObject *v) { if (SwigPyPacked_Check(v)) { SwigPyPacked *sobj = (SwigPyPacked *) v; free(sobj->pack); } PyObject_DEL(v); } SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void) { static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer"; static PyTypeObject swigpypacked_type; static int type_init = 0; if (!type_init) { const PyTypeObject tmp = { #if PY_VERSION_HEX>=0x03000000 PyVarObject_HEAD_INIT(NULL, 0) #else PyObject_HEAD_INIT(NULL) 0, /* ob_size */ #endif "SwigPyPacked", /* tp_name */ sizeof(SwigPyPacked), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)SwigPyPacked_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ #if PY_VERSION_HEX>=0x03000000 0, /* tp_reserved in 3.0.1 */ #else (cmpfunc)SwigPyPacked_compare, /* tp_compare */ #endif (reprfunc)SwigPyPacked_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)0, /* tp_hash */ (ternaryfunc)0, /* tp_call */ (reprfunc)SwigPyPacked_str, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ swigpacked_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ 0, /* tp_version_tag */ #if PY_VERSION_HEX >= 0x03040000 0, /* tp_finalize */ #endif #if PY_VERSION_HEX >= 0x03080000 0, /* tp_vectorcall */ #endif #if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000) 0, /* tp_print */ #endif #ifdef COUNT_ALLOCS 0, /* tp_allocs */ 0, /* tp_frees */ 0, /* tp_maxalloc */ 0, /* tp_prev */ 0 /* tp_next */ #endif }; swigpypacked_type = tmp; type_init = 1; if (PyType_Ready(&swigpypacked_type) < 0) return NULL; } return &swigpypacked_type; } SWIGRUNTIME PyObject * SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty) { SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type()); if (sobj) { void *pack = malloc(size); if (pack) { memcpy(pack, ptr, size); sobj->pack = pack; sobj->ty = ty; sobj->size = size; } else { PyObject_DEL((PyObject *) sobj); sobj = 0; } } return (PyObject *) sobj; } SWIGRUNTIME swig_type_info * SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size) { if (SwigPyPacked_Check(obj)) { SwigPyPacked *sobj = (SwigPyPacked *)obj; if (sobj->size != size) return 0; memcpy(ptr, sobj->pack, size); return sobj->ty; } else { return 0; } } /* ----------------------------------------------------------------------------- * pointers/data manipulation * ----------------------------------------------------------------------------- */ static PyObject *Swig_This_global = NULL; SWIGRUNTIME PyObject * SWIG_This(void) { if (Swig_This_global == NULL) Swig_This_global = SWIG_Python_str_FromChar("this"); return Swig_This_global; } /* #define SWIG_PYTHON_SLOW_GETSET_THIS */ /* TODO: I don't know how to implement the fast getset in Python 3 right now */ #if PY_VERSION_HEX>=0x03000000 #define SWIG_PYTHON_SLOW_GETSET_THIS #endif SWIGRUNTIME SwigPyObject * SWIG_Python_GetSwigThis(PyObject *pyobj) { PyObject *obj; if (SwigPyObject_Check(pyobj)) return (SwigPyObject *) pyobj; #ifdef SWIGPYTHON_BUILTIN (void)obj; # ifdef PyWeakref_CheckProxy if (PyWeakref_CheckProxy(pyobj)) { pyobj = PyWeakref_GET_OBJECT(pyobj); if (pyobj && SwigPyObject_Check(pyobj)) return (SwigPyObject*) pyobj; } # endif return NULL; #else obj = 0; #if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) if (PyInstance_Check(pyobj)) { obj = _PyInstance_Lookup(pyobj, SWIG_This()); } else { PyObject **dictptr = _PyObject_GetDictPtr(pyobj); if (dictptr != NULL) { PyObject *dict = *dictptr; obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0; } else { #ifdef PyWeakref_CheckProxy if (PyWeakref_CheckProxy(pyobj)) { PyObject *wobj = PyWeakref_GET_OBJECT(pyobj); return wobj ? SWIG_Python_GetSwigThis(wobj) : 0; } #endif obj = PyObject_GetAttr(pyobj,SWIG_This()); if (obj) { Py_DECREF(obj); } else { if (PyErr_Occurred()) PyErr_Clear(); return 0; } } } #else obj = PyObject_GetAttr(pyobj,SWIG_This()); if (obj) { Py_DECREF(obj); } else { if (PyErr_Occurred()) PyErr_Clear(); return 0; } #endif if (obj && !SwigPyObject_Check(obj)) { /* a PyObject is called 'this', try to get the 'real this' SwigPyObject from it */ return SWIG_Python_GetSwigThis(obj); } return (SwigPyObject *)obj; #endif } /* Acquire a pointer value */ SWIGRUNTIME int SWIG_Python_AcquirePtr(PyObject *obj, int own) { if (own == SWIG_POINTER_OWN) { SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj); if (sobj) { int oldown = sobj->own; sobj->own = own; return oldown; } } return 0; } /* Convert a pointer value */ SWIGRUNTIME int SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) { int res; SwigPyObject *sobj; int implicit_conv = (flags & SWIG_POINTER_IMPLICIT_CONV) != 0; if (!obj) return SWIG_ERROR; if (obj == Py_None && !implicit_conv) { if (ptr) *ptr = 0; return (flags & SWIG_POINTER_NO_NULL) ? SWIG_NullReferenceError : SWIG_OK; } res = SWIG_ERROR; sobj = SWIG_Python_GetSwigThis(obj); if (own) *own = 0; while (sobj) { void *vptr = sobj->ptr; if (ty) { swig_type_info *to = sobj->ty; if (to == ty) { /* no type cast needed */ if (ptr) *ptr = vptr; break; } else { swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); if (!tc) { sobj = (SwigPyObject *)sobj->next; } else { if (ptr) { int newmemory = 0; *ptr = SWIG_TypeCast(tc,vptr,&newmemory); if (newmemory == SWIG_CAST_NEW_MEMORY) { assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */ if (own) *own = *own | SWIG_CAST_NEW_MEMORY; } } break; } } } else { if (ptr) *ptr = vptr; break; } } if (sobj) { if (own) *own = *own | sobj->own; if (flags & SWIG_POINTER_DISOWN) { sobj->own = 0; } res = SWIG_OK; } else { if (implicit_conv) { SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; if (data && !data->implicitconv) { PyObject *klass = data->klass; if (klass) { PyObject *impconv; data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/ impconv = SWIG_Python_CallFunctor(klass, obj); data->implicitconv = 0; if (PyErr_Occurred()) { PyErr_Clear(); impconv = 0; } if (impconv) { SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv); if (iobj) { void *vptr; res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0); if (SWIG_IsOK(res)) { if (ptr) { *ptr = vptr; /* transfer the ownership to 'ptr' */ iobj->own = 0; res = SWIG_AddCast(res); res = SWIG_AddNewMask(res); } else { res = SWIG_AddCast(res); } } } Py_DECREF(impconv); } } } if (!SWIG_IsOK(res) && obj == Py_None) { if (ptr) *ptr = 0; if (PyErr_Occurred()) PyErr_Clear(); res = SWIG_OK; } } } return res; } /* Convert a function ptr value */ SWIGRUNTIME int SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) { if (!PyCFunction_Check(obj)) { return SWIG_ConvertPtr(obj, ptr, ty, 0); } else { void *vptr = 0; swig_cast_info *tc; /* here we get the method pointer for callbacks */ const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc); const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0; if (desc) desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0; if (!desc) return SWIG_ERROR; tc = SWIG_TypeCheck(desc,ty); if (tc) { int newmemory = 0; *ptr = SWIG_TypeCast(tc,vptr,&newmemory); assert(!newmemory); /* newmemory handling not yet implemented */ } else { return SWIG_ERROR; } return SWIG_OK; } } /* Convert a packed pointer value */ SWIGRUNTIME int SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) { swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz); if (!to) return SWIG_ERROR; if (ty) { if (to != ty) { /* check type cast? */ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); if (!tc) return SWIG_ERROR; } } return SWIG_OK; } /* ----------------------------------------------------------------------------- * Create a new pointer object * ----------------------------------------------------------------------------- */ /* Create a new instance object, without calling __init__, and set the 'this' attribute. */ SWIGRUNTIME PyObject* SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this) { PyObject *inst = 0; PyObject *newraw = data->newraw; if (newraw) { inst = PyObject_Call(newraw, data->newargs, NULL); if (inst) { #if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) PyObject **dictptr = _PyObject_GetDictPtr(inst); if (dictptr != NULL) { PyObject *dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); *dictptr = dict; PyDict_SetItem(dict, SWIG_This(), swig_this); } } #else if (PyObject_SetAttr(inst, SWIG_This(), swig_this) == -1) { Py_DECREF(inst); inst = 0; } #endif } } else { #if PY_VERSION_HEX >= 0x03000000 PyObject *empty_args = PyTuple_New(0); if (empty_args) { PyObject *empty_kwargs = PyDict_New(); if (empty_kwargs) { inst = ((PyTypeObject *)data->newargs)->tp_new((PyTypeObject *)data->newargs, empty_args, empty_kwargs); Py_DECREF(empty_kwargs); if (inst) { if (PyObject_SetAttr(inst, SWIG_This(), swig_this) == -1) { Py_DECREF(inst); inst = 0; } else { Py_TYPE(inst)->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; } } } Py_DECREF(empty_args); } #else PyObject *dict = PyDict_New(); if (dict) { PyDict_SetItem(dict, SWIG_This(), swig_this); inst = PyInstance_NewRaw(data->newargs, dict); Py_DECREF(dict); } #endif } return inst; } SWIGRUNTIME int SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this) { #if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) PyObject **dictptr = _PyObject_GetDictPtr(inst); if (dictptr != NULL) { PyObject *dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); *dictptr = dict; } return PyDict_SetItem(dict, SWIG_This(), swig_this); } #endif return PyObject_SetAttr(inst, SWIG_This(), swig_this); } SWIGINTERN PyObject * SWIG_Python_InitShadowInstance(PyObject *args) { PyObject *obj[2]; if (!SWIG_Python_UnpackTuple(args, "swiginit", 2, 2, obj)) { return NULL; } else { SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]); if (sthis) { SwigPyObject_append((PyObject*) sthis, obj[1]); } else { if (SWIG_Python_SetSwigThis(obj[0], obj[1]) != 0) return NULL; } return SWIG_Py_Void(); } } /* Create a new pointer object */ SWIGRUNTIME PyObject * SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) { SwigPyClientData *clientdata; PyObject * robj; int own; if (!ptr) return SWIG_Py_Void(); clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0; own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0; if (clientdata && clientdata->pytype) { SwigPyObject *newobj; if (flags & SWIG_BUILTIN_TP_INIT) { newobj = (SwigPyObject*) self; if (newobj->ptr) { PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0); while (newobj->next) newobj = (SwigPyObject *) newobj->next; newobj->next = next_self; newobj = (SwigPyObject *)next_self; #ifdef SWIGPYTHON_BUILTIN newobj->dict = 0; #endif } } else { newobj = PyObject_New(SwigPyObject, clientdata->pytype); #ifdef SWIGPYTHON_BUILTIN newobj->dict = 0; #endif } if (newobj) { newobj->ptr = ptr; newobj->ty = type; newobj->own = own; newobj->next = 0; return (PyObject*) newobj; } return SWIG_Py_Void(); } assert(!(flags & SWIG_BUILTIN_TP_INIT)); robj = SwigPyObject_New(ptr, type, own); if (robj && clientdata && !(flags & SWIG_POINTER_NOSHADOW)) { PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj); Py_DECREF(robj); robj = inst; } return robj; } /* Create a new packed object */ SWIGRUNTIMEINLINE PyObject * SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) { return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void(); } /* -----------------------------------------------------------------------------* * Get type list * -----------------------------------------------------------------------------*/ #ifdef SWIG_LINK_RUNTIME void *SWIG_ReturnGlobalTypeList(void *); #endif SWIGRUNTIME swig_module_info * SWIG_Python_GetModule(void *SWIGUNUSEDPARM(clientdata)) { static void *type_pointer = (void *)0; /* first check if module already created */ if (!type_pointer) { #ifdef SWIG_LINK_RUNTIME type_pointer = SWIG_ReturnGlobalTypeList((void *)0); #else type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0); if (PyErr_Occurred()) { PyErr_Clear(); type_pointer = (void *)0; } #endif } return (swig_module_info *) type_pointer; } SWIGRUNTIME void SWIG_Python_DestroyModule(PyObject *obj) { swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME); swig_type_info **types = swig_module->types; size_t i; for (i =0; i < swig_module->size; ++i) { swig_type_info *ty = types[i]; if (ty->owndata) { SwigPyClientData *data = (SwigPyClientData *) ty->clientdata; if (data) SwigPyClientData_Del(data); } } Py_DECREF(SWIG_This()); Swig_This_global = NULL; } SWIGRUNTIME void SWIG_Python_SetModule(swig_module_info *swig_module) { #if PY_VERSION_HEX >= 0x03000000 /* Add a dummy module object into sys.modules */ PyObject *module = PyImport_AddModule("swig_runtime_data" SWIG_RUNTIME_VERSION); #else static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */ PyObject *module = Py_InitModule("swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table); #endif PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule); if (pointer && module) { PyModule_AddObject(module, "type_pointer_capsule" SWIG_TYPE_TABLE_NAME, pointer); } else { Py_XDECREF(pointer); } } /* The python cached type query */ SWIGRUNTIME PyObject * SWIG_Python_TypeCache(void) { static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New(); return cache; } SWIGRUNTIME swig_type_info * SWIG_Python_TypeQuery(const char *type) { PyObject *cache = SWIG_Python_TypeCache(); PyObject *key = SWIG_Python_str_FromChar(type); PyObject *obj = PyDict_GetItem(cache, key); swig_type_info *descriptor; if (obj) { descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL); } else { swig_module_info *swig_module = SWIG_GetModule(0); descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type); if (descriptor) { obj = PyCapsule_New((void*) descriptor, NULL, NULL); PyDict_SetItem(cache, key, obj); Py_DECREF(obj); } } Py_DECREF(key); return descriptor; } /* For backward compatibility only */ #define SWIG_POINTER_EXCEPTION 0 #define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg) #define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags) SWIGRUNTIME int SWIG_Python_AddErrMesg(const char* mesg, int infront) { if (PyErr_Occurred()) { PyObject *type = 0; PyObject *value = 0; PyObject *traceback = 0; PyErr_Fetch(&type, &value, &traceback); if (value) { PyObject *old_str = PyObject_Str(value); const char *tmp = SWIG_Python_str_AsChar(old_str); const char *errmesg = tmp ? tmp : "Invalid error message"; Py_XINCREF(type); PyErr_Clear(); if (infront) { PyErr_Format(type, "%s %s", mesg, errmesg); } else { PyErr_Format(type, "%s %s", errmesg, mesg); } SWIG_Python_str_DelForPy3(tmp); Py_DECREF(old_str); } return 1; } else { return 0; } } SWIGRUNTIME int SWIG_Python_ArgFail(int argnum) { if (PyErr_Occurred()) { /* add information about failing argument */ char mesg[256]; PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum); return SWIG_Python_AddErrMesg(mesg, 1); } else { return 0; } } SWIGRUNTIMEINLINE const char * SwigPyObject_GetDesc(PyObject *self) { SwigPyObject *v = (SwigPyObject *)self; swig_type_info *ty = v ? v->ty : 0; return ty ? ty->str : ""; } SWIGRUNTIME void SWIG_Python_TypeError(const char *type, PyObject *obj) { if (type) { #if defined(SWIG_COBJECT_TYPES) if (obj && SwigPyObject_Check(obj)) { const char *otype = (const char *) SwigPyObject_GetDesc(obj); if (otype) { PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received", type, otype); return; } } else #endif { const char *otype = (obj ? obj->ob_type->tp_name : 0); if (otype) { PyObject *str = PyObject_Str(obj); const char *cstr = str ? SWIG_Python_str_AsChar(str) : 0; if (cstr) { PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received", type, otype, cstr); SWIG_Python_str_DelForPy3(cstr); } else { PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received", type, otype); } Py_XDECREF(str); return; } } PyErr_Format(PyExc_TypeError, "a '%s' is expected", type); } else { PyErr_Format(PyExc_TypeError, "unexpected type is received"); } } /* Convert a pointer value, signal an exception on a type mismatch */ SWIGRUNTIME void * SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) { void *result; if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) { PyErr_Clear(); #if SWIG_POINTER_EXCEPTION if (flags) { SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); SWIG_Python_ArgFail(argnum); } #endif } return result; } #ifdef SWIGPYTHON_BUILTIN SWIGRUNTIME int SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) { PyTypeObject *tp = obj->ob_type; PyObject *descr; PyObject *encoded_name; descrsetfunc f; int res = -1; # ifdef Py_USING_UNICODE if (PyString_Check(name)) { name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL); if (!name) return -1; } else if (!PyUnicode_Check(name)) # else if (!PyString_Check(name)) # endif { PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name); return -1; } else { Py_INCREF(name); } if (!tp->tp_dict) { if (PyType_Ready(tp) < 0) goto done; } descr = _PyType_Lookup(tp, name); f = NULL; if (descr != NULL) f = descr->ob_type->tp_descr_set; if (!f) { if (PyString_Check(name)) { encoded_name = name; Py_INCREF(name); } else { encoded_name = PyUnicode_AsUTF8String(name); if (!encoded_name) return -1; } PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name)); Py_DECREF(encoded_name); } else { res = f(descr, obj, value); } done: Py_DECREF(name); return res; } #endif #ifdef __cplusplus } #endif #define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) #define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else #ifdef __cplusplus extern "C" { #endif /* Method creation and docstring support functions */ SWIGINTERN PyMethodDef *SWIG_PythonGetProxyDoc(const char *name); SWIGINTERN PyObject *SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func); SWIGINTERN PyObject *SWIG_PyStaticMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func); #ifdef __cplusplus } #endif /* -------- TYPES TABLE (BEGIN) -------- */ #define SWIGTYPE_p_char swig_types[0] #define SWIGTYPE_p_chmFile swig_types[1] #define SWIGTYPE_p_chmUnitInfo swig_types[2] #define SWIGTYPE_p_f_p_struct_chmFile_p_struct_chmUnitInfo_p_void__int swig_types[3] #define SWIGTYPE_p_long_long swig_types[4] #define SWIGTYPE_p_unsigned_char swig_types[5] #define SWIGTYPE_p_unsigned_long_long swig_types[6] static swig_type_info *swig_types[8]; static swig_module_info swig_module = {swig_types, 7, 0, 0, 0, 0}; #define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) #define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) /* -------- TYPES TABLE (END) -------- */ #ifdef SWIG_TypeQuery # undef SWIG_TypeQuery #endif #define SWIG_TypeQuery SWIG_Python_TypeQuery /*----------------------------------------------- @(target):= _chmlib.so ------------------------------------------------*/ #if PY_VERSION_HEX >= 0x03000000 # define SWIG_init PyInit__chmlib #else # define SWIG_init init_chmlib #endif #define SWIG_name "_chmlib" #define SWIGVERSION 0x040002 #define SWIG_VERSION SWIGVERSION #define SWIG_as_voidptr(a) (void *)((const void *)(a)) #define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a)) /* Copyright (C) 2003 Rubens Ramos Based on code by: Copyright (C) 2003 Razvan Cojocaru pychm is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA $Id$ */ #include "chm_lib.h" #include static PyObject *my_callback = NULL; static PyObject * my_set_callback(PyObject *dummy, PyObject *arg) { PyObject *result = NULL; if (!PyCallable_Check(arg)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } Py_XINCREF(arg); /* Add a reference to new callback */ Py_XDECREF(my_callback); /* Dispose of previous callback */ my_callback = arg; /* Remember new callback */ /* Boilerplate to return "None" */ Py_INCREF(Py_None); result = Py_None; return result; } int dummy_enumerator (struct chmFile *h, struct chmUnitInfo *ui, void *context) { PyObject *arglist; PyObject *result; PyObject *py_h; PyObject *py_ui; PyObject *py_c; py_h = SWIG_NewPointerObj((void *) h, SWIGTYPE_p_chmFile, 0); py_ui = SWIG_NewPointerObj((void *) ui, SWIGTYPE_p_chmUnitInfo, 0); /* The following was: py_c = PyCObject_AsVoidPtr(context); which did not make sense because the function takes a PyObject * and returns a void *, not the reverse. This was probably never used?? In doubt, replace with a call which makes sense and hope for the best... */ py_c = PyCapsule_New(context, "context", NULL); /* Time to call the callback */ arglist = Py_BuildValue("(OOO)", py_h, py_ui, py_c); if (arglist) { result = PyObject_CallObject(my_callback, arglist); Py_DECREF(arglist); Py_DECREF(result); Py_DECREF(py_h); Py_DECREF(py_ui); Py_DECREF(py_c); if (result == NULL) { return 0; /* Pass error back */ } else { return 1; } } else return 0; } SWIGINTERNINLINE PyObject* SWIG_From_int (int value) { return PyInt_FromLong((long) value); } SWIGINTERN int SWIG_AsVal_double (PyObject *obj, double *val) { int res = SWIG_TypeError; if (PyFloat_Check(obj)) { if (val) *val = PyFloat_AsDouble(obj); return SWIG_OK; #if PY_VERSION_HEX < 0x03000000 } else if (PyInt_Check(obj)) { if (val) *val = (double) PyInt_AsLong(obj); return SWIG_OK; #endif } else if (PyLong_Check(obj)) { double v = PyLong_AsDouble(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { PyErr_Clear(); } } #ifdef SWIG_PYTHON_CAST_MODE { int dispatch = 0; double d = PyFloat_AsDouble(obj); if (!PyErr_Occurred()) { if (val) *val = d; return SWIG_AddCast(SWIG_OK); } else { PyErr_Clear(); } if (!dispatch) { long v = PyLong_AsLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); } else { PyErr_Clear(); } } } #endif return res; } #include #include SWIGINTERNINLINE int SWIG_CanCastAsInteger(double *d, double min, double max) { double x = *d; if ((min <= x && x <= max)) { double fx = floor(x); double cx = ceil(x); double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ if ((errno == EDOM) || (errno == ERANGE)) { errno = 0; } else { double summ, reps, diff; if (rd < x) { diff = x - rd; } else if (rd > x) { diff = rd - x; } else { return 1; } summ = rd + x; reps = diff/summ; if (reps < 8*DBL_EPSILON) { *d = rd; return 1; } } } return 0; } SWIGINTERN int SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val) { #if PY_VERSION_HEX < 0x03000000 if (PyInt_Check(obj)) { long v = PyInt_AsLong(obj); if (v >= 0) { if (val) *val = v; return SWIG_OK; } else { return SWIG_OverflowError; } } else #endif if (PyLong_Check(obj)) { unsigned long v = PyLong_AsUnsignedLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { PyErr_Clear(); return SWIG_OverflowError; } } #ifdef SWIG_PYTHON_CAST_MODE { int dispatch = 0; unsigned long v = PyLong_AsUnsignedLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_AddCast(SWIG_OK); } else { PyErr_Clear(); } if (!dispatch) { double d; int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) { if (val) *val = (unsigned long)(d); return res; } } } #endif return SWIG_TypeError; } #include #if !defined(SWIG_NO_LLONG_MAX) # if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) # define LLONG_MAX __LONG_LONG_MAX__ # define LLONG_MIN (-LLONG_MAX - 1LL) # define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) # endif #endif #if defined(LLONG_MAX) && !defined(SWIG_LONG_LONG_AVAILABLE) # define SWIG_LONG_LONG_AVAILABLE #endif #ifdef SWIG_LONG_LONG_AVAILABLE SWIGINTERN int SWIG_AsVal_unsigned_SS_long_SS_long (PyObject *obj, unsigned long long *val) { int res = SWIG_TypeError; if (PyLong_Check(obj)) { unsigned long long v = PyLong_AsUnsignedLongLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { PyErr_Clear(); res = SWIG_OverflowError; } } else { unsigned long v; res = SWIG_AsVal_unsigned_SS_long (obj,&v); if (SWIG_IsOK(res)) { if (val) *val = v; return res; } } #ifdef SWIG_PYTHON_CAST_MODE { const double mant_max = 1LL << DBL_MANT_DIG; double d; res = SWIG_AsVal_double (obj,&d); if (SWIG_IsOK(res) && !SWIG_CanCastAsInteger(&d, 0, mant_max)) return SWIG_OverflowError; if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, mant_max)) { if (val) *val = (unsigned long long)(d); return SWIG_AddCast(res); } res = SWIG_TypeError; } #endif return res; } #endif #ifdef SWIG_LONG_LONG_AVAILABLE SWIGINTERNINLINE PyObject* SWIG_From_unsigned_SS_long_SS_long (unsigned long long value) { return (value > LONG_MAX) ? PyLong_FromUnsignedLongLong(value) : PyInt_FromLong((long)(value)); } #endif SWIGINTERN int SWIG_AsVal_long (PyObject *obj, long* val) { #if PY_VERSION_HEX < 0x03000000 if (PyInt_Check(obj)) { if (val) *val = PyInt_AsLong(obj); return SWIG_OK; } else #endif if (PyLong_Check(obj)) { long v = PyLong_AsLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { PyErr_Clear(); return SWIG_OverflowError; } } #ifdef SWIG_PYTHON_CAST_MODE { int dispatch = 0; long v = PyInt_AsLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_AddCast(SWIG_OK); } else { PyErr_Clear(); } if (!dispatch) { double d; int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) { if (val) *val = (long)(d); return res; } } } #endif return SWIG_TypeError; } SWIGINTERN int SWIG_AsVal_int (PyObject * obj, int *val) { long v; int res = SWIG_AsVal_long (obj, &v); if (SWIG_IsOK(res)) { if ((v < INT_MIN || v > INT_MAX)) { return SWIG_OverflowError; } else { if (val) *val = (int)(v); } } return res; } SWIGINTERN swig_type_info* SWIG_pchar_descriptor(void) { static int init = 0; static swig_type_info* info = 0; if (!init) { info = SWIG_TypeQuery("_p_char"); init = 1; } return info; } SWIGINTERN int SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc) { #if PY_VERSION_HEX>=0x03000000 #if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) if (PyBytes_Check(obj)) #else if (PyUnicode_Check(obj)) #endif #else if (PyString_Check(obj)) #endif { char *cstr; Py_ssize_t len; int ret = SWIG_OK; #if PY_VERSION_HEX>=0x03000000 #if !defined(SWIG_PYTHON_STRICT_BYTE_CHAR) if (!alloc && cptr) { /* We can't allow converting without allocation, since the internal representation of string in Python 3 is UCS-2/UCS-4 but we require a UTF-8 representation. TODO(bhy) More detailed explanation */ return SWIG_RuntimeError; } obj = PyUnicode_AsUTF8String(obj); if (!obj) return SWIG_TypeError; if (alloc) *alloc = SWIG_NEWOBJ; #endif if (PyBytes_AsStringAndSize(obj, &cstr, &len) == -1) return SWIG_TypeError; #else if (PyString_AsStringAndSize(obj, &cstr, &len) == -1) return SWIG_TypeError; #endif if (cptr) { if (alloc) { if (*alloc == SWIG_NEWOBJ) { *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); *alloc = SWIG_NEWOBJ; } else { *cptr = cstr; *alloc = SWIG_OLDOBJ; } } else { #if PY_VERSION_HEX>=0x03000000 #if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) *cptr = PyBytes_AsString(obj); #else assert(0); /* Should never reach here with Unicode strings in Python 3 */ #endif #else *cptr = SWIG_Python_str_AsChar(obj); if (!*cptr) ret = SWIG_TypeError; #endif } } if (psize) *psize = len + 1; #if PY_VERSION_HEX>=0x03000000 && !defined(SWIG_PYTHON_STRICT_BYTE_CHAR) Py_XDECREF(obj); #endif return ret; } else { #if defined(SWIG_PYTHON_2_UNICODE) #if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) #error "Cannot use both SWIG_PYTHON_2_UNICODE and SWIG_PYTHON_STRICT_BYTE_CHAR at once" #endif #if PY_VERSION_HEX<0x03000000 if (PyUnicode_Check(obj)) { char *cstr; Py_ssize_t len; if (!alloc && cptr) { return SWIG_RuntimeError; } obj = PyUnicode_AsUTF8String(obj); if (!obj) return SWIG_TypeError; if (PyString_AsStringAndSize(obj, &cstr, &len) != -1) { if (cptr) { if (alloc) *alloc = SWIG_NEWOBJ; *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); } if (psize) *psize = len + 1; Py_XDECREF(obj); return SWIG_OK; } else { Py_XDECREF(obj); } } #endif #endif swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); if (pchar_descriptor) { void* vptr = 0; if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) { if (cptr) *cptr = (char *) vptr; if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0; if (alloc) *alloc = SWIG_OLDOBJ; return SWIG_OK; } } } return SWIG_TypeError; } SWIGINTERN int SWIG_AsCharArray(PyObject * obj, char *val, size_t size) { char* cptr = 0; size_t csize = 0; int alloc = SWIG_OLDOBJ; int res = SWIG_AsCharPtrAndSize(obj, &cptr, &csize, &alloc); if (SWIG_IsOK(res)) { /* special case of single char conversion when we don't need space for NUL */ if (size == 1 && csize == 2 && cptr && !cptr[1]) --csize; if (csize <= size) { if (val) { if (csize) memcpy(val, cptr, csize*sizeof(char)); if (csize < size) memset(val + csize, 0, (size - csize)*sizeof(char)); } if (alloc == SWIG_NEWOBJ) { free((char*)cptr); res = SWIG_DelNewMask(res); } return res; } if (alloc == SWIG_NEWOBJ) free((char*)cptr); } return SWIG_TypeError; } SWIGINTERNINLINE PyObject * SWIG_FromCharPtrAndSize(const char* carray, size_t size) { if (carray) { if (size > INT_MAX) { swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); return pchar_descriptor ? SWIG_InternalNewPointerObj((char *)(carray), pchar_descriptor, 0) : SWIG_Py_Void(); } else { #if PY_VERSION_HEX >= 0x03000000 #if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) return PyBytes_FromStringAndSize(carray, (Py_ssize_t)(size)); #else return PyUnicode_DecodeUTF8(carray, (Py_ssize_t)(size), "surrogateescape"); #endif #else return PyString_FromStringAndSize(carray, (Py_ssize_t)(size)); #endif } } else { return SWIG_Py_Void(); } } SWIGINTERN size_t SWIG_strnlen(const char* s, size_t maxlen) { const char *p; for (p = s; maxlen-- && *p; p++) ; return p - s; } #ifdef SWIG_LONG_LONG_AVAILABLE SWIGINTERN int SWIG_AsVal_long_SS_long (PyObject *obj, long long *val) { int res = SWIG_TypeError; if (PyLong_Check(obj)) { long long v = PyLong_AsLongLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { PyErr_Clear(); res = SWIG_OverflowError; } } else { long v; res = SWIG_AsVal_long (obj,&v); if (SWIG_IsOK(res)) { if (val) *val = v; return res; } } #ifdef SWIG_PYTHON_CAST_MODE { const double mant_max = 1LL << DBL_MANT_DIG; const double mant_min = -mant_max; double d; res = SWIG_AsVal_double (obj,&d); if (SWIG_IsOK(res) && !SWIG_CanCastAsInteger(&d, mant_min, mant_max)) return SWIG_OverflowError; if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, mant_min, mant_max)) { if (val) *val = (long long)(d); return SWIG_AddCast(res); } res = SWIG_TypeError; } #endif return res; } #endif #define t_output_helper SWIG_Python_AppendOutput #ifdef SWIG_LONG_LONG_AVAILABLE SWIGINTERNINLINE PyObject* SWIG_From_long_SS_long (long long value) { return ((value < LONG_MIN) || (value > LONG_MAX)) ? PyLong_FromLongLong(value) : PyInt_FromLong((long)(value)); } #endif #ifdef __cplusplus extern "C" { #endif SWIGINTERN PyObject *_wrap_chmUnitInfo_start_set(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; LONGUINT64 arg2 ; void *argp1 = 0 ; int res1 = 0 ; unsigned long long val2 ; int ecode2 = 0 ; PyObject *swig_obj[2] ; if (!SWIG_Python_UnpackTuple(args, "chmUnitInfo_start_set", 2, 2, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chmUnitInfo_start_set" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); ecode2 = SWIG_AsVal_unsigned_SS_long_SS_long(swig_obj[1], &val2); if (!SWIG_IsOK(ecode2)) { SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "chmUnitInfo_start_set" "', argument " "2"" of type '" "LONGUINT64""'"); } arg2 = (LONGUINT64)(val2); if (arg1) (arg1)->start = arg2; resultobj = SWIG_Py_Void(); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chmUnitInfo_start_get(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; void *argp1 = 0 ; int res1 = 0 ; PyObject *swig_obj[1] ; LONGUINT64 result; if (!args) SWIG_fail; swig_obj[0] = args; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chmUnitInfo_start_get" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); result = (LONGUINT64) ((arg1)->start); resultobj = SWIG_From_unsigned_SS_long_SS_long((unsigned long long)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chmUnitInfo_length_set(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; LONGUINT64 arg2 ; void *argp1 = 0 ; int res1 = 0 ; unsigned long long val2 ; int ecode2 = 0 ; PyObject *swig_obj[2] ; if (!SWIG_Python_UnpackTuple(args, "chmUnitInfo_length_set", 2, 2, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chmUnitInfo_length_set" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); ecode2 = SWIG_AsVal_unsigned_SS_long_SS_long(swig_obj[1], &val2); if (!SWIG_IsOK(ecode2)) { SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "chmUnitInfo_length_set" "', argument " "2"" of type '" "LONGUINT64""'"); } arg2 = (LONGUINT64)(val2); if (arg1) (arg1)->length = arg2; resultobj = SWIG_Py_Void(); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chmUnitInfo_length_get(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; void *argp1 = 0 ; int res1 = 0 ; PyObject *swig_obj[1] ; LONGUINT64 result; if (!args) SWIG_fail; swig_obj[0] = args; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chmUnitInfo_length_get" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); result = (LONGUINT64) ((arg1)->length); resultobj = SWIG_From_unsigned_SS_long_SS_long((unsigned long long)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chmUnitInfo_space_set(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; int arg2 ; void *argp1 = 0 ; int res1 = 0 ; int val2 ; int ecode2 = 0 ; PyObject *swig_obj[2] ; if (!SWIG_Python_UnpackTuple(args, "chmUnitInfo_space_set", 2, 2, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chmUnitInfo_space_set" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); if (!SWIG_IsOK(ecode2)) { SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "chmUnitInfo_space_set" "', argument " "2"" of type '" "int""'"); } arg2 = (int)(val2); if (arg1) (arg1)->space = arg2; resultobj = SWIG_Py_Void(); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chmUnitInfo_space_get(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; void *argp1 = 0 ; int res1 = 0 ; PyObject *swig_obj[1] ; int result; if (!args) SWIG_fail; swig_obj[0] = args; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chmUnitInfo_space_get" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); result = (int) ((arg1)->space); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chmUnitInfo_path_set(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; char *arg2 ; void *argp1 = 0 ; int res1 = 0 ; char temp2[256+1] ; int res2 ; PyObject *swig_obj[2] ; if (!SWIG_Python_UnpackTuple(args, "chmUnitInfo_path_set", 2, 2, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chmUnitInfo_path_set" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); res2 = SWIG_AsCharArray(swig_obj[1], temp2, 256+1); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "chmUnitInfo_path_set" "', argument " "2"" of type '" "char [256+1]""'"); } arg2 = (char *)(temp2); if (arg2) memcpy(arg1->path,arg2,256+1*sizeof(char)); else memset(arg1->path,0,256+1*sizeof(char)); resultobj = SWIG_Py_Void(); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chmUnitInfo_path_get(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; void *argp1 = 0 ; int res1 = 0 ; PyObject *swig_obj[1] ; char *result = 0 ; if (!args) SWIG_fail; swig_obj[0] = args; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chmUnitInfo_path_get" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); result = (char *)(char *) ((arg1)->path); { size_t size = SWIG_strnlen(result, 256+1); resultobj = SWIG_FromCharPtrAndSize(result, size); } return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_new_chmUnitInfo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *result = 0 ; if (!SWIG_Python_UnpackTuple(args, "new_chmUnitInfo", 0, 0, 0)) SWIG_fail; result = (struct chmUnitInfo *)calloc(1, sizeof(struct chmUnitInfo)); resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_chmUnitInfo, SWIG_POINTER_NEW | 0 ); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_delete_chmUnitInfo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmUnitInfo *arg1 = (struct chmUnitInfo *) 0 ; void *argp1 = 0 ; int res1 = 0 ; PyObject *swig_obj[1] ; if (!args) SWIG_fail; swig_obj[0] = args; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmUnitInfo, SWIG_POINTER_DISOWN | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_chmUnitInfo" "', argument " "1"" of type '" "struct chmUnitInfo *""'"); } arg1 = (struct chmUnitInfo *)(argp1); free((char *) arg1); resultobj = SWIG_Py_Void(); return resultobj; fail: return NULL; } SWIGINTERN PyObject *chmUnitInfo_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *obj; if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; SWIG_TypeNewClientData(SWIGTYPE_p_chmUnitInfo, SWIG_NewClientData(obj)); return SWIG_Py_Void(); } SWIGINTERN PyObject *chmUnitInfo_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { return SWIG_Python_InitShadowInstance(args); } SWIGINTERN PyObject *_wrap_chm_open(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; char *arg1 = (char *) 0 ; int res1 ; char *buf1 = 0 ; int alloc1 = 0 ; PyObject *swig_obj[1] ; struct chmFile *result = 0 ; if (!args) SWIG_fail; swig_obj[0] = args; res1 = SWIG_AsCharPtrAndSize(swig_obj[0], &buf1, NULL, &alloc1); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chm_open" "', argument " "1"" of type '" "char const *""'"); } arg1 = (char *)(buf1); result = (struct chmFile *)chm_open((char const *)arg1); resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_chmFile, 0 | 0 ); if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); return resultobj; fail: if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); return NULL; } SWIGINTERN PyObject *_wrap_chm_close(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmFile *arg1 = (struct chmFile *) 0 ; void *argp1 = 0 ; int res1 = 0 ; PyObject *swig_obj[1] ; if (!args) SWIG_fail; swig_obj[0] = args; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmFile, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chm_close" "', argument " "1"" of type '" "struct chmFile *""'"); } arg1 = (struct chmFile *)(argp1); chm_close(arg1); resultobj = SWIG_Py_Void(); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chm_set_param(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmFile *arg1 = (struct chmFile *) 0 ; int arg2 ; int arg3 ; void *argp1 = 0 ; int res1 = 0 ; int val2 ; int ecode2 = 0 ; int val3 ; int ecode3 = 0 ; PyObject *swig_obj[3] ; if (!SWIG_Python_UnpackTuple(args, "chm_set_param", 3, 3, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmFile, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chm_set_param" "', argument " "1"" of type '" "struct chmFile *""'"); } arg1 = (struct chmFile *)(argp1); ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); if (!SWIG_IsOK(ecode2)) { SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "chm_set_param" "', argument " "2"" of type '" "int""'"); } arg2 = (int)(val2); ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); if (!SWIG_IsOK(ecode3)) { SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "chm_set_param" "', argument " "3"" of type '" "int""'"); } arg3 = (int)(val3); chm_set_param(arg1,arg2,arg3); resultobj = SWIG_Py_Void(); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chm_resolve_object(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmFile *arg1 = (struct chmFile *) 0 ; char *arg2 = (char *) 0 ; struct chmUnitInfo *arg3 = (struct chmUnitInfo *) 0 ; void *argp1 = 0 ; int res1 = 0 ; int res2 ; char *buf2 = 0 ; int alloc2 = 0 ; struct chmUnitInfo *temp3 = (struct chmUnitInfo *) calloc(1, sizeof(struct chmUnitInfo)) ; PyObject *swig_obj[2] ; int result; { arg3 = temp3; } if (!SWIG_Python_UnpackTuple(args, "chm_resolve_object", 2, 2, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmFile, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chm_resolve_object" "', argument " "1"" of type '" "struct chmFile *""'"); } arg1 = (struct chmFile *)(argp1); res2 = SWIG_AsCharPtrAndSize(swig_obj[1], &buf2, NULL, &alloc2); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "chm_resolve_object" "', argument " "2"" of type '" "char const *""'"); } arg2 = (char *)(buf2); result = (int)chm_resolve_object(arg1,(char const *)arg2,arg3); resultobj = SWIG_From_int((int)(result)); { PyObject *o, *o2, *o3; o = SWIG_NewPointerObj((void *) arg3, SWIGTYPE_p_chmUnitInfo, 1); if ((!resultobj) || (resultobj == Py_None)) { resultobj = o; } else { if (!PyTuple_Check(resultobj)) { PyObject *o2 = resultobj; resultobj = PyTuple_New(1); PyTuple_SetItem(resultobj,0,o2); } o3 = PyTuple_New(1); PyTuple_SetItem(o3,0,o); o2 = resultobj; resultobj = PySequence_Concat(o2,o3); Py_DECREF(o2); Py_DECREF(o3); } } if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return resultobj; fail: if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return NULL; } SWIGINTERN PyObject *_wrap_chm_retrieve_object(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmFile *arg1 = (struct chmFile *) 0 ; struct chmUnitInfo *arg2 = (struct chmUnitInfo *) 0 ; unsigned char *arg3 = (unsigned char *) 0 ; LONGUINT64 arg4 ; LONGINT64 arg5 ; void *argp1 = 0 ; int res1 = 0 ; void *argp2 = 0 ; int res2 = 0 ; unsigned char temp3 ; int res3 = SWIG_TMPOBJ ; unsigned long long val4 ; int ecode4 = 0 ; long long val5 ; int ecode5 = 0 ; PyObject *swig_obj[4] ; LONGINT64 result; arg3 = &temp3; if (!SWIG_Python_UnpackTuple(args, "chm_retrieve_object", 4, 4, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmFile, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chm_retrieve_object" "', argument " "1"" of type '" "struct chmFile *""'"); } arg1 = (struct chmFile *)(argp1); res2 = SWIG_ConvertPtr(swig_obj[1], &argp2,SWIGTYPE_p_chmUnitInfo, 0 | 0 ); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "chm_retrieve_object" "', argument " "2"" of type '" "struct chmUnitInfo *""'"); } arg2 = (struct chmUnitInfo *)(argp2); ecode4 = SWIG_AsVal_unsigned_SS_long_SS_long(swig_obj[2], &val4); if (!SWIG_IsOK(ecode4)) { SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "chm_retrieve_object" "', argument " "4"" of type '" "LONGUINT64""'"); } arg4 = (LONGUINT64)(val4); ecode5 = SWIG_AsVal_long_SS_long(swig_obj[3], &val5); if (!SWIG_IsOK(ecode5)) { SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "chm_retrieve_object" "', argument " "5"" of type '" "LONGINT64""'"); } arg5 = (LONGINT64)(val5); { /* nasty hack */ arg3 = (unsigned char *) malloc(arg5); if (arg3 == NULL) SWIG_fail; } result = (LONGINT64)chm_retrieve_object(arg1,arg2,arg3,arg4,arg5); resultobj = SWIG_From_long_SS_long((long long)(result)); { PyObject *o; o = SWIG_FromCharPtrAndSize((const char*)arg3, arg5); /* o = PyString_FromStringAndSize(arg3, arg5);*/ resultobj = t_output_helper(resultobj,o); free(arg3); } return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chm_enumerate(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmFile *arg1 = (struct chmFile *) 0 ; int arg2 ; CHM_ENUMERATOR arg3 = (CHM_ENUMERATOR) 0 ; void *arg4 = (void *) 0 ; void *argp1 = 0 ; int res1 = 0 ; int val2 ; int ecode2 = 0 ; PyObject *swig_obj[4] ; int result; if (!SWIG_Python_UnpackTuple(args, "chm_enumerate", 4, 4, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmFile, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chm_enumerate" "', argument " "1"" of type '" "struct chmFile *""'"); } arg1 = (struct chmFile *)(argp1); ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); if (!SWIG_IsOK(ecode2)) { SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "chm_enumerate" "', argument " "2"" of type '" "int""'"); } arg2 = (int)(val2); { if (!my_set_callback(self, swig_obj[2])) goto fail; arg3 = dummy_enumerator; } { if (!(arg4 = PyCapsule_New(swig_obj[3], "context", NULL))) goto fail; } result = (int)chm_enumerate(arg1,arg2,arg3,arg4); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_chm_enumerate_dir(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; struct chmFile *arg1 = (struct chmFile *) 0 ; char *arg2 = (char *) 0 ; int arg3 ; CHM_ENUMERATOR arg4 = (CHM_ENUMERATOR) 0 ; void *arg5 = (void *) 0 ; void *argp1 = 0 ; int res1 = 0 ; int res2 ; char *buf2 = 0 ; int alloc2 = 0 ; int val3 ; int ecode3 = 0 ; PyObject *swig_obj[5] ; int result; if (!SWIG_Python_UnpackTuple(args, "chm_enumerate_dir", 5, 5, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_chmFile, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "chm_enumerate_dir" "', argument " "1"" of type '" "struct chmFile *""'"); } arg1 = (struct chmFile *)(argp1); res2 = SWIG_AsCharPtrAndSize(swig_obj[1], &buf2, NULL, &alloc2); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "chm_enumerate_dir" "', argument " "2"" of type '" "char const *""'"); } arg2 = (char *)(buf2); ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); if (!SWIG_IsOK(ecode3)) { SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "chm_enumerate_dir" "', argument " "3"" of type '" "int""'"); } arg3 = (int)(val3); { if (!my_set_callback(self, swig_obj[3])) goto fail; arg4 = dummy_enumerator; } { if (!(arg5 = PyCapsule_New(swig_obj[4], "context", NULL))) goto fail; } result = (int)chm_enumerate_dir(arg1,(char const *)arg2,arg3,arg4,arg5); resultobj = SWIG_From_int((int)(result)); if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return resultobj; fail: if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return NULL; } static PyMethodDef SwigMethods[] = { { "SWIG_PyInstanceMethod_New", SWIG_PyInstanceMethod_New, METH_O, NULL}, { "chmUnitInfo_start_set", _wrap_chmUnitInfo_start_set, METH_VARARGS, NULL}, { "chmUnitInfo_start_get", _wrap_chmUnitInfo_start_get, METH_O, NULL}, { "chmUnitInfo_length_set", _wrap_chmUnitInfo_length_set, METH_VARARGS, NULL}, { "chmUnitInfo_length_get", _wrap_chmUnitInfo_length_get, METH_O, NULL}, { "chmUnitInfo_space_set", _wrap_chmUnitInfo_space_set, METH_VARARGS, NULL}, { "chmUnitInfo_space_get", _wrap_chmUnitInfo_space_get, METH_O, NULL}, { "chmUnitInfo_path_set", _wrap_chmUnitInfo_path_set, METH_VARARGS, NULL}, { "chmUnitInfo_path_get", _wrap_chmUnitInfo_path_get, METH_O, NULL}, { "new_chmUnitInfo", _wrap_new_chmUnitInfo, METH_NOARGS, NULL}, { "delete_chmUnitInfo", _wrap_delete_chmUnitInfo, METH_O, NULL}, { "chmUnitInfo_swigregister", chmUnitInfo_swigregister, METH_O, NULL}, { "chmUnitInfo_swiginit", chmUnitInfo_swiginit, METH_VARARGS, NULL}, { "chm_open", _wrap_chm_open, METH_O, NULL}, { "chm_close", _wrap_chm_close, METH_O, NULL}, { "chm_set_param", _wrap_chm_set_param, METH_VARARGS, NULL}, { "chm_resolve_object", _wrap_chm_resolve_object, METH_VARARGS, NULL}, { "chm_retrieve_object", _wrap_chm_retrieve_object, METH_VARARGS, NULL}, { "chm_enumerate", _wrap_chm_enumerate, METH_VARARGS, NULL}, { "chm_enumerate_dir", _wrap_chm_enumerate_dir, METH_VARARGS, NULL}, { NULL, NULL, 0, NULL } }; static PyMethodDef SwigMethods_proxydocs[] = { { NULL, NULL, 0, NULL } }; /* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_chmFile = {"_p_chmFile", "struct chmFile *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_chmUnitInfo = {"_p_chmUnitInfo", "struct chmUnitInfo *|chmUnitInfo *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_f_p_struct_chmFile_p_struct_chmUnitInfo_p_void__int = {"_p_f_p_struct_chmFile_p_struct_chmUnitInfo_p_void__int", "int (*)(struct chmFile *,struct chmUnitInfo *,void *)|CHM_ENUMERATOR", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_long_long = {"_p_long_long", "LONGINT64 *|long long *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_unsigned_char = {"_p_unsigned_char", "unsigned char *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_unsigned_long_long = {"_p_unsigned_long_long", "LONGUINT64 *|unsigned long long *", 0, 0, (void*)0, 0}; static swig_type_info *swig_type_initial[] = { &_swigt__p_char, &_swigt__p_chmFile, &_swigt__p_chmUnitInfo, &_swigt__p_f_p_struct_chmFile_p_struct_chmUnitInfo_p_void__int, &_swigt__p_long_long, &_swigt__p_unsigned_char, &_swigt__p_unsigned_long_long, }; static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_chmFile[] = { {&_swigt__p_chmFile, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_chmUnitInfo[] = { {&_swigt__p_chmUnitInfo, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_f_p_struct_chmFile_p_struct_chmUnitInfo_p_void__int[] = { {&_swigt__p_f_p_struct_chmFile_p_struct_chmUnitInfo_p_void__int, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_long_long[] = { {&_swigt__p_long_long, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_unsigned_char[] = { {&_swigt__p_unsigned_char, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_unsigned_long_long[] = { {&_swigt__p_unsigned_long_long, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info *swig_cast_initial[] = { _swigc__p_char, _swigc__p_chmFile, _swigc__p_chmUnitInfo, _swigc__p_f_p_struct_chmFile_p_struct_chmUnitInfo_p_void__int, _swigc__p_long_long, _swigc__p_unsigned_char, _swigc__p_unsigned_long_long, }; /* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ static swig_const_info swig_const_table[] = { {0, 0, 0, 0.0, 0, 0}}; #ifdef __cplusplus } #endif /* ----------------------------------------------------------------------------- * Type initialization: * This problem is tough by the requirement that no dynamic * memory is used. Also, since swig_type_info structures store pointers to * swig_cast_info structures and swig_cast_info structures store pointers back * to swig_type_info structures, we need some lookup code at initialization. * The idea is that swig generates all the structures that are needed. * The runtime then collects these partially filled structures. * The SWIG_InitializeModule function takes these initial arrays out of * swig_module, and does all the lookup, filling in the swig_module.types * array with the correct data and linking the correct swig_cast_info * structures together. * * The generated swig_type_info structures are assigned statically to an initial * array. We just loop through that array, and handle each type individually. * First we lookup if this type has been already loaded, and if so, use the * loaded structure instead of the generated one. Then we have to fill in the * cast linked list. The cast data is initially stored in something like a * two-dimensional array. Each row corresponds to a type (there are the same * number of rows as there are in the swig_type_initial array). Each entry in * a column is one of the swig_cast_info structures for that type. * The cast_initial array is actually an array of arrays, because each row has * a variable number of columns. So to actually build the cast linked list, * we find the array of casts associated with the type, and loop through it * adding the casts to the list. The one last trick we need to do is making * sure the type pointer in the swig_cast_info struct is correct. * * First off, we lookup the cast->type name to see if it is already loaded. * There are three cases to handle: * 1) If the cast->type has already been loaded AND the type we are adding * casting info to has not been loaded (it is in this module), THEN we * replace the cast->type pointer with the type pointer that has already * been loaded. * 2) If BOTH types (the one we are adding casting info to, and the * cast->type) are loaded, THEN the cast info has already been loaded by * the previous module so we just ignore it. * 3) Finally, if cast->type has not already been loaded, then we add that * swig_cast_info to the linked list (because the cast->type) pointer will * be correct. * ----------------------------------------------------------------------------- */ #ifdef __cplusplus extern "C" { #if 0 } /* c-mode */ #endif #endif #if 0 #define SWIGRUNTIME_DEBUG #endif SWIGRUNTIME void SWIG_InitializeModule(void *clientdata) { size_t i; swig_module_info *module_head, *iter; int init; /* check to see if the circular list has been setup, if not, set it up */ if (swig_module.next==0) { /* Initialize the swig_module */ swig_module.type_initial = swig_type_initial; swig_module.cast_initial = swig_cast_initial; swig_module.next = &swig_module; init = 1; } else { init = 0; } /* Try and load any already created modules */ module_head = SWIG_GetModule(clientdata); if (!module_head) { /* This is the first module loaded for this interpreter */ /* so set the swig module into the interpreter */ SWIG_SetModule(clientdata, &swig_module); } else { /* the interpreter has loaded a SWIG module, but has it loaded this one? */ iter=module_head; do { if (iter==&swig_module) { /* Our module is already in the list, so there's nothing more to do. */ return; } iter=iter->next; } while (iter!= module_head); /* otherwise we must add our module into the list */ swig_module.next = module_head->next; module_head->next = &swig_module; } /* When multiple interpreters are used, a module could have already been initialized in a different interpreter, but not yet have a pointer in this interpreter. In this case, we do not want to continue adding types... everything should be set up already */ if (init == 0) return; /* Now work on filling in swig_module.types */ #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: size %lu\n", (unsigned long)swig_module.size); #endif for (i = 0; i < swig_module.size; ++i) { swig_type_info *type = 0; swig_type_info *ret; swig_cast_info *cast; #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: type %lu %s\n", (unsigned long)i, swig_module.type_initial[i]->name); #endif /* if there is another module already loaded */ if (swig_module.next != &swig_module) { type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); } if (type) { /* Overwrite clientdata field */ #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: found type %s\n", type->name); #endif if (swig_module.type_initial[i]->clientdata) { type->clientdata = swig_module.type_initial[i]->clientdata; #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); #endif } } else { type = swig_module.type_initial[i]; } /* Insert casting types */ cast = swig_module.cast_initial[i]; while (cast->type) { /* Don't need to add information already in the list */ ret = 0; #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); #endif if (swig_module.next != &swig_module) { ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); #ifdef SWIGRUNTIME_DEBUG if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); #endif } if (ret) { if (type == swig_module.type_initial[i]) { #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: skip old type %s\n", ret->name); #endif cast->type = ret; ret = 0; } else { /* Check for casting already in the list */ swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); #ifdef SWIGRUNTIME_DEBUG if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); #endif if (!ocast) ret = 0; } } if (!ret) { #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); #endif if (type->cast) { type->cast->prev = cast; cast->next = type->cast; } type->cast = cast; } cast++; } /* Set entry in modules->types array equal to the type */ swig_module.types[i] = type; } swig_module.types[i] = 0; #ifdef SWIGRUNTIME_DEBUG printf("**** SWIG_InitializeModule: Cast List ******\n"); for (i = 0; i < swig_module.size; ++i) { int j = 0; swig_cast_info *cast = swig_module.cast_initial[i]; printf("SWIG_InitializeModule: type %lu %s\n", (unsigned long)i, swig_module.type_initial[i]->name); while (cast->type) { printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); cast++; ++j; } printf("---- Total casts: %d\n",j); } printf("**** SWIG_InitializeModule: Cast List ******\n"); #endif } /* This function will propagate the clientdata field of type to * any new swig_type_info structures that have been added into the list * of equivalent types. It is like calling * SWIG_TypeClientData(type, clientdata) a second time. */ SWIGRUNTIME void SWIG_PropagateClientData(void) { size_t i; swig_cast_info *equiv; static int init_run = 0; if (init_run) return; init_run = 1; for (i = 0; i < swig_module.size; i++) { if (swig_module.types[i]->clientdata) { equiv = swig_module.types[i]->cast; while (equiv) { if (!equiv->converter) { if (equiv->type && !equiv->type->clientdata) SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); } equiv = equiv->next; } } } } #ifdef __cplusplus #if 0 { /* c-mode */ #endif } #endif #ifdef __cplusplus extern "C" { #endif /* Python-specific SWIG API */ #define SWIG_newvarlink() SWIG_Python_newvarlink() #define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr) #define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants) /* ----------------------------------------------------------------------------- * global variable support code. * ----------------------------------------------------------------------------- */ typedef struct swig_globalvar { char *name; /* Name of global variable */ PyObject *(*get_attr)(void); /* Return the current value */ int (*set_attr)(PyObject *); /* Set the value */ struct swig_globalvar *next; } swig_globalvar; typedef struct swig_varlinkobject { PyObject_HEAD swig_globalvar *vars; } swig_varlinkobject; SWIGINTERN PyObject * swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) { #if PY_VERSION_HEX >= 0x03000000 return PyUnicode_InternFromString(""); #else return PyString_FromString(""); #endif } SWIGINTERN PyObject * swig_varlink_str(swig_varlinkobject *v) { #if PY_VERSION_HEX >= 0x03000000 PyObject *str = PyUnicode_InternFromString("("); PyObject *tail; PyObject *joined; swig_globalvar *var; for (var = v->vars; var; var=var->next) { tail = PyUnicode_FromString(var->name); joined = PyUnicode_Concat(str, tail); Py_DecRef(str); Py_DecRef(tail); str = joined; if (var->next) { tail = PyUnicode_InternFromString(", "); joined = PyUnicode_Concat(str, tail); Py_DecRef(str); Py_DecRef(tail); str = joined; } } tail = PyUnicode_InternFromString(")"); joined = PyUnicode_Concat(str, tail); Py_DecRef(str); Py_DecRef(tail); str = joined; #else PyObject *str = PyString_FromString("("); swig_globalvar *var; for (var = v->vars; var; var=var->next) { PyString_ConcatAndDel(&str,PyString_FromString(var->name)); if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", ")); } PyString_ConcatAndDel(&str,PyString_FromString(")")); #endif return str; } SWIGINTERN void swig_varlink_dealloc(swig_varlinkobject *v) { swig_globalvar *var = v->vars; while (var) { swig_globalvar *n = var->next; free(var->name); free(var); var = n; } } SWIGINTERN PyObject * swig_varlink_getattr(swig_varlinkobject *v, char *n) { PyObject *res = NULL; swig_globalvar *var = v->vars; while (var) { if (strcmp(var->name,n) == 0) { res = (*var->get_attr)(); break; } var = var->next; } if (res == NULL && !PyErr_Occurred()) { PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); } return res; } SWIGINTERN int swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) { int res = 1; swig_globalvar *var = v->vars; while (var) { if (strcmp(var->name,n) == 0) { res = (*var->set_attr)(p); break; } var = var->next; } if (res == 1 && !PyErr_Occurred()) { PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); } return res; } SWIGINTERN PyTypeObject* swig_varlink_type(void) { static char varlink__doc__[] = "Swig var link object"; static PyTypeObject varlink_type; static int type_init = 0; if (!type_init) { const PyTypeObject tmp = { #if PY_VERSION_HEX >= 0x03000000 PyVarObject_HEAD_INIT(NULL, 0) #else PyObject_HEAD_INIT(NULL) 0, /* ob_size */ #endif "swigvarlink", /* tp_name */ sizeof(swig_varlinkobject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) swig_varlink_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc) swig_varlink_getattr, /* tp_getattr */ (setattrfunc) swig_varlink_setattr, /* tp_setattr */ 0, /* tp_compare */ (reprfunc) swig_varlink_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc) swig_varlink_str, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ 0, /* tp_flags */ varlink__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */ 0, /* tp_del */ 0, /* tp_version_tag */ #if PY_VERSION_HEX >= 0x03040000 0, /* tp_finalize */ #endif #if PY_VERSION_HEX >= 0x03080000 0, /* tp_vectorcall */ #endif #if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000) 0, /* tp_print */ #endif #ifdef COUNT_ALLOCS 0, /* tp_allocs */ 0, /* tp_frees */ 0, /* tp_maxalloc */ 0, /* tp_prev */ 0 /* tp_next */ #endif }; varlink_type = tmp; type_init = 1; if (PyType_Ready(&varlink_type) < 0) return NULL; } return &varlink_type; } /* Create a variable linking object for use later */ SWIGINTERN PyObject * SWIG_Python_newvarlink(void) { swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type()); if (result) { result->vars = 0; } return ((PyObject*) result); } SWIGINTERN void SWIG_Python_addvarlink(PyObject *p, const char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) { swig_varlinkobject *v = (swig_varlinkobject *) p; swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar)); if (gv) { size_t size = strlen(name)+1; gv->name = (char *)malloc(size); if (gv->name) { memcpy(gv->name, name, size); gv->get_attr = get_attr; gv->set_attr = set_attr; gv->next = v->vars; } } v->vars = gv; } SWIGINTERN PyObject * SWIG_globals(void) { static PyObject *globals = 0; if (!globals) { globals = SWIG_newvarlink(); } return globals; } /* ----------------------------------------------------------------------------- * constants/methods manipulation * ----------------------------------------------------------------------------- */ /* Install Constants */ SWIGINTERN void SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) { PyObject *obj = 0; size_t i; for (i = 0; constants[i].type; ++i) { switch(constants[i].type) { case SWIG_PY_POINTER: obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); break; case SWIG_PY_BINARY: obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); break; default: obj = 0; break; } if (obj) { PyDict_SetItemString(d, constants[i].name, obj); Py_DECREF(obj); } } } /* -----------------------------------------------------------------------------*/ /* Fix SwigMethods to carry the callback ptrs when needed */ /* -----------------------------------------------------------------------------*/ SWIGINTERN void SWIG_Python_FixMethods(PyMethodDef *methods, swig_const_info *const_table, swig_type_info **types, swig_type_info **types_initial) { size_t i; for (i = 0; methods[i].ml_name; ++i) { const char *c = methods[i].ml_doc; if (!c) continue; c = strstr(c, "swig_ptr: "); if (c) { int j; swig_const_info *ci = 0; const char *name = c + 10; for (j = 0; const_table[j].type; ++j) { if (strncmp(const_table[j].name, name, strlen(const_table[j].name)) == 0) { ci = &(const_table[j]); break; } } if (ci) { void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0; if (ptr) { size_t shift = (ci->ptype) - types; swig_type_info *ty = types_initial[shift]; size_t ldoc = (c - methods[i].ml_doc); size_t lptr = strlen(ty->name)+2*sizeof(void*)+2; char *ndoc = (char*)malloc(ldoc + lptr + 10); if (ndoc) { char *buff = ndoc; memcpy(buff, methods[i].ml_doc, ldoc); buff += ldoc; memcpy(buff, "swig_ptr: ", 10); buff += 10; SWIG_PackVoidPtr(buff, ptr, ty->name, lptr); methods[i].ml_doc = ndoc; } } } } } } /* ----------------------------------------------------------------------------- * Method creation and docstring support functions * ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- * Function to find the method definition with the correct docstring for the * proxy module as opposed to the low-level API * ----------------------------------------------------------------------------- */ SWIGINTERN PyMethodDef *SWIG_PythonGetProxyDoc(const char *name) { /* Find the function in the modified method table */ size_t offset = 0; int found = 0; while (SwigMethods_proxydocs[offset].ml_meth != NULL) { if (strcmp(SwigMethods_proxydocs[offset].ml_name, name) == 0) { found = 1; break; } offset++; } /* Use the copy with the modified docstring if available */ return found ? &SwigMethods_proxydocs[offset] : NULL; } /* ----------------------------------------------------------------------------- * Wrapper of PyInstanceMethod_New() used in Python 3 * It is exported to the generated module, used for -fastproxy * ----------------------------------------------------------------------------- */ SWIGINTERN PyObject *SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func) { if (PyCFunction_Check(func)) { PyCFunctionObject *funcobj = (PyCFunctionObject *)func; PyMethodDef *ml = SWIG_PythonGetProxyDoc(funcobj->m_ml->ml_name); if (ml) func = PyCFunction_NewEx(ml, funcobj->m_self, funcobj->m_module); } #if PY_VERSION_HEX >= 0x03000000 return PyInstanceMethod_New(func); #else return PyMethod_New(func, NULL, NULL); #endif } /* ----------------------------------------------------------------------------- * Wrapper of PyStaticMethod_New() * It is exported to the generated module, used for -fastproxy * ----------------------------------------------------------------------------- */ SWIGINTERN PyObject *SWIG_PyStaticMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func) { if (PyCFunction_Check(func)) { PyCFunctionObject *funcobj = (PyCFunctionObject *)func; PyMethodDef *ml = SWIG_PythonGetProxyDoc(funcobj->m_ml->ml_name); if (ml) func = PyCFunction_NewEx(ml, funcobj->m_self, funcobj->m_module); } return PyStaticMethod_New(func); } #ifdef __cplusplus } #endif /* -----------------------------------------------------------------------------* * Partial Init method * -----------------------------------------------------------------------------*/ #ifdef __cplusplus extern "C" #endif SWIGEXPORT #if PY_VERSION_HEX >= 0x03000000 PyObject* #else void #endif SWIG_init(void) { PyObject *m, *d, *md, *globals; #if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef SWIG_module = { PyModuleDef_HEAD_INIT, SWIG_name, NULL, -1, SwigMethods, NULL, NULL, NULL, NULL }; #endif #if defined(SWIGPYTHON_BUILTIN) static SwigPyClientData SwigPyObject_clientdata = { 0, 0, 0, 0, 0, 0, 0 }; static PyGetSetDef this_getset_def = { (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL }; static SwigPyGetSet thisown_getset_closure = { SwigPyObject_own, SwigPyObject_own }; static PyGetSetDef thisown_getset_def = { (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure }; PyTypeObject *builtin_pytype; int builtin_base_count; swig_type_info *builtin_basetype; PyObject *tuple; PyGetSetDescrObject *static_getset; PyTypeObject *metatype; PyTypeObject *swigpyobject; SwigPyClientData *cd; PyObject *public_interface, *public_symbol; PyObject *this_descr; PyObject *thisown_descr; PyObject *self = 0; int i; (void)builtin_pytype; (void)builtin_base_count; (void)builtin_basetype; (void)tuple; (void)static_getset; (void)self; /* Metaclass is used to implement static member variables */ metatype = SwigPyObjectType(); assert(metatype); #endif (void)globals; /* Create singletons now to avoid potential deadlocks with multi-threaded usage after module initialization */ SWIG_This(); SWIG_Python_TypeCache(); SwigPyPacked_type(); #ifndef SWIGPYTHON_BUILTIN SwigPyObject_type(); #endif /* Fix SwigMethods to carry the callback ptrs when needed */ SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial); #if PY_VERSION_HEX >= 0x03000000 m = PyModule_Create(&SWIG_module); #else m = Py_InitModule(SWIG_name, SwigMethods); #endif md = d = PyModule_GetDict(m); (void)md; SWIG_InitializeModule(0); #ifdef SWIGPYTHON_BUILTIN swigpyobject = SwigPyObject_TypeOnce(); SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject"); assert(SwigPyObject_stype); cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; if (!cd) { SwigPyObject_stype->clientdata = &SwigPyObject_clientdata; SwigPyObject_clientdata.pytype = swigpyobject; } else if (swigpyobject->tp_basicsize != cd->pytype->tp_basicsize) { PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules."); # if PY_VERSION_HEX >= 0x03000000 return NULL; # else return; # endif } /* All objects have a 'this' attribute */ this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def); (void)this_descr; /* All objects have a 'thisown' attribute */ thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def); (void)thisown_descr; public_interface = PyList_New(0); public_symbol = 0; (void)public_symbol; PyDict_SetItemString(md, "__all__", public_interface); Py_DECREF(public_interface); for (i = 0; SwigMethods[i].ml_name != NULL; ++i) SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name); for (i = 0; swig_const_table[i].name != 0; ++i) SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name); #endif SWIG_InstallConstants(d,swig_const_table); SWIG_Python_SetConstant(d, "CHM_UNCOMPRESSED",SWIG_From_int((int)((0)))); SWIG_Python_SetConstant(d, "CHM_COMPRESSED",SWIG_From_int((int)((1)))); SWIG_Python_SetConstant(d, "CHM_MAX_PATHLEN",SWIG_From_int((int)(256))); SWIG_Python_SetConstant(d, "CHM_PARAM_MAX_BLOCKS_CACHED",SWIG_From_int((int)(0))); SWIG_Python_SetConstant(d, "CHM_RESOLVE_SUCCESS",SWIG_From_int((int)((0)))); SWIG_Python_SetConstant(d, "CHM_RESOLVE_FAILURE",SWIG_From_int((int)((1)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATE_NORMAL",SWIG_From_int((int)((1)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATE_META",SWIG_From_int((int)((2)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATE_SPECIAL",SWIG_From_int((int)((4)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATE_FILES",SWIG_From_int((int)((8)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATE_DIRS",SWIG_From_int((int)((16)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATE_ALL",SWIG_From_int((int)((31)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATOR_FAILURE",SWIG_From_int((int)((0)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATOR_CONTINUE",SWIG_From_int((int)((1)))); SWIG_Python_SetConstant(d, "CHM_ENUMERATOR_SUCCESS",SWIG_From_int((int)((2)))); #if PY_VERSION_HEX >= 0x03000000 return m; #else return; #endif } recoll-1.36.1/python/pychm/setup.py.in0000644000175000017500000000303314515753036014573 00000000000000from setuptools import setup, Extension long_description = ''' Version of the chm package modified to support Python 3 and bundled with Recoll. The chm package provides three modules, chm, chmlib and extra, which provide access to the API implemented by the C library chmlib and some additional classes and functions. They are used to access MS-ITSS encoded files - Compressed Html Help files (.chm). ''' # For shadow builds: references to the source tree import os srcdir = '@srcdir@' # See setup.cfg.in if not os.path.exists('recollchm.egg-info'): os.mkdir('recollchm.egg-info') setup(name="recollchm", version="0.8.4.1+git", description="Python package to handle CHM files", author="Rubens Ramos", author_email="rubensr@users.sourceforge.net", maintainer="Mikhail Gusarov", maintainer_email="dottedmag@dottedmag.net", url="https://github.com/dottedmag/pychm", license="GPL", long_description=long_description, package_dir = {'' : srcdir}, py_modules=["recollchm.chm", "recollchm.chmlib"], ext_modules=[Extension("recollchm._chmlib", [os.path.join(srcdir, "recollchm/swig_chm.c")], libraries=["chm"], extra_compile_args=["-DSWIG_COBJECT_TYPES"]), Extension("recollchm.extra", [os.path.join(srcdir, "recollchm/extra.c")], extra_compile_args=["-D__PYTHON__"], libraries=["chm"])] ) recoll-1.36.1/python/recoll/0000755000175000017500000000000014521161751012667 500000000000000recoll-1.36.1/python/recoll/pyresultstore.cpp0000644000175000017500000002773114427373216016277 00000000000000/* Copyright (C) 2007-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include "qresultstore.h" #include "log.h" #include "rclutil.h" #include "pyrecoll.h" using namespace std; #if PY_MAJOR_VERSION >=3 # define Py_TPFLAGS_HAVE_ITER 0 #else #define PyLong_FromLong PyInt_FromLong #endif struct recoll_QRSDocObject; typedef struct { PyObject_HEAD /* Type-specific fields go here. */ Rcl::QResultStore *store; } recoll_QResultStoreObject; static void QResultStore_dealloc(recoll_QResultStoreObject *self) { LOGDEB1("QResultStore_dealloc.\n"); delete self->store; Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * QResultStore_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { LOGDEB1("QResultStore_new\n"); recoll_QResultStoreObject *self = (recoll_QResultStoreObject *)type->tp_alloc(type, 0); if (self == 0) return 0; self->store = new Rcl::QResultStore(); return (PyObject *)self; } PyDoc_STRVAR(qrs_doc_QResultStoreObject, "QResultStore()\n" "\n" "A QResultStore can efficiently store query result documents.\n" ); static int QResultStore_init( recoll_QResultStoreObject *self, PyObject *args, PyObject *kwargs) { LOGDEB("QResultStore_init\n"); return 0; } PyDoc_STRVAR( qrs_doc_storeQuery, "storeQuery(query, fieldspec=[], isinc=False)\n" "\n" "Stores the results from the input query object, possibly " "excluding/including the specified fields.\n" ); static PyObject * QResultStore_storeQuery(recoll_QResultStoreObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("QResultStore_storeQuery\n"); static const char* kwlist[] = {"query", "fieldspec", "isinc", NULL}; PyObject *q{nullptr}; PyObject *fieldspec{nullptr}; PyObject *isinco = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", (char**)kwlist, &recoll_QueryType, &q, &fieldspec, &isinco)) return nullptr; recoll_QueryObject *query = (recoll_QueryObject*)q; if (nullptr == query->query) { PyErr_SetString(PyExc_ValueError, "query not initialised (null query ?)"); return nullptr; } bool isinc{false}; if (nullptr != isinco && PyObject_IsTrue(isinco)) isinc = true; std::set fldspec; if (nullptr != fieldspec) { // fieldspec must be either single string or list of strings if (PyUnicode_Check(fieldspec)) { PyObject *utf8o = PyUnicode_AsUTF8String(fieldspec); if (nullptr == utf8o) { PyErr_SetString(PyExc_AttributeError, "storeQuery: can't encode field name??"); return nullptr; } fldspec.insert(PyBytes_AsString(utf8o)); Py_DECREF(utf8o); } else if (PySequence_Check(fieldspec)) { for (Py_ssize_t i = 0; i < PySequence_Size(fieldspec); i++) { PyObject *utf8o = PyUnicode_AsUTF8String(PySequence_GetItem(fieldspec, i)); if (nullptr == utf8o) { PyErr_SetString(PyExc_AttributeError, "storeQuery: can't encode field name??"); return nullptr; } fldspec.insert(PyBytes_AsString(utf8o)); Py_DECREF(utf8o); } } else { PyErr_SetString(PyExc_TypeError, "fieldspec arg must be str or sequence of str"); return nullptr; } } self->store->storeQuery(*(query->query), fldspec, isinc); Py_RETURN_NONE; } PyDoc_STRVAR( qrs_doc_getField, "getField(index, fieldname)\n" "\n" "Retrieve tha value of field from result at index .\n" ); static PyObject * QResultStore_getField(recoll_QResultStoreObject* self, PyObject *args) { int index; const char *fieldname; if (!PyArg_ParseTuple(args, "is", &index, &fieldname)) { return nullptr; } const char *result = self->store->fieldValue(index, fieldname); if (nullptr == result) { Py_RETURN_NONE; } else { return PyBytes_FromString(result); } } static PyMethodDef QResultStore_methods[] = { {"storeQuery", (PyCFunction)QResultStore_storeQuery, METH_VARARGS|METH_KEYWORDS, qrs_doc_storeQuery}, {"getField", (PyCFunction)QResultStore_getField, METH_VARARGS, qrs_doc_getField}, {NULL} /* Sentinel */ }; static Py_ssize_t QResultStore_Size(PyObject *o) { return ((recoll_QResultStoreObject*)o)->store->getCount(); } static PyObject* QResultStore_GetItem(PyObject *o, Py_ssize_t i) { if (i < 0 || i >= ((recoll_QResultStoreObject*)o)->store->getCount()) { return nullptr; } PyObject *args = Py_BuildValue("Oi", o, i); auto res = PyObject_CallObject((PyObject *)&recoll_QRSDocType, args); Py_DECREF(args); return res; } static PySequenceMethods resultstore_as_sequence = { (lenfunc)QResultStore_Size, // sq_length (binaryfunc)0, // sq_concat (ssizeargfunc)0, // sq_repeat (ssizeargfunc)QResultStore_GetItem, // sq_item 0, // was sq_slice (ssizeobjargproc)0, // sq_ass_item 0, // was sq_ass_slice (objobjproc)0, // sq_contains (binaryfunc)0, // sq_inplace_concat (ssizeargfunc)0, // sq_inplace_repeat }; PyTypeObject recoll_QResultStoreType = { PyVarObject_HEAD_INIT(NULL, 0) "_recoll.QResultStore", /*tp_name*/ sizeof(recoll_QResultStoreObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)QResultStore_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ &resultstore_as_sequence, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ qrs_doc_QResultStoreObject, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ QResultStore_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)QResultStore_init, /* tp_init */ 0, /* tp_alloc */ QResultStore_new, /* tp_new */ }; //////////////////////////////////////////////////////////////////////// // QRSDoc iterator typedef struct recoll_QRSDocObject { PyObject_HEAD /* Type-specific fields go here. */ recoll_QResultStoreObject *pystore; int index; } recoll_QRSDocObject; static void QRSDoc_dealloc(recoll_QRSDocObject *self) { LOGDEB1("QRSDoc_dealloc\n"); Py_DECREF(self->pystore); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * QRSDoc_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { recoll_QRSDocObject *self = (recoll_QRSDocObject *)type->tp_alloc(type, 0); if (self == 0) return 0; return (PyObject *)self; } PyDoc_STRVAR(qrs_doc_QRSDocObject, "QRSDoc(resultstore, index)\n" "\n" "A QRSDoc gives access to one result from a qresultstore.\n" ); static int QRSDoc_init( recoll_QRSDocObject *self, PyObject *args, PyObject *kwargs) { recoll_QResultStoreObject *pystore; int index; if (!PyArg_ParseTuple(args, "O!i", &recoll_QResultStoreType, &pystore, &index)) { return -1; } Py_INCREF(pystore); self->pystore = pystore; self->index = index; return 0; } static PyObject * QRSDoc_subscript(recoll_QRSDocObject *self, PyObject *key) { if (self->pystore == 0) { PyErr_SetString(PyExc_AttributeError, "store??"); return NULL; } string name; if (pys2cpps(key, name) < 0) { PyErr_SetString(PyExc_AttributeError, "name??"); Py_RETURN_NONE; } const char *value = self->pystore->store->fieldValue(self->index, name); if (nullptr == value) { Py_RETURN_NONE; } string urlstring; if (name == "url") { printableUrl("UTF-8", value, urlstring); value = urlstring.c_str(); } PyObject *bytes = PyBytes_FromString(value); PyObject *u = PyUnicode_FromEncodedObject(bytes, "UTF-8", "backslashreplace"); Py_DECREF(bytes); return u; } static PyMappingMethods qrsdoc_as_mapping = { (lenfunc)0, /*mp_length*/ (binaryfunc)QRSDoc_subscript, /*mp_subscript*/ (objobjargproc)0, /*mp_ass_subscript*/ }; static PyMethodDef QRSDoc_methods[] = { {NULL} /* Sentinel */ }; PyTypeObject recoll_QRSDocType = { PyVarObject_HEAD_INIT(NULL, 0) "_recoll.QRSDoc", /*tp_name*/ sizeof(recoll_QRSDocObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)QRSDoc_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ &qrsdoc_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ qrs_doc_QRSDocObject, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ QRSDoc_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)QRSDoc_init, /* tp_init */ 0, /* tp_alloc */ QRSDoc_new, /* tp_new */ }; recoll-1.36.1/python/recoll/pyrclextract.cpp0000644000175000017500000002251614443541772016055 00000000000000/* Copyright (C) 2007-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include "log.h" #include "rcldoc.h" #include "internfile.h" #include "rclconfig.h" #include "rclinit.h" #include "pyrecoll.h" using namespace std; ////////////////////////////////////////////////////////////////////// /// Extractor object code typedef struct { PyObject_HEAD /* Type-specific fields go here. */ FileInterner *xtr; std::shared_ptr rclconfig; recoll_DocObject *docobject; } rclx_ExtractorObject; static void Extractor_dealloc(rclx_ExtractorObject *self) { LOGDEB("Extractor_dealloc\n" ); if (self->docobject) { Py_DECREF(&self->docobject); } self->rclconfig.reset(); delete self->xtr; Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * Extractor_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { LOGDEB("Extractor_new\n" ); rclx_ExtractorObject *self = (rclx_ExtractorObject *)type->tp_alloc(type, 0); if (self == 0) return 0; self->xtr = 0; self->docobject = 0; return (PyObject *)self; } static int Extractor_init(rclx_ExtractorObject *self, PyObject *args, PyObject *kwargs) { LOGDEB("Extractor_init\n" ); static const char* kwlist[] = {"doc", NULL}; recoll_DocObject *dobj = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!:Extractor_init", (char**)kwlist, &recoll_DocType, &dobj)) return -1; if (dobj->doc == 0) { PyErr_SetString(PyExc_AttributeError, "Null Doc ?"); return -1; } self->docobject = dobj; Py_INCREF(dobj); self->rclconfig = dobj->rclconfig; self->xtr = new FileInterner(*dobj->doc, self->rclconfig.get(), FileInterner::FIF_forPreview); return 0; } PyDoc_STRVAR(doc_Extractor_textextract, "textextract(ipath)\n" "Extract document defined by ipath and return a doc object. The doc.text\n" "field has the document text as either text/plain or text/html\n" "according to doc.mimetype.\n" ); static PyObject * Extractor_textextract(rclx_ExtractorObject* self, PyObject *args, PyObject *kwargs) { LOGDEB("Extractor_textextract\n" ); static const char* kwlist[] = {"ipath", NULL}; char *sipath = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "es:Extractor_textextract", (char**)kwlist, "utf-8", &sipath)) return 0; string ipath(sipath); PyMem_Free(sipath); if (self->xtr == 0) { PyErr_SetString(PyExc_AttributeError, "extract: null object"); return 0; } /* Call the doc class object to create a new doc. */ recoll_DocObject *result = (recoll_DocObject *)PyObject_CallObject((PyObject *)&recoll_DocType, 0); if (!result) { PyErr_SetString(PyExc_AttributeError, "extract: doc create failed"); return 0; } FileInterner::Status status = self->xtr->internfile(*(result->doc), ipath); if (status != FileInterner::FIDone && status != FileInterner::FIAgain) { PyErr_SetString(PyExc_AttributeError, "internfile failure"); return 0; } string html = self->xtr->get_html(); if (!html.empty()) { result->doc->text = html; result->doc->mimetype = "text/html"; } // Is this actually needed ? Useful for url which is also formatted . Rcl::Doc *doc = result->doc; printableUrl(self->rclconfig->getDefCharset(), doc->url, doc->meta[Rcl::Doc::keyurl]); doc->meta[Rcl::Doc::keytp] = doc->mimetype; doc->meta[Rcl::Doc::keyipt] = doc->ipath; doc->meta[Rcl::Doc::keyfs] = doc->fbytes; doc->meta[Rcl::Doc::keyds] = doc->dbytes; return (PyObject *)result; } PyDoc_STRVAR(doc_Extractor_idoctofile, "idoctofile(ipath='', mimetype='', ofilename='')\n" "Extract document defined by ipath into a file, in its native format.\n" ); static PyObject * Extractor_idoctofile(rclx_ExtractorObject* self, PyObject *args, PyObject *kwargs) { LOGDEB("Extractor_idoctofile\n" ); static const char* kwlist[] = {"ipath", "mimetype", "ofilename", NULL}; char *sipath = 0; char *smt = 0; char *soutfile = 0; // no freeing if (!PyArg_ParseTupleAndKeywords(args,kwargs, "eses|s:Extractor_idoctofile", (char**)kwlist, "utf-8", &sipath, "utf-8", &smt, &soutfile)) return 0; string ipath(sipath); PyMem_Free(sipath); string mimetype(smt); PyMem_Free(smt); string outfile; if (soutfile && *soutfile) outfile.assign(soutfile); if (self->xtr == 0) { PyErr_SetString(PyExc_AttributeError, "idoctofile: null object"); return 0; } // If ipath is empty and we want the original mimetype, we can't // use FileInterner::internToFile() because the first conversion // was performed by the FileInterner constructor, so that we can't // reach the original object this way. Instead, if the data comes // from a file (m_fn set), we just copy it, else, we call // idoctofile, which will call topdoctofile (and re-fetch the // data, yes, wastefull) TempFile temp; bool status = false; LOGDEB("Extractor_idoctofile: ipath [" << ipath << "] mimetype [" << mimetype << "] doc mimetype [" << self->docobject->doc->mimetype << "\n"); if (ipath.empty() && !mimetype.compare(self->docobject->doc->mimetype)) { status = FileInterner::idocToFile(temp, outfile, self->rclconfig.get(), *self->docobject->doc); } else { self->xtr->setTargetMType(mimetype); status = self->xtr->interntofile(temp, outfile, ipath, mimetype); } if (!status) { PyErr_SetString(PyExc_AttributeError, "interntofile failure"); return 0; } if (outfile.empty()) temp.setnoremove(1); PyObject *result = outfile.empty() ? PyBytes_FromString(temp.filename()) : PyBytes_FromString(outfile.c_str()); return (PyObject *)result; } static PyMethodDef Extractor_methods[] = { {"textextract", (PyCFunction)Extractor_textextract, METH_VARARGS|METH_KEYWORDS, doc_Extractor_textextract}, {"idoctofile", (PyCFunction)Extractor_idoctofile, METH_VARARGS|METH_KEYWORDS, doc_Extractor_idoctofile}, {NULL} /* Sentinel */ }; PyDoc_STRVAR(doc_ExtractorObject, "Extractor()\n" "\n" "An Extractor object can extract data from a native simple or compound\n" "object.\n" ); PyTypeObject rclx_ExtractorType = { PyVarObject_HEAD_INIT(NULL, 0) "_rclextract.Extractor", /*tp_name*/ sizeof(rclx_ExtractorObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Extractor_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ doc_ExtractorObject, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Extractor_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Extractor_init, /* tp_init */ 0, /* tp_alloc */ Extractor_new, /* tp_new */ }; recoll-1.36.1/python/recoll/setup.cfg.in0000644000175000017500000000031014515753157015041 00000000000000 # There is nothing to configure here, we exist just to be copied into the build dir, and so that # The egg stuff gets created there and not in the source tree. [egg_info] egg_base = Recoll.egg-info recoll-1.36.1/python/recoll/recoll/0000755000175000017500000000000014521161751014147 500000000000000recoll-1.36.1/python/recoll/recoll/conftree.py0000644000175000017500000003236014517767745016276 00000000000000# Copyright (C) 2016-2022 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import print_function import collections import locale import re import os import sys import base64 import platform import shlex def _debug(s): print("%s"%s, file=sys.stderr) class ConfSimple(object): """A ConfSimple class reads a recoll configuration file, which is a typical ini file (see the Recoll manual). It's a dictionary of dictionaries which lets you retrieve named values from the top level or a subsection""" def __init__(self, confname, tildexp = False, readonly = True, casesensitive = True): self.casesens = casesensitive self.submaps = self._makedict() self.submaps[b''] = self._makedict() self.subkeys_unsorted = [] self.dotildexpand = tildexp self.readonly = readonly self.confname = confname try: f = open(confname, 'rb') except Exception as exc: #_debug("Open Exception: %s" % exc) # File does not exist -> empty config, not an error. return self._parseinput(f) f.close() def _makedict(self): if self.casesens: return dict() else: return CaseInsensitiveDict() def _parseinput(self, f): appending = False line = b'' submapkey = b'' for cline in f: cline = cline.rstrip(b'\r\n') if appending: line = line + cline else: line = cline line = line.strip() if line == b'' or line[0] == b'#'[0]: continue if line[len(line)-1] == b'\\'[0]: line = line[0:len(line)-1] appending = True continue appending = False #_debug(line) if line[0] == b'['[0]: line = line.strip(b'[]') if self.dotildexpand: submapkey = os.path.expanduser(line) if type(submapkey) == type(u''): submapkey = submapkey.encode('utf-8') else: submapkey = line #_debug("Submapkey: [%s]" % submapkey) self.subkeys_unsorted.append(submapkey) continue nm, sep, value = line.partition(b'=') if sep == b'': # No equal sign in line -> considered comment continue nm = nm.strip() value = value.strip() #_debug("sk [%s] nm: [%s] value: [%s]" % (submapkey, nm, value)) if not submapkey in self.submaps: self.submaps[submapkey] = self._makedict() self.submaps[submapkey][nm] = value def getSubKeys_unsorted(self): return [k.decode('utf-8') for k in self.subkeys_unsorted] def getbin(self, nm, sk = b''): '''Returns None if not found, empty string if found empty''' if type(nm) != type(b'') or type(sk) != type(b''): raise TypeError("getbin: parameters must be binary not unicode") #_debug("ConfSimple::getbin nm [%s] sk [%s]" % (nm, sk)) if not sk in self.submaps: return None if not nm in self.submaps[sk]: return None return self.submaps[sk][nm] def get(self, nm, sk = b''): dodecode = False if type(nm) == type(u''): dodecode = True nm = nm.encode('utf-8') if type(sk) == type(u''): sk = sk.encode('utf-8') #v = ConfSimple.getbin(self, nm, sk) v = self.getbin(nm, sk) if v is not None and dodecode: v = v.decode('utf-8') return v def getNamesbin(self, sk = b''): if not sk in self.submaps: return None return list(self.submaps[sk].keys()) def getNames(self, sk = ''): dodecode = False if type(sk) == type(u''): dodecode = True sk = sk.encode('utf-8') if not sk in self.submaps: return None names = self.getNamesbin(sk) if names and dodecode: names = [nm.decode('utf-8') for nm in names] return names def _rewrite(self): if self.readonly: raise Exception("ConfSimple is readonly") tname = self.confname + "-" f = open(tname, 'wb') # First output null subkey submap if b'' in self.submaps: for nm,value in self.submaps[b''].items(): f.write(nm + b'=' + value + b'\n') for sk,mp in self.submaps.items(): if sk == b'': continue f.write(b'[' + sk + b']\n') for nm,value in mp.items(): f.write(nm + b'=' + value + b'\n') f.close() try: # os.replace works on Windows even if dst exists, but py3 only os.replace(tname, self.confname) except: try: os.rename(tname, self.confname) except: import shutil shutil.move(tname, self.confname) def setbin(self, nm, value, sk = b''): if self.readonly: raise Exception("ConfSimple is readonly") if sk not in self.submaps: self.submaps[sk] = self._makedict() self.submaps[sk][nm] = value self._rewrite() return True def set(self, nm, value, sk = b''): if self.readonly: raise Exception("ConfSimple is readonly") if type(nm) == type(u''): nm = nm.encode('utf-8') if type(value) == type(u''): value = value.encode('utf-8') if type(sk) == type(u''): sk = sk.encode('utf-8') return self.setbin(nm, value, sk) class ConfTree(ConfSimple): """A ConfTree adds path-hierarchical interpretation of the section keys, which should be '/'-separated values. When a value is requested for a given path, it will also be searched in the sections corresponding to the ancestors. E.g. get(name, '/a/b') will also look in sections '/a' and '/' or '' (the last 2 are equivalent)""" def getbin(self, nm, sk = b''): if type(nm) != type(b'') or type(sk) != type(b''): raise TypeError("getbin: parameters must be binary not unicode") #_debug("ConfTree::getbin: nm [%s] sk [%s]" % (nm, sk)) # Note the test for root. There does not seem to be a direct # way to do this in os.path if not sk: return ConfSimple.getbin(self, nm, sk) # Try all sk ancestors as submaps (/a/b/c-> /a/b/c, /a/b, /a, b'') while True: if sk in self.submaps: return ConfSimple.getbin(self, nm, sk) if sk + b'/' in self.submaps: return ConfSimple.getbin(self, nm, sk + b'/') nsk = os.path.dirname(sk) if nsk == sk: # sk was already root, we're done. break; sk = nsk return ConfSimple.getbin(self, nm) class ConfStack(object): """ A ConfStack manages the superposition of a list of Configuration objects. Values are looked for in each object from the list until found. This typically provides for defaults overridden by sparse values in the topmost file.""" def __init__(self, nm, dirs, tp = 'simple'): fnames = [] for dir in dirs: fnm = os.path.join(dir, nm) fnames.append(fnm) self._construct(tp, fnames) def _construct(self, tp, fnames): self.confs = [] for fname in fnames: if tp.lower() == 'simple': conf = ConfSimple(fname) else: conf = ConfTree(fname) self.confs.append(conf) # Accepts / returns binary strings (non-unicode) def getbin(self, nm, sk = b''): if type(nm) != type(b'') or type(sk) != type(b''): raise TypeError("getbin: parameters must be binary not unicode") for conf in self.confs: value = conf.getbin(nm, sk) if value is not None: return value return None def get(self, nm, sk = b''): dodecode = False if type(nm) == type(u''): dodecode = True nm = nm.encode('utf-8') if type(sk) == type(u''): sk = sk.encode('utf-8') #v = ConfSimple.getbin(self, nm, sk) v = self.getbin(nm, sk) if v is not None and dodecode: v = v.decode('utf-8') return v # Split string of strings, with possible quoting and escaping. # The default is do do Recoll stringToStrings emulation: whitespace # separated, and doublequotes only (C-style). E.G.: # word1 word2 "compound \\"quoted\\" string" -> # ['word1', 'word2', 'compound "quoted string'] # # This is not the shlex default and can be changed by setting the # parameters def stringToStrings(s, quotes = '"', escape = '\\', escapedquotes = '"', whitespace = None): lex = shlex.shlex(s, posix=True) lex.whitespace_split = True if quotes is not None: lex.quotes = quotes if escape is not None: lex.escape = escape if escapedquotes is not None: lex.escapedquotes = escapedquotes if whitespace is not None: lex.whitespace = whitespace l = [] while True: tok = lex.get_token() if not tok: break l.append(tok) return l def stringsToString(vs): out = [] for s in vs: if s.find(" ") != -1 or s.find("\t") != -1 or s.find("\\") != -1 or \ s.find('"') != -1: out.append('"' + s.replace('\\', '\\\\').replace('"', '\\"') + '"') else: out.append(s) return " ".join(out) def valToBool(s): if not s: return False try: val = int(s) return val != 0 except: pass if type(s) == type(b''): s = s.decode("UTF-8") return s[0] in "tTyY" # CaseInsensitiveDict was copied from: # Requests - https://github.com/psf/requests # Copyright 2019 Kenneth Reitz # Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses try: from collections.abc import MutableMapping except: from collections import MutableMapping class CaseInsensitiveDict(MutableMapping): """ A case-insensitive ``dict``-like object. Implements all methods and operations of ``collections.MutableMapping`` as well as dict's ``copy``. Also provides ``lower_items``. All keys are expected to be strings. The structure remembers the case of the last key to be set, and ``iter(instance)``, ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` will contain case-sensitive keys. However, querying and contains testing is case insensitive: cid = CaseInsensitiveDict() cid['Accept'] = 'application/json' cid['aCCEPT'] == 'application/json' # True list(cid) == ['Accept'] # True For example, ``headers['content-encoding']`` will return the value of a ``'Content-Encoding'`` response header, regardless of how the header name was originally stored. If the constructor, ``.update``, or equality comparison operations are given keys that have equal ``.lower()``s, the behavior is undefined. """ def __init__(self, data=None, **kwargs): self._store = dict() if data is None: data = {} self.update(data, **kwargs) def __setitem__(self, key, value): # Use the lowercased key for lookups, but store the actual # key alongside the value. self._store[key.lower()] = (key, value) def __getitem__(self, key): return self._store[key.lower()][1] def __delitem__(self, key): del self._store[key.lower()] def __iter__(self): return (casedkey for casedkey, mappedvalue in self._store.values()) def __len__(self): return len(self._store) def lower_items(self): """Like iteritems(), but with all lowercase keys.""" return ( (lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items() ) def __eq__(self, other): if isinstance(other, collections.Mapping): other = CaseInsensitiveDict(other) else: return NotImplemented # Compare insensitively return dict(self.lower_items()) == dict(other.lower_items()) # Copy is required def copy(self): return CaseInsensitiveDict(self._store.values()) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, dict(self.items())) recoll-1.36.1/python/recoll/recoll/__init__.py0000644000175000017500000000000014410615043016161 00000000000000recoll-1.36.1/python/recoll/recoll/recoll.py0000644000175000017500000000206714410615043015721 00000000000000# Copyright (C) 2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # We used to have two C extensions: recoll and rclextract, which was a really # bad idea. They are now merged into the _recoll C extension module. The two # python modules recoll.py and rclextract.py only exist for compatibility (for # now: maybe we'll do something with them in the future). from ._recoll import * recoll-1.36.1/python/recoll/recoll/rclconfig.py0000644000175000017500000001565414517767177016445 00000000000000#!/usr/bin/env python3 from __future__ import print_function import locale import re import os import sys import base64 import platform try: from . import conftree except: import conftree def msg(s): print("%s" % s, file=sys.stderr) class RclDynConf: def __init__(self, fname): self.data = conftree.ConfSimple(fname) def getStringList(self, sk): nms = self.data.getNames(sk) out = [] if nms is not None: for nm in nms: out.append(base64.b64decode(self.data.get(nm, sk))) return out class RclConfig: def __init__(self, argcnf = None): self.config = None self.mimemap = None platsys = platform.system() # Find configuration directory if argcnf: self.confdir = os.path.abspath(argcnf) elif "RECOLL_CONFDIR" in os.environ: self.confdir = os.environ["RECOLL_CONFDIR"] else: if platsys == "Windows": if "LOCALAPPDATA" in os.environ: dir = os.environ["LOCALAPPDATA"] else: dir = os.path.expanduser("~") self.confdir = os.path.join(dir, "Recoll") else: self.confdir = os.path.expanduser("~/.recoll") #msg(f"Confdir: [{self.confdir}]") # Find datadir to get at the base configuration files. This is tricky because this module # could be loaded either from the recoll filters/ directory, or from the Recoll Python # extension. In the first case, the base configuration should be in __file__/../examples. # In the second case, if we are loaded from a recoll filter, we can use sys.argv[0], # but else, we have to guess. self.datadir = None if "RECOLL_DATADIR" in os.environ: self.datadir = os.environ["RECOLL_DATADIR"] else: if platsys == "Windows": # Note that this is not guaranteed to work if we come from the Python extension and # not started from a recoll filter. Then we try a guess, which will fail if recoll # is installed in a non-default (or multiple) place. dirs = (os.path.join(os.path.dirname(sys.argv[0]), "..", ".."), "C:/Program Files (X86)/Recoll/", "C:/Program Files/Recoll/", "C:/install/recoll/") for dir in dirs: if os.path.exists(os.path.join(dir, "Share")): self.datadir = os.path.join(dir, "Share") break elif platsys == "Darwin": # On Darwin things are simpler because we don't supply a Python package. We are # certainly loaded from filters/ self.datadir = os.path.join(os.path.dirname(__file__), "..") else: # Try to use __file__ and sys.argv[0]. This may fail if we're not a filter for filtersdir in (os.path.dirname(__file__), os.path.dirname(sys.argv[0])): if filtersdir.find("recoll/filters") != -1: #msg("Using __file__ to compute datadir") self.datadir = os.path.dirname(filtersdir) break if self.datadir is None: # On ux platforms, the datadir value is set by "configure" in the C code. # Try to guess #msg("Trying to guess datadir") dirs = ("/opt/local", "/opt", "/usr", "/usr/local", "/usr/pkg") for dir in dirs: dd = os.path.join(dir, "share/recoll") if os.path.exists(dd): self.datadir = dd if self.datadir is None: #msg("DATADIR could not be computed. Trying /usr/share/recoll as last resort") self.datadir = "/usr/share/recoll" #msg(f"DATADIR: [{self.datadir}]") baseconfdir = os.path.join(self.datadir, "examples") f = None try: f = open(os.path.join(baseconfdir, "recoll.conf"), "r") except: pass if f is None: raise(Exception( "Can't open default/system recoll.conf in [%s]. " % baseconfdir + "Please set RECOLL_DATADIR in the environment to point " + "to the installed recoll data files.")) else: f.close() self.cdirs = [] # Additional config directory, values override user ones if "RECOLL_CONFTOP" in os.environ: self.cdirs.append(os.environ["RECOLL_CONFTOP"]) self.cdirs.append(self.confdir) # Additional config directory, overrides system's, overridden by user's if "RECOLL_CONFMID" in os.environ: self.cdirs.append(os.environ["RECOLL_CONFMID"]) self.cdirs.append(os.path.join(self.datadir, "examples")) #msg("Config dirs: {self.cdirs}") self.keydir = '' def getConfDir(self): return self.confdir def getDataDir(self): return self.datadir def getDbDir(self): dir = self.getConfParam("dbdir") if os.path.isabs(dir): return dir cachedir = self.getConfParam("cachedir") if not cachedir: cachedir = self.confdir return os.path.join(cachedir, dir) def setKeyDir(self, dir): self.keydir = dir def getConfParam(self, nm): if not self.config: self.config = conftree.ConfStack("recoll.conf", self.cdirs, "tree") return self.config.get(nm, self.keydir) # This is a simplified version of the c++ code, intended mostly for the # test mode of rclexecm.py. We don't attempt to check the data, so this # will not work on extension-less paths (e.g. mbox/mail/etc.) def mimeType(self, path): if not self.mimemap: self.mimemap = conftree.ConfStack("mimemap", self.cdirs, "tree") if os.path.exists(path): if os.path.isdir(path): return "inode/directory" if os.path.islink(path): return "inode/symlink" if not os.path.isfile(path): return "inode/x-fsspecial" try: size = os.path.getsize(path) if size == 0: return "inode/x-empty" except: pass ext = os.path.splitext(path)[1] return self.mimemap.get(ext, self.keydir) class RclExtraDbs: def __init__(self, config): self.config = config def getActDbs(self): dyncfile = os.path.join(self.config.getConfDir(), "history") dync = RclDynConf(dyncfile) return dync.getStringList("actExtDbs") if __name__ == '__main__': config = RclConfig() print("topdirs = %s" % config.getConfParam('topdirs')) extradbs = RclExtraDbs(config) print("%s" % extradbs.getActDbs()) recoll-1.36.1/python/recoll/recoll/rclextract.py0000644000175000017500000000210014410615043016600 00000000000000# Copyright (C) 2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # We used to have two C extensions: recoll and rclextract, which was a really # bad idea. They are now merged into the _recoll C extension module. The two # python modules recoll.py and rclextract.py only exist for compatibility (for # now: maybe we'll do something with them in the future). from ._recoll import Extractor recoll-1.36.1/python/recoll/pyrecoll.cpp0000644000175000017500000023251614467105035015157 00000000000000/* Copyright (C) 2007-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include "rclinit.h" #include "rclconfig.h" #include "rcldb.h" #include "searchdata.h" #include "rclquery.h" #include "pathut.h" #include "rclutil.h" #include "wasatorcl.h" #include "log.h" #include "plaintorich.h" #include "hldata.h" #include "smallut.h" #include "idxstatus.h" #include "rcldoc.h" #include "pyrecoll.h" using namespace std; #ifdef _WIN32 #define strcasecmp _stricmp #endif /* _WIN32 */ #if PY_MAJOR_VERSION >=3 # define Py_TPFLAGS_HAVE_ITER 0 #else #define PyLong_FromLong PyInt_FromLong #endif // To keep old code going after we moved the static rclconfig to the // db object (to fix multiple dbs issues), we keep a copy of the last // created rclconfig in RCLCONFIG. This is set into the doc objec by // doc_init, then reset to the db's by db::doc() or query::iter_next, // the proper Doc creators. static shared_ptr RCLCONFIG; ////////////////////////////////////////////////////////////////////// /// SEARCHDATA SearchData code typedef struct { PyObject_HEAD /* Type-specific fields go here. */ std::shared_ptr sd; } recoll_SearchDataObject; static void SearchData_dealloc(recoll_SearchDataObject *self) { LOGDEB("SearchData_dealloc. Releasing. Count before: " << self->sd.use_count() << "\n"); self->sd.reset(); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * SearchData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { LOGDEB("SearchData_new\n"); recoll_SearchDataObject *self; self = (recoll_SearchDataObject *)type->tp_alloc(type, 0); if (self == 0) return 0; return (PyObject *)self; } PyDoc_STRVAR(doc_SearchDataObject, "SearchData([type=AND|OR], [stemlang=somelanguage|null])\n" "\n" "A SearchData object describes a query. It has a number of global\n" "parameters and a chain of search clauses.\n" ); static int SearchData_init(recoll_SearchDataObject *self, PyObject *args, PyObject *kwargs) { LOGDEB("SearchData_init\n"); static const char* kwlist[] = {"type", "stemlang", NULL}; char *stp = 0; char *steml = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sz", (char**)kwlist, &stp, &steml)) return -1; Rcl::SClType tp = Rcl::SCLT_AND; if (stp && strcasecmp(stp, "or")) { tp = Rcl::SCLT_OR; } string stemlang; if (steml) { stemlang = steml; } else { stemlang = "english"; } self->sd = std::shared_ptr(new Rcl::SearchData(tp, stemlang)); return 0; } /* Note: addclause necessite And/Or vient du fait que le string peut avoir plusieurs mots. A transferer dans l'i/f Python ou pas ? */ PyDoc_STRVAR(doc_addclause, "addclause(type='and'|'or'|'filename'|'phrase'|'near'|'path'|'sub',\n" " qstring=string, slack=int, field=string, stemming=1|0,\n" " subSearch=SearchData, exclude=0|1, anchorstart=0|1, anchorend=0|1,\n" " casesens=0|1, diacsens=0|1)\n" "Adds a simple clause to the SearchData And/Or chain, or a subquery\n" "defined by another SearchData object\n" ); /* Forward declaration only, definition needs recoll_searchDataType */ static PyObject * SearchData_addclause(recoll_SearchDataObject* self, PyObject *args, PyObject *kwargs); static PyMethodDef SearchData_methods[] = { {"addclause", (PyCFunction)SearchData_addclause, METH_VARARGS|METH_KEYWORDS, doc_addclause}, {NULL} /* Sentinel */ }; static PyTypeObject recoll_SearchDataType = { PyVarObject_HEAD_INIT(NULL, 0) "_recoll.SearchData", /*tp_name*/ sizeof(recoll_SearchDataObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)SearchData_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ doc_SearchDataObject, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ SearchData_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)SearchData_init, /* tp_init */ 0, /* tp_alloc */ SearchData_new, /* tp_new */ }; static PyObject * SearchData_addclause(recoll_SearchDataObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("SearchData_addclause\n"); if (!self->sd) { LOGERR("SearchData_addclause: not init??\n"); PyErr_SetString(PyExc_AttributeError, "sd"); return 0; } static const char *kwlist[] = {"type", "qstring", "slack", "field", "stemming", "subsearch", "exclude", "anchorstart", "anchorend", "casesens", "diacsens", NULL}; char *tp = 0; char *qs = 0; // needs freeing int slack = 0; char *fld = 0; // needs freeing PyObject *dostem = 0; recoll_SearchDataObject *sub = 0; PyObject *exclude = 0; PyObject *anchorstart = 0; PyObject *anchorend = 0; PyObject *casesens = 0; PyObject *diacsens = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ses|iesOO!OOOOO", (char**)kwlist, &tp, "utf-8", &qs, &slack, "utf-8", &fld, &dostem, &recoll_SearchDataType, &sub, &exclude, &anchorstart, &anchorend, &casesens, &diacsens )) return 0; Rcl::SearchDataClause *cl = 0; switch (tp[0]) { case 'a': case 'A': if (strcasecmp(tp, "and")) goto defaultcase; cl = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND, qs, fld ? fld : ""); break; case 'f': case 'F': if (strcasecmp(tp, "filename")) goto defaultcase; cl = new Rcl::SearchDataClauseFilename(qs); break; case 'o': case 'O': if (strcasecmp(tp, "or")) goto defaultcase; cl = new Rcl::SearchDataClauseSimple(Rcl::SCLT_OR, qs, fld ? fld : ""); break; case 'n': case 'N': if (strcasecmp(tp, "near")) goto defaultcase; cl = new Rcl::SearchDataClauseDist(Rcl::SCLT_NEAR, qs, slack, fld ? fld : ""); break; case 'p': case 'P': if (!strcasecmp(tp, "phrase")) { cl = new Rcl::SearchDataClauseDist(Rcl::SCLT_PHRASE, qs, slack, fld ? fld : ""); } else if (!strcasecmp(tp, "path")) { cl = new Rcl::SearchDataClausePath(qs); } else { goto defaultcase; } break; case 's': case 'S': if (strcasecmp(tp, "sub")) goto defaultcase; cl = new Rcl::SearchDataClauseSub(sub->sd); break; defaultcase: default: PyErr_SetString(PyExc_AttributeError, "Bad tp arg"); return 0; } PyMem_Free(qs); PyMem_Free(fld); if (dostem != 0 && !PyObject_IsTrue(dostem)) { cl->addModifier(Rcl::SearchDataClause::SDCM_NOSTEMMING); } if (exclude != 0 && !PyObject_IsTrue(exclude)) { cl->setexclude(true); } if (anchorstart && PyObject_IsTrue(anchorstart)) { cl->addModifier(Rcl::SearchDataClause::SDCM_ANCHORSTART); } if (anchorend && PyObject_IsTrue(anchorend)) { cl->addModifier(Rcl::SearchDataClause::SDCM_ANCHOREND); } if (casesens && PyObject_IsTrue(casesens)) { cl->addModifier(Rcl::SearchDataClause::SDCM_CASESENS); } if (diacsens && PyObject_IsTrue(diacsens)) { cl->addModifier(Rcl::SearchDataClause::SDCM_DIACSENS); } self->sd->addClause(cl); Py_RETURN_NONE; } /////////////////////////////////////////////////////////////////////// ///// DOC Doc code static void Doc_dealloc(recoll_DocObject *self) { LOGDEB("Doc_dealloc\n"); deleteZ(self->doc); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * Doc_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { LOGDEB("Doc_new\n"); recoll_DocObject *self; self = (recoll_DocObject *)type->tp_alloc(type, 0); if (self == 0) return 0; self->doc = 0; return (PyObject *)self; } static int Doc_init(recoll_DocObject *self, PyObject *, PyObject *) { LOGDEB("Doc_init\n"); delete self->doc; self->doc = new Rcl::Doc; if (self->doc == 0) return -1; self->rclconfig = RCLCONFIG; return 0; } PyDoc_STRVAR( doc_Doc_getbinurl, "getbinurl(none) -> binary url\n" "\n" "Returns an URL with a path part which is a as bit for bit copy of the \n" "file system path, without encoding\n" ); static PyObject * Doc_getbinurl(recoll_DocObject *self) { LOGDEB0("Doc_getbinurl\n"); if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc is NULL"); Py_RETURN_NONE; } return PyBytes_FromStringAndSize(self->doc->url.c_str(), self->doc->url.size()); } PyDoc_STRVAR( doc_Doc_setbinurl, "setbinurl(url) -> binary url\n" "\n" "Set the URL from binary path like file://may/contain/unencodable/bytes\n" ); static PyObject * Doc_setbinurl(recoll_DocObject *self, PyObject *value) { LOGDEB0("Doc_setbinurl\n"); if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc??"); return 0; } if (PyByteArray_Check(value)) { self->doc->url = string(PyByteArray_AsString(value), PyByteArray_Size(value)); } else if (PyBytes_Check(value)) { self->doc->url = string(PyBytes_AsString(value), PyBytes_Size(value)); } else { PyErr_SetString(PyExc_TypeError, "setbinurl needs bytearray or bytes argument"); return 0; } printableUrl(self->rclconfig->getDefCharset(), self->doc->url, self->doc->meta[Rcl::Doc::keyurl]); Py_RETURN_NONE; } PyDoc_STRVAR(doc_Doc_keys, "keys() -> list of doc object keys (attribute names)\n" ); static PyObject * Doc_keys(recoll_DocObject *self) { LOGDEB0("Doc_keys\n"); if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc"); return 0; } PyObject *pkeys = PyList_New(0); if (!pkeys) return 0; for (const auto& entry : self->doc->meta) { PyList_Append( pkeys, PyUnicode_Decode(entry.first.c_str(), entry.first.size(), "UTF-8", "replace")); } return pkeys; } PyDoc_STRVAR(doc_Doc_items, "items() -> dictionary of doc object keys/values\n" ); static PyObject * Doc_items(recoll_DocObject *self) { LOGDEB0("Doc_items\n"); if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc"); return 0; } PyObject *pdict = PyDict_New(); if (!pdict) return 0; for (const auto& entry : self->doc->meta) { PyDict_SetItem(pdict, PyUnicode_Decode( entry.first.c_str(), entry.first.size(), "UTF-8", "replace"), PyUnicode_Decode( entry.second.c_str(), entry.second.size(), "UTF-8", "replace")); } return pdict; } static bool idocget(recoll_DocObject *self, const string& key, string& value) { switch (key.at(0)) { case 'u': if (!key.compare(Rcl::Doc::keyurl)) { value = self->doc->url; return true; } break; case 'f': if (!key.compare(Rcl::Doc::keyfs)) { value = self->doc->fbytes; return true; } else if (!key.compare(Rcl::Doc::keyfmt)) { value = self->doc->fmtime; return true; } break; case 'd': if (!key.compare(Rcl::Doc::keyds)) { value = self->doc->dbytes; return true; } else if (!key.compare(Rcl::Doc::keydmt)) { value = self->doc->dmtime; return true; } break; case 'i': if (!key.compare(Rcl::Doc::keyipt)) { value = self->doc->ipath; return true; } break; case 'm': if (!key.compare(Rcl::Doc::keytp)) { value = self->doc->mimetype; return true; } else if (!key.compare(Rcl::Doc::keymt)) { value = self->doc->dmtime.empty() ? self->doc->fmtime : self->doc->dmtime; return true; } break; case 'o': if (!key.compare(Rcl::Doc::keyoc)) { value = self->doc->origcharset; return true; } break; case 's': if (!key.compare(Rcl::Doc::keysig)) { value = self->doc->sig; return true; } else if (!key.compare(Rcl::Doc::keysz)) { value = self->doc->dbytes.empty() ? self->doc->fbytes : self->doc->dbytes; return true; } break; case 't': if (!key.compare("text")) { value = self->doc->text; return true; } break; case 'x': if (!key.compare("xdocid")) { ulltodecstr(self->doc->xdocid, value); return true; } break; } if (self->doc->getmeta(key, 0)) { value = self->doc->meta[key]; return true; } return false; } PyDoc_STRVAR(doc_Doc_get, "get(key) -> value\n" "Retrieve the named doc attribute\n" ); static PyObject * Doc_get(recoll_DocObject *self, PyObject *args) { LOGDEB1("Doc_get\n"); if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc??"); return 0; } char *sutf8 = 0; // needs freeing if (!PyArg_ParseTuple(args, "es:Doc_get", "utf-8", &sutf8)) { return 0; } string key(sutf8); PyMem_Free(sutf8); string value; if (idocget(self, key, value)) { return PyUnicode_Decode(value.c_str(), value.size(), "UTF-8","replace"); } Py_RETURN_NONE; } static PyMethodDef Doc_methods[] = { {"getbinurl", (PyCFunction)Doc_getbinurl, METH_NOARGS, doc_Doc_getbinurl}, {"setbinurl", (PyCFunction)Doc_setbinurl, METH_O, doc_Doc_setbinurl}, {"keys", (PyCFunction)Doc_keys, METH_NOARGS, doc_Doc_keys}, {"items", (PyCFunction)Doc_items, METH_NOARGS, doc_Doc_items}, {"get", (PyCFunction)Doc_get, METH_VARARGS, doc_Doc_get}, {NULL} /* Sentinel */ }; int pys2cpps(PyObject *pyval, std::string& out) { if (PyUnicode_Check(pyval)) { PyObject* utf8o = PyUnicode_AsUTF8String(pyval); if (utf8o == 0) { return -1; } out = PyBytes_AsString(utf8o); Py_DECREF(utf8o); } else if (PyBytes_Check(pyval)) { out = PyBytes_AsString(pyval); } else { return -1; } return 0; } // Note that this returns None if the attribute is not found instead of raising // an exception as would be standard. We don't change it to keep existing code // working. static PyObject * Doc_getattro(recoll_DocObject *self, PyObject *nameobj) { if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc"); return 0; } if (!self->rclconfig || !self->rclconfig->ok()) { PyErr_SetString(PyExc_AttributeError, "Configuration not initialized"); return 0; } PyObject *meth = PyObject_GenericGetAttr((PyObject*)self, nameobj); if (meth) { return meth; } PyErr_Clear(); string name; if (pys2cpps(nameobj, name) < 0) { PyErr_SetString(PyExc_AttributeError, "name not unicode nor string??"); Py_RETURN_NONE; } string key = self->rclconfig->fieldQCanon(name); string value; if (idocget(self, key, value)) { LOGDEB1("Doc_getattro: [" << key << "] -> [" << value << "]\n"); // Return a python unicode object return PyUnicode_Decode(value.c_str(), value.size(), "utf-8","replace"); } Py_RETURN_NONE; } static int Doc_setattro(recoll_DocObject *self, PyObject *nameobj, PyObject *value) { if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc??"); return -1; } if (!self->rclconfig || !self->rclconfig->ok()) { PyErr_SetString(PyExc_AttributeError, "Configuration not initialized"); return -1; } string name; if (pys2cpps(nameobj, name) < 0) { PyErr_SetString(PyExc_AttributeError, "name not unicode nor string??"); return -1; } string uvalue; if (pys2cpps(value, uvalue) < 0) { PyErr_SetString(PyExc_AttributeError, "value neither bytes nor str"); return -1; } string key = self->rclconfig->fieldQCanon(name); LOGDEB0("Doc_setattr: doc " << self->doc << " [" << key << "] (" << name << ") -> [" << uvalue << "]\n"); // Note that some attributes are set both as struct fields and // meta members, keep compat with movedocfields() used when // fetching from query. self->doc->meta[key] = uvalue; switch (key.at(0)) { case 't': if (key == "text") { self->doc->text.swap(uvalue); } break; case 'u': if (key == Rcl::Doc::keyurl) { self->doc->url.swap(uvalue); printableUrl(self->rclconfig->getDefCharset(), self->doc->url, self->doc->meta[Rcl::Doc::keyurl]); } break; case 'f': if (key == Rcl::Doc::keyfs) { self->doc->fbytes.swap(uvalue); self->doc->meta[Rcl::Doc::keyfs] = self->doc->fbytes; } else if (key == Rcl::Doc::keyfmt) { self->doc->fmtime.swap(uvalue); } break; case 'd': if (key == Rcl::Doc::keyds) { self->doc->dbytes.swap(uvalue); self->doc->meta[Rcl::Doc::keyds] = self->doc->dbytes; } else if (key == Rcl::Doc::keydmt) { self->doc->dmtime.swap(uvalue); } break; case 'i': if (key == Rcl::Doc::keyipt) { self->doc->ipath.swap(uvalue); self->doc->meta[Rcl::Doc::keyipt] = self->doc->ipath; } break; case 'm': if (key == Rcl::Doc::keytp) { self->doc->mimetype.swap(uvalue); self->doc->meta[Rcl::Doc::keytp] = self->doc->mimetype; } else if (key == Rcl::Doc::keymt) { self->doc->dmtime.swap(uvalue); } break; case 'o': if (key == Rcl::Doc::keyoc) { self->doc->origcharset.swap(uvalue); } break; case 's': if (key == Rcl::Doc::keysig) { self->doc->sig.swap(uvalue); } else if (key == Rcl::Doc::keysz) { self->doc->dbytes.swap(uvalue); } break; } return 0; } static Py_ssize_t Doc_length(recoll_DocObject *self) { if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc??"); return -1; } return self->doc->meta.size(); } static PyObject * Doc_subscript(recoll_DocObject *self, PyObject *key) { // Can't just return getattro because this first checks for a method name if (self->doc == 0) { PyErr_SetString(PyExc_AttributeError, "doc??"); return NULL; } if (!self->rclconfig || !self->rclconfig->ok()) { PyErr_SetString(PyExc_AttributeError, "Configuration not initialized"); return NULL; } string name; if (pys2cpps(key, name) < 0) { PyErr_SetString(PyExc_AttributeError, "key not unicode nor string??"); Py_RETURN_NONE; } string skey = self->rclconfig->fieldQCanon(name); string value; if (idocget(self, skey, value)) { return PyUnicode_Decode(value.c_str(), value.size(), "UTF-8", "backslashreplace"); } Py_RETURN_NONE; } static int Doc_ass_subscript(recoll_DocObject *self, PyObject *key, PyObject *val) { return Doc_setattro(self, key, val); } static PyMappingMethods doc_as_mapping = { (lenfunc)Doc_length, /*mp_length*/ (binaryfunc)Doc_subscript, /*mp_subscript*/ (objobjargproc)Doc_ass_subscript, /*mp_ass_subscript*/ }; PyDoc_STRVAR( doc_DocObject, "Doc()\n" "\n" "A Doc object contains index data for a given document.\n" "The data is extracted from the index when searching, or set by the\n" "indexer program when updating. The Doc object has no useful methods but\n" "many attributes to be read or set by its user. It matches exactly the\n" "Rcl::Doc c++ object. Some of the attributes are predefined, but, \n" "especially when indexing, others can be set, the name of which will be\n" "processed as field names by the indexing configuration.\n" "Inputs can be specified as unicode or strings.\n" "Outputs are unicode objects.\n" "All dates are specified as unix timestamps, printed as strings\n" "Predefined attributes (index/query/both):\n" " text (index): document plain text\n" " url (both)\n" " fbytes (both) optional) file size in bytes\n" " filename (both)\n" " fmtime (both) optional file modification date. Unix time printed \n" " as string\n" " dbytes (both) document text bytes\n" " dmtime (both) document creation/modification date\n" " ipath (both) value private to the app.: internal access path\n" " inside file\n" " mtype (both) mime type for original document\n" " mtime (query) dmtime if set else fmtime\n" " origcharset (both) charset the text was converted from\n" " size (query) dbytes if set, else fbytes\n" " sig (both) app-defined file modification signature. \n" " For up to date checks\n" " relevancyrating (query)\n" " abstract (both)\n" " author (both)\n" " title (both)\n" " keywords (both)\n" ); PyTypeObject recoll_DocType = { PyVarObject_HEAD_INIT(NULL, 0) "_recoll.Doc", /*tp_name*/ sizeof(recoll_DocObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Doc_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ &doc_as_mapping, /*tp_as_mapping */ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ (getattrofunc)Doc_getattro,/*tp_getattro*/ (setattrofunc)Doc_setattro,/*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ doc_DocObject, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Doc_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Doc_init, /* tp_init */ 0, /* tp_alloc */ Doc_new, /* tp_new */ }; ////////////////////////////////////////////////////// /// QUERY Query object typedef struct recoll_DbObject { PyObject_HEAD /* Type-specific fields go here. */ Rcl::Db *db; std::shared_ptr rclconfig; } recoll_DbObject; PyDoc_STRVAR(doc_Query_close, "close(). Deallocate query. Object is unusable after the call." ); static PyObject * Query_close(recoll_QueryObject *self) { LOGDEB("Query_close\n"); if (self->query) { deleteZ(self->query); } deleteZ(self->sortfield); if (self->connection) { Py_DECREF(self->connection); self->connection = 0; } Py_RETURN_NONE; } static void Query_dealloc(recoll_QueryObject *self) { LOGDEB("Query_dealloc\n"); PyObject *ret = Query_close(self); Py_DECREF(ret); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * Query_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { LOGDEB("Query_new\n"); recoll_QueryObject *self; self = (recoll_QueryObject *)type->tp_alloc(type, 0); if (self == 0) return 0; self->query = 0; self->next = -1; self->rowcount = -1; self->sortfield = new string; self->ascending = 1; self->arraysize = 1; self->connection = 0; self->fetchtext = false; return (PyObject *)self; } // Query_init creates an unusable object. The only way to create a // valid Query Object is through db_query(). (or we'd need to add a Db // parameter to the Query object creation method) static int Query_init(recoll_QueryObject *self, PyObject *, PyObject *) { LOGDEB("Query_init\n"); delete self->query; self->query = 0; self->next = -1; self->ascending = true; return 0; } static PyObject * Query_iter(PyObject *self) { Py_INCREF(self); return self; } PyDoc_STRVAR(doc_Query_sortby, "sortby(field=fieldname, ascending=True)\n" "Sort results by 'fieldname', in ascending or descending order.\n" "Only one field can be used, no subsorts for now.\n" "Must be called before executing the search\n" ); static PyObject * Query_sortby(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Query_sortby\n"); static const char *kwlist[] = {"field", "ascending", NULL}; char *sfield = 0; PyObject *ascobj = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", (char**)kwlist, &sfield, &ascobj)) return 0; if (sfield) { self->sortfield->assign(sfield); } else { self->sortfield->clear(); } if (ascobj == 0) { self->ascending = true; } else { self->ascending = PyObject_IsTrue(ascobj); } Py_RETURN_NONE; } PyDoc_STRVAR(doc_Query_execute, "execute(query_string, stemming=1|0, stemlang=\"stemming language\", " "fetchtext=False)\n" "\n" "Starts a search for query_string, a Recoll search language string\n" "(mostly Xesam-compatible).\n" "The query can be a simple list of terms (and'ed by default), or more\n" "complicated with field specs etc. See the Recoll manual.\n" ); static PyObject * Query_execute(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Query_execute\n"); static const char *kwlist[] = {"query_string", "stemming", "stemlang", "fetchtext", "collapseduplicates", NULL}; char *sutf8 = 0; // needs freeing char *sstemlang = 0; PyObject *dostemobj = 0; PyObject *fetchtextobj = 0; PyObject *collapseobj = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "es|OesOO:Query_execute", (char**)kwlist, "utf-8", &sutf8, &dostemobj, "utf-8", &sstemlang, &fetchtextobj, &collapseobj)) { return 0; } bool dostem{true}; if (dostemobj != 0 && !PyObject_IsTrue(dostemobj)) dostem = false; if (fetchtextobj != 0 && PyObject_IsTrue(fetchtextobj)) { self->fetchtext = true; } else { self->fetchtext = false; } string utf8(sutf8); PyMem_Free(sutf8); string stemlang("english"); if (sstemlang) { stemlang.assign(sstemlang); PyMem_Free(sstemlang); } LOGDEB0("Query_execute: [" << utf8 << "] dostem " << dostem << " stemlang [" << stemlang << "]\n"); if (self->query == 0) { PyErr_SetString(PyExc_AttributeError, "query"); return 0; } if (collapseobj != 0 && PyObject_IsTrue(collapseobj)) { self->query->setCollapseDuplicates(true); } else { self->query->setCollapseDuplicates(false); } // SearchData defaults to stemming in english // Use default for now but need to add way to specify language string reason; std::shared_ptr rq = wasaStringToRcl( self->connection->rclconfig.get(),dostem ? stemlang : "", utf8, reason); if (!rq) { PyErr_SetString(PyExc_ValueError, reason.c_str()); return 0; } self->query->setSortBy(*self->sortfield, self->ascending); self->query->setQuery(rq); int cnt = self->query->getResCnt(); self->next = 0; self->rowcount = cnt; return Py_BuildValue("i", cnt); } PyDoc_STRVAR(doc_Query_executesd, "executesd(SearchData, fetchtext=False)\n" "\n" "Starts a search for the query defined by the SearchData object.\n" ); static PyObject * Query_executesd(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Query_executeSD\n"); static const char *kwlist[] = {"searchdata", "fetchtext", "collapseduplicates", NULL}; recoll_SearchDataObject *pysd = 0; PyObject *fetchtextobj = 0; PyObject *collapseobj = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO:Query_execute", (char **)kwlist, &recoll_SearchDataType, &pysd, &fetchtextobj, &collapseobj)) { return 0; } if (pysd == 0 || self->query == 0) { PyErr_SetString(PyExc_AttributeError, "query"); return 0; } if (fetchtextobj != 0 && PyObject_IsTrue(fetchtextobj)) { self->fetchtext = true; } else { self->fetchtext = false; } if (collapseobj != 0 && PyObject_IsTrue(collapseobj)) { self->query->setCollapseDuplicates(true); } else { self->query->setCollapseDuplicates(false); } self->query->setSortBy(*self->sortfield, self->ascending); self->query->setQuery(pysd->sd); int cnt = self->query->getResCnt(); self->next = 0; self->rowcount = cnt; return Py_BuildValue("i", cnt); } // Move some data from the dedicated fields to the meta array to make // fetching attributes easier. Needed because we only use the meta // array when enumerating keys. Also for url which is also formatted. // But note that some fields are not copied, and are only reachable if // one knows their name (e.g. xdocid). static void movedocfields(const RclConfig* rclconfig, Rcl::Doc *doc) { printableUrl(rclconfig->getDefCharset(), doc->url, doc->meta[Rcl::Doc::keyurl]); doc->meta[Rcl::Doc::keytp] = doc->mimetype; doc->meta[Rcl::Doc::keyipt] = doc->ipath; doc->meta[Rcl::Doc::keyfs] = doc->fbytes; doc->meta[Rcl::Doc::keyds] = doc->dbytes; } static PyObject * Query_iternext(PyObject *_self) { LOGDEB0("Query_iternext\n"); recoll_QueryObject* self = (recoll_QueryObject*)_self; if (self->query == 0) { PyErr_SetString(PyExc_AttributeError, "query"); return 0; } int cnt = self->query->getResCnt(); if (cnt <= 0 || self->next < 0) { // This happens if there are no results and is not an error return 0; } recoll_DocObject *result = (recoll_DocObject *)PyObject_CallObject((PyObject *)&recoll_DocType, 0); if (!result) { PyErr_SetString(PyExc_EnvironmentError, "doc create failed"); return 0; } result->rclconfig = self->connection->rclconfig; // We used to check against rowcount here, but this was wrong: // xapian result count estimate are sometimes wrong, we must go on // fetching until we fail if (!self->query->getDoc(self->next, *result->doc, self->fetchtext)) { return 0; } self->next++; movedocfields(self->connection->rclconfig.get(), result->doc); return (PyObject *)result; } PyDoc_STRVAR(doc_Query_fetchone, "fetchone(None) -> Doc\n" "\n" "Fetches the next Doc object in the current search results.\n" ); static PyObject * Query_fetchone(PyObject *_self) { LOGDEB0("Query_fetchone/next\n"); recoll_DocObject *result = (recoll_DocObject *)Query_iternext(_self); if (!result) { Py_RETURN_NONE; } return (PyObject *)result; } PyDoc_STRVAR(doc_Query_fetchmany, "fetchmany([size=query.arraysize]) -> Doc list\n" "\n" "Fetches the next Doc objects in the current search results.\n" ); static PyObject * Query_fetchmany(PyObject* _self, PyObject *args, PyObject *kwargs) { LOGDEB0("Query_fetchmany\n"); recoll_QueryObject* self = (recoll_QueryObject*)_self; static const char *kwlist[] = {"size", NULL}; int size = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", (char**)kwlist, &size)) return 0; if (size == 0) size = self->arraysize; PyObject *reslist = PyList_New(0); for (int i = 0; i < size; i++) { recoll_DocObject *docobj = (recoll_DocObject *)Query_iternext(_self); if (!docobj) { break; } PyList_Append(reslist, (PyObject*)docobj); Py_DECREF(docobj); } if (PyErr_Occurred()) { Py_DECREF(reslist); return NULL; } else { return reslist; } } PyDoc_STRVAR(doc_Query_scroll, "scroll(value, [, mode='relative'/'absolute' ]) -> new int position\n" "\n" "Adjusts the position in the current result set.\n" ); static PyObject * Query_scroll(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Query_scroll\n"); static const char *kwlist[] = {"position", "mode", NULL}; int pos = 0; char *smode = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|s", (char**)kwlist, &pos, &smode)) return 0; bool isrelative = 1; if (smode != 0) { if (!strcasecmp(smode, "relative")) { isrelative = 1; } else if (!strcasecmp(smode, "absolute")) { isrelative = 0; } else { PyErr_SetString(PyExc_ValueError, "bad mode value"); return 0; } } if (self->query == 0) { PyErr_SetString(PyExc_AttributeError, "null query"); return 0; } int newpos = isrelative ? self->next + pos : pos; if (newpos < 0 || newpos >= self->rowcount) { PyErr_SetString(PyExc_IndexError, "position out of range"); return 0; } self->next = newpos; return Py_BuildValue("i", newpos); } PyDoc_STRVAR(doc_Query_highlight, "highlight(text, ishtml = 0/1, methods = object))\n" "Will insert tags around the match areas\n" "in the input text and return the modified text\n" "ishtml can be set to indicate that the input text is html and html special\n" " characters should not be escaped\n" "methods if set should be an object with methods startMatch(i) and endMatch()\n" " which will be called for each match and should return a begin and end tag\n" ); class PyPlainToRich: public PlainToRich { public: PyPlainToRich() {} PyPlainToRich(PyObject *methods, bool eolbr = false, // Do not use default markers if methods is nullptr bool nohl = false) : m_methods(methods) { m_eolbr = eolbr; m_nohl = nohl; } virtual ~PyPlainToRich() = default; virtual string startMatch(unsigned int idx) { // if nohl is set or methods was explicitely set to None, do nothing. if (m_nohl || m_methods == Py_None) { return ""; } // Use either user-defined methods or our default PyObject *res = 0; if (m_methods && PyObject_HasAttrString(m_methods, (char *)"startMatch")) { res = PyObject_CallMethod(m_methods, (char *)"startMatch", (char *)"(i)", idx); } if (res == 0) { return ""; } PyObject *res1 = res; if (PyUnicode_Check(res)) res1 = PyUnicode_AsUTF8String(res); return PyBytes_AsString(res1); } virtual string endMatch() { if (m_nohl || m_methods == Py_None) { return ""; } PyObject *res = 0; if (m_methods && PyObject_HasAttrString(m_methods, (char *)"endMatch")) res = PyObject_CallMethod(m_methods, (char *)"endMatch", 0); if (res == 0) { return ""; } PyObject *res1 = res; if (PyUnicode_Check(res)) res1 = PyUnicode_AsUTF8String(res); return PyBytes_AsString(res1); } PyObject *m_methods{nullptr}; bool m_nohl{false}; }; static PyObject * Query_highlight(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Query_highlight\n"); static const char *kwlist[] = {"text", "ishtml", "eolbr", "methods", NULL}; char *sutf8 = 0; // needs freeing int ishtml = 0; PyObject *ishtmlobj = 0; int eolbr = 1; PyObject *eolbrobj = 0; PyObject *methods = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "es|OOO:Query_highlight", (char**)kwlist, "utf-8", &sutf8, &ishtmlobj, &eolbrobj, &methods)) { return 0; } string utf8(sutf8); PyMem_Free(sutf8); if (ishtmlobj && PyObject_IsTrue(ishtmlobj)) ishtml = 1; if (eolbrobj && !PyObject_IsTrue(eolbrobj)) eolbr = 0; LOGDEB0("Query_highlight: ishtml " << ishtml << "\n"); if (self->query == 0) { PyErr_SetString(PyExc_AttributeError, "query"); return 0; } std::shared_ptr sd = self->query->getSD(); if (!sd) { PyErr_SetString(PyExc_ValueError, "Query not initialized"); return 0; } HighlightData hldata; sd->getTerms(hldata); PyPlainToRich hler(methods, eolbr); hler.set_inputhtml(ishtml); list out; hler.plaintorich(utf8, out, hldata, 5000000); if (out.empty()) { PyErr_SetString(PyExc_ValueError, "Plaintorich failed"); return 0; } // cf python manual:The bytes will be interpreted as being UTF-8 encoded. PyObject* unicode = PyUnicode_FromStringAndSize(out.begin()->c_str(), out.begin()->size()); // We used to return a copy of the unicode object. Can't see why any more return unicode; } // Helper shared by db.makeDocAbstract() and query.makeDocAbstract() static std::string makedocabstract(Rcl::Query *query, const Rcl::Doc& doc, PyObject *hlmethods, bool nohl = false) { string abstract; PyPlainToRich hler(hlmethods, false, nohl); hler.set_inputhtml(0); vector vabs; query->makeDocAbstract(doc, &hler, vabs); for (unsigned int i = 0; i < vabs.size(); i++) { if (vabs[i].empty()) continue; abstract += vabs[i]; abstract += "..."; } return abstract; } PyDoc_STRVAR(doc_Query_makedocabstract, "makedocabstract(doc, methods = object, nohl=False))\n" "Will create a snippets abstract for doc by selecting text around the match\n" " terms\n" "If methods is set, will also perform highlighting. See the highlight method\n" "If methods is not set and nohl is true, it will disable the default insertion of " "match regions\n" ); static PyObject *Query_makedocabstract(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Query_makeDocAbstract\n"); static const char *kwlist[] = {"doc", "methods", "nohl", NULL}; recoll_DocObject *pydoc = 0; PyObject *hlmethods = 0; PyObject *nohlobj = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO:Query_makeDocAbstract", (char **)kwlist, &recoll_DocType, &pydoc, &hlmethods, &nohlobj)) { return 0; } bool nohl{false}; if (nohlobj != 0 && PyObject_IsTrue(nohlobj)) nohl = true; if (pydoc->doc == 0) { LOGERR("Query_makeDocAbstract: doc not found " << pydoc->doc << "\n"); PyErr_SetString(PyExc_AttributeError, "doc"); return 0; } if (nullptr == self->query) { LOGERR("Query_makeDocAbstract: query not found " << self->query<< "\n"); PyErr_SetString(PyExc_AttributeError, "query"); return 0; } std::string abstract = makedocabstract(self->query, *pydoc->doc, hlmethods, nohl); // Return a python unicode object return PyUnicode_Decode(abstract.c_str(), abstract.size(), "UTF-8", "replace"); } PyDoc_STRVAR(doc_Query_getsnippets, "getsnippets(doc, maxoccs = -1, ctxwords = -1, sortbypage=False, " "methods = object, nohl=False))\n" "Will return a list of snippets for doc by selecting text around the match terms\n" "If methods is set, will also perform highlighting. See the highlight method\n" ); static PyObject * Query_getsnippets(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Query_getSnippets\n"); static const char *kwlist[] = {"doc", "methods", "maxoccs", "ctxwords", "sortbypage", "nohl", NULL}; recoll_DocObject *pydoc = 0; PyObject *hlmethods = 0; int maxoccs = -1; int ctxwords = -1; PyObject *osortbp = 0; bool sortbypage = false; PyObject *nohlobj = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OiiOO:Query_getSnippets", (char **)kwlist, &recoll_DocType, &pydoc, &hlmethods, &maxoccs, &ctxwords, &osortbp, &nohlobj)) { return 0; } if (osortbp && PyObject_IsTrue(osortbp)) sortbypage = true; bool nohl{false}; if (nohlobj != 0 && PyObject_IsTrue(nohlobj)) nohl = true; if (pydoc->doc == 0) { LOGERR("Query_getsnippets: doc not found " << pydoc->doc << "\n"); PyErr_SetString(PyExc_AttributeError, "doc"); return 0; } if (self->query == 0) { LOGERR("Query_getsnippets: query not found " << self->query<< "\n"); PyErr_SetString(PyExc_AttributeError, "query"); return 0; } std::shared_ptr sd = self->query->getSD(); if (!sd) { PyErr_SetString(PyExc_ValueError, "Query not initialized"); return 0; } std::vector snippets; PyPlainToRich hler(hlmethods, false, nohl); self->query->makeDocAbstract(*(pydoc->doc), &hler, snippets, maxoccs, ctxwords, sortbypage); PyObject *sniplist = PyList_New(0); for (const auto& snip : snippets) { const std::string *textp = &snip.snippet; PyList_Append(sniplist, Py_BuildValue( "(iOO)", snip.page, PyUnicode_Decode(snip.term.c_str(), snip.term.size(), "UTF-8", "replace"), PyUnicode_Decode(textp->c_str(), textp->size(), "UTF-8", "replace"))); } return sniplist; } PyDoc_STRVAR(doc_Query_getxquery, "getxquery(None) -> Unicode string\n" "\n" "Retrieves the Xapian query description as a Unicode string.\n" "Meaningful only after executexx\n" ); static PyObject * Query_getxquery(recoll_QueryObject* self, PyObject *, PyObject *) { LOGDEB0("Query_getxquery self->query " << self->query << "\n"); if (self->query == 0) { PyErr_SetString(PyExc_AttributeError, "query"); return 0; } std::shared_ptr sd = self->query->getSD(); if (!sd) { PyErr_SetString(PyExc_ValueError, "Query not initialized"); return 0; } string desc = sd->getDescription(); return PyUnicode_Decode(desc.c_str(), desc.size(), "UTF-8", "replace"); } PyDoc_STRVAR(doc_Query_getgroups, "getgroups(None) -> a list of pairs\n" "\n" "Retrieves the expanded query terms. Meaningful only after executexx\n" "In each pair, the first entry is a list of user terms, the second a list of\n" "query terms as derived from the user terms and used in the Xapian Query.\n" "The size of each list is one for simple terms, or more for group and phrase\n" "clauses\n" ); static PyObject * Query_getgroups(recoll_QueryObject* self, PyObject *, PyObject *) { LOGDEB0("Query_getgroups\n"); if (self->query == 0) { PyErr_SetString(PyExc_AttributeError, "query"); return 0; } std::shared_ptr sd = self->query->getSD(); if (!sd) { PyErr_SetString(PyExc_ValueError, "Query not initialized"); return 0; } HighlightData hld; sd->getTerms(hld); PyObject *mainlist = PyList_New(0); PyObject *ulist; PyObject *xlist; // We walk the groups vector. For each we retrieve the user group, // make a python list of each, then group those in a pair, and // append this to the main list. for (unsigned int i = 0; i < hld.index_term_groups.size(); i++) { HighlightData::TermGroup& tg(hld.index_term_groups[i]); unsigned int ugidx = tg.grpsugidx; ulist = PyList_New(hld.ugroups[ugidx].size()); for (unsigned int j = 0; j < hld.ugroups[ugidx].size(); j++) { PyList_SetItem(ulist, j, PyUnicode_Decode(hld.ugroups[ugidx][j].c_str(), hld.ugroups[ugidx][j].size(), "UTF-8", "replace")); } // Not sure that this makes any sense after we changed from // multiply_groups to using or-plists. TBD: check if (tg.kind == HighlightData::TermGroup::TGK_TERM) { xlist = PyList_New(1); PyList_SetItem(xlist, 0, PyUnicode_Decode(tg.term.c_str(), tg.term.size(), "UTF-8", "replace")); } else { xlist = PyList_New(tg.orgroups.size()); for (unsigned int j = 0; j < tg.orgroups.size(); j++) { PyList_SetItem(xlist, j, PyUnicode_Decode(tg.orgroups[j][0].c_str(), tg.orgroups[j][0].size(), "UTF-8", "replace")); } } PyList_Append(mainlist, Py_BuildValue("(OO)", ulist, xlist)); } return mainlist; } static PyMethodDef Query_methods[] = { {"execute", (PyCFunction)Query_execute, METH_VARARGS|METH_KEYWORDS, doc_Query_execute}, {"executesd", (PyCFunction)Query_executesd, METH_VARARGS|METH_KEYWORDS, doc_Query_executesd}, {"next", (PyCFunction)Query_fetchone, METH_NOARGS, doc_Query_fetchone}, {"fetchone", (PyCFunction)Query_fetchone, METH_NOARGS, doc_Query_fetchone}, {"fetchmany", (PyCFunction)Query_fetchmany, METH_VARARGS|METH_KEYWORDS, doc_Query_fetchmany}, {"close", (PyCFunction)Query_close, METH_NOARGS, doc_Query_close}, {"sortby", (PyCFunction)Query_sortby, METH_VARARGS|METH_KEYWORDS, doc_Query_sortby}, {"highlight", (PyCFunction)Query_highlight, METH_VARARGS|METH_KEYWORDS, doc_Query_highlight}, {"getxquery", (PyCFunction)Query_getxquery, METH_NOARGS, doc_Query_getxquery}, {"getgroups", (PyCFunction)Query_getgroups, METH_NOARGS, doc_Query_getgroups}, {"makedocabstract", (PyCFunction)Query_makedocabstract, METH_VARARGS|METH_KEYWORDS, doc_Query_makedocabstract}, {"getsnippets", (PyCFunction)Query_getsnippets, METH_VARARGS|METH_KEYWORDS, doc_Query_getsnippets}, {"scroll", (PyCFunction)Query_scroll, METH_VARARGS|METH_KEYWORDS, doc_Query_scroll}, {NULL} /* Sentinel */ }; static PyMemberDef Query_members[] = { {(char*)"rownumber", T_INT, offsetof(recoll_QueryObject, next), 0, (char*)"Next index to be fetched from results. Normally increments after\n" "each fetchone() call, but can be set/reset before the call effect\n" "seeking. Starts at 0" }, {(char*)"rowcount", T_INT, offsetof(recoll_QueryObject, rowcount), READONLY, (char*)"Number of records returned by the last execute" }, {(char*)"arraysize", T_INT, offsetof(recoll_QueryObject, arraysize), 0, (char*)"Default number of records processed by fetchmany (r/w)" }, {(char*)"connection", T_OBJECT_EX, offsetof(recoll_QueryObject, connection), 0, (char*)"Connection object this is from" }, {NULL} /* Sentinel */ }; PyDoc_STRVAR(doc_QueryObject, "Recoll Query objects are used to execute index searches. \n" "They must be created by the Db.query() method.\n" ); PyTypeObject recoll_QueryType = { PyVarObject_HEAD_INIT(NULL, 0) "_recoll.Query", /*tp_name*/ sizeof(recoll_QueryObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Query_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ doc_QueryObject, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ Query_iter, /* tp_iter */ Query_iternext, /* tp_iternext */ Query_methods, /* tp_methods */ Query_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Query_init, /* tp_init */ 0, /* tp_alloc */ Query_new, /* tp_new */ }; /////////////////////////////////////////////// ////// DB Db object code static PyObject * Db_close(recoll_DbObject *self) { LOGDEB("Db_close. self " << self << "\n"); if (self->db) { delete self->db; self->db = 0; } self->rclconfig.reset(); Py_RETURN_NONE; } static void Db_dealloc(recoll_DbObject *self) { LOGDEB("Db_dealloc\n"); PyObject *ret = Db_close(self); Py_DECREF(ret); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * Db_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { LOGDEB2("Db_new\n"); recoll_DbObject *self; self = (recoll_DbObject *)type->tp_alloc(type, 0); if (self == 0) return 0; self->db = 0; return (PyObject *)self; } static int Db_init(recoll_DbObject *self, PyObject *args, PyObject *kwargs) { static const char *kwlist[] = {"confdir", "extra_dbs", "writable", NULL}; PyObject *extradbs = 0; char *confdir = 0; int writable = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sOi", (char**)kwlist, &confdir, &extradbs, &writable)) return -1; // If the user creates several dbs, changing the confdir, we call // recollinit repeatedly, which *should* be ok, except that it // resets the log file. string reason; if (confdir) { string cfd = confdir; self->rclconfig = std::shared_ptr( recollinit(RCLINIT_PYTHON, 0, 0, reason, &cfd)); } else { self->rclconfig = std::shared_ptr( recollinit(RCLINIT_PYTHON, 0, 0, reason, 0)); } RCLCONFIG = self->rclconfig; LOGDEB("Db_init\n"); if (!self->rclconfig) { PyErr_SetString(PyExc_EnvironmentError, reason.c_str()); return -1; } if (!self->rclconfig->ok()) { PyErr_SetString(PyExc_EnvironmentError, "Bad config ?"); return -1; } if (writable) { // Make sure that we have an updater as there may be non-initialisation calls in other parts // of the code statusUpdater(self->rclconfig.get(), true); } delete self->db; self->db = new Rcl::Db(self->rclconfig.get()); if (!self->db->open(writable ? Rcl::Db::DbUpd : Rcl::Db::DbRO)) { LOGERR("Db_init: db open error\n"); PyErr_SetString(PyExc_EnvironmentError, "Can't open index"); return -1; } if (extradbs) { if (!PySequence_Check(extradbs)) { PyErr_SetString(PyExc_TypeError, "extra_dbs must be a sequence"); deleteZ(self->db); return -1; } int dbcnt = PySequence_Size(extradbs); if (dbcnt == -1) { PyErr_SetString(PyExc_TypeError, "extra_dbs could not be sized"); deleteZ(self->db); return -1; } for (int i = 0; i < dbcnt; i++) { PyObject *item = PySequence_GetItem(extradbs, i); string dbname; if (PyUnicode_Check(item)) { PyObject *utf8o = PyUnicode_AsUTF8String(item); if (nullptr != utf8o) { dbname = PyBytes_AsString(utf8o); Py_DECREF(utf8o); } } else if (PyBytes_Check(item)) { dbname = PyBytes_AsString(item); } if (dbname.empty()) { PyErr_SetString(PyExc_TypeError, "extra_dbs items must be bytes or strings"); deleteZ(self->db); Py_DECREF(item); return -1; } Py_DECREF(item); string errmsg = string("extra db could not be opened: ") + dbname; if (!self->db->addQueryDb(dbname)) { PyErr_SetString(PyExc_EnvironmentError, errmsg.c_str()); deleteZ(self->db); return -1; } } } return 0; } static PyObject * Db_query(recoll_DbObject* self) { LOGDEB("Db_query\n"); if (self->db == 0) { LOGERR("Db_query: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); return 0; } recoll_QueryObject *result = (recoll_QueryObject *) PyObject_CallObject((PyObject *)&recoll_QueryType, 0); if (!result) return 0; result->query = new Rcl::Query(self->db); result->connection = self; Py_INCREF(self); return (PyObject *)result; } static PyObject * Db_doc(recoll_DbObject* self) { LOGDEB("Db_doc\n"); if (self->db == 0) { LOGERR("Db_doc: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); return 0; } recoll_DocObject *result = (recoll_DocObject *) PyObject_CallObject((PyObject *)&recoll_DocType, 0); if (!result) return 0; result->rclconfig = self->rclconfig; Py_INCREF(self); return (PyObject *)result; } static PyObject * Db_getDoc(recoll_DbObject* self, PyObject *args, PyObject *kwargs) { LOGDEB("Db_doc\n"); if (self->db == 0) { LOGERR("Db_doc: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); return 0; } int idxidx = 0; char *cudi = 0; static const char *kwlist[] = {"idxidx", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", (char**)kwlist, &cudi, &idxidx)) { return 0; } std::string udi(cudi); recoll_DocObject *pydoc = (recoll_DocObject *)PyObject_CallObject((PyObject *)&recoll_DocType, 0); if (!pydoc) return 0; pydoc->rclconfig = self->rclconfig; if (!self->db->getDoc(std::string(udi), idxidx, *(pydoc->doc), true)) { PyErr_SetString(PyExc_AttributeError, "Doc not found: bad UDI or idx index"); return 0; } Py_INCREF(self); return (PyObject *)pydoc; } static PyObject * Db_setAbstractParams(recoll_DbObject *self, PyObject *args, PyObject *kwargs) { LOGDEB0("Db_setAbstractParams\n"); static const char *kwlist[] = {"maxchars", "contextwords", NULL}; int ctxwords = -1, maxchars = -1; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", (char**)kwlist, &maxchars, &ctxwords)) return 0; if (self->db == 0) { LOGERR("Db_setAbstractParams: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db id not found"); return 0; } LOGDEB0("Db_setAbstractParams: mxchrs " << maxchars << ", ctxwrds " << ctxwords << "\n"); self->db->setAbstractParams(-1, maxchars, ctxwords); Py_RETURN_NONE; } static PyObject * Db_makeDocAbstract(recoll_DbObject* self, PyObject *args) { LOGDEB0("Db_makeDocAbstract\n"); recoll_DocObject *pydoc = 0; recoll_QueryObject *pyquery = 0; if (!PyArg_ParseTuple(args, "O!O!:Db_makeDocAbstract", &recoll_DocType, &pydoc, &recoll_QueryType, &pyquery)) { return 0; } if (self->db == 0) { LOGERR("Db_makeDocAbstract: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); return 0; } if (pydoc->doc == 0) { LOGERR("Db_makeDocAbstract: doc not found " << pydoc->doc << "\n"); PyErr_SetString(PyExc_AttributeError, "doc"); return 0; } if (pyquery->query == 0) { LOGERR("Db_makeDocAbstract: query not found "<< pyquery->query << "\n"); PyErr_SetString(PyExc_AttributeError, "query"); return 0; } string abstract = makedocabstract(pyquery->query, *pydoc->doc, nullptr); // Return a python unicode object return PyUnicode_Decode(abstract.c_str(), abstract.size(), "UTF-8", "replace"); } PyDoc_STRVAR( doc_Db_termMatch, "termMatch(match_type='wildcard|regexp|stem', expr, field='', " "maxlen=-1, casesens=False, diacsens=False, lang='english', freqs=False)" " returns the expanded term list\n" "\n" "Expands the input expression according to the mode and parameters and " "returns the expanded term list, as raw terms if freqs is False, or " "(term, totcnt, docnt) tuples if freqs is True.\n" ); static PyObject * Db_termMatch(recoll_DbObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Db_termMatch\n"); static const char *kwlist[] = {"type", "expr", "field", "maxlen", "casesens", "diacsens", "freqs", "lang", NULL}; char *tp = 0; char *expr = 0; // needs freeing char *field = 0; // needs freeing int maxlen = -1; PyObject *casesens = 0; PyObject *diacsens = 0; PyObject *freqs = 0; char *lang = 0; // needs freeing PyObject *ret = 0; int typ_sens = 0; Rcl::TermMatchResult result(true); /* strip prefixes in results */ bool showfreqs = false; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ses|esiOOOes", (char**)kwlist, &tp, "utf-8", &expr, "utf-8", &field, &maxlen, &casesens, &diacsens, &freqs, "utf-8", &lang)) return 0; if (self->db == 0) { LOGERR("Db_termMatch: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); goto out; } if (!strcasecmp(tp, "wildcard")) { typ_sens = Rcl::Db::ET_WILD; } else if (!strcasecmp(tp, "regexp")) { typ_sens = Rcl::Db::ET_REGEXP; } else if (!strcasecmp(tp, "stem")) { typ_sens = Rcl::Db::ET_STEM; } else { PyErr_SetString(PyExc_AttributeError, "Bad type arg"); goto out; } if (casesens != 0 && PyObject_IsTrue(casesens)) { typ_sens |= Rcl::Db::ET_CASESENS; } if (diacsens != 0 && PyObject_IsTrue(diacsens)) { typ_sens |= Rcl::Db::ET_DIACSENS; } if (freqs != 0 && PyObject_IsTrue(freqs)) { showfreqs = true; } if (!self->db->termMatch(typ_sens, lang ? lang : "english", expr, result, maxlen, field ? field : "")) { LOGERR("Db_termMatch: db termMatch error\n"); PyErr_SetString(PyExc_AttributeError, "rcldb termMatch error"); goto out; } ret = PyList_New(result.entries.size()); for (unsigned int i = 0; i < result.entries.size(); i++) { PyObject *term = PyUnicode_FromString(result.entries[i].term.c_str()); if (showfreqs) { PyObject *totcnt = PyLong_FromLong(result.entries[i].wcf); PyObject *doccnt = PyLong_FromLong(result.entries[i].docs); PyObject *tup = PyTuple_New(3); PyTuple_SetItem(tup, 0, term); PyTuple_SetItem(tup, 1, totcnt); PyTuple_SetItem(tup, 2, doccnt); PyList_SetItem(ret, i, tup); } else { PyList_SetItem(ret, i, term); } } out: PyMem_Free(expr); PyMem_Free(field); PyMem_Free(lang); return ret; } static PyObject * Db_needUpdate(recoll_DbObject* self, PyObject *args, PyObject *kwds) { LOGDEB0("Db_needUpdate\n"); char *udi = 0; // needs freeing char *sig = 0; // needs freeing if (!PyArg_ParseTuple(args, "eses:Db_needUpdate", "utf-8", &udi, "utf-8", &sig)) { return 0; } if (self->db == 0) { LOGERR("Db_needUpdate: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); PyMem_Free(udi); PyMem_Free(sig); return 0; } bool result = self->db->needUpdate(udi, sig); PyMem_Free(udi); PyMem_Free(sig); return Py_BuildValue("i", result); } static PyObject * Db_delete(recoll_DbObject* self, PyObject *args, PyObject *kwds) { LOGDEB0("Db_delete\n"); char *udi = 0; // needs freeing if (!PyArg_ParseTuple(args, "es:Db_delete", "utf-8", &udi)) { return 0; } if (self->db == 0) { LOGERR("Db_delete: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); PyMem_Free(udi); return 0; } bool result = self->db->purgeFile(udi); PyMem_Free(udi); return Py_BuildValue("i", result); } static PyObject * Db_preparePurge(recoll_DbObject* self, PyObject *args, PyObject *kwds) { LOGDEB0("Db_preparePurge\n"); char *backend = 0; // needs freeing if (!PyArg_ParseTuple(args, "es:Db_preparePurge", "utf-8", &backend)) { return 0; } if (self->db == 0) { LOGERR("Db_preparePurge: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); PyMem_Free(backend); return 0; } bool result = self->db->preparePurge(backend); return Py_BuildValue("i", result); } static PyObject * Db_purge(recoll_DbObject* self) { LOGDEB0("Db_purge\n"); if (self->db == 0) { LOGERR("Db_purge: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); return 0; } bool result = self->db->purge(); return Py_BuildValue("i", result); } #if PY_MAJOR_VERSION >=3 static PyObject* Db_createStemDbs(recoll_DbObject* self, PyObject* args) { PyObject* pylangs; std::vector langs; bool opret; if (self->db == 0) { LOGERR("Db_createStemDbs: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); return 0; } if (!PyArg_ParseTuple(args, "O", &pylangs)) { return NULL; } if (PyUnicode_Check(pylangs)) { Py_ssize_t sz; const char *bytes = PyUnicode_AsUTF8AndSize(pylangs, &sz); langs.push_back(std::string(bytes, sz)); } else { if (!PySequence_Check(pylangs)) { PyErr_SetString(PyExc_TypeError, "Input must be a list or tuple."); return NULL; } PyObject *seq = PySequence_Fast(pylangs, "createStemDbs: input must be str or sequence"); if (NULL == seq) { return NULL; } Py_ssize_t lsz = PySequence_Fast_GET_SIZE(seq); for (int i = 0; i < lsz; i++) { Py_ssize_t sz; const char *bytes; PyObject *pylang = PySequence_Fast_GET_ITEM(seq, i); if (!PyUnicode_Check(pylang)) { PyErr_SetString(PyExc_TypeError, "Input must be a list or tuple of str."); return NULL; } bytes = PyUnicode_AsUTF8AndSize(pylang, &sz); langs.push_back(std::string(bytes, sz)); } } opret = self->db->createStemDbs(langs); if (opret) { Py_RETURN_NONE; } else { PyErr_SetString(PyExc_SystemError, "Db update failed"); return NULL; } } #endif /* Python3 */ static PyObject * Db_addOrUpdate(recoll_DbObject* self, PyObject *args, PyObject *kwargs) { LOGDEB0("Db_addOrUpdate\n"); static const char *kwlist[] = {"udi", "doc", "parent_udi", NULL}; char *sudi = 0; // needs freeing char *sparent_udi = 0; // needs freeing recoll_DocObject *pydoc; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "esO!|es:Db_addOrUpdate", (char **)kwlist, "utf-8", &sudi, &recoll_DocType, &pydoc, "utf-8", &sparent_udi)) { return 0; } string udi(sudi); string parent_udi(sparent_udi ? sparent_udi : ""); PyMem_Free(sudi); PyMem_Free(sparent_udi); if (self->db == 0) { LOGERR("Db_addOrUpdate: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db"); return 0; } if (pydoc->doc == 0) { LOGERR("Db_addOrUpdate: doc not found " << pydoc->doc << "\n"); PyErr_SetString(PyExc_AttributeError, "doc"); return 0; } if (!self->db->addOrUpdate(udi, parent_udi, *pydoc->doc)) { LOGERR("Db_addOrUpdate: rcldb error\n"); PyErr_SetString(PyExc_AttributeError, "rcldb error"); return 0; } Py_RETURN_NONE; } static PyMethodDef Db_methods[] = { {"close", (PyCFunction)Db_close, METH_NOARGS, "close() closes the index connection. The object is unusable after this." }, {"query", (PyCFunction)Db_query, METH_NOARGS, "query() -> Query. Return a new, blank query object for this index." }, {"doc", (PyCFunction)Db_doc, METH_NOARGS, "doc() -> Doc. Return a new, blank doc object for this index." }, {"getDoc",(PyCFunction)Db_getDoc, METH_VARARGS|METH_KEYWORDS, "getDoc(udi, idxidx=0) -> Doc.\n" "Retrieve document from given udi and index number (default 0, main index)." }, {"cursor", (PyCFunction)Db_query, METH_NOARGS, "cursor() -> Query. Alias for query(). Return query object." }, {"setAbstractParams", (PyCFunction)Db_setAbstractParams, METH_VARARGS|METH_KEYWORDS, "setAbstractParams(maxchars, contextwords).\n" "Set the parameters used to build 'keyword-in-context' abstracts" }, {"makeDocAbstract", (PyCFunction)Db_makeDocAbstract, METH_VARARGS, "makeDocAbstract(Doc, Query) -> string\n" "Build and return 'keyword-in-context' abstract for document\n" "and query." }, {"termMatch", (PyCFunction)Db_termMatch, METH_VARARGS|METH_KEYWORDS, doc_Db_termMatch }, {"needUpdate", (PyCFunction)Db_needUpdate, METH_VARARGS, "needUpdate(udi, sig) -> Bool.\n" "Check if the index is up to date for the document defined by udi,\n" "having the current signature sig." }, {"delete", (PyCFunction)Db_delete, METH_VARARGS, "delete(udi) -> Bool.\n" "Purge index from all data for udi. If udi matches a container\n" "document, purge all subdocs (docs with a parent_udi matching udi)." }, {"preparePurge", (PyCFunction)Db_preparePurge, METH_VARARGS, "preparePurge(backend_name) -> Bool.\n" "Mark all documents which do *not* belong to this indexer as present.\n" "Mandatory call before starting an update if there are other backends for\n" "this index and you are going to call purge() after the update, else all\n" "documents for other backends will be deleted from the index by the purge.\n" }, {"purge", (PyCFunction)Db_purge, METH_NOARGS, "purge() -> Bool.\n" "Delete all documents that were not touched during the just finished\n" "indexing pass (since open-for-write). These are the documents for\n" "the needUpdate() call was not performed, indicating that they no\n" "longer exist in the primary storage system.\n" }, {"addOrUpdate", (PyCFunction)Db_addOrUpdate, METH_VARARGS|METH_KEYWORDS, "addOrUpdate(udi, doc, parent_udi=None) -> None\n" "Add or update index data for a given document\n" "The udi string must define a unique id for the document. It is not\n" "interpreted inside Recoll\n" "doc is a Doc object\n" "if parent_udi is set, this is a unique identifier for the\n" "top-level container (ie mbox file)" }, #if PY_MAJOR_VERSION >=3 {"createStemDbs", (PyCFunction)Db_createStemDbs, METH_VARARGS, "createStemDbs(lang|lang sequence) -> None\n" "Create stemming dictionaries for the specified languages\n" }, #endif {NULL} /* Sentinel */ }; PyDoc_STRVAR(doc_DbObject, "Db([confdir=None], [extra_dbs=None], [writable = False])\n" "\n" "A Db object holds a connection to a Recoll index. Use the connect()\n" "function to create one.\n" "confdir specifies a Recoll configuration directory (default: \n" " $RECOLL_CONFDIR or ~/.recoll).\n" "extra_dbs is a list of external databases (xapian directories)\n" "writable decides if we can index new data through this connection\n" ); static PyTypeObject recoll_DbType = { PyVarObject_HEAD_INIT(NULL, 0) "_recoll.Db", /*tp_name*/ sizeof(recoll_DbObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Db_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ doc_DbObject, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Db_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Db_init, /* tp_init */ 0, /* tp_alloc */ Db_new, /* tp_new */ }; ////////////////////////////////////////////////////////////////////////// // Module methods static PyObject * recoll_connect(PyObject *self, PyObject *args, PyObject *kwargs) { LOGDEB2("recoll_connect\n"); recoll_DbObject *db = (recoll_DbObject *) PyObject_Call((PyObject *)&recoll_DbType, args, kwargs); return (PyObject *)db; } PyDoc_STRVAR(doc_connect, "connect([confdir=None], [extra_dbs=None], [writable = False])\n" " -> Db.\n" "\n" "Connects to a Recoll database and returns a Db object.\n" "confdir specifies a Recoll configuration directory\n" "(the default is built like for any Recoll program).\n" "extra_dbs is a list of external databases (xapian directories)\n" "writable decides if we can index new data through this connection\n" ); static PyMethodDef recoll_methods[] = { {"connect", (PyCFunction)recoll_connect, METH_VARARGS|METH_KEYWORDS, doc_connect}, {NULL, NULL, 0, NULL} /* Sentinel */ }; PyDoc_STRVAR(pyrecoll_doc_string, "This is an interface to the Recoll full text indexer."); struct module_state { PyObject *error; }; #if PY_MAJOR_VERSION >= 3 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) #else #define GETSTATE(m) (&_state) static struct module_state _state; #endif #if PY_MAJOR_VERSION >= 3 static int recoll_traverse(PyObject *m, visitproc visit, void *arg) { Py_VISIT(GETSTATE(m)->error); return 0; } static int recoll_clear(PyObject *m) { Py_CLEAR(GETSTATE(m)->error); return 0; } static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_recoll", NULL, sizeof(struct module_state), recoll_methods, NULL, recoll_traverse, recoll_clear, NULL }; #define INITERROR return NULL extern "C" PyObject * PyInit__recoll(void) #else #define INITERROR return PyMODINIT_FUNC init_recoll(void) #endif { // Note: we can't call recollinit here, because the confdir is only really // known when the first db object is created (it is an optional parameter). // Using a default here may end up with variables such as stripchars being // wrong #if PY_MAJOR_VERSION >= 3 PyObject *module = PyModule_Create(&moduledef); #else PyObject *module = Py_InitModule("_recoll", recoll_methods); #endif if (module == NULL) INITERROR; struct module_state *st = GETSTATE(module); // The first parameter is a char *. Hopefully we don't initialize // modules too often... st->error = PyErr_NewException(strdup("_recoll.Error"), NULL, NULL); if (st->error == NULL) { Py_DECREF(module); INITERROR; } if (PyType_Ready(&recoll_DbType) < 0) INITERROR; Py_INCREF((PyObject*)&recoll_DbType); PyModule_AddObject(module, "Db", (PyObject *)&recoll_DbType); if (PyType_Ready(&recoll_QueryType) < 0) INITERROR; Py_INCREF((PyObject*)&recoll_QueryType); PyModule_AddObject(module, "Query", (PyObject *)&recoll_QueryType); if (PyType_Ready(&recoll_DocType) < 0) INITERROR; Py_INCREF((PyObject*)&recoll_DocType); PyModule_AddObject(module, "Doc", (PyObject *)&recoll_DocType); if (PyType_Ready(&recoll_SearchDataType) < 0) INITERROR; Py_INCREF((PyObject*)&recoll_SearchDataType); PyModule_AddObject(module, "SearchData", (PyObject *)&recoll_SearchDataType); PyModule_AddStringConstant(module, "__doc__", pyrecoll_doc_string); if (PyType_Ready(&rclx_ExtractorType) < 0) INITERROR; Py_INCREF(&rclx_ExtractorType); PyModule_AddObject(module, "Extractor", (PyObject *)&rclx_ExtractorType); if (PyType_Ready(&recoll_QResultStoreType) < 0) INITERROR; Py_INCREF(&recoll_QResultStoreType); PyModule_AddObject(module, "QResultStore", (PyObject *)&recoll_QResultStoreType); if (PyType_Ready(&recoll_QRSDocType) < 0) INITERROR; Py_INCREF((PyObject*)&recoll_QRSDocType); PyModule_AddObject(module, "QRSDoc", (PyObject *)&recoll_QRSDocType); #if PY_MAJOR_VERSION >= 3 return module; #endif } recoll-1.36.1/python/recoll/pyrecoll.h0000644000175000017500000000402714410615043014607 00000000000000/* Copyright (C) 2012-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _PYRECOLL_H_INCLUDED_ #define _PYRECOLL_H_INCLUDED_ /* Shared definitions for pyrecoll.cpp and pyrclextract.cpp */ #include #include #include class RclConfig; namespace Rcl { class Doc; class Query; }; typedef struct { PyObject_HEAD /* Type-specific fields go here. */ Rcl::Doc *doc; /* Each doc object has a pointer to the global config, for convenience */ std::shared_ptr rclconfig; } recoll_DocObject; struct recoll_DbObject; typedef struct { PyObject_HEAD /* Type-specific fields go here. */ Rcl::Query *query; int next; // Index of result to be fetched next or -1 if uninit int rowcount; // Number of records returned by last execute std::string *sortfield; // Need to allocate in here, main program is C. int ascending; int arraysize; // Default size for fetchmany recoll_DbObject* connection; bool fetchtext; } recoll_QueryObject; extern PyTypeObject recoll_DocType; extern PyTypeObject recoll_QueryType; extern PyTypeObject rclx_ExtractorType; extern PyTypeObject recoll_QResultStoreType; extern PyTypeObject recoll_QRSDocType; extern int pys2cpps(PyObject *pyval, std::string& out); #endif // _PYRECOLL_H_INCLUDED_ recoll-1.36.1/python/recoll/setup.py.in0000644000175000017500000000523114515753236014737 00000000000000from setuptools import setup, Extension import os import sys sysname = os.uname()[0] # See setup.cfg.in if not os.path.exists('Recoll.egg-info'): os.mkdir('Recoll.egg-info') # For shadow builds: references to the source tree top = os.path.join('@srcdir@', '..', '..') pytop = '@srcdir@' publiclib = @RECOLL_PUBLIC_LIB@ # For shadow builds: reference to the top of the local tree (for finding # generated .h files, e.g. autoconfig.h) localtop = os.path.join(os.path.dirname(__file__), '..', '..') # Ensure that we can build with an uninstalled lib from a build tree library_dirs = [os.path.realpath(os.path.join(localtop, '.libs'))] if "CYGWIN" in os.environ: libraries = ['recoll', 'xapian', 'iconv', 'z'] else: libraries = ['recoll'] extra_compile_args = ['-std=c++11'] define_macros = [('RECOLL_DATADIR', '"@RECOLL_DATADIR@"'),] DEF_EXT4_BIRTH_TIME = @DEF_EXT4_BIRTH_TIME@ if DEF_EXT4_BIRTH_TIME == 1: define_macros.append(('EXT4_BIRTH_TIME', 1)) VERSION = open(os.path.join(top, "RECOLL-VERSION.txt")).read().strip() include_dirs = [ os.path.join(localtop, 'common'), os.path.join(top, 'common'), os.path.join(top, 'index'), os.path.join(top, 'internfile'), os.path.join(top, 'query'), os.path.join(top, 'rcldb'), os.path.join(top, 'utils'), ] if not publiclib: if 'libdir' in os.environ and os.environ['libdir'] != "": runtime_library_dirs = [os.path.join(os.environ['libdir'], 'recoll')] else: runtime_library_dirs = [os.path.join('@prefix@', 'lib', 'recoll')] else: include_dirs.append('/usr/include/recoll') runtime_library_dirs = list() module1 = Extension('_recoll', define_macros = define_macros, include_dirs = include_dirs, extra_compile_args = extra_compile_args, libraries = libraries, library_dirs = library_dirs, runtime_library_dirs = runtime_library_dirs, sources = [os.path.join(pytop, 'pyrecoll.cpp'), os.path.join(pytop, 'pyresultstore.cpp'), os.path.join(pytop, 'pyrclextract.cpp') ]) setup (name = 'Recoll', version = VERSION, description = 'Query/Augment a Recoll full text index', author = 'J.F. Dockes', author_email = 'jfd@recoll.org', url = 'http://www.recoll.org', license = 'GPL', package_dir = {'' : os.path.join(top, 'python', 'recoll')}, long_description = ''' ''', packages = ['recoll'], ext_package = 'recoll', ext_modules = [module1]) recoll-1.36.1/python/pyaspell/0000755000175000017500000000000014521161751013240 500000000000000recoll-1.36.1/python/pyaspell/aspell.c0000644000175000017500000005534214426501047014615 00000000000000/***************************************************************************** Python wrapper for aspell, version 1.12 Tested with: * aspell 0.60.2 & python 3.2 Released under BSD license Wojciech Muła wojciech_mula@poczta.onet.pl History: - 2011-03-31: * added __contains__ method - 2011-03-xx: * ConfigKeys returns dictionary - 2011-02-21, 2011-03-06: * compliance with py3k - 2006-04-xx: * license is BSD now - 18.08.2005: * fixed method ConfigKeys - now works with aspell 0.60 thanks to Gora Mohanty for note * fixed stupid bug in Speller - 29.01.2005: * added method ConfigKeys() - 30.12.2004: * new() constructor replaced with Speller() * constructor accepts now much simpler syntax for passing multiple arguments * removed methods releated to configuratinon from AspellSpeller object * global method ConfigKeys() - 7.10.2004: * fixed saveAllwords method patch by Helmut Jarausch - 28.08.2004: * tested with python 2.3.4 * now aspell.new accepts list of config keys (see typescript2.txt for examples) - 20-22.08.2004: * first version of module $Id$ ******************************************************************************/ #include #include #define Speller(pyobject) (((aspell_AspellObject*)pyobject)->speller) #define Encoding(pyobject) (((aspell_AspellObject*)pyobject)->encoding) static char* DefaultEncoding = "ascii"; static PyTypeObject aspell_AspellType; /* error reported by speller */ static PyObject* _AspellSpellerException; /* error reported by speller's config */ static PyObject* _AspellConfigException; /* error reported by module */ static PyObject* _AspellModuleException; typedef struct { PyObject_HEAD char* encoding; /* internal encoding */ AspellSpeller* speller; /* the speller */ } aspell_AspellObject; /* helper function: converts an aspell word list into python list */ static PyObject* AspellWordList2PythonList(PyObject* self, const AspellWordList* wordlist) { PyObject* list; PyObject* elem; AspellStringEnumeration* elements; const char* word; list = PyList_New(0); if (!list) { PyErr_SetString(PyExc_Exception, "can't create new list"); return NULL; } elements = aspell_word_list_elements(wordlist); while ( (word=aspell_string_enumeration_next(elements)) != 0) { elem = PyUnicode_Decode(word, strlen(word), Encoding(self), NULL); if (elem == 0) { delete_aspell_string_enumeration(elements); Py_DECREF(list); return NULL; } if (PyList_Append(list, elem) == -1) { delete_aspell_string_enumeration(elements); Py_DECREF(elem); Py_DECREF(list); return NULL; } } delete_aspell_string_enumeration(elements); return list; } /* helper function: converts an aspell string list into python list */ static PyObject* AspellStringList2PythonList(const AspellStringList* wordlist) { PyObject* list; AspellStringEnumeration* elements; const char* word; list = PyList_New(0); if (!list) { PyErr_SetString(PyExc_Exception, "can't create new list"); return NULL; } elements = aspell_string_list_elements(wordlist); while ( (word=aspell_string_enumeration_next(elements)) != 0) if (PyList_Append(list, Py_BuildValue("s", word)) == -1) { PyErr_SetString(PyExc_Exception, "It is almost impossible, but happend! Can't append element to the list."); delete_aspell_string_enumeration(elements); Py_DECREF(list); return NULL; } delete_aspell_string_enumeration(elements); return list; } static PyObject* get_single_arg_string( PyObject* self, // [in] PyObject* obj, // [in] char** word, // [out] Py_ssize_t* size // [out] ); /* Create a new speller *******************************************************/ static PyObject* new_speller(PyTypeObject* self, PyObject* args, PyObject* kwargs) { aspell_AspellObject* newobj; AspellSpeller* speller = 0; AspellConfig* config; AspellCanHaveError* possible_error; int i; int n; /* arg count */ char *key, *value; char *encoding; config = new_aspell_config(); if (config == NULL) { PyErr_SetString(_AspellModuleException, "can't create config"); return NULL; } /* check constructor arguments */ n = PyTuple_Size(args); switch (n) { case 0: /* no arguments passed */ break; case 2: /* constructor is called with single pair: key & value */ if (PyArg_ParseTuple(args, "ss", &key, &value)) { if (!aspell_config_replace(config, key, value)) { PyErr_SetString(_AspellConfigException, aspell_config_error_message(config)); goto arg_error; } break; } PyErr_Clear(); default: /* list of tuples key&value */ for (i=0; ispeller = speller; newobj->encoding = encoding; return (PyObject*)newobj; /* argument error: before return NULL we need to delete speller's config we've created */ arg_error: delete_aspell_config(config); return NULL; } /* Delete speller *************************************************************/ static void speller_dealloc(PyObject* self) { if (Encoding(self) != DefaultEncoding) free(Encoding(self)); delete_aspell_speller( Speller(self) ); PyObject_Del(self); } static PyObject* configkeys_helper(PyObject* self) { AspellConfig* config; AspellKeyInfoEnumeration *keys_enumeration; AspellStringList* lst; AspellMutableContainer* amc; const AspellKeyInfo *key_info; PyObject *dict = 0, *obj = 0, *value = 0; const char* string; unsigned int integer; unsigned int boolean; char *key_type = 0; if (self) config = aspell_speller_config(Speller(self)); else config = new_aspell_config(); if (config == NULL) { PyErr_SetString(_AspellModuleException, "can't create config"); return NULL; } keys_enumeration = aspell_config_possible_elements(config, 1); if (!keys_enumeration) { if (!self) delete_aspell_config(config); PyErr_SetString(_AspellConfigException, "can't get list of config keys"); return NULL; } dict = PyDict_New(); if (dict == NULL) { if (!self) delete_aspell_config(config); return NULL; } while ((key_info = aspell_key_info_enumeration_next(keys_enumeration))) { /* key type -> string */ switch (key_info->type) { case AspellKeyInfoString: key_type = "string"; string = aspell_config_retrieve(config, key_info->name); if (aspell_config_error(config) != NULL) goto config_get_error; obj = PyUnicode_FromString( string ); break; case AspellKeyInfoInt: key_type = "integer"; integer = aspell_config_retrieve_int(config, key_info->name); if (aspell_config_error(config) != NULL) goto config_get_error; obj = PyLong_FromLong( integer ); break; case AspellKeyInfoBool: key_type = "boolean"; boolean = aspell_config_retrieve_bool(config, key_info->name); if (aspell_config_error(config) != NULL) goto config_get_error; obj = PyBool_FromLong( boolean ); break; case AspellKeyInfoList: key_type = "list"; lst = new_aspell_string_list(); amc = aspell_string_list_to_mutable_container(lst); aspell_config_retrieve_list(config, key_info->name, amc); if (aspell_config_error(config) != NULL) goto config_get_error; obj = AspellStringList2PythonList(lst); delete_aspell_string_list(lst); break; } /* value */ value = Py_BuildValue("(sOs)", key_type, obj, key_info->desc ? key_info->desc : "internal" ); if (value) { if (PyDict_SetItemString(dict, key_info->name, value)) { goto python_error; } else Py_DECREF(value); } else goto python_error; } delete_aspell_key_info_enumeration(keys_enumeration); if (!self) delete_aspell_config(config); return dict; config_get_error: PyErr_SetString(_AspellConfigException, aspell_config_error_message(config)); python_error: delete_aspell_key_info_enumeration(keys_enumeration); if (!self) delete_aspell_config(config); Py_DECREF(dict); return NULL; } /* ConfigKeys *****************************************************************/ static PyObject* configkeys(PyObject* _) { return configkeys_helper(NULL); } /* method:ConfigKeys **********************************************************/ static PyObject* m_configkeys(PyObject* self, PyObject* args) { return configkeys_helper(self); } /* method:setConfigKey ********************************************************/ static PyObject* m_set_config_key(PyObject* self, PyObject* args) { AspellConfig* config; const AspellKeyInfo* info; char* key; char* string; Py_ssize_t length; long number; char buffer[32]; PyObject* arg1; PyObject* value; if (PyTuple_Size(args) != 2) { PyErr_Format(PyExc_TypeError, "expected two arguments"); return NULL; } if (!PyArg_ParseTuple(args, "sO", &key, &arg1)) { PyErr_Format(PyExc_TypeError, "first argument must be a string"); return NULL; } config = aspell_speller_config(Speller(self)); info = aspell_config_keyinfo(config, key); if (aspell_config_error(config) != 0) { PyErr_SetString(_AspellConfigException, aspell_config_error_message(config)); return NULL; } switch (info->type) { case AspellKeyInfoList: // Can't figure out the splitting char for lists, // it seems to be the ':', but doesn't work. case AspellKeyInfoString: value = get_single_arg_string(self, arg1, &string, &length); if (value == NULL) { PyErr_Format(PyExc_TypeError, "second argument have to be string"); return NULL; } aspell_config_replace(config, key, string); Py_DECREF(value); break; case AspellKeyInfoInt: number = PyLong_AsLong(arg1); if (number == -1 && PyErr_Occurred()) { return NULL; } snprintf(buffer, 32, "%ld", number); aspell_config_replace(config, key, buffer); break; case AspellKeyInfoBool: if (PyBool_Check(arg1)) { aspell_config_replace(config, key, (arg1 == Py_True) ? "true" : "false"); } else { PyErr_Format(PyExc_TypeError, "second argument have to be boolean"); return NULL; } break; default: PyErr_Format(_AspellModuleException, "unsupported aspell config item type"); return NULL; } if (aspell_config_error(config) != 0) { PyErr_SetString(_AspellConfigException, aspell_config_error_message(config)); return NULL; } Py_RETURN_NONE; } static PyObject* get_single_arg_string( PyObject* self, // [in] PyObject* obj, // [in] char** word, // [out] Py_ssize_t* size // [out] ) { PyObject* buf; /* unicode */ if (PyUnicode_Check(obj)) /* convert to buffer */ buf = PyUnicode_AsEncodedString(obj, Encoding(self), "strict"); else /* buffer */ if (PyBytes_Check(obj)) { buf = obj; Py_INCREF(buf); // PyTuple_GetItem returns borrowed reference } else { PyErr_SetString(PyExc_TypeError, "string of bytes required"); return NULL; } /* unpack buffer */ if (buf) { if (PyBytes_AsStringAndSize(buf, word, size) != -1) { return buf; } else { Py_DECREF(buf); return NULL; } } else return NULL; } static PyObject* get_arg_string( PyObject* self, // [in] PyObject* args, // [in] const int index,// [in] args[index] char** word, // [out] Py_ssize_t* size // [out] ) { PyObject* obj; obj = PyTuple_GetItem(args, index); if (obj) return get_single_arg_string(self, obj, word, size); else return NULL; } /* method:__contains__ ********************************************************/ static int m_contains(PyObject* self, PyObject* args) { char* word; Py_ssize_t length; PyObject* buf; buf = get_single_arg_string(self, args, &word, &length); if (buf != NULL) switch (aspell_speller_check(Speller(self), word, length)) { case 0: Py_DECREF(buf); return 0; case 1: Py_DECREF(buf); return 1; default: Py_DECREF(buf); PyErr_SetString(_AspellSpellerException, aspell_speller_error_message(Speller(self))); return -1; } // switch else return -1; } /* method:check ***************************************************************/ static PyObject* m_check(PyObject* self, PyObject* args) { PyObject* word; word = PyTuple_GetItem(args, 0); if (word) switch (m_contains(self, word)) { case 0: Py_RETURN_FALSE; case 1: Py_RETURN_TRUE; default: return NULL; } else return NULL; } /* method:suggest ************************************************************/ static PyObject* m_suggest(PyObject* self, PyObject* args) { char* word; Py_ssize_t length; PyObject* buf; PyObject* list; buf = get_arg_string(self, args, 0, &word, &length); if (buf) { list = AspellWordList2PythonList( self, aspell_speller_suggest(Speller(self), word, length) ); Py_DECREF(buf); return list; } else return NULL; } /* method:getMainwordlist *****************************************************/ static PyObject* m_getMainwordlist(PyObject* self, PyObject* args) { return AspellWordList2PythonList(self, aspell_speller_main_word_list(Speller(self))); } /* method:getPersonalwordlist *************************************************/ static PyObject* m_getPersonalwordlist(PyObject* self, PyObject* args) { return AspellWordList2PythonList(self, aspell_speller_personal_word_list(Speller(self))); } /* method:getSessionwordlist **************************************************/ static PyObject* m_getSessionwordlist(PyObject* self, PyObject* args) { return AspellWordList2PythonList(self, aspell_speller_session_word_list(Speller(self))); } /* check for any aspell error after a lib call and either raises exception one or returns none */ static PyObject* AspellCheckError(PyObject* self) { if (aspell_speller_error(Speller(self)) != 0) { PyErr_SetString(_AspellSpellerException, aspell_speller_error_message(Speller(self))); return NULL; } else Py_RETURN_NONE; } /* method:addtoPersonal *******************************************************/ static PyObject* m_addtoPersonal(PyObject* self, PyObject* args) { char *word; Py_ssize_t length; if (!PyArg_ParseTuple(args, "s#", &word, &length)) { PyErr_SetString(PyExc_TypeError, "a string is required"); return NULL; } aspell_speller_add_to_personal(Speller(self), word, length); return AspellCheckError(self); } /* method:addtoSession ********************************************************/ static PyObject* m_addtoSession(PyObject* self, PyObject* args) { char *word; Py_ssize_t length; PyObject* buf; buf = get_arg_string(self, args, 0, &word, &length); if (buf) { aspell_speller_add_to_session(Speller(self), word, length); Py_DECREF(buf); return AspellCheckError(self); } else return NULL; } /* method:clearsession ********************************************************/ static PyObject* m_clearsession(PyObject* self, PyObject* args) { aspell_speller_clear_session(Speller(self)); return AspellCheckError(self); } /* method:saveallwords ********************************************************/ static PyObject* m_saveallwords(PyObject* self, PyObject* args) { aspell_speller_save_all_word_lists(Speller(self)); return AspellCheckError(self); } /* method:addReplacement ******************************************************/ static PyObject* m_addReplacement(PyObject* self, PyObject* args) { char *mis; Py_ssize_t ml; char *cor; Py_ssize_t cl; PyObject* Mbuf; PyObject* Cbuf; Mbuf = get_arg_string(self, args, 0, &mis, &ml); if (Mbuf == NULL) { PyErr_SetString(PyExc_TypeError, "first argument have to be a string or bytes"); return NULL; } Cbuf = get_arg_string(self, args, 1, &cor, &cl); if (Cbuf == NULL) { Py_DECREF(Mbuf); PyErr_SetString(PyExc_TypeError, "second argument have to be a string or bytes"); return NULL; } aspell_speller_store_replacement(Speller(self), mis, ml, cor, cl); Py_DECREF(Mbuf); Py_DECREF(Cbuf); return AspellCheckError(self); } /* AspellSpeller methods table */ static PyMethodDef aspell_object_methods[] = { { "ConfigKeys", (PyCFunction)m_configkeys, METH_VARARGS, "ConfigKeys() => dictionary of config keys\n" "Keys are string, values are 3-touple:\n" "\t1. key type={string|integer|boolean|list}\n" "\t2. current value\n" "\t3. description (if 'internal' no description available)" }, { "setConfigKey", (PyCFunction)m_set_config_key, METH_VARARGS, "changeConfig(key, value)\n" "Sets a new config value for given key" }, { "check", (PyCFunction)m_check, METH_VARARGS, "check(word) => bool\n" "Checks spelling of word.\n" "Returns if word is correct." }, { "suggest", (PyCFunction)m_suggest, METH_VARARGS, "suggest(word) => list of words\n" "Returns a list of suggested spelling for given word.\n" "Even if word is correct (i.e. check(word) returned 1) aspell performs action." }, { "getMainwordlist", (PyCFunction)m_getMainwordlist, METH_VARARGS, "getMainwordlist() => list of words\n" "Return a list of words stored in the main dictionary." }, { "getPersonalwordlist", (PyCFunction)m_getPersonalwordlist, METH_VARARGS, "getPersonalwordlist() => list of words\n" "Return a list of words stored in the personal dictionary." }, { "getSessionwordlist", (PyCFunction)m_getSessionwordlist, METH_VARARGS, "getSessionwordlist() => list of words\n" "Return a list of words stored in the session dictionary." }, { "clearSession", (PyCFunction)m_clearsession, METH_VARARGS, "clearSession() => None\n" "Clear current session, i.e. erases all words added thru addtoSession method since last saveallwords() call." }, { "saveAllwords", (PyCFunction)m_saveallwords, METH_VARARGS, "saveAllwords() => None\n" "Save all words added thru addtoPersonal() and addtoSession() methods." }, { "addtoPersonal", (PyCFunction)m_addtoPersonal, METH_VARARGS, "addtoPersonal(word) => None\n" "Add word to the personal dictionary" }, { "addtoSession", (PyCFunction)m_addtoSession, METH_VARARGS, "addtoSession(word) => None\n" "Add word to the session dictionary" }, { "addReplacement", (PyCFunction)m_addReplacement, METH_VARARGS, "addReplacement(misspeled word, correct word) => None\n" "Add a replacement pair, i.e. a misspeled and correct words.\n" "For example 'teh' and 'the'." }, {NULL, NULL, 0, NULL} }; static PyTypeObject aspell_AspellType = { PyVarObject_HEAD_INIT(NULL, 0) "AspellSpeller", /* tp_name */ sizeof(aspell_AspellObject), /* tp_size */ 0, /* tp_itemsize? */ (destructor)speller_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ aspell_object_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ new_speller, /* tp_new */ }; static PySequenceMethods speller_as_sequence; static PyMethodDef aspell_module_methods[] = { { "ConfigKeys", (PyCFunction)configkeys, METH_VARARGS, "ConfigKeys() => dictionary of config keys\n" "Keys are string, values are 3-touple:\n" "\t1. key type={string|integer|boolean|list}\n" "\t2. current value\n" "\t3. description (if 'internal' no description available)" }, {NULL, NULL, 0, NULL} }; static PyModuleDef aspellmodule = { PyModuleDef_HEAD_INIT, "aspell", "aspell wrapper", -1, aspell_module_methods, }; PyMODINIT_FUNC PyInit_recollaspell(void) { PyObject *module; module = PyModule_Create(&aspellmodule); if (module == NULL) return NULL; speller_as_sequence.sq_contains = m_contains; aspell_AspellType.tp_as_sequence = &speller_as_sequence; if (PyType_Ready(&aspell_AspellType) < 0) { Py_DECREF(module); return NULL; } else PyModule_AddObject(module, "Speller", (PyObject*)&aspell_AspellType); _AspellSpellerException = PyErr_NewException("aspell.AspellSpellerError", NULL, NULL); _AspellModuleException = PyErr_NewException("aspell.AspellModuleError", NULL, NULL); _AspellConfigException = PyErr_NewException("aspell.AspellConfigError", NULL, NULL); PyModule_AddObject(module, "AspellSpellerError", _AspellSpellerException); PyModule_AddObject(module, "AspellModuleError", _AspellModuleException); PyModule_AddObject(module, "AspellConfigError", _AspellConfigException); return module; } /* vim:ts=2 sw=2 */ recoll-1.36.1/python/pyaspell/LICENSE0000644000175000017500000000273114426500174014170 00000000000000Copyright (c) 2006-2019 Wojciech Muła All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Wojciech Muła nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. recoll-1.36.1/python/pyaspell/pyaspell.py0000644000175000017500000002267714426500174015401 00000000000000# -*- coding: iso-8859-2 -*- # Aspell interface using ctypes. # $Date: 2008-10-02 21:29:05 $, $Revision: 1.5 $ # # This is straightforward translation of my # aspell-python, C extension. # # License: BSD # # author: Wojciech Mua # e-mail: wojciech_mula@poczta.onet.pl # www : http://0x80.pl/proj/aspell-python/ # # TODO: add method to get/change **current** speller's config # # Changes: # 2011-02-20 # * python3 compatible # * fixed docs # # 2008-09-xx: # * fixed typo in save_all, thanks to Thomas Waldecker (thomas!yospot.de) # try: import ctypes import ctypes.util except ImportError: raise ImportError("ctypes library is needed") class AspellError(Exception): pass class AspellConfigError(AspellError): pass class AspellSpellerError(AspellError): pass try: bytes def _to_bytes(s): return s.encode() def _from_bytes(s): return str(s) except NameError: def _to_bytes(s): return s def _from_bytes(s): return s class AspellLinux(object): """ Aspell speller object. Allows to check spelling, get suggested spelling list, manage user dictionaries, and other. Must be closed with 'close' method, or one may experience problems, like segfaults. """ def __init__(self, configkeys=None, libname=None): """ Parameters: * configkeys - list of configuration parameters; each element is a pair key & value (both strings) if None, then default configuration is used * libname - explicitly set aspell library name; if None then default name is used """ if libname is None: libname = ctypes.util.find_library('aspell') self.__lib = ctypes.CDLL(libname) # Initialize speller # 1. create configuration config = self.__lib.new_aspell_config() if config == None: raise AspellError("Can't create aspell config object") # 2. parse configkeys arg. if configkeys is not None: assert type(configkeys) in [tuple, list], "Tuple or list expected" if len(configkeys) == 2 and \ type(configkeys[0]) is str and \ type(configkeys[1]) is str: configkeys = [configkeys] for key, value in configkeys: assert type(key) is str, "Key must be string" assert type(value) is str, "Value must be string" if not self.__lib.aspell_config_replace(config, _to_bytes(key), _to_bytes(value)): raise self._aspell_config_error(config) # 3. create speller possible_error = self.__lib.new_aspell_speller(config) self.__lib.delete_aspell_config(config) if self.__lib.aspell_error_number(possible_error) != 0: self.__lib.delete_aspell_can_have_error(possible_error) raise AspellError("Can't create speller object") self.__speller = self.__lib.to_aspell_speller(possible_error) def check(self, word): """ Check if word is present in main, personal or session dictionary. Boolean value is returned """ if type(word) is str: return bool( self.__lib.aspell_speller_check( self.__speller, _to_bytes(word), len(word) )) else: raise TypeError("String expected") __contains__ = check def suggest(self, word): """ Return list of spelling suggestions of given word. Works even if word is correct. """ if type(word) is str: return self._aspellwordlist( self.__lib.aspell_speller_suggest( self.__speller, _to_bytes(word), len(word) )) else: raise TypeError("String expected") def personal_dict(self, word=None): """ Aspell's personal dictionary is a user defined, persistent list of word (saved in certain file). If 'word' is not given, then method returns list of words stored in dict. If 'word' is given, then is added to personal dict. New words are not saved automatically, method 'save_all' have to be call. """ if word is not None: # add new word assert type(word) is str, "String expected" self.__lib.aspell_speller_add_to_personal( self.__speller, _to_bytes(word), len(word) ) self._aspell_check_error() else: # return list of words from personal dictionary return self._aspellwordlist( self.__lib.aspell_speller_personal_word_list(self.__speller) ) def session_dict(self, word=None, clear=False): """ Aspell's session dictionary is a user defined, volatile list of word, that is destroyed with aspell object. If 'word' is None, then list of words from session dictionary is returned. If 'word' is present, then is added to dict. If 'clear' is True, then session dictionary is cleared. """ if clear: self.__lib.aspell_speller_clear_session(self.__speller) self._aspell_check_error() return if word is not None: # add new word assert type(word) is str, "String expected" self.__lib.aspell_speller_add_to_session( self.__speller, _to_bytes(word), len(word) ) self._aspell_check_error() else: # return list of words from personal dictionary return self._aspellwordlist( self.__lib.aspell_speller_session_word_list(self.__speller) ) def add_replacement_pair(self, misspelled, correct): """ Add replacement pair, i.e. pair of misspelled and correct word. It affects on order of words appear on list returned by 'suggest' method. """ assert type(misspelled) is str, "String is required" assert type(correct) is str, "String is required" self.__lib.aspell_speller_store_replacement( self.__speller, _to_bytes(misspelled), len(misspelled), _to_bytes(correct), len(correct) ) self._aspell_check_error() def save_all(self): """ Saves all words added to personal or session dictionary to the apell's defined file. """ self.__lib.aspell_speller_save_all_word_lists(self.__speller) self._aspell_check_error() def configkeys(self): """ Returns list of all available config keys that can be passed to constructor. List contains a 3-tuples: 1. key name 2. default value of type: * bool * int * string * list of string 3. short description if None, then this key is undocumented is should not be used, unless one know what really do """ config = self.__lib.aspell_speller_config(self.__speller) if config is None: raise AspellConfigError("Can't get speller's config") keys_enum = self.__lib.aspell_config_possible_elements(config, 1) if keys_enum is None: raise AspellError("Can't get list of config keys") class KeyInfo(ctypes.Structure): _fields_ = [ ("name", ctypes.c_char_p), ("type", ctypes.c_int), ("default", ctypes.c_char_p), ("desc", ctypes.c_char_p), ("flags", ctypes.c_int), ("other_data", ctypes.c_int), ] key_next = self.__lib.aspell_key_info_enumeration_next key_next.restype = ctypes.POINTER(KeyInfo) list = [] while True: key_info = key_next(keys_enum) if not key_info: break else: key_info = key_info.contents if key_info.type == 0: # string list.append(( _from_bytes(key_info.name), _from_bytes(key_info.default), _from_bytes(key_info.desc), )) elif key_info.type == 1: # integer list.append(( _from_bytes(key_info.name), int(key_info.default), _from_bytes(key_info.desc), )) elif key_info.type == 2: # boolean if _from_bytes(key_info.default.lower()) == 'true': list.append(( _from_bytes(key_info.name), True, _from_bytes(key_info.desc), )) else: list.append(( _from_bytes(key_info.name), False, _from_bytes(key_info.desc), )) elif key_info.type == 3: # list list.append(( _from_bytes(key_info.name), _from_bytes(key_info.default.split()), _from_bytes(key_info.desc), )) self.__lib.delete_aspell_key_info_enumeration(keys_enum) return list def close(self): """ Close aspell speller object. """ self.__lib.delete_aspell_speller(self.__speller) # XXX: internal function, do not call directly def _aspellwordlist(self, wordlist_id): """ XXX: internal function Converts aspell list into python list. """ elements = self.__lib.aspell_word_list_elements(wordlist_id) list = [] while True: wordptr = self.__lib.aspell_string_enumeration_next(elements) if not wordptr: break else: word = ctypes.c_char_p(wordptr) list.append(_from_bytes(word.value)) self.__lib.delete_aspell_string_enumeration(elements) return list def _aspell_config_error(self, config): """ XXX: internal function Raise exception if operation of speller config caused an error. Additionally destroy config object. """ # make exception object & copy error msg exc = AspellConfigError( _from_bytes(ctypes.c_char_p( self.__lib.aspell_config_error_message(config) ).value) ) # then destroy config objcet self.__lib.delete_aspell_config(config) # and then raise exception raise exc def _aspell_check_error(self): """ XXX: internal function Raise exception if previous speller operation caused an error. """ if self.__lib.aspell_speller_error(self.__speller) != 0: msg = self.__lib.aspell_speller_error_message(self.__speller) raise AspellSpellerError(_from_bytes(msg)) #class Aspell = AspellLinux if __name__ == '__main__': # TODO: more test cases a = Aspell(("lang", "en")) print("when" in a) print(a.check("when")) print(a.suggest("wehn")) a.add_replacement_pair("wehn", "ween") print(a.suggest("wehn")) print(a.session_dict()) print(a.check("pyaspell")) a.session_dict("pyaspell") print(a.session_dict()) print(a.check("pyaspell")) a.session_dict(clear=True) print(a.session_dict()) for item in a.configkeys(): print(item) a.close() # vim: ts=4 sw=4 recoll-1.36.1/python/pyaspell/README.rst0000644000175000017500000003217314426501047014655 00000000000000======================================================================== aspell-python - Python bindings for GNU aspell ======================================================================== This version of aspell-python has been trimmed, and renamed for use with Recoll. .. image:: https://travis-ci.org/WojciechMula/aspell-python.svg?branch=master :target: https://travis-ci.org/WojciechMula/aspell-python .. contents:: Introduction ============ `GNU Aspell`__ is a leading spelling engine, fast, with many dictionaries available. Take a look at `Python Cookbook`__ --- Ryan Kelly have collected links to all python bindings for spellers. **aspell-python** is a Python wrapper for GNU Aspell, there are two variants: * ``pyaspell.py`` --- Python library, that utilize ctypes__ module; compatible with python3; * ``aspell-python`` --- C extension, two versions are available, one for Python 2.x, and Python 3.x. C exension exist in two versions: one compatible with Python 2.x and other with Python 3.x. Version for Py2 has been tested with Python 2.1, Python 2.3.4 and Python 2.4.1. Probably it works fine with all Python versions not older than 2.0. Version for Py3 has been tested with Python 3.2. __ http://docs.python.org/library/ctypes.html __ http://aspell.net __ http://code.activestate.com/recipes/117221/ License ======= Both libraries are licensed under BSD license Author ====== Wojciech Muła, wojciech_mula@poczta.onet.pl Thanks to: * **Adam Karpierz** for conviencing me to change license from GPL to BSD and for compiling early versions of C extension under Windows * **Gora Mohanty** for reporting a bug. Installation ============ To build & install module for python2.x please use script setup.2.py, i.e.:: $ python setup.2.py build $ python setup.2.py install Module for python3.x is build with setup.3.py:: $ python3 setup.3.py build $ python3 setup.3.py install Note ``python3`` name. Many Linux distributions ship both Python 2 and 3, and use the different name to distinguish versions. Details ------- You need to have libaspell headers installed, Debian package is called ``libaspell-dev``, other distributions should have similar package. In order to install **aspell-python** for all users, you must be a root. If you are, type following command:: $ python setup.py install It builds package and installs ``aspell.so`` in directory ``/usr/lib/{python}/site-packages``. If you don't have root login, you can append ``--user`` to the install command, to install it for the current user in ``~/.local/lib/{python}/site-packages``. Windows issues -------------- To correctly install aspell's dictionaries in Windows some additional work is needed. `Eric Woudenberg`__ has prepared detailed step-by-step instruction avaiable in file `windows.rst `_. __ http://www.woudy.org/ API === **Aspell-python** module is seen in python under name ``aspell``. So, **aspell-python** module is imported in following way:: import aspell The module provides Speller_ class, two methods, and three types of exceptions --- all described below. Methods ------- .. _ConfigKeysMeth: ConfigKeys() => dictionary ~~~~~~~~~~~~~~~~~~~~~~~~~~ Method returns a dictionary, where keys are names of configuration item, values are 3-tuples: * key type (``string``, ``integer``, ``boolean``, ``list``) * default value for the key * short description - "internal" means that aspell doesn't provide any description of item and you shouldn't set/change it, unless you know what you do Aspell's documentation covers in details all of keys and their meaning. Below is a list of most useful and obvious options (it is a filtered output of ``ConfigKeys``). :: ('data-dir', 'string', '/usr/lib/aspell-0.60', 'location of language data files') ('dict-dir', 'string', '/usr/lib/aspell-0.60', 'location of the main word list') ('encoding', 'string', 'ISO-8859-2', 'encoding to expect data to be in') ('home-dir', 'string', '/home/wojtek', 'location for personal files') ('ignore', 'integer', 1, 'ignore words <= n chars') ('ignore-accents', 'boolean', False, 'ignore accents when checking words -- CURRENTLY IGNORED') ('ignore-case', 'boolean', False, 'ignore case when checking words') ('ignore-repl', 'boolean', False, 'ignore commands to store replacement pairs') ('keyboard', 'string', 'standard', 'keyboard definition to use for typo analysis') ('lang', 'string', 'pl_PL', 'language code') ('master', 'string', 'pl_PL', 'base name of the main dictionary to use') ('personal-path', 'string', '/home/wojtek/.aspell.pl_PL.pws', 'internal') ('repl-path', 'string', '/home/wojtek/.aspell.pl_PL.prepl', 'internal') ('run-together', 'boolean', False, 'consider run-together words legal') ('save-repl', 'boolean', True, 'save replacement pairs on save all') ('warn', 'boolean', True, 'enable warnings') ('backup', 'boolean', True, 'create a backup file by appending ".bak"') ('reverse', 'boolean', False, 'reverse the order of the suggest list') ('suggest', 'boolean', True, 'suggest possible replacements') Classes ------- _`Speller`\ () ~~~~~~~~~~~~~~ Method creates an AspellSpeller_ object which is an interface to the GNU Aspell. ``Speller`` called with no parameters creates speller using default configuration. If you want to change or set some parameter you can pass pair of strings: key and it's value. One can get available keys using ConfigKeys_. >>> aspell.Speller("key", "value") If you want to set more than one pair of key&value, pass the list of pairs to the Speller(). >>> aspell.Speller( ("k1","v1"), ("k2","v2"), ("k3","v3") ) Exceptions ---------- Module defines following errors: * AspellConfigError_, * AspellModuleError_ and * AspellSpellerError_. Additionally ``TypeError`` is raised when you pass wrong parameters to method. _`AspellConfigError` ~~~~~~~~~~~~~~~~~~~~ Error is reported by methods Speller_ and ConfigKeys_. The most common error is passing unknown key. >>> s = aspell.Speller('python', '2.3') Traceback (most recent call last): File "", line 1, in ? aspell.AspellConfigError: The key "python" is unknown. >>> _`AspellModuleError` ~~~~~~~~~~~~~~~~~~~~ Error is reported when module can't allocate aspell structures. _`AspellSpellerError` ~~~~~~~~~~~~~~~~~~~~~ Error is reported by ``libaspell``. >>> # we set master dictionary file, the file doesn't exist >>> s = Speller('master', '/home/dictionary.rws') Traceback (most recent call last): File "", line 1, in ? aspell.AspellSpellerError: The file "/home/dictionary.rws" can not be opened for reading. >>> _`AspellSpeller` Object ----------------------- The AspellSpeller object provides interface to the aspell. It has several methods, described below. * ConfigKeys_ * check_ * suggest_ * addReplacement_ * addtoPersonal_ * saveAllwords_ * addtoSession_ * clearSession_ * getPersonalwordlist_ * getSessionwordlist_ * getMainwordlist_ In examples the assumption is that following code has been executed earlier: >>> import aspell >>> s = aspell.Speller('lang', 'en') >>> s >>> _`ConfigKeys`\ () => dictionary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **New in version 1.1, changed in 1.13.** Method returns current configuration of speller. Result has the same meaning as ``ConfigKeys()`` procedure. _`setConfigKey`\ (key, value) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **New in version 1.14** Method alters configuration value. Note that depending on key's type value is expected to be: string, boolean or integer. Although setting all keys is possible, changes to some of them have no effect. For example changing **lang** doesn't change current language, it's an aspell limitation (feature). _`check`\ (word) => boolean ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Method checks spelling of given ``word``. If ``word`` is present in the main or personal (see addtoPersonal_) or session dictionary (see addtoSession_) returns True, otherwise False. >>> s.check('word') # correct word True >>> s.check('wrod') # incorrect False >>> **New in version 1.13.** It's possible to use operator ``in`` or ``not in`` instead of ``check()``. >>> 'word' in s True >>> 'wrod' in s False >>> _`suggest` (word) => list of suggestions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Method returns a list of suggested spellings for given word. Even if word is correct, i.e. method check_ returned 1, action is performed. >>> s.suggest('wrod') # we made mistake, what aspell suggests? ['word', 'Rod', 'rod', 'Brod', 'prod', 'trod', 'Wood', 'wood', 'wried'] >>> **Warning!** ``suggest()`` in aspell 0.50 is very, very slow. I recommend caching it's results if program calls the function several times with the same argument. _`addReplacement`\ (incorrect, correct) => None ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Adds a replacement pair, it affects order of words in suggest_ result. >>> # we choose 7th word from previous result >>> s.addReplacement('wrod', 'trod') >>> # and the selected word appears at the 1st position >>> s.suggest('word') ['trod', 'word', 'Rod', 'rod', 'Brod', 'prod', 'Wood', 'wood', 'wried'] If config key ``save-repl`` is ``true`` method saveAllwords_ saves the replacement pairs to file ``~/.aspell.{lang_code}.prepl``. _`addtoPersonal`\ (word) => None ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Adds word to the personal dictionary, which is stored in file ``~./.aspell.{lang_code}.pws``. The added words are available for AspellSpeller object, but they remain unsaved until method saveAllwords_ is called. :: # personal dictionary is empty now $ cat ~/.aspell.en.pws personal_ws-1.1 en 0 $ python >>> import aspell >>> s = aspell.Speller('lang', 'en') # word 'aspell' doesn't exist >>> s.check('aspell') 0 # we add it to the personal dictionary >>> s.addtoPersonal('aspell') # and now aspell knows it >>> s.check('aspell') 1 # we save personal dictionary >>> s.saveAllwords() # new word appeared in the file $ cat ~/.aspell.en.pws personal_ws-1.1 en 1 aspell # check it once again $ python >>> import aspell >>> s = aspell.Speller('lang', 'en') # aspell still knows it's own name >>> s.check('aspell') 1 >>> s.check('aaa') 0 >>> s.check('bbb') 0 # add incorrect words, they shouldn't be saved >>> s.addtoPersonal('aaa') >>> s.addtoPersonal('bbb') >>> s.check('aaa') 1 >>> s.check('bbb') 1 # we've exit without saving, words 'aaa' and 'bbb' doesn't exists $ cat ~/.aspell.en.pws personal_ws-1.1 en 1 aspell $ _`addtoSession`\ (word) => None ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Adds word to the session dictionary. The session dictionary is volatile, it is not saved to any file. It is destroyed with AspellSpeller_ object or when method clearSession_ is called. _`saveAllwords`\ () => None ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Save all words from personal dictionary. _`clearSession`\ () => None ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Clears session dictionary. >>> import aspell >>> s = aspell.Speller('lang', 'en') >>> s.check('linux') 0 >>> s.addtoSession('linux') >>> s.check('linux') 1 >>> s.clearSession() >>> s.check('linux') 0 _`getPersonalwordlist`\ () => [list of strings] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Returns list of words from personal dictionary. _`getSessionwordlist`\ () => [list of strings] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Returns list of words from session dictionary. >>> s.addtoSession('aaa') >>> s.addtoSession('bbb') >>> s.getSessionwordlist() ['aaa', 'bbb'] >>> s.clearSession() >>> s.getSessionwordlist() [] >>> _`getMainwordlist`\ () => [list of strings] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Returns list of words from the main dictionary. Known problems ============== All version of aspell I've tested have the same error - calling method getMainwordlist_ produces ``SIGKILL``. It is aspell problem and if you really need a full list of words, use external program ``word-list-compress``. .. list-table:: * - method - aspell 0.50.5 - aspell 0.60.2 - aspell 0.60.3 * - ConfigKeys_ - ok - ok - ok * - Speller_ - ok - ok - ok * - check_ - ok - ok - ok * - suggest_ - ok - ok - ok * - addReplacement_ - ok - ok - ok * - addtoPersonal_ - ok - ok - ok * - saveAllwords_ - ok - ok - ok * - addtoSession_ - ok - ok - ok * - clearSession_ - ok - AspellSpellerError_ - ok * - getPersonalwordlist_ - ok - **SIGKILL** - ok * - getSessionwordlist_ - ok - **SIGKILL** - ok * - getMainwordlist_ - **SIGKILL** - **SIGKILL** - **SIGKILL** Character encoding ================== Aspell uses 8-bit encoding. The encoding depend on dictionary setting and is stored in key ``encoding``. One can obtain this key using speller's ConfigKeys_. If your application uses other encoding than aspell, the translation is needed. Here is a sample session (polish dictionary is used). >>> import aspell >>> s=aspell.Speller('lang', 'pl') >>> >>> s.ConfigKeys()['encoding'] ('string', u'iso-8859-1', 'encoding to expect data to be in') >>> enc =s.ConfigKeys()['encoding'][1] >>> enc # dictionary encoding 'iso-8859-1' >>> word # encoding of word is utf8 # 'gżegżółka' means in some polish dialects 'cuckoo' 'g\xc5\xbceg\xc5\xbc\xc3\xb3\xc5\x82ka' >>> s.check(word) 0 >>> s.check( unicode(word, 'utf-8').encode(enc) ) 1 recoll-1.36.1/python/pyaspell/setup.py.in0000644000175000017500000000222314515750657015312 00000000000000# -*- coding: utf-8 -*- from setuptools import setup, Extension # For shadow builds: references to the source tree import os srcdir = '@srcdir@' setup (name = 'recoll-aspell-python-py3', version = '1.15', ext_modules = [Extension('recollaspell', [os.path.join(srcdir, "aspell.c")], libraries = ["aspell"], )], description = "Wrapper around GNU Aspell for Python 3", author = "Wojciech Muła", author_email = "wojciech_mula@poczta.onet.pl", maintainer = "Wojciech Muła", maintainer_email = "wojciech_mula@poczta.onet.pl", url = "http://github.com/WojciechMula/aspell-python", platforms = ["Linux", "Windows"], license = "BSD (3 clauses)", long_description = "aspell-python - C extension for Python 3", classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: BSD License", "Programming Language :: C", "Topic :: Software Development :: Libraries", "Topic :: Text Editors :: Text Processing", ], ) recoll-1.36.1/python/README.txt0000644000175000017500000000020014410615043013010 00000000000000How to build and use the python interface is documented in the Recoll user manual, inside the "Programming interface" chapter. recoll-1.36.1/unac/0000755000175000017500000000000014521161751011014 500000000000000recoll-1.36.1/unac/unac.h0000644000175000017500000010600214426500174012032 00000000000000/* * Copyright (C) 2000, 2001, 2002 Loic Dachary * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * Provides functions to strip accents from a string in all the * charset supported by iconv(3). * * See the unac(3) manual page for more information. * */ #ifndef _unac_h #define _unac_h #ifdef __cplusplus extern "C" { #endif /* Generated by builder. Do not modify. Start defines */ #define UNAC_BLOCK_SHIFT 3 #define UNAC_BLOCK_MASK ((1 << UNAC_BLOCK_SHIFT) - 1) #define UNAC_BLOCK_SIZE (1 << UNAC_BLOCK_SHIFT) #define UNAC_BLOCK_COUNT 744 #define UNAC_INDEXES_SIZE (0x10000 >> UNAC_BLOCK_SHIFT) /* Generated by builder. Do not modify. End defines */ /* * Return the unaccented equivalent of the UTF-16 character * in the pointer

. The length of the unsigned short array pointed * by

is returned in the argument. * The C++ prototype of this macro would be: * * void unac_char(const unsigned short c, unsigned short*& p, int& l, int o) * * See unac(3) in IMPLEMENTATION NOTES for more information about the * tables (unac_data_table, unac_positions) layout. * * Each transformed char has 3 possible outputs: unaccented, unaccented and * folded, or just folded. These are kept at offset 0,1,2 in the position table */ #define unac_uf_char_utf16_(c,p,l,o) \ { \ unsigned short index = unac_indexes[(c) >> UNAC_BLOCK_SHIFT]; \ unsigned char position = 3*((c) & UNAC_BLOCK_MASK) + (o); \ (p) = &(unac_data_table[index][unac_positions[index][position]]); \ (l) = unac_positions[index][position + 1] \ - unac_positions[index][position]; \ if((l) == 1 && *(p) == 0xFFFF) { \ (p) = nullptr; \ (l) = 0; \ } \ } #define unac_char_utf16(c,p,l) unac_uf_char_utf16_((c),(p),(l),0) #define unacfold_char_utf16(c,p,l) unac_uf_char_utf16_((c),(p),(l),1) #define fold_char_utf16(c,p,l) unac_uf_char_utf16_((c),(p),(l),2) /* * Return the unaccented equivalent of the UTF-16 string of * length in the pointer . The length of the UTF-16 * string returned in is stored in . If the pointer * *out is null, a new string is allocated using malloc(3). If the * pointer *out is not null, the available length must also be given * in the *out_length argument. The pointer passed to *out must have * been allocated by malloc(3) and may be reallocated by realloc(3) if * needs be. It is the responsibility of the caller to free the * pointer returned in *out. The return value is 0 on success and -1 * on error, in which case the errno variable is set to the * corresponding error code. */ int unac_string_utf16(const char* in, size_t in_length, char** out, size_t* out_length); int unacfold_string_utf16(const char* in, size_t in_length, char** out, size_t* out_length); int fold_string_utf16(const char* in, size_t in_length, char** out, size_t* out_length); int unacmaybefold_string_utf16(const char* in, size_t in_length, char** outp, size_t* out_lengthp, int what); int unacmaybefold_string(const char* charset, const char* in, size_t in_length, char** outp, size_t* out_lengthp, int what); /* * The semantic of this function is stricly equal to the function * unac_string_utf16. The argument applies to the content of the * input string. It is converted to UTF-16 using iconv(3) before calling * the unac_string function and the result is converted from UTF-16 to * the specified before returning it in the pointer. * For efficiency purpose it is recommended that the caller uses * unac_string and iconv(3) to save buffer allocations overhead. * The return value is 0 on success and -1 on error, in which case * the errno variable is set to the corresponding error code. */ int unac_string(const char* charset, const char* in, size_t in_length, char** out, size_t* out_length); int unacfold_string(const char* charset, const char* in, size_t in_length, char** out, size_t* out_length); int fold_string(const char* charset, const char* in, size_t in_length, char** out, size_t* out_length); #ifdef BUILDING_RECOLL #include /** * Set exceptions for unaccenting, for characters which should not be * handled according to what the Unicode tables say. For example "a * with circle above" should not be stripped to a in swedish, etc. * * @param spectrans defines the translations as a blank separated list of * UTF-8 strings. Inside each string, the first character is the exception * the rest is the translation (which may be empty). You can use double * quotes for translations which should include white space. The double-quote * can't be an exception character, deal with it... */ void unac_set_except_translations(const char *spectrans); #endif /* BUILDING_RECOLL */ /* * Return unac version number. */ const char* unac_version(void); #define UNAC_DEBUG_NONE 0x00 #define UNAC_DEBUG_LOW 0x01 #define UNAC_DEBUG_HIGH 0x02 #ifdef HAVE_VSNPRINTF #define UNAC_DEBUG_AVAILABLE 1 /* * Set the unac debug level. is one of: * UNAC_DEBUG_NONE for no debug messages at all * UNAC_DEBUG_LOW for minimal information * UNAC_DEBUG_HIGH for extremely verbose information, * only usable when translating a few short strings. * * unac_debug with anything but UNAC_DEBUG_NONE is not * thread safe. */ #define unac_debug(l) unac_debug_callback((l), 0, (void*)0); /* * Set the debug level and define a printing function callback. * The debug level is the same as in unac_debug. The * is in charge of dealing with the debug messages, * presumably to print them to the user. The is an opaque * pointer that is passed along to , should it * need to manage a persistent context. * * The prototype of allows two arguments. The first * is the debug message (const char*), the second is the opaque * pointer given as argument to unac_debug_callback. * * If is NULL, messages are printed on the standard * error output using fprintf(stderr...). * * unac_debug_callback with anything but UNAC_DEBUG_NONE is not * thread safe. * */ typedef void (*unac_debug_print_t)(const char* message, void* data); void unac_debug_callback(int level, unac_debug_print_t function, void* data); #endif /* HAVE_VSNPRINTF */ /* Generated by builder. Do not modify. Start declarations */ extern unsigned short unac_indexes[UNAC_INDEXES_SIZE]; extern unsigned char unac_positions[UNAC_BLOCK_COUNT][3*UNAC_BLOCK_SIZE + 1]; extern unsigned short* unac_data_table[UNAC_BLOCK_COUNT]; extern unsigned short unac_data0[]; extern unsigned short unac_data1[]; extern unsigned short unac_data2[]; extern unsigned short unac_data3[]; extern unsigned short unac_data4[]; extern unsigned short unac_data5[]; extern unsigned short unac_data6[]; extern unsigned short unac_data7[]; extern unsigned short unac_data8[]; extern unsigned short unac_data9[]; extern unsigned short unac_data10[]; extern unsigned short unac_data11[]; extern unsigned short unac_data12[]; extern unsigned short unac_data13[]; extern unsigned short unac_data14[]; extern unsigned short unac_data15[]; extern unsigned short unac_data16[]; extern unsigned short unac_data17[]; extern unsigned short unac_data18[]; extern unsigned short unac_data19[]; extern unsigned short unac_data20[]; extern unsigned short unac_data21[]; extern unsigned short unac_data22[]; extern unsigned short unac_data23[]; extern unsigned short unac_data24[]; extern unsigned short unac_data25[]; extern unsigned short unac_data26[]; extern unsigned short unac_data27[]; extern unsigned short unac_data28[]; extern unsigned short unac_data29[]; extern unsigned short unac_data30[]; extern unsigned short unac_data31[]; extern unsigned short unac_data32[]; extern unsigned short unac_data33[]; extern unsigned short unac_data34[]; extern unsigned short unac_data35[]; extern unsigned short unac_data36[]; extern unsigned short unac_data37[]; extern unsigned short unac_data38[]; extern unsigned short unac_data39[]; extern unsigned short unac_data40[]; extern unsigned short unac_data41[]; extern unsigned short unac_data42[]; extern unsigned short unac_data43[]; extern unsigned short unac_data44[]; extern unsigned short unac_data45[]; extern unsigned short unac_data46[]; extern unsigned short unac_data47[]; extern unsigned short unac_data48[]; extern unsigned short unac_data49[]; extern unsigned short unac_data50[]; extern unsigned short unac_data51[]; extern unsigned short unac_data52[]; extern unsigned short unac_data53[]; extern unsigned short unac_data54[]; extern unsigned short unac_data55[]; extern unsigned short unac_data56[]; extern unsigned short unac_data57[]; extern unsigned short unac_data58[]; extern unsigned short unac_data59[]; extern unsigned short unac_data60[]; extern unsigned short unac_data61[]; extern unsigned short unac_data62[]; extern unsigned short unac_data63[]; extern unsigned short unac_data64[]; extern unsigned short unac_data65[]; extern unsigned short unac_data66[]; extern unsigned short unac_data67[]; extern unsigned short unac_data68[]; extern unsigned short unac_data69[]; extern unsigned short unac_data70[]; extern unsigned short unac_data71[]; extern unsigned short unac_data72[]; extern unsigned short unac_data73[]; extern unsigned short unac_data74[]; extern unsigned short unac_data75[]; extern unsigned short unac_data76[]; extern unsigned short unac_data77[]; extern unsigned short unac_data78[]; extern unsigned short unac_data79[]; extern unsigned short unac_data80[]; extern unsigned short unac_data81[]; extern unsigned short unac_data82[]; extern unsigned short unac_data83[]; extern unsigned short unac_data84[]; extern unsigned short unac_data85[]; extern unsigned short unac_data86[]; extern unsigned short unac_data87[]; extern unsigned short unac_data88[]; extern unsigned short unac_data89[]; extern unsigned short unac_data90[]; extern unsigned short unac_data91[]; extern unsigned short unac_data92[]; extern unsigned short unac_data93[]; extern unsigned short unac_data94[]; extern unsigned short unac_data95[]; extern unsigned short unac_data96[]; extern unsigned short unac_data97[]; extern unsigned short unac_data98[]; extern unsigned short unac_data99[]; extern unsigned short unac_data100[]; extern unsigned short unac_data101[]; extern unsigned short unac_data102[]; extern unsigned short unac_data103[]; extern unsigned short unac_data104[]; extern unsigned short unac_data105[]; extern unsigned short unac_data106[]; extern unsigned short unac_data107[]; extern unsigned short unac_data108[]; extern unsigned short unac_data109[]; extern unsigned short unac_data110[]; extern unsigned short unac_data111[]; extern unsigned short unac_data112[]; extern unsigned short unac_data113[]; extern unsigned short unac_data114[]; extern unsigned short unac_data115[]; extern unsigned short unac_data116[]; extern unsigned short unac_data117[]; extern unsigned short unac_data118[]; extern unsigned short unac_data119[]; extern unsigned short unac_data120[]; extern unsigned short unac_data121[]; extern unsigned short unac_data122[]; extern unsigned short unac_data123[]; extern unsigned short unac_data124[]; extern unsigned short unac_data125[]; extern unsigned short unac_data126[]; extern unsigned short unac_data127[]; extern unsigned short unac_data128[]; extern unsigned short unac_data129[]; extern unsigned short unac_data130[]; extern unsigned short unac_data131[]; extern unsigned short unac_data132[]; extern unsigned short unac_data133[]; extern unsigned short unac_data134[]; extern unsigned short unac_data135[]; extern unsigned short unac_data136[]; extern unsigned short unac_data137[]; extern unsigned short unac_data138[]; extern unsigned short unac_data139[]; extern unsigned short unac_data140[]; extern unsigned short unac_data141[]; extern unsigned short unac_data142[]; extern unsigned short unac_data143[]; extern unsigned short unac_data144[]; extern unsigned short unac_data145[]; extern unsigned short unac_data146[]; extern unsigned short unac_data147[]; extern unsigned short unac_data148[]; extern unsigned short unac_data149[]; extern unsigned short unac_data150[]; extern unsigned short unac_data151[]; extern unsigned short unac_data152[]; extern unsigned short unac_data153[]; extern unsigned short unac_data154[]; extern unsigned short unac_data155[]; extern unsigned short unac_data156[]; extern unsigned short unac_data157[]; extern unsigned short unac_data158[]; extern unsigned short unac_data159[]; extern unsigned short unac_data160[]; extern unsigned short unac_data161[]; extern unsigned short unac_data162[]; extern unsigned short unac_data163[]; extern unsigned short unac_data164[]; extern unsigned short unac_data165[]; extern unsigned short unac_data166[]; extern unsigned short unac_data167[]; extern unsigned short unac_data168[]; extern unsigned short unac_data169[]; extern unsigned short unac_data170[]; extern unsigned short unac_data171[]; extern unsigned short unac_data172[]; extern unsigned short unac_data173[]; extern unsigned short unac_data174[]; extern unsigned short unac_data175[]; extern unsigned short unac_data176[]; extern unsigned short unac_data177[]; extern unsigned short unac_data178[]; extern unsigned short unac_data179[]; extern unsigned short unac_data180[]; extern unsigned short unac_data181[]; extern unsigned short unac_data182[]; extern unsigned short unac_data183[]; extern unsigned short unac_data184[]; extern unsigned short unac_data185[]; extern unsigned short unac_data186[]; extern unsigned short unac_data187[]; extern unsigned short unac_data188[]; extern unsigned short unac_data189[]; extern unsigned short unac_data190[]; extern unsigned short unac_data191[]; extern unsigned short unac_data192[]; extern unsigned short unac_data193[]; extern unsigned short unac_data194[]; extern unsigned short unac_data195[]; extern unsigned short unac_data196[]; extern unsigned short unac_data197[]; extern unsigned short unac_data198[]; extern unsigned short unac_data199[]; extern unsigned short unac_data200[]; extern unsigned short unac_data201[]; extern unsigned short unac_data202[]; extern unsigned short unac_data203[]; extern unsigned short unac_data204[]; extern unsigned short unac_data205[]; extern unsigned short unac_data206[]; extern unsigned short unac_data207[]; extern unsigned short unac_data208[]; extern unsigned short unac_data209[]; extern unsigned short unac_data210[]; extern unsigned short unac_data211[]; extern unsigned short unac_data212[]; extern unsigned short unac_data213[]; extern unsigned short unac_data214[]; extern unsigned short unac_data215[]; extern unsigned short unac_data216[]; extern unsigned short unac_data217[]; extern unsigned short unac_data218[]; extern unsigned short unac_data219[]; extern unsigned short unac_data220[]; extern unsigned short unac_data221[]; extern unsigned short unac_data222[]; extern unsigned short unac_data223[]; extern unsigned short unac_data224[]; extern unsigned short unac_data225[]; extern unsigned short unac_data226[]; extern unsigned short unac_data227[]; extern unsigned short unac_data228[]; extern unsigned short unac_data229[]; extern unsigned short unac_data230[]; extern unsigned short unac_data231[]; extern unsigned short unac_data232[]; extern unsigned short unac_data233[]; extern unsigned short unac_data234[]; extern unsigned short unac_data235[]; extern unsigned short unac_data236[]; extern unsigned short unac_data237[]; extern unsigned short unac_data238[]; extern unsigned short unac_data239[]; extern unsigned short unac_data240[]; extern unsigned short unac_data241[]; extern unsigned short unac_data242[]; extern unsigned short unac_data243[]; extern unsigned short unac_data244[]; extern unsigned short unac_data245[]; extern unsigned short unac_data246[]; extern unsigned short unac_data247[]; extern unsigned short unac_data248[]; extern unsigned short unac_data249[]; extern unsigned short unac_data250[]; extern unsigned short unac_data251[]; extern unsigned short unac_data252[]; extern unsigned short unac_data253[]; extern unsigned short unac_data254[]; extern unsigned short unac_data255[]; extern unsigned short unac_data256[]; extern unsigned short unac_data257[]; extern unsigned short unac_data258[]; extern unsigned short unac_data259[]; extern unsigned short unac_data260[]; extern unsigned short unac_data261[]; extern unsigned short unac_data262[]; extern unsigned short unac_data263[]; extern unsigned short unac_data264[]; extern unsigned short unac_data265[]; extern unsigned short unac_data266[]; extern unsigned short unac_data267[]; extern unsigned short unac_data268[]; extern unsigned short unac_data269[]; extern unsigned short unac_data270[]; extern unsigned short unac_data271[]; extern unsigned short unac_data272[]; extern unsigned short unac_data273[]; extern unsigned short unac_data274[]; extern unsigned short unac_data275[]; extern unsigned short unac_data276[]; extern unsigned short unac_data277[]; extern unsigned short unac_data278[]; extern unsigned short unac_data279[]; extern unsigned short unac_data280[]; extern unsigned short unac_data281[]; extern unsigned short unac_data282[]; extern unsigned short unac_data283[]; extern unsigned short unac_data284[]; extern unsigned short unac_data285[]; extern unsigned short unac_data286[]; extern unsigned short unac_data287[]; extern unsigned short unac_data288[]; extern unsigned short unac_data289[]; extern unsigned short unac_data290[]; extern unsigned short unac_data291[]; extern unsigned short unac_data292[]; extern unsigned short unac_data293[]; extern unsigned short unac_data294[]; extern unsigned short unac_data295[]; extern unsigned short unac_data296[]; extern unsigned short unac_data297[]; extern unsigned short unac_data298[]; extern unsigned short unac_data299[]; extern unsigned short unac_data300[]; extern unsigned short unac_data301[]; extern unsigned short unac_data302[]; extern unsigned short unac_data303[]; extern unsigned short unac_data304[]; extern unsigned short unac_data305[]; extern unsigned short unac_data306[]; extern unsigned short unac_data307[]; extern unsigned short unac_data308[]; extern unsigned short unac_data309[]; extern unsigned short unac_data310[]; extern unsigned short unac_data311[]; extern unsigned short unac_data312[]; extern unsigned short unac_data313[]; extern unsigned short unac_data314[]; extern unsigned short unac_data315[]; extern unsigned short unac_data316[]; extern unsigned short unac_data317[]; extern unsigned short unac_data318[]; extern unsigned short unac_data319[]; extern unsigned short unac_data320[]; extern unsigned short unac_data321[]; extern unsigned short unac_data322[]; extern unsigned short unac_data323[]; extern unsigned short unac_data324[]; extern unsigned short unac_data325[]; extern unsigned short unac_data326[]; extern unsigned short unac_data327[]; extern unsigned short unac_data328[]; extern unsigned short unac_data329[]; extern unsigned short unac_data330[]; extern unsigned short unac_data331[]; extern unsigned short unac_data332[]; extern unsigned short unac_data333[]; extern unsigned short unac_data334[]; extern unsigned short unac_data335[]; extern unsigned short unac_data336[]; extern unsigned short unac_data337[]; extern unsigned short unac_data338[]; extern unsigned short unac_data339[]; extern unsigned short unac_data340[]; extern unsigned short unac_data341[]; extern unsigned short unac_data342[]; extern unsigned short unac_data343[]; extern unsigned short unac_data344[]; extern unsigned short unac_data345[]; extern unsigned short unac_data346[]; extern unsigned short unac_data347[]; extern unsigned short unac_data348[]; extern unsigned short unac_data349[]; extern unsigned short unac_data350[]; extern unsigned short unac_data351[]; extern unsigned short unac_data352[]; extern unsigned short unac_data353[]; extern unsigned short unac_data354[]; extern unsigned short unac_data355[]; extern unsigned short unac_data356[]; extern unsigned short unac_data357[]; extern unsigned short unac_data358[]; extern unsigned short unac_data359[]; extern unsigned short unac_data360[]; extern unsigned short unac_data361[]; extern unsigned short unac_data362[]; extern unsigned short unac_data363[]; extern unsigned short unac_data364[]; extern unsigned short unac_data365[]; extern unsigned short unac_data366[]; extern unsigned short unac_data367[]; extern unsigned short unac_data368[]; extern unsigned short unac_data369[]; extern unsigned short unac_data370[]; extern unsigned short unac_data371[]; extern unsigned short unac_data372[]; extern unsigned short unac_data373[]; extern unsigned short unac_data374[]; extern unsigned short unac_data375[]; extern unsigned short unac_data376[]; extern unsigned short unac_data377[]; extern unsigned short unac_data378[]; extern unsigned short unac_data379[]; extern unsigned short unac_data380[]; extern unsigned short unac_data381[]; extern unsigned short unac_data382[]; extern unsigned short unac_data383[]; extern unsigned short unac_data384[]; extern unsigned short unac_data385[]; extern unsigned short unac_data386[]; extern unsigned short unac_data387[]; extern unsigned short unac_data388[]; extern unsigned short unac_data389[]; extern unsigned short unac_data390[]; extern unsigned short unac_data391[]; extern unsigned short unac_data392[]; extern unsigned short unac_data393[]; extern unsigned short unac_data394[]; extern unsigned short unac_data395[]; extern unsigned short unac_data396[]; extern unsigned short unac_data397[]; extern unsigned short unac_data398[]; extern unsigned short unac_data399[]; extern unsigned short unac_data400[]; extern unsigned short unac_data401[]; extern unsigned short unac_data402[]; extern unsigned short unac_data403[]; extern unsigned short unac_data404[]; extern unsigned short unac_data405[]; extern unsigned short unac_data406[]; extern unsigned short unac_data407[]; extern unsigned short unac_data408[]; extern unsigned short unac_data409[]; extern unsigned short unac_data410[]; extern unsigned short unac_data411[]; extern unsigned short unac_data412[]; extern unsigned short unac_data413[]; extern unsigned short unac_data414[]; extern unsigned short unac_data415[]; extern unsigned short unac_data416[]; extern unsigned short unac_data417[]; extern unsigned short unac_data418[]; extern unsigned short unac_data419[]; extern unsigned short unac_data420[]; extern unsigned short unac_data421[]; extern unsigned short unac_data422[]; extern unsigned short unac_data423[]; extern unsigned short unac_data424[]; extern unsigned short unac_data425[]; extern unsigned short unac_data426[]; extern unsigned short unac_data427[]; extern unsigned short unac_data428[]; extern unsigned short unac_data429[]; extern unsigned short unac_data430[]; extern unsigned short unac_data431[]; extern unsigned short unac_data432[]; extern unsigned short unac_data433[]; extern unsigned short unac_data434[]; extern unsigned short unac_data435[]; extern unsigned short unac_data436[]; extern unsigned short unac_data437[]; extern unsigned short unac_data438[]; extern unsigned short unac_data439[]; extern unsigned short unac_data440[]; extern unsigned short unac_data441[]; extern unsigned short unac_data442[]; extern unsigned short unac_data443[]; extern unsigned short unac_data444[]; extern unsigned short unac_data445[]; extern unsigned short unac_data446[]; extern unsigned short unac_data447[]; extern unsigned short unac_data448[]; extern unsigned short unac_data449[]; extern unsigned short unac_data450[]; extern unsigned short unac_data451[]; extern unsigned short unac_data452[]; extern unsigned short unac_data453[]; extern unsigned short unac_data454[]; extern unsigned short unac_data455[]; extern unsigned short unac_data456[]; extern unsigned short unac_data457[]; extern unsigned short unac_data458[]; extern unsigned short unac_data459[]; extern unsigned short unac_data460[]; extern unsigned short unac_data461[]; extern unsigned short unac_data462[]; extern unsigned short unac_data463[]; extern unsigned short unac_data464[]; extern unsigned short unac_data465[]; extern unsigned short unac_data466[]; extern unsigned short unac_data467[]; extern unsigned short unac_data468[]; extern unsigned short unac_data469[]; extern unsigned short unac_data470[]; extern unsigned short unac_data471[]; extern unsigned short unac_data472[]; extern unsigned short unac_data473[]; extern unsigned short unac_data474[]; extern unsigned short unac_data475[]; extern unsigned short unac_data476[]; extern unsigned short unac_data477[]; extern unsigned short unac_data478[]; extern unsigned short unac_data479[]; extern unsigned short unac_data480[]; extern unsigned short unac_data481[]; extern unsigned short unac_data482[]; extern unsigned short unac_data483[]; extern unsigned short unac_data484[]; extern unsigned short unac_data485[]; extern unsigned short unac_data486[]; extern unsigned short unac_data487[]; extern unsigned short unac_data488[]; extern unsigned short unac_data489[]; extern unsigned short unac_data490[]; extern unsigned short unac_data491[]; extern unsigned short unac_data492[]; extern unsigned short unac_data493[]; extern unsigned short unac_data494[]; extern unsigned short unac_data495[]; extern unsigned short unac_data496[]; extern unsigned short unac_data497[]; extern unsigned short unac_data498[]; extern unsigned short unac_data499[]; extern unsigned short unac_data500[]; extern unsigned short unac_data501[]; extern unsigned short unac_data502[]; extern unsigned short unac_data503[]; extern unsigned short unac_data504[]; extern unsigned short unac_data505[]; extern unsigned short unac_data506[]; extern unsigned short unac_data507[]; extern unsigned short unac_data508[]; extern unsigned short unac_data509[]; extern unsigned short unac_data510[]; extern unsigned short unac_data511[]; extern unsigned short unac_data512[]; extern unsigned short unac_data513[]; extern unsigned short unac_data514[]; extern unsigned short unac_data515[]; extern unsigned short unac_data516[]; extern unsigned short unac_data517[]; extern unsigned short unac_data518[]; extern unsigned short unac_data519[]; extern unsigned short unac_data520[]; extern unsigned short unac_data521[]; extern unsigned short unac_data522[]; extern unsigned short unac_data523[]; extern unsigned short unac_data524[]; extern unsigned short unac_data525[]; extern unsigned short unac_data526[]; extern unsigned short unac_data527[]; extern unsigned short unac_data528[]; extern unsigned short unac_data529[]; extern unsigned short unac_data530[]; extern unsigned short unac_data531[]; extern unsigned short unac_data532[]; extern unsigned short unac_data533[]; extern unsigned short unac_data534[]; extern unsigned short unac_data535[]; extern unsigned short unac_data536[]; extern unsigned short unac_data537[]; extern unsigned short unac_data538[]; extern unsigned short unac_data539[]; extern unsigned short unac_data540[]; extern unsigned short unac_data541[]; extern unsigned short unac_data542[]; extern unsigned short unac_data543[]; extern unsigned short unac_data544[]; extern unsigned short unac_data545[]; extern unsigned short unac_data546[]; extern unsigned short unac_data547[]; extern unsigned short unac_data548[]; extern unsigned short unac_data549[]; extern unsigned short unac_data550[]; extern unsigned short unac_data551[]; extern unsigned short unac_data552[]; extern unsigned short unac_data553[]; extern unsigned short unac_data554[]; extern unsigned short unac_data555[]; extern unsigned short unac_data556[]; extern unsigned short unac_data557[]; extern unsigned short unac_data558[]; extern unsigned short unac_data559[]; extern unsigned short unac_data560[]; extern unsigned short unac_data561[]; extern unsigned short unac_data562[]; extern unsigned short unac_data563[]; extern unsigned short unac_data564[]; extern unsigned short unac_data565[]; extern unsigned short unac_data566[]; extern unsigned short unac_data567[]; extern unsigned short unac_data568[]; extern unsigned short unac_data569[]; extern unsigned short unac_data570[]; extern unsigned short unac_data571[]; extern unsigned short unac_data572[]; extern unsigned short unac_data573[]; extern unsigned short unac_data574[]; extern unsigned short unac_data575[]; extern unsigned short unac_data576[]; extern unsigned short unac_data577[]; extern unsigned short unac_data578[]; extern unsigned short unac_data579[]; extern unsigned short unac_data580[]; extern unsigned short unac_data581[]; extern unsigned short unac_data582[]; extern unsigned short unac_data583[]; extern unsigned short unac_data584[]; extern unsigned short unac_data585[]; extern unsigned short unac_data586[]; extern unsigned short unac_data587[]; extern unsigned short unac_data588[]; extern unsigned short unac_data589[]; extern unsigned short unac_data590[]; extern unsigned short unac_data591[]; extern unsigned short unac_data592[]; extern unsigned short unac_data593[]; extern unsigned short unac_data594[]; extern unsigned short unac_data595[]; extern unsigned short unac_data596[]; extern unsigned short unac_data597[]; extern unsigned short unac_data598[]; extern unsigned short unac_data599[]; extern unsigned short unac_data600[]; extern unsigned short unac_data601[]; extern unsigned short unac_data602[]; extern unsigned short unac_data603[]; extern unsigned short unac_data604[]; extern unsigned short unac_data605[]; extern unsigned short unac_data606[]; extern unsigned short unac_data607[]; extern unsigned short unac_data608[]; extern unsigned short unac_data609[]; extern unsigned short unac_data610[]; extern unsigned short unac_data611[]; extern unsigned short unac_data612[]; extern unsigned short unac_data613[]; extern unsigned short unac_data614[]; extern unsigned short unac_data615[]; extern unsigned short unac_data616[]; extern unsigned short unac_data617[]; extern unsigned short unac_data618[]; extern unsigned short unac_data619[]; extern unsigned short unac_data620[]; extern unsigned short unac_data621[]; extern unsigned short unac_data622[]; extern unsigned short unac_data623[]; extern unsigned short unac_data624[]; extern unsigned short unac_data625[]; extern unsigned short unac_data626[]; extern unsigned short unac_data627[]; extern unsigned short unac_data628[]; extern unsigned short unac_data629[]; extern unsigned short unac_data630[]; extern unsigned short unac_data631[]; extern unsigned short unac_data632[]; extern unsigned short unac_data633[]; extern unsigned short unac_data634[]; extern unsigned short unac_data635[]; extern unsigned short unac_data636[]; extern unsigned short unac_data637[]; extern unsigned short unac_data638[]; extern unsigned short unac_data639[]; extern unsigned short unac_data640[]; extern unsigned short unac_data641[]; extern unsigned short unac_data642[]; extern unsigned short unac_data643[]; extern unsigned short unac_data644[]; extern unsigned short unac_data645[]; extern unsigned short unac_data646[]; extern unsigned short unac_data647[]; extern unsigned short unac_data648[]; extern unsigned short unac_data649[]; extern unsigned short unac_data650[]; extern unsigned short unac_data651[]; extern unsigned short unac_data652[]; extern unsigned short unac_data653[]; extern unsigned short unac_data654[]; extern unsigned short unac_data655[]; extern unsigned short unac_data656[]; extern unsigned short unac_data657[]; extern unsigned short unac_data658[]; extern unsigned short unac_data659[]; extern unsigned short unac_data660[]; extern unsigned short unac_data661[]; extern unsigned short unac_data662[]; extern unsigned short unac_data663[]; extern unsigned short unac_data664[]; extern unsigned short unac_data665[]; extern unsigned short unac_data666[]; extern unsigned short unac_data667[]; extern unsigned short unac_data668[]; extern unsigned short unac_data669[]; extern unsigned short unac_data670[]; extern unsigned short unac_data671[]; extern unsigned short unac_data672[]; extern unsigned short unac_data673[]; extern unsigned short unac_data674[]; extern unsigned short unac_data675[]; extern unsigned short unac_data676[]; extern unsigned short unac_data677[]; extern unsigned short unac_data678[]; extern unsigned short unac_data679[]; extern unsigned short unac_data680[]; extern unsigned short unac_data681[]; extern unsigned short unac_data682[]; extern unsigned short unac_data683[]; extern unsigned short unac_data684[]; extern unsigned short unac_data685[]; extern unsigned short unac_data686[]; extern unsigned short unac_data687[]; extern unsigned short unac_data688[]; extern unsigned short unac_data689[]; extern unsigned short unac_data690[]; extern unsigned short unac_data691[]; extern unsigned short unac_data692[]; extern unsigned short unac_data693[]; extern unsigned short unac_data694[]; extern unsigned short unac_data695[]; extern unsigned short unac_data696[]; extern unsigned short unac_data697[]; extern unsigned short unac_data698[]; extern unsigned short unac_data699[]; extern unsigned short unac_data700[]; extern unsigned short unac_data701[]; extern unsigned short unac_data702[]; extern unsigned short unac_data703[]; extern unsigned short unac_data704[]; extern unsigned short unac_data705[]; extern unsigned short unac_data706[]; extern unsigned short unac_data707[]; extern unsigned short unac_data708[]; extern unsigned short unac_data709[]; extern unsigned short unac_data710[]; extern unsigned short unac_data711[]; extern unsigned short unac_data712[]; extern unsigned short unac_data713[]; extern unsigned short unac_data714[]; extern unsigned short unac_data715[]; extern unsigned short unac_data716[]; extern unsigned short unac_data717[]; extern unsigned short unac_data718[]; extern unsigned short unac_data719[]; extern unsigned short unac_data720[]; extern unsigned short unac_data721[]; extern unsigned short unac_data722[]; extern unsigned short unac_data723[]; extern unsigned short unac_data724[]; extern unsigned short unac_data725[]; extern unsigned short unac_data726[]; extern unsigned short unac_data727[]; extern unsigned short unac_data728[]; extern unsigned short unac_data729[]; extern unsigned short unac_data730[]; extern unsigned short unac_data731[]; extern unsigned short unac_data732[]; extern unsigned short unac_data733[]; extern unsigned short unac_data734[]; extern unsigned short unac_data735[]; extern unsigned short unac_data736[]; extern unsigned short unac_data737[]; extern unsigned short unac_data738[]; extern unsigned short unac_data739[]; extern unsigned short unac_data740[]; extern unsigned short unac_data741[]; extern unsigned short unac_data742[]; extern unsigned short unac_data743[]; /* Generated by builder. Do not modify. End declarations */ #ifdef __cplusplus } #endif #endif /* _unac_h */ recoll-1.36.1/unac/AUTHORS0000644000175000017500000000003414410615043011774 00000000000000Loic Dachary loic@senga.org recoll-1.36.1/unac/unac.c0000644000175000017500000253313114426500174012036 00000000000000/* * Copyright (C) 2000, 2001, 2002 Loic Dachary * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #endif /* RECOLL */ #ifdef BUILDING_RECOLL /* Yes, recoll unac is actually c++, lets face modernity, I will not be caught writing another binary search */ #include #include #include #include #include #include #include using std::string; using std::vector; #include "smallut.h" /* Storage for the exception translations. These are chars which should not be translated according to what UnicodeData says, but instead according to some local rule. There will usually be very few of them, but they must be looked up for every translated char. */ std::unordered_map except_trans; static inline bool is_except_char(unsigned short c, string& trans) { auto it = except_trans.find(c); if (it == except_trans.end()) return false; trans = it->second; return true; } #endif /* BUILDING_RECOLL*/ /* * If configure.in has not defined this symbol, assume const. It * does not harm much: a warning will be issued during compilation. */ #ifndef ICONV_CONST #ifdef RCL_ICONV_INBUF_CONST #define ICONV_CONST const #else #define ICONV_CONST #endif #endif /* ICONV_CONST */ #include #include #include #include #ifdef HAVE_VSNPRINTF #include #include #endif /* HAVE_VSNPRINTF */ #include "unac.h" #include "unac_version.h" /* Generated by builder. Do not modify. Start tables */ /* * 00A0 NO-BREAK SPACE * 0020 SPACE * 00A8 DIAERESIS * 0020 SPACE * 00AA FEMININE ORDINAL INDICATOR * 0061 LATIN SMALL LETTER A * 00AF MACRON * 0020 SPACE * 00B2 SUPERSCRIPT TWO * 0032 DIGIT TWO * 00B3 SUPERSCRIPT THREE * 0033 DIGIT THREE * 00B4 ACUTE ACCENT * 0020 SPACE * 00B5 MICRO SIGN * 03BC GREEK SMALL LETTER MU * 00B8 CEDILLA * 0020 SPACE * 00B9 SUPERSCRIPT ONE * 0031 DIGIT ONE * 00BA MASCULINE ORDINAL INDICATOR * 006F LATIN SMALL LETTER O * 00BC VULGAR FRACTION ONE QUARTER * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0034 DIGIT FOUR * 00BD VULGAR FRACTION ONE HALF * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0032 DIGIT TWO * 00BE VULGAR FRACTION THREE QUARTERS * 0033 DIGIT THREE * 2044 FRACTION SLASH * 0034 DIGIT FOUR * 00C0 LATIN CAPITAL LETTER A WITH GRAVE * 0041 LATIN CAPITAL LETTER A * 00C1 LATIN CAPITAL LETTER A WITH ACUTE * 0041 LATIN CAPITAL LETTER A * 00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX * 0041 LATIN CAPITAL LETTER A * 00C3 LATIN CAPITAL LETTER A WITH TILDE * 0041 LATIN CAPITAL LETTER A * 00C4 LATIN CAPITAL LETTER A WITH DIAERESIS * 0041 LATIN CAPITAL LETTER A * 00C5 LATIN CAPITAL LETTER A WITH RING ABOVE * 0041 LATIN CAPITAL LETTER A * 00C7 LATIN CAPITAL LETTER C WITH CEDILLA * 0043 LATIN CAPITAL LETTER C * 00C8 LATIN CAPITAL LETTER E WITH GRAVE * 0045 LATIN CAPITAL LETTER E * 00C9 LATIN CAPITAL LETTER E WITH ACUTE * 0045 LATIN CAPITAL LETTER E * 00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX * 0045 LATIN CAPITAL LETTER E * 00CB LATIN CAPITAL LETTER E WITH DIAERESIS * 0045 LATIN CAPITAL LETTER E * 00CC LATIN CAPITAL LETTER I WITH GRAVE * 0049 LATIN CAPITAL LETTER I * 00CD LATIN CAPITAL LETTER I WITH ACUTE * 0049 LATIN CAPITAL LETTER I * 00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX * 0049 LATIN CAPITAL LETTER I * 00CF LATIN CAPITAL LETTER I WITH DIAERESIS * 0049 LATIN CAPITAL LETTER I * 00D1 LATIN CAPITAL LETTER N WITH TILDE * 004E LATIN CAPITAL LETTER N * 00D2 LATIN CAPITAL LETTER O WITH GRAVE * 004F LATIN CAPITAL LETTER O * 00D3 LATIN CAPITAL LETTER O WITH ACUTE * 004F LATIN CAPITAL LETTER O * 00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX * 004F LATIN CAPITAL LETTER O * 00D5 LATIN CAPITAL LETTER O WITH TILDE * 004F LATIN CAPITAL LETTER O * 00D6 LATIN CAPITAL LETTER O WITH DIAERESIS * 004F LATIN CAPITAL LETTER O * 00D9 LATIN CAPITAL LETTER U WITH GRAVE * 0055 LATIN CAPITAL LETTER U * 00DA LATIN CAPITAL LETTER U WITH ACUTE * 0055 LATIN CAPITAL LETTER U * 00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX * 0055 LATIN CAPITAL LETTER U * 00DC LATIN CAPITAL LETTER U WITH DIAERESIS * 0055 LATIN CAPITAL LETTER U * 00DD LATIN CAPITAL LETTER Y WITH ACUTE * 0059 LATIN CAPITAL LETTER Y * 00E0 LATIN SMALL LETTER A WITH GRAVE * 0061 LATIN SMALL LETTER A * 00E1 LATIN SMALL LETTER A WITH ACUTE * 0061 LATIN SMALL LETTER A * 00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX * 0061 LATIN SMALL LETTER A * 00E3 LATIN SMALL LETTER A WITH TILDE * 0061 LATIN SMALL LETTER A * 00E4 LATIN SMALL LETTER A WITH DIAERESIS * 0061 LATIN SMALL LETTER A * 00E5 LATIN SMALL LETTER A WITH RING ABOVE * 0061 LATIN SMALL LETTER A * 00E7 LATIN SMALL LETTER C WITH CEDILLA * 0063 LATIN SMALL LETTER C * 00E8 LATIN SMALL LETTER E WITH GRAVE * 0065 LATIN SMALL LETTER E * 00E9 LATIN SMALL LETTER E WITH ACUTE * 0065 LATIN SMALL LETTER E * 00EA LATIN SMALL LETTER E WITH CIRCUMFLEX * 0065 LATIN SMALL LETTER E * 00EB LATIN SMALL LETTER E WITH DIAERESIS * 0065 LATIN SMALL LETTER E * 00EC LATIN SMALL LETTER I WITH GRAVE * 0069 LATIN SMALL LETTER I * 00ED LATIN SMALL LETTER I WITH ACUTE * 0069 LATIN SMALL LETTER I * 00EE LATIN SMALL LETTER I WITH CIRCUMFLEX * 0069 LATIN SMALL LETTER I * 00EF LATIN SMALL LETTER I WITH DIAERESIS * 0069 LATIN SMALL LETTER I * 00F1 LATIN SMALL LETTER N WITH TILDE * 006E LATIN SMALL LETTER N * 00F2 LATIN SMALL LETTER O WITH GRAVE * 006F LATIN SMALL LETTER O * 00F3 LATIN SMALL LETTER O WITH ACUTE * 006F LATIN SMALL LETTER O * 00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX * 006F LATIN SMALL LETTER O * 00F5 LATIN SMALL LETTER O WITH TILDE * 006F LATIN SMALL LETTER O * 00F6 LATIN SMALL LETTER O WITH DIAERESIS * 006F LATIN SMALL LETTER O * 00F9 LATIN SMALL LETTER U WITH GRAVE * 0075 LATIN SMALL LETTER U * 00FA LATIN SMALL LETTER U WITH ACUTE * 0075 LATIN SMALL LETTER U * 00FB LATIN SMALL LETTER U WITH CIRCUMFLEX * 0075 LATIN SMALL LETTER U * 00FC LATIN SMALL LETTER U WITH DIAERESIS * 0075 LATIN SMALL LETTER U * 00FD LATIN SMALL LETTER Y WITH ACUTE * 0079 LATIN SMALL LETTER Y * 00FF LATIN SMALL LETTER Y WITH DIAERESIS * 0079 LATIN SMALL LETTER Y * 0100 LATIN CAPITAL LETTER A WITH MACRON * 0041 LATIN CAPITAL LETTER A * 0101 LATIN SMALL LETTER A WITH MACRON * 0061 LATIN SMALL LETTER A * 0102 LATIN CAPITAL LETTER A WITH BREVE * 0041 LATIN CAPITAL LETTER A * 0103 LATIN SMALL LETTER A WITH BREVE * 0061 LATIN SMALL LETTER A * 0104 LATIN CAPITAL LETTER A WITH OGONEK * 0041 LATIN CAPITAL LETTER A * 0105 LATIN SMALL LETTER A WITH OGONEK * 0061 LATIN SMALL LETTER A * 0106 LATIN CAPITAL LETTER C WITH ACUTE * 0043 LATIN CAPITAL LETTER C * 0107 LATIN SMALL LETTER C WITH ACUTE * 0063 LATIN SMALL LETTER C * 0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX * 0043 LATIN CAPITAL LETTER C * 0109 LATIN SMALL LETTER C WITH CIRCUMFLEX * 0063 LATIN SMALL LETTER C * 010A LATIN CAPITAL LETTER C WITH DOT ABOVE * 0043 LATIN CAPITAL LETTER C * 010B LATIN SMALL LETTER C WITH DOT ABOVE * 0063 LATIN SMALL LETTER C * 010C LATIN CAPITAL LETTER C WITH CARON * 0043 LATIN CAPITAL LETTER C * 010D LATIN SMALL LETTER C WITH CARON * 0063 LATIN SMALL LETTER C * 010E LATIN CAPITAL LETTER D WITH CARON * 0044 LATIN CAPITAL LETTER D * 010F LATIN SMALL LETTER D WITH CARON * 0064 LATIN SMALL LETTER D * 0112 LATIN CAPITAL LETTER E WITH MACRON * 0045 LATIN CAPITAL LETTER E * 0113 LATIN SMALL LETTER E WITH MACRON * 0065 LATIN SMALL LETTER E * 0114 LATIN CAPITAL LETTER E WITH BREVE * 0045 LATIN CAPITAL LETTER E * 0115 LATIN SMALL LETTER E WITH BREVE * 0065 LATIN SMALL LETTER E * 0116 LATIN CAPITAL LETTER E WITH DOT ABOVE * 0045 LATIN CAPITAL LETTER E * 0117 LATIN SMALL LETTER E WITH DOT ABOVE * 0065 LATIN SMALL LETTER E * 0118 LATIN CAPITAL LETTER E WITH OGONEK * 0045 LATIN CAPITAL LETTER E * 0119 LATIN SMALL LETTER E WITH OGONEK * 0065 LATIN SMALL LETTER E * 011A LATIN CAPITAL LETTER E WITH CARON * 0045 LATIN CAPITAL LETTER E * 011B LATIN SMALL LETTER E WITH CARON * 0065 LATIN SMALL LETTER E * 011C LATIN CAPITAL LETTER G WITH CIRCUMFLEX * 0047 LATIN CAPITAL LETTER G * 011D LATIN SMALL LETTER G WITH CIRCUMFLEX * 0067 LATIN SMALL LETTER G * 011E LATIN CAPITAL LETTER G WITH BREVE * 0047 LATIN CAPITAL LETTER G * 011F LATIN SMALL LETTER G WITH BREVE * 0067 LATIN SMALL LETTER G * 0120 LATIN CAPITAL LETTER G WITH DOT ABOVE * 0047 LATIN CAPITAL LETTER G * 0121 LATIN SMALL LETTER G WITH DOT ABOVE * 0067 LATIN SMALL LETTER G * 0122 LATIN CAPITAL LETTER G WITH CEDILLA * 0047 LATIN CAPITAL LETTER G * 0123 LATIN SMALL LETTER G WITH CEDILLA * 0067 LATIN SMALL LETTER G * 0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX * 0048 LATIN CAPITAL LETTER H * 0125 LATIN SMALL LETTER H WITH CIRCUMFLEX * 0068 LATIN SMALL LETTER H * 0128 LATIN CAPITAL LETTER I WITH TILDE * 0049 LATIN CAPITAL LETTER I * 0129 LATIN SMALL LETTER I WITH TILDE * 0069 LATIN SMALL LETTER I * 012A LATIN CAPITAL LETTER I WITH MACRON * 0049 LATIN CAPITAL LETTER I * 012B LATIN SMALL LETTER I WITH MACRON * 0069 LATIN SMALL LETTER I * 012C LATIN CAPITAL LETTER I WITH BREVE * 0049 LATIN CAPITAL LETTER I * 012D LATIN SMALL LETTER I WITH BREVE * 0069 LATIN SMALL LETTER I * 012E LATIN CAPITAL LETTER I WITH OGONEK * 0049 LATIN CAPITAL LETTER I * 012F LATIN SMALL LETTER I WITH OGONEK * 0069 LATIN SMALL LETTER I * 0130 LATIN CAPITAL LETTER I WITH DOT ABOVE * 0049 LATIN CAPITAL LETTER I * 0132 LATIN CAPITAL LIGATURE IJ * 0049 LATIN CAPITAL LETTER I * 004A LATIN CAPITAL LETTER J * 0133 LATIN SMALL LIGATURE IJ * 0069 LATIN SMALL LETTER I * 006A LATIN SMALL LETTER J * 0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX * 004A LATIN CAPITAL LETTER J * 0135 LATIN SMALL LETTER J WITH CIRCUMFLEX * 006A LATIN SMALL LETTER J * 0136 LATIN CAPITAL LETTER K WITH CEDILLA * 004B LATIN CAPITAL LETTER K * 0137 LATIN SMALL LETTER K WITH CEDILLA * 006B LATIN SMALL LETTER K * 0139 LATIN CAPITAL LETTER L WITH ACUTE * 004C LATIN CAPITAL LETTER L * 013A LATIN SMALL LETTER L WITH ACUTE * 006C LATIN SMALL LETTER L * 013B LATIN CAPITAL LETTER L WITH CEDILLA * 004C LATIN CAPITAL LETTER L * 013C LATIN SMALL LETTER L WITH CEDILLA * 006C LATIN SMALL LETTER L * 013D LATIN CAPITAL LETTER L WITH CARON * 004C LATIN CAPITAL LETTER L * 013E LATIN SMALL LETTER L WITH CARON * 006C LATIN SMALL LETTER L * 013F LATIN CAPITAL LETTER L WITH MIDDLE DOT * 004C LATIN CAPITAL LETTER L * 00B7 MIDDLE DOT * 0140 LATIN SMALL LETTER L WITH MIDDLE DOT * 006C LATIN SMALL LETTER L * 00B7 MIDDLE DOT * 0143 LATIN CAPITAL LETTER N WITH ACUTE * 004E LATIN CAPITAL LETTER N * 0144 LATIN SMALL LETTER N WITH ACUTE * 006E LATIN SMALL LETTER N * 0145 LATIN CAPITAL LETTER N WITH CEDILLA * 004E LATIN CAPITAL LETTER N * 0146 LATIN SMALL LETTER N WITH CEDILLA * 006E LATIN SMALL LETTER N * 0147 LATIN CAPITAL LETTER N WITH CARON * 004E LATIN CAPITAL LETTER N * 0148 LATIN SMALL LETTER N WITH CARON * 006E LATIN SMALL LETTER N * 0149 LATIN SMALL LETTER N PRECEDED BY APOSTROPHE * 02BC MODIFIER LETTER APOSTROPHE * 006E LATIN SMALL LETTER N * 014C LATIN CAPITAL LETTER O WITH MACRON * 004F LATIN CAPITAL LETTER O * 014D LATIN SMALL LETTER O WITH MACRON * 006F LATIN SMALL LETTER O * 014E LATIN CAPITAL LETTER O WITH BREVE * 004F LATIN CAPITAL LETTER O * 014F LATIN SMALL LETTER O WITH BREVE * 006F LATIN SMALL LETTER O * 0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUTE * 004F LATIN CAPITAL LETTER O * 0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE * 006F LATIN SMALL LETTER O * 0154 LATIN CAPITAL LETTER R WITH ACUTE * 0052 LATIN CAPITAL LETTER R * 0155 LATIN SMALL LETTER R WITH ACUTE * 0072 LATIN SMALL LETTER R * 0156 LATIN CAPITAL LETTER R WITH CEDILLA * 0052 LATIN CAPITAL LETTER R * 0157 LATIN SMALL LETTER R WITH CEDILLA * 0072 LATIN SMALL LETTER R * 0158 LATIN CAPITAL LETTER R WITH CARON * 0052 LATIN CAPITAL LETTER R * 0159 LATIN SMALL LETTER R WITH CARON * 0072 LATIN SMALL LETTER R * 015A LATIN CAPITAL LETTER S WITH ACUTE * 0053 LATIN CAPITAL LETTER S * 015B LATIN SMALL LETTER S WITH ACUTE * 0073 LATIN SMALL LETTER S * 015C LATIN CAPITAL LETTER S WITH CIRCUMFLEX * 0053 LATIN CAPITAL LETTER S * 015D LATIN SMALL LETTER S WITH CIRCUMFLEX * 0073 LATIN SMALL LETTER S * 015E LATIN CAPITAL LETTER S WITH CEDILLA * 0053 LATIN CAPITAL LETTER S * 015F LATIN SMALL LETTER S WITH CEDILLA * 0073 LATIN SMALL LETTER S * 0160 LATIN CAPITAL LETTER S WITH CARON * 0053 LATIN CAPITAL LETTER S * 0161 LATIN SMALL LETTER S WITH CARON * 0073 LATIN SMALL LETTER S * 0162 LATIN CAPITAL LETTER T WITH CEDILLA * 0054 LATIN CAPITAL LETTER T * 0163 LATIN SMALL LETTER T WITH CEDILLA * 0074 LATIN SMALL LETTER T * 0164 LATIN CAPITAL LETTER T WITH CARON * 0054 LATIN CAPITAL LETTER T * 0165 LATIN SMALL LETTER T WITH CARON * 0074 LATIN SMALL LETTER T * 0168 LATIN CAPITAL LETTER U WITH TILDE * 0055 LATIN CAPITAL LETTER U * 0169 LATIN SMALL LETTER U WITH TILDE * 0075 LATIN SMALL LETTER U * 016A LATIN CAPITAL LETTER U WITH MACRON * 0055 LATIN CAPITAL LETTER U * 016B LATIN SMALL LETTER U WITH MACRON * 0075 LATIN SMALL LETTER U * 016C LATIN CAPITAL LETTER U WITH BREVE * 0055 LATIN CAPITAL LETTER U * 016D LATIN SMALL LETTER U WITH BREVE * 0075 LATIN SMALL LETTER U * 016E LATIN CAPITAL LETTER U WITH RING ABOVE * 0055 LATIN CAPITAL LETTER U * 016F LATIN SMALL LETTER U WITH RING ABOVE * 0075 LATIN SMALL LETTER U * 0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUTE * 0055 LATIN CAPITAL LETTER U * 0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE * 0075 LATIN SMALL LETTER U * 0172 LATIN CAPITAL LETTER U WITH OGONEK * 0055 LATIN CAPITAL LETTER U * 0173 LATIN SMALL LETTER U WITH OGONEK * 0075 LATIN SMALL LETTER U * 0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX * 0057 LATIN CAPITAL LETTER W * 0175 LATIN SMALL LETTER W WITH CIRCUMFLEX * 0077 LATIN SMALL LETTER W * 0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX * 0059 LATIN CAPITAL LETTER Y * 0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX * 0079 LATIN SMALL LETTER Y * 0178 LATIN CAPITAL LETTER Y WITH DIAERESIS * 0059 LATIN CAPITAL LETTER Y * 0179 LATIN CAPITAL LETTER Z WITH ACUTE * 005A LATIN CAPITAL LETTER Z * 017A LATIN SMALL LETTER Z WITH ACUTE * 007A LATIN SMALL LETTER Z * 017B LATIN CAPITAL LETTER Z WITH DOT ABOVE * 005A LATIN CAPITAL LETTER Z * 017C LATIN SMALL LETTER Z WITH DOT ABOVE * 007A LATIN SMALL LETTER Z * 017D LATIN CAPITAL LETTER Z WITH CARON * 005A LATIN CAPITAL LETTER Z * 017E LATIN SMALL LETTER Z WITH CARON * 007A LATIN SMALL LETTER Z * 017F LATIN SMALL LETTER LONG S * 0073 LATIN SMALL LETTER S * 01A0 LATIN CAPITAL LETTER O WITH HORN * 004F LATIN CAPITAL LETTER O * 01A1 LATIN SMALL LETTER O WITH HORN * 006F LATIN SMALL LETTER O * 01AF LATIN CAPITAL LETTER U WITH HORN * 0055 LATIN CAPITAL LETTER U * 01B0 LATIN SMALL LETTER U WITH HORN * 0075 LATIN SMALL LETTER U * 01C4 LATIN CAPITAL LETTER DZ WITH CARON * 0044 LATIN CAPITAL LETTER D * 005A LATIN CAPITAL LETTER Z * 01C5 LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON * 0044 LATIN CAPITAL LETTER D * 007A LATIN SMALL LETTER Z * 01C6 LATIN SMALL LETTER DZ WITH CARON * 0064 LATIN SMALL LETTER D * 007A LATIN SMALL LETTER Z * 01C7 LATIN CAPITAL LETTER LJ * 004C LATIN CAPITAL LETTER L * 004A LATIN CAPITAL LETTER J * 01C8 LATIN CAPITAL LETTER L WITH SMALL LETTER J * 004C LATIN CAPITAL LETTER L * 006A LATIN SMALL LETTER J * 01C9 LATIN SMALL LETTER LJ * 006C LATIN SMALL LETTER L * 006A LATIN SMALL LETTER J * 01CA LATIN CAPITAL LETTER NJ * 004E LATIN CAPITAL LETTER N * 004A LATIN CAPITAL LETTER J * 01CB LATIN CAPITAL LETTER N WITH SMALL LETTER J * 004E LATIN CAPITAL LETTER N * 006A LATIN SMALL LETTER J * 01CC LATIN SMALL LETTER NJ * 006E LATIN SMALL LETTER N * 006A LATIN SMALL LETTER J * 01CD LATIN CAPITAL LETTER A WITH CARON * 0041 LATIN CAPITAL LETTER A * 01CE LATIN SMALL LETTER A WITH CARON * 0061 LATIN SMALL LETTER A * 01CF LATIN CAPITAL LETTER I WITH CARON * 0049 LATIN CAPITAL LETTER I * 01D0 LATIN SMALL LETTER I WITH CARON * 0069 LATIN SMALL LETTER I * 01D1 LATIN CAPITAL LETTER O WITH CARON * 004F LATIN CAPITAL LETTER O * 01D2 LATIN SMALL LETTER O WITH CARON * 006F LATIN SMALL LETTER O * 01D3 LATIN CAPITAL LETTER U WITH CARON * 0055 LATIN CAPITAL LETTER U * 01D4 LATIN SMALL LETTER U WITH CARON * 0075 LATIN SMALL LETTER U * 01D5 LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON * 0055 LATIN CAPITAL LETTER U * 01D6 LATIN SMALL LETTER U WITH DIAERESIS AND MACRON * 0075 LATIN SMALL LETTER U * 01D7 LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE * 0055 LATIN CAPITAL LETTER U * 01D8 LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE * 0075 LATIN SMALL LETTER U * 01D9 LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON * 0055 LATIN CAPITAL LETTER U * 01DA LATIN SMALL LETTER U WITH DIAERESIS AND CARON * 0075 LATIN SMALL LETTER U * 01DB LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE * 0055 LATIN CAPITAL LETTER U * 01DC LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE * 0075 LATIN SMALL LETTER U * 01DE LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON * 0041 LATIN CAPITAL LETTER A * 01DF LATIN SMALL LETTER A WITH DIAERESIS AND MACRON * 0061 LATIN SMALL LETTER A * 01E0 LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON * 0041 LATIN CAPITAL LETTER A * 01E1 LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON * 0061 LATIN SMALL LETTER A * 01E2 LATIN CAPITAL LETTER AE WITH MACRON * 00C6 LATIN CAPITAL LETTER AE * 01E3 LATIN SMALL LETTER AE WITH MACRON * 00E6 LATIN SMALL LETTER AE * 01E6 LATIN CAPITAL LETTER G WITH CARON * 0047 LATIN CAPITAL LETTER G * 01E7 LATIN SMALL LETTER G WITH CARON * 0067 LATIN SMALL LETTER G * 01E8 LATIN CAPITAL LETTER K WITH CARON * 004B LATIN CAPITAL LETTER K * 01E9 LATIN SMALL LETTER K WITH CARON * 006B LATIN SMALL LETTER K * 01EA LATIN CAPITAL LETTER O WITH OGONEK * 004F LATIN CAPITAL LETTER O * 01EB LATIN SMALL LETTER O WITH OGONEK * 006F LATIN SMALL LETTER O * 01EC LATIN CAPITAL LETTER O WITH OGONEK AND MACRON * 004F LATIN CAPITAL LETTER O * 01ED LATIN SMALL LETTER O WITH OGONEK AND MACRON * 006F LATIN SMALL LETTER O * 01EE LATIN CAPITAL LETTER EZH WITH CARON * 01B7 LATIN CAPITAL LETTER EZH * 01EF LATIN SMALL LETTER EZH WITH CARON * 0292 LATIN SMALL LETTER EZH * 01F0 LATIN SMALL LETTER J WITH CARON * 006A LATIN SMALL LETTER J * 01F1 LATIN CAPITAL LETTER DZ * 0044 LATIN CAPITAL LETTER D * 005A LATIN CAPITAL LETTER Z * 01F2 LATIN CAPITAL LETTER D WITH SMALL LETTER Z * 0044 LATIN CAPITAL LETTER D * 007A LATIN SMALL LETTER Z * 01F3 LATIN SMALL LETTER DZ * 0064 LATIN SMALL LETTER D * 007A LATIN SMALL LETTER Z * 01F4 LATIN CAPITAL LETTER G WITH ACUTE * 0047 LATIN CAPITAL LETTER G * 01F5 LATIN SMALL LETTER G WITH ACUTE * 0067 LATIN SMALL LETTER G * 01F8 LATIN CAPITAL LETTER N WITH GRAVE * 004E LATIN CAPITAL LETTER N * 01F9 LATIN SMALL LETTER N WITH GRAVE * 006E LATIN SMALL LETTER N * 01FA LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE * 0041 LATIN CAPITAL LETTER A * 01FB LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE * 0061 LATIN SMALL LETTER A * 01FC LATIN CAPITAL LETTER AE WITH ACUTE * 00C6 LATIN CAPITAL LETTER AE * 01FD LATIN SMALL LETTER AE WITH ACUTE * 00E6 LATIN SMALL LETTER AE * 01FE LATIN CAPITAL LETTER O WITH STROKE AND ACUTE * 00D8 LATIN CAPITAL LETTER O WITH STROKE * 01FF LATIN SMALL LETTER O WITH STROKE AND ACUTE * 00F8 LATIN SMALL LETTER O WITH STROKE * 0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAVE * 0041 LATIN CAPITAL LETTER A * 0201 LATIN SMALL LETTER A WITH DOUBLE GRAVE * 0061 LATIN SMALL LETTER A * 0202 LATIN CAPITAL LETTER A WITH INVERTED BREVE * 0041 LATIN CAPITAL LETTER A * 0203 LATIN SMALL LETTER A WITH INVERTED BREVE * 0061 LATIN SMALL LETTER A * 0204 LATIN CAPITAL LETTER E WITH DOUBLE GRAVE * 0045 LATIN CAPITAL LETTER E * 0205 LATIN SMALL LETTER E WITH DOUBLE GRAVE * 0065 LATIN SMALL LETTER E * 0206 LATIN CAPITAL LETTER E WITH INVERTED BREVE * 0045 LATIN CAPITAL LETTER E * 0207 LATIN SMALL LETTER E WITH INVERTED BREVE * 0065 LATIN SMALL LETTER E * 0208 LATIN CAPITAL LETTER I WITH DOUBLE GRAVE * 0049 LATIN CAPITAL LETTER I * 0209 LATIN SMALL LETTER I WITH DOUBLE GRAVE * 0069 LATIN SMALL LETTER I * 020A LATIN CAPITAL LETTER I WITH INVERTED BREVE * 0049 LATIN CAPITAL LETTER I * 020B LATIN SMALL LETTER I WITH INVERTED BREVE * 0069 LATIN SMALL LETTER I * 020C LATIN CAPITAL LETTER O WITH DOUBLE GRAVE * 004F LATIN CAPITAL LETTER O * 020D LATIN SMALL LETTER O WITH DOUBLE GRAVE * 006F LATIN SMALL LETTER O * 020E LATIN CAPITAL LETTER O WITH INVERTED BREVE * 004F LATIN CAPITAL LETTER O * 020F LATIN SMALL LETTER O WITH INVERTED BREVE * 006F LATIN SMALL LETTER O * 0210 LATIN CAPITAL LETTER R WITH DOUBLE GRAVE * 0052 LATIN CAPITAL LETTER R * 0211 LATIN SMALL LETTER R WITH DOUBLE GRAVE * 0072 LATIN SMALL LETTER R * 0212 LATIN CAPITAL LETTER R WITH INVERTED BREVE * 0052 LATIN CAPITAL LETTER R * 0213 LATIN SMALL LETTER R WITH INVERTED BREVE * 0072 LATIN SMALL LETTER R * 0214 LATIN CAPITAL LETTER U WITH DOUBLE GRAVE * 0055 LATIN CAPITAL LETTER U * 0215 LATIN SMALL LETTER U WITH DOUBLE GRAVE * 0075 LATIN SMALL LETTER U * 0216 LATIN CAPITAL LETTER U WITH INVERTED BREVE * 0055 LATIN CAPITAL LETTER U * 0217 LATIN SMALL LETTER U WITH INVERTED BREVE * 0075 LATIN SMALL LETTER U * 0218 LATIN CAPITAL LETTER S WITH COMMA BELOW * 0053 LATIN CAPITAL LETTER S * 0219 LATIN SMALL LETTER S WITH COMMA BELOW * 0073 LATIN SMALL LETTER S * 021A LATIN CAPITAL LETTER T WITH COMMA BELOW * 0054 LATIN CAPITAL LETTER T * 021B LATIN SMALL LETTER T WITH COMMA BELOW * 0074 LATIN SMALL LETTER T * 021E LATIN CAPITAL LETTER H WITH CARON * 0048 LATIN CAPITAL LETTER H * 021F LATIN SMALL LETTER H WITH CARON * 0068 LATIN SMALL LETTER H * 0226 LATIN CAPITAL LETTER A WITH DOT ABOVE * 0041 LATIN CAPITAL LETTER A * 0227 LATIN SMALL LETTER A WITH DOT ABOVE * 0061 LATIN SMALL LETTER A * 0228 LATIN CAPITAL LETTER E WITH CEDILLA * 0045 LATIN CAPITAL LETTER E * 0229 LATIN SMALL LETTER E WITH CEDILLA * 0065 LATIN SMALL LETTER E * 022A LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON * 004F LATIN CAPITAL LETTER O * 022B LATIN SMALL LETTER O WITH DIAERESIS AND MACRON * 006F LATIN SMALL LETTER O * 022C LATIN CAPITAL LETTER O WITH TILDE AND MACRON * 004F LATIN CAPITAL LETTER O * 022D LATIN SMALL LETTER O WITH TILDE AND MACRON * 006F LATIN SMALL LETTER O * 022E LATIN CAPITAL LETTER O WITH DOT ABOVE * 004F LATIN CAPITAL LETTER O * 022F LATIN SMALL LETTER O WITH DOT ABOVE * 006F LATIN SMALL LETTER O * 0230 LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON * 004F LATIN CAPITAL LETTER O * 0231 LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON * 006F LATIN SMALL LETTER O * 0232 LATIN CAPITAL LETTER Y WITH MACRON * 0059 LATIN CAPITAL LETTER Y * 0233 LATIN SMALL LETTER Y WITH MACRON * 0079 LATIN SMALL LETTER Y * 02B0 MODIFIER LETTER SMALL H * 0068 LATIN SMALL LETTER H * 02B1 MODIFIER LETTER SMALL H WITH HOOK * 0266 LATIN SMALL LETTER H WITH HOOK * 02B2 MODIFIER LETTER SMALL J * 006A LATIN SMALL LETTER J * 02B3 MODIFIER LETTER SMALL R * 0072 LATIN SMALL LETTER R * 02B4 MODIFIER LETTER SMALL TURNED R * 0279 LATIN SMALL LETTER TURNED R * 02B5 MODIFIER LETTER SMALL TURNED R WITH HOOK * 027B LATIN SMALL LETTER TURNED R WITH HOOK * 02B6 MODIFIER LETTER SMALL CAPITAL INVERTED R * 0281 LATIN LETTER SMALL CAPITAL INVERTED R * 02B7 MODIFIER LETTER SMALL W * 0077 LATIN SMALL LETTER W * 02B8 MODIFIER LETTER SMALL Y * 0079 LATIN SMALL LETTER Y * 02D8 BREVE * 0020 SPACE * 02D9 DOT ABOVE * 0020 SPACE * 02DA RING ABOVE * 0020 SPACE * 02DB OGONEK * 0020 SPACE * 02DC SMALL TILDE * 0020 SPACE * 02DD DOUBLE ACUTE ACCENT * 0020 SPACE * 02E0 MODIFIER LETTER SMALL GAMMA * 0263 LATIN SMALL LETTER GAMMA * 02E1 MODIFIER LETTER SMALL L * 006C LATIN SMALL LETTER L * 02E2 MODIFIER LETTER SMALL S * 0073 LATIN SMALL LETTER S * 02E3 MODIFIER LETTER SMALL X * 0078 LATIN SMALL LETTER X * 02E4 MODIFIER LETTER SMALL REVERSED GLOTTAL STOP * 0295 LATIN LETTER PHARYNGEAL VOICED FRICATIVE * 0300 COMBINING GRAVE ACCENT * 0000 * 0301 COMBINING ACUTE ACCENT * 0000 * 0302 COMBINING CIRCUMFLEX ACCENT * 0000 * 0303 COMBINING TILDE * 0000 * 0304 COMBINING MACRON * 0000 * 0305 COMBINING OVERLINE * 0000 * 0306 COMBINING BREVE * 0000 * 0307 COMBINING DOT ABOVE * 0000 * 0308 COMBINING DIAERESIS * 0000 * 0309 COMBINING HOOK ABOVE * 0000 * 030A COMBINING RING ABOVE * 0000 * 030B COMBINING DOUBLE ACUTE ACCENT * 0000 * 030C COMBINING CARON * 0000 * 030D COMBINING VERTICAL LINE ABOVE * 0000 * 030E COMBINING DOUBLE VERTICAL LINE ABOVE * 0000 * 030F COMBINING DOUBLE GRAVE ACCENT * 0000 * 0310 COMBINING CANDRABINDU * 0000 * 0311 COMBINING INVERTED BREVE * 0000 * 0312 COMBINING TURNED COMMA ABOVE * 0000 * 0313 COMBINING COMMA ABOVE * 0000 * 0314 COMBINING REVERSED COMMA ABOVE * 0000 * 0315 COMBINING COMMA ABOVE RIGHT * 0000 * 0316 COMBINING GRAVE ACCENT BELOW * 0000 * 0317 COMBINING ACUTE ACCENT BELOW * 0000 * 0318 COMBINING LEFT TACK BELOW * 0000 * 0319 COMBINING RIGHT TACK BELOW * 0000 * 031A COMBINING LEFT ANGLE ABOVE * 0000 * 031B COMBINING HORN * 0000 * 031C COMBINING LEFT HALF RING BELOW * 0000 * 031D COMBINING UP TACK BELOW * 0000 * 031E COMBINING DOWN TACK BELOW * 0000 * 031F COMBINING PLUS SIGN BELOW * 0000 * 0320 COMBINING MINUS SIGN BELOW * 0000 * 0321 COMBINING PALATALIZED HOOK BELOW * 0000 * 0322 COMBINING RETROFLEX HOOK BELOW * 0000 * 0323 COMBINING DOT BELOW * 0000 * 0324 COMBINING DIAERESIS BELOW * 0000 * 0325 COMBINING RING BELOW * 0000 * 0326 COMBINING COMMA BELOW * 0000 * 0327 COMBINING CEDILLA * 0000 * 0328 COMBINING OGONEK * 0000 * 0329 COMBINING VERTICAL LINE BELOW * 0000 * 032A COMBINING BRIDGE BELOW * 0000 * 032B COMBINING INVERTED DOUBLE ARCH BELOW * 0000 * 032C COMBINING CARON BELOW * 0000 * 032D COMBINING CIRCUMFLEX ACCENT BELOW * 0000 * 032E COMBINING BREVE BELOW * 0000 * 032F COMBINING INVERTED BREVE BELOW * 0000 * 0330 COMBINING TILDE BELOW * 0000 * 0331 COMBINING MACRON BELOW * 0000 * 0332 COMBINING LOW LINE * 0000 * 0333 COMBINING DOUBLE LOW LINE * 0000 * 0334 COMBINING TILDE OVERLAY * 0000 * 0335 COMBINING SHORT STROKE OVERLAY * 0000 * 0336 COMBINING LONG STROKE OVERLAY * 0000 * 0337 COMBINING SHORT SOLIDUS OVERLAY * 0000 * 0338 COMBINING LONG SOLIDUS OVERLAY * 0000 * 0339 COMBINING RIGHT HALF RING BELOW * 0000 * 033A COMBINING INVERTED BRIDGE BELOW * 0000 * 033B COMBINING SQUARE BELOW * 0000 * 033C COMBINING SEAGULL BELOW * 0000 * 033D COMBINING X ABOVE * 0000 * 033E COMBINING VERTICAL TILDE * 0000 * 033F COMBINING DOUBLE OVERLINE * 0000 * 0340 COMBINING GRAVE TONE MARK * 0000 * 0341 COMBINING ACUTE TONE MARK * 0000 * 0342 COMBINING GREEK PERISPOMENI * 0000 * 0343 COMBINING GREEK KORONIS * 0000 * 0344 COMBINING GREEK DIALYTIKA TONOS * 0000 * 0345 COMBINING GREEK YPOGEGRAMMENI * 0000 * 0346 COMBINING BRIDGE ABOVE * 0000 * 0347 COMBINING EQUALS SIGN BELOW * 0000 * 0348 COMBINING DOUBLE VERTICAL LINE BELOW * 0000 * 0349 COMBINING LEFT ANGLE BELOW * 0000 * 034A COMBINING NOT TILDE ABOVE * 0000 * 034B COMBINING HOMOTHETIC ABOVE * 0000 * 034C COMBINING ALMOST EQUAL TO ABOVE * 0000 * 034D COMBINING LEFT RIGHT ARROW BELOW * 0000 * 034E COMBINING UPWARDS ARROW BELOW * 0000 * 034F COMBINING GRAPHEME JOINER * 0000 * 0350 COMBINING RIGHT ARROWHEAD ABOVE * 0000 * 0351 COMBINING LEFT HALF RING ABOVE * 0000 * 0352 COMBINING FERMATA * 0000 * 0353 COMBINING X BELOW * 0000 * 0354 COMBINING LEFT ARROWHEAD BELOW * 0000 * 0355 COMBINING RIGHT ARROWHEAD BELOW * 0000 * 0356 COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW * 0000 * 0357 COMBINING RIGHT HALF RING ABOVE * 0000 * 0358 COMBINING DOT ABOVE RIGHT * 0000 * 0359 COMBINING ASTERISK BELOW * 0000 * 035A COMBINING DOUBLE RING BELOW * 0000 * 035B COMBINING ZIGZAG ABOVE * 0000 * 035C COMBINING DOUBLE BREVE BELOW * 0000 * 035D COMBINING DOUBLE BREVE * 0000 * 035E COMBINING DOUBLE MACRON * 0000 * 035F COMBINING DOUBLE MACRON BELOW * 0000 * 0360 COMBINING DOUBLE TILDE * 0000 * 0361 COMBINING DOUBLE INVERTED BREVE * 0000 * 0362 COMBINING DOUBLE RIGHTWARDS ARROW BELOW * 0000 * 0363 COMBINING LATIN SMALL LETTER A * 0000 * 0364 COMBINING LATIN SMALL LETTER E * 0000 * 0365 COMBINING LATIN SMALL LETTER I * 0000 * 0366 COMBINING LATIN SMALL LETTER O * 0000 * 0367 COMBINING LATIN SMALL LETTER U * 0000 * 0368 COMBINING LATIN SMALL LETTER C * 0000 * 0369 COMBINING LATIN SMALL LETTER D * 0000 * 036A COMBINING LATIN SMALL LETTER H * 0000 * 036B COMBINING LATIN SMALL LETTER M * 0000 * 036C COMBINING LATIN SMALL LETTER R * 0000 * 036D COMBINING LATIN SMALL LETTER T * 0000 * 036E COMBINING LATIN SMALL LETTER V * 0000 * 036F COMBINING LATIN SMALL LETTER X * 0000 * 0374 GREEK NUMERAL SIGN * 02B9 MODIFIER LETTER PRIME * 037A GREEK YPOGEGRAMMENI * 0020 SPACE * 037E GREEK QUESTION MARK * 003B SEMICOLON * 0384 GREEK TONOS * 0020 SPACE * 0385 GREEK DIALYTIKA TONOS * 0020 SPACE * 0386 GREEK CAPITAL LETTER ALPHA WITH TONOS * 0391 GREEK CAPITAL LETTER ALPHA * 0387 GREEK ANO TELEIA * 00B7 MIDDLE DOT * 0388 GREEK CAPITAL LETTER EPSILON WITH TONOS * 0395 GREEK CAPITAL LETTER EPSILON * 0389 GREEK CAPITAL LETTER ETA WITH TONOS * 0397 GREEK CAPITAL LETTER ETA * 038A GREEK CAPITAL LETTER IOTA WITH TONOS * 0399 GREEK CAPITAL LETTER IOTA * 038C GREEK CAPITAL LETTER OMICRON WITH TONOS * 039F GREEK CAPITAL LETTER OMICRON * 038E GREEK CAPITAL LETTER UPSILON WITH TONOS * 03A5 GREEK CAPITAL LETTER UPSILON * 038F GREEK CAPITAL LETTER OMEGA WITH TONOS * 03A9 GREEK CAPITAL LETTER OMEGA * 0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS * 03B9 GREEK SMALL LETTER IOTA * 03AA GREEK CAPITAL LETTER IOTA WITH DIALYTIKA * 0399 GREEK CAPITAL LETTER IOTA * 03AB GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA * 03A5 GREEK CAPITAL LETTER UPSILON * 03AC GREEK SMALL LETTER ALPHA WITH TONOS * 03B1 GREEK SMALL LETTER ALPHA * 03AD GREEK SMALL LETTER EPSILON WITH TONOS * 03B5 GREEK SMALL LETTER EPSILON * 03AE GREEK SMALL LETTER ETA WITH TONOS * 03B7 GREEK SMALL LETTER ETA * 03AF GREEK SMALL LETTER IOTA WITH TONOS * 03B9 GREEK SMALL LETTER IOTA * 03B0 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS * 03C5 GREEK SMALL LETTER UPSILON * 03CA GREEK SMALL LETTER IOTA WITH DIALYTIKA * 03B9 GREEK SMALL LETTER IOTA * 03CB GREEK SMALL LETTER UPSILON WITH DIALYTIKA * 03C5 GREEK SMALL LETTER UPSILON * 03CC GREEK SMALL LETTER OMICRON WITH TONOS * 03BF GREEK SMALL LETTER OMICRON * 03CD GREEK SMALL LETTER UPSILON WITH TONOS * 03C5 GREEK SMALL LETTER UPSILON * 03CE GREEK SMALL LETTER OMEGA WITH TONOS * 03C9 GREEK SMALL LETTER OMEGA * 03D0 GREEK BETA SYMBOL * 03B2 GREEK SMALL LETTER BETA * 03D1 GREEK THETA SYMBOL * 03B8 GREEK SMALL LETTER THETA * 03D2 GREEK UPSILON WITH HOOK SYMBOL * 03A5 GREEK CAPITAL LETTER UPSILON * 03D3 GREEK UPSILON WITH ACUTE AND HOOK SYMBOL * 03A5 GREEK CAPITAL LETTER UPSILON * 03D4 GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL * 03A5 GREEK CAPITAL LETTER UPSILON * 03D5 GREEK PHI SYMBOL * 03C6 GREEK SMALL LETTER PHI * 03D6 GREEK PI SYMBOL * 03C0 GREEK SMALL LETTER PI * 03F0 GREEK KAPPA SYMBOL * 03BA GREEK SMALL LETTER KAPPA * 03F1 GREEK RHO SYMBOL * 03C1 GREEK SMALL LETTER RHO * 03F2 GREEK LUNATE SIGMA SYMBOL * 03C2 GREEK SMALL LETTER FINAL SIGMA * 03F4 GREEK CAPITAL THETA SYMBOL * 0398 GREEK CAPITAL LETTER THETA * 03F5 GREEK LUNATE EPSILON SYMBOL * 03B5 GREEK SMALL LETTER EPSILON * 03F9 GREEK CAPITAL LUNATE SIGMA SYMBOL * 03A3 GREEK CAPITAL LETTER SIGMA * 0400 CYRILLIC CAPITAL LETTER IE WITH GRAVE * 0415 CYRILLIC CAPITAL LETTER IE * 0401 CYRILLIC CAPITAL LETTER IO * 0415 CYRILLIC CAPITAL LETTER IE * 0403 CYRILLIC CAPITAL LETTER GJE * 0413 CYRILLIC CAPITAL LETTER GHE * 0407 CYRILLIC CAPITAL LETTER YI * 0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I * 040C CYRILLIC CAPITAL LETTER KJE * 041A CYRILLIC CAPITAL LETTER KA * 040D CYRILLIC CAPITAL LETTER I WITH GRAVE * 0418 CYRILLIC CAPITAL LETTER I * 040E CYRILLIC CAPITAL LETTER SHORT U * 0423 CYRILLIC CAPITAL LETTER U * 0419 CYRILLIC CAPITAL LETTER SHORT I * 0418 CYRILLIC CAPITAL LETTER I * 0439 CYRILLIC SMALL LETTER SHORT I * 0438 CYRILLIC SMALL LETTER I * 0450 CYRILLIC SMALL LETTER IE WITH GRAVE * 0435 CYRILLIC SMALL LETTER IE * 0451 CYRILLIC SMALL LETTER IO * 0435 CYRILLIC SMALL LETTER IE * 0453 CYRILLIC SMALL LETTER GJE * 0433 CYRILLIC SMALL LETTER GHE * 0457 CYRILLIC SMALL LETTER YI * 0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I * 045C CYRILLIC SMALL LETTER KJE * 043A CYRILLIC SMALL LETTER KA * 045D CYRILLIC SMALL LETTER I WITH GRAVE * 0438 CYRILLIC SMALL LETTER I * 045E CYRILLIC SMALL LETTER SHORT U * 0443 CYRILLIC SMALL LETTER U * 0476 CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT * 0474 CYRILLIC CAPITAL LETTER IZHITSA * 0477 CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT * 0475 CYRILLIC SMALL LETTER IZHITSA * 0483 COMBINING CYRILLIC TITLO * 0000 * 0484 COMBINING CYRILLIC PALATALIZATION * 0000 * 0485 COMBINING CYRILLIC DASIA PNEUMATA * 0000 * 0486 COMBINING CYRILLIC PSILI PNEUMATA * 0000 * 0487 COMBINING CYRILLIC POKRYTIE * 0000 * 0488 COMBINING CYRILLIC HUNDRED THOUSANDS SIGN * 0000 * 0489 COMBINING CYRILLIC MILLIONS SIGN * 0000 * 04C1 CYRILLIC CAPITAL LETTER ZHE WITH BREVE * 0416 CYRILLIC CAPITAL LETTER ZHE * 04C2 CYRILLIC SMALL LETTER ZHE WITH BREVE * 0436 CYRILLIC SMALL LETTER ZHE * 04D0 CYRILLIC CAPITAL LETTER A WITH BREVE * 0410 CYRILLIC CAPITAL LETTER A * 04D1 CYRILLIC SMALL LETTER A WITH BREVE * 0430 CYRILLIC SMALL LETTER A * 04D2 CYRILLIC CAPITAL LETTER A WITH DIAERESIS * 0410 CYRILLIC CAPITAL LETTER A * 04D3 CYRILLIC SMALL LETTER A WITH DIAERESIS * 0430 CYRILLIC SMALL LETTER A * 04D6 CYRILLIC CAPITAL LETTER IE WITH BREVE * 0415 CYRILLIC CAPITAL LETTER IE * 04D7 CYRILLIC SMALL LETTER IE WITH BREVE * 0435 CYRILLIC SMALL LETTER IE * 04DA CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS * 04D8 CYRILLIC CAPITAL LETTER SCHWA * 04DB CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS * 04D9 CYRILLIC SMALL LETTER SCHWA * 04DC CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS * 0416 CYRILLIC CAPITAL LETTER ZHE * 04DD CYRILLIC SMALL LETTER ZHE WITH DIAERESIS * 0436 CYRILLIC SMALL LETTER ZHE * 04DE CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS * 0417 CYRILLIC CAPITAL LETTER ZE * 04DF CYRILLIC SMALL LETTER ZE WITH DIAERESIS * 0437 CYRILLIC SMALL LETTER ZE * 04E2 CYRILLIC CAPITAL LETTER I WITH MACRON * 0418 CYRILLIC CAPITAL LETTER I * 04E3 CYRILLIC SMALL LETTER I WITH MACRON * 0438 CYRILLIC SMALL LETTER I * 04E4 CYRILLIC CAPITAL LETTER I WITH DIAERESIS * 0418 CYRILLIC CAPITAL LETTER I * 04E5 CYRILLIC SMALL LETTER I WITH DIAERESIS * 0438 CYRILLIC SMALL LETTER I * 04E6 CYRILLIC CAPITAL LETTER O WITH DIAERESIS * 041E CYRILLIC CAPITAL LETTER O * 04E7 CYRILLIC SMALL LETTER O WITH DIAERESIS * 043E CYRILLIC SMALL LETTER O * 04EA CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS * 04E8 CYRILLIC CAPITAL LETTER BARRED O * 04EB CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS * 04E9 CYRILLIC SMALL LETTER BARRED O * 04EC CYRILLIC CAPITAL LETTER E WITH DIAERESIS * 042D CYRILLIC CAPITAL LETTER E * 04ED CYRILLIC SMALL LETTER E WITH DIAERESIS * 044D CYRILLIC SMALL LETTER E * 04EE CYRILLIC CAPITAL LETTER U WITH MACRON * 0423 CYRILLIC CAPITAL LETTER U * 04EF CYRILLIC SMALL LETTER U WITH MACRON * 0443 CYRILLIC SMALL LETTER U * 04F0 CYRILLIC CAPITAL LETTER U WITH DIAERESIS * 0423 CYRILLIC CAPITAL LETTER U * 04F1 CYRILLIC SMALL LETTER U WITH DIAERESIS * 0443 CYRILLIC SMALL LETTER U * 04F2 CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE * 0423 CYRILLIC CAPITAL LETTER U * 04F3 CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE * 0443 CYRILLIC SMALL LETTER U * 04F4 CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS * 0427 CYRILLIC CAPITAL LETTER CHE * 04F5 CYRILLIC SMALL LETTER CHE WITH DIAERESIS * 0447 CYRILLIC SMALL LETTER CHE * 04F8 CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS * 042B CYRILLIC CAPITAL LETTER YERU * 04F9 CYRILLIC SMALL LETTER YERU WITH DIAERESIS * 044B CYRILLIC SMALL LETTER YERU * 0587 ARMENIAN SMALL LIGATURE ECH YIWN * 0565 ARMENIAN SMALL LETTER ECH * 0582 ARMENIAN SMALL LETTER YIWN * 0591 HEBREW ACCENT ETNAHTA * 0000 * 0592 HEBREW ACCENT SEGOL * 0000 * 0593 HEBREW ACCENT SHALSHELET * 0000 * 0594 HEBREW ACCENT ZAQEF QATAN * 0000 * 0595 HEBREW ACCENT ZAQEF GADOL * 0000 * 0596 HEBREW ACCENT TIPEHA * 0000 * 0597 HEBREW ACCENT REVIA * 0000 * 0598 HEBREW ACCENT ZARQA * 0000 * 0599 HEBREW ACCENT PASHTA * 0000 * 059A HEBREW ACCENT YETIV * 0000 * 059B HEBREW ACCENT TEVIR * 0000 * 059C HEBREW ACCENT GERESH * 0000 * 059D HEBREW ACCENT GERESH MUQDAM * 0000 * 059E HEBREW ACCENT GERSHAYIM * 0000 * 059F HEBREW ACCENT QARNEY PARA * 0000 * 05A0 HEBREW ACCENT TELISHA GEDOLA * 0000 * 05A1 HEBREW ACCENT PAZER * 0000 * 05A2 HEBREW ACCENT ATNAH HAFUKH * 0000 * 05A3 HEBREW ACCENT MUNAH * 0000 * 05A4 HEBREW ACCENT MAHAPAKH * 0000 * 05A5 HEBREW ACCENT MERKHA * 0000 * 05A6 HEBREW ACCENT MERKHA KEFULA * 0000 * 05A7 HEBREW ACCENT DARGA * 0000 * 05A8 HEBREW ACCENT QADMA * 0000 * 05A9 HEBREW ACCENT TELISHA QETANA * 0000 * 05AA HEBREW ACCENT YERAH BEN YOMO * 0000 * 05AB HEBREW ACCENT OLE * 0000 * 05AC HEBREW ACCENT ILUY * 0000 * 05AD HEBREW ACCENT DEHI * 0000 * 05AE HEBREW ACCENT ZINOR * 0000 * 05AF HEBREW MARK MASORA CIRCLE * 0000 * 05B0 HEBREW POINT SHEVA * 0000 * 05B1 HEBREW POINT HATAF SEGOL * 0000 * 05B2 HEBREW POINT HATAF PATAH * 0000 * 05B3 HEBREW POINT HATAF QAMATS * 0000 * 05B4 HEBREW POINT HIRIQ * 0000 * 05B5 HEBREW POINT TSERE * 0000 * 05B6 HEBREW POINT SEGOL * 0000 * 05B7 HEBREW POINT PATAH * 0000 * 05B8 HEBREW POINT QAMATS * 0000 * 05B9 HEBREW POINT HOLAM * 0000 * 05BA HEBREW POINT HOLAM HASER FOR VAV * 0000 * 05BB HEBREW POINT QUBUTS * 0000 * 05BC HEBREW POINT DAGESH OR MAPIQ * 0000 * 05BD HEBREW POINT METEG * 0000 * 05BF HEBREW POINT RAFE * 0000 * 05C1 HEBREW POINT SHIN DOT * 0000 * 05C2 HEBREW POINT SIN DOT * 0000 * 05C4 HEBREW MARK UPPER DOT * 0000 * 05C5 HEBREW MARK LOWER DOT * 0000 * 05C7 HEBREW POINT QAMATS QATAN * 0000 * 0610 ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM * 0000 * 0611 ARABIC SIGN ALAYHE ASSALLAM * 0000 * 0612 ARABIC SIGN RAHMATULLAH ALAYHE * 0000 * 0613 ARABIC SIGN RADI ALLAHOU ANHU * 0000 * 0614 ARABIC SIGN TAKHALLUS * 0000 * 0615 ARABIC SMALL HIGH TAH * 0000 * 0616 ARABIC SMALL HIGH LIGATURE ALEF WITH LAM WITH YEH * 0000 * 0617 ARABIC SMALL HIGH ZAIN * 0000 * 0618 ARABIC SMALL FATHA * 0000 * 0619 ARABIC SMALL DAMMA * 0000 * 061A ARABIC SMALL KASRA * 0000 * 0622 ARABIC LETTER ALEF WITH MADDA ABOVE * 0627 ARABIC LETTER ALEF * 0623 ARABIC LETTER ALEF WITH HAMZA ABOVE * 0627 ARABIC LETTER ALEF * 0624 ARABIC LETTER WAW WITH HAMZA ABOVE * 0648 ARABIC LETTER WAW * 0625 ARABIC LETTER ALEF WITH HAMZA BELOW * 0627 ARABIC LETTER ALEF * 0626 ARABIC LETTER YEH WITH HAMZA ABOVE * 064A ARABIC LETTER YEH * 064B ARABIC FATHATAN * 0000 * 064C ARABIC DAMMATAN * 0000 * 064D ARABIC KASRATAN * 0000 * 064E ARABIC FATHA * 0000 * 064F ARABIC DAMMA * 0000 * 0650 ARABIC KASRA * 0000 * 0651 ARABIC SHADDA * 0000 * 0652 ARABIC SUKUN * 0000 * 0653 ARABIC MADDAH ABOVE * 0000 * 0654 ARABIC HAMZA ABOVE * 0000 * 0655 ARABIC HAMZA BELOW * 0000 * 0656 ARABIC SUBSCRIPT ALEF * 0000 * 0657 ARABIC INVERTED DAMMA * 0000 * 0658 ARABIC MARK NOON GHUNNA * 0000 * 0659 ARABIC ZWARAKAY * 0000 * 065A ARABIC VOWEL SIGN SMALL V ABOVE * 0000 * 065B ARABIC VOWEL SIGN INVERTED SMALL V ABOVE * 0000 * 065C ARABIC VOWEL SIGN DOT BELOW * 0000 * 065D ARABIC REVERSED DAMMA * 0000 * 065E ARABIC FATHA WITH TWO DOTS * 0000 * 065F ARABIC WAVY HAMZA BELOW * 0000 * 0670 ARABIC LETTER SUPERSCRIPT ALEF * 0000 * 0675 ARABIC LETTER HIGH HAMZA ALEF * 0627 ARABIC LETTER ALEF * 0674 ARABIC LETTER HIGH HAMZA * 0676 ARABIC LETTER HIGH HAMZA WAW * 0648 ARABIC LETTER WAW * 0674 ARABIC LETTER HIGH HAMZA * 0677 ARABIC LETTER U WITH HAMZA ABOVE * 06C7 ARABIC LETTER U * 0674 ARABIC LETTER HIGH HAMZA * 0678 ARABIC LETTER HIGH HAMZA YEH * 064A ARABIC LETTER YEH * 0674 ARABIC LETTER HIGH HAMZA * 06C0 ARABIC LETTER HEH WITH YEH ABOVE * 06D5 ARABIC LETTER AE * 06C2 ARABIC LETTER HEH GOAL WITH HAMZA ABOVE * 06C1 ARABIC LETTER HEH GOAL * 06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE * 06D2 ARABIC LETTER YEH BARREE * 06D6 ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA * 0000 * 06D7 ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA * 0000 * 06D8 ARABIC SMALL HIGH MEEM INITIAL FORM * 0000 * 06D9 ARABIC SMALL HIGH LAM ALEF * 0000 * 06DA ARABIC SMALL HIGH JEEM * 0000 * 06DB ARABIC SMALL HIGH THREE DOTS * 0000 * 06DC ARABIC SMALL HIGH SEEN * 0000 * 06DF ARABIC SMALL HIGH ROUNDED ZERO * 0000 * 06E0 ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO * 0000 * 06E1 ARABIC SMALL HIGH DOTLESS HEAD OF KHAH * 0000 * 06E2 ARABIC SMALL HIGH MEEM ISOLATED FORM * 0000 * 06E3 ARABIC SMALL LOW SEEN * 0000 * 06E4 ARABIC SMALL HIGH MADDA * 0000 * 06E7 ARABIC SMALL HIGH YEH * 0000 * 06E8 ARABIC SMALL HIGH NOON * 0000 * 06EA ARABIC EMPTY CENTRE LOW STOP * 0000 * 06EB ARABIC EMPTY CENTRE HIGH STOP * 0000 * 06EC ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE * 0000 * 06ED ARABIC SMALL LOW MEEM * 0000 * 0711 SYRIAC LETTER SUPERSCRIPT ALAPH * 0000 * 0730 SYRIAC PTHAHA ABOVE * 0000 * 0731 SYRIAC PTHAHA BELOW * 0000 * 0732 SYRIAC PTHAHA DOTTED * 0000 * 0733 SYRIAC ZQAPHA ABOVE * 0000 * 0734 SYRIAC ZQAPHA BELOW * 0000 * 0735 SYRIAC ZQAPHA DOTTED * 0000 * 0736 SYRIAC RBASA ABOVE * 0000 * 0737 SYRIAC RBASA BELOW * 0000 * 0738 SYRIAC DOTTED ZLAMA HORIZONTAL * 0000 * 0739 SYRIAC DOTTED ZLAMA ANGULAR * 0000 * 073A SYRIAC HBASA ABOVE * 0000 * 073B SYRIAC HBASA BELOW * 0000 * 073C SYRIAC HBASA-ESASA DOTTED * 0000 * 073D SYRIAC ESASA ABOVE * 0000 * 073E SYRIAC ESASA BELOW * 0000 * 073F SYRIAC RWAHA * 0000 * 0740 SYRIAC FEMININE DOT * 0000 * 0741 SYRIAC QUSHSHAYA * 0000 * 0742 SYRIAC RUKKAKHA * 0000 * 0743 SYRIAC TWO VERTICAL DOTS ABOVE * 0000 * 0744 SYRIAC TWO VERTICAL DOTS BELOW * 0000 * 0745 SYRIAC THREE DOTS ABOVE * 0000 * 0746 SYRIAC THREE DOTS BELOW * 0000 * 0747 SYRIAC OBLIQUE LINE ABOVE * 0000 * 0748 SYRIAC OBLIQUE LINE BELOW * 0000 * 0749 SYRIAC MUSIC * 0000 * 074A SYRIAC BARREKH * 0000 * 07A6 THAANA ABAFILI * 0000 * 07A7 THAANA AABAAFILI * 0000 * 07A8 THAANA IBIFILI * 0000 * 07A9 THAANA EEBEEFILI * 0000 * 07AA THAANA UBUFILI * 0000 * 07AB THAANA OOBOOFILI * 0000 * 07AC THAANA EBEFILI * 0000 * 07AD THAANA EYBEYFILI * 0000 * 07AE THAANA OBOFILI * 0000 * 07AF THAANA OABOAFILI * 0000 * 07B0 THAANA SUKUN * 0000 * 07EB NKO COMBINING SHORT HIGH TONE * 0000 * 07EC NKO COMBINING SHORT LOW TONE * 0000 * 07ED NKO COMBINING SHORT RISING TONE * 0000 * 07EE NKO COMBINING LONG DESCENDING TONE * 0000 * 07EF NKO COMBINING LONG HIGH TONE * 0000 * 07F0 NKO COMBINING LONG LOW TONE * 0000 * 07F1 NKO COMBINING LONG RISING TONE * 0000 * 07F2 NKO COMBINING NASALIZATION MARK * 0000 * 07F3 NKO COMBINING DOUBLE DOT ABOVE * 0000 * 07FD NKO DANTAYALAN * 0000 * 0816 SAMARITAN MARK IN * 0000 * 0817 SAMARITAN MARK IN-ALAF * 0000 * 0818 SAMARITAN MARK OCCLUSION * 0000 * 0819 SAMARITAN MARK DAGESH * 0000 * 081B SAMARITAN MARK EPENTHETIC YUT * 0000 * 081C SAMARITAN VOWEL SIGN LONG E * 0000 * 081D SAMARITAN VOWEL SIGN E * 0000 * 081E SAMARITAN VOWEL SIGN OVERLONG AA * 0000 * 081F SAMARITAN VOWEL SIGN LONG AA * 0000 * 0820 SAMARITAN VOWEL SIGN AA * 0000 * 0821 SAMARITAN VOWEL SIGN OVERLONG A * 0000 * 0822 SAMARITAN VOWEL SIGN LONG A * 0000 * 0823 SAMARITAN VOWEL SIGN A * 0000 * 0825 SAMARITAN VOWEL SIGN SHORT A * 0000 * 0826 SAMARITAN VOWEL SIGN LONG U * 0000 * 0827 SAMARITAN VOWEL SIGN U * 0000 * 0829 SAMARITAN VOWEL SIGN LONG I * 0000 * 082A SAMARITAN VOWEL SIGN I * 0000 * 082B SAMARITAN VOWEL SIGN O * 0000 * 082C SAMARITAN VOWEL SIGN SUKUN * 0000 * 082D SAMARITAN MARK NEQUDAA * 0000 * 0859 MANDAIC AFFRICATION MARK * 0000 * 085A MANDAIC VOCALIZATION MARK * 0000 * 085B MANDAIC GEMINATION MARK * 0000 * 0898 ARABIC SMALL HIGH WORD AL-JUZ * 0000 * 0899 ARABIC SMALL LOW WORD ISHMAAM * 0000 * 089A ARABIC SMALL LOW WORD IMAALA * 0000 * 089B ARABIC SMALL LOW WORD TASHEEL * 0000 * 089C ARABIC MADDA WAAJIB * 0000 * 089D ARABIC SUPERSCRIPT ALEF MOKHASSAS * 0000 * 089E ARABIC DOUBLED MADDA * 0000 * 089F ARABIC HALF MADDA OVER MADDA * 0000 * 08CA ARABIC SMALL HIGH FARSI YEH * 0000 * 08CB ARABIC SMALL HIGH YEH BARREE WITH TWO DOTS BELOW * 0000 * 08CC ARABIC SMALL HIGH WORD SAH * 0000 * 08CD ARABIC SMALL HIGH ZAH * 0000 * 08CE ARABIC LARGE ROUND DOT ABOVE * 0000 * 08CF ARABIC LARGE ROUND DOT BELOW * 0000 * 08D0 ARABIC SUKUN BELOW * 0000 * 08D1 ARABIC LARGE CIRCLE BELOW * 0000 * 08D2 ARABIC LARGE ROUND DOT INSIDE CIRCLE BELOW * 0000 * 08D3 ARABIC SMALL LOW WAW * 0000 * 08D4 ARABIC SMALL HIGH WORD AR-RUB * 0000 * 08D5 ARABIC SMALL HIGH SAD * 0000 * 08D6 ARABIC SMALL HIGH AIN * 0000 * 08D7 ARABIC SMALL HIGH QAF * 0000 * 08D8 ARABIC SMALL HIGH NOON WITH KASRA * 0000 * 08D9 ARABIC SMALL LOW NOON WITH KASRA * 0000 * 08DA ARABIC SMALL HIGH WORD ATH-THALATHA * 0000 * 08DB ARABIC SMALL HIGH WORD AS-SAJDA * 0000 * 08DC ARABIC SMALL HIGH WORD AN-NISF * 0000 * 08DD ARABIC SMALL HIGH WORD SAKTA * 0000 * 08DE ARABIC SMALL HIGH WORD QIF * 0000 * 08DF ARABIC SMALL HIGH WORD WAQFA * 0000 * 08E0 ARABIC SMALL HIGH FOOTNOTE MARKER * 0000 * 08E1 ARABIC SMALL HIGH SIGN SAFHA * 0000 * 08E3 ARABIC TURNED DAMMA BELOW * 0000 * 08E4 ARABIC CURLY FATHA * 0000 * 08E5 ARABIC CURLY DAMMA * 0000 * 08E6 ARABIC CURLY KASRA * 0000 * 08E7 ARABIC CURLY FATHATAN * 0000 * 08E8 ARABIC CURLY DAMMATAN * 0000 * 08E9 ARABIC CURLY KASRATAN * 0000 * 08EA ARABIC TONE ONE DOT ABOVE * 0000 * 08EB ARABIC TONE TWO DOTS ABOVE * 0000 * 08EC ARABIC TONE LOOP ABOVE * 0000 * 08ED ARABIC TONE ONE DOT BELOW * 0000 * 08EE ARABIC TONE TWO DOTS BELOW * 0000 * 08EF ARABIC TONE LOOP BELOW * 0000 * 08F0 ARABIC OPEN FATHATAN * 0000 * 08F1 ARABIC OPEN DAMMATAN * 0000 * 08F2 ARABIC OPEN KASRATAN * 0000 * 08F3 ARABIC SMALL HIGH WAW * 0000 * 08F4 ARABIC FATHA WITH RING * 0000 * 08F5 ARABIC FATHA WITH DOT ABOVE * 0000 * 08F6 ARABIC KASRA WITH DOT BELOW * 0000 * 08F7 ARABIC LEFT ARROWHEAD ABOVE * 0000 * 08F8 ARABIC RIGHT ARROWHEAD ABOVE * 0000 * 08F9 ARABIC LEFT ARROWHEAD BELOW * 0000 * 08FA ARABIC RIGHT ARROWHEAD BELOW * 0000 * 08FB ARABIC DOUBLE RIGHT ARROWHEAD ABOVE * 0000 * 08FC ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WITH DOT * 0000 * 08FD ARABIC RIGHT ARROWHEAD ABOVE WITH DOT * 0000 * 08FE ARABIC DAMMA WITH DOT * 0000 * 08FF ARABIC MARK SIDEWAYS NOON GHUNNA * 0000 * 0A01 GURMUKHI SIGN ADAK BINDI * 0000 * 0A02 GURMUKHI SIGN BINDI * 0000 * 0A03 GURMUKHI SIGN VISARGA * 0000 * 0A33 GURMUKHI LETTER LLA * 0A32 GURMUKHI LETTER LA * 0A36 GURMUKHI LETTER SHA * 0A38 GURMUKHI LETTER SA * 0A3C GURMUKHI SIGN NUKTA * 0000 * 0A3E GURMUKHI VOWEL SIGN AA * 0000 * 0A3F GURMUKHI VOWEL SIGN I * 0000 * 0A40 GURMUKHI VOWEL SIGN II * 0000 * 0A41 GURMUKHI VOWEL SIGN U * 0000 * 0A42 GURMUKHI VOWEL SIGN UU * 0000 * 0A47 GURMUKHI VOWEL SIGN EE * 0000 * 0A48 GURMUKHI VOWEL SIGN AI * 0000 * 0A4B GURMUKHI VOWEL SIGN OO * 0000 * 0A4C GURMUKHI VOWEL SIGN AU * 0000 * 0A4D GURMUKHI SIGN VIRAMA * 0000 * 0A51 GURMUKHI SIGN UDAAT * 0000 * 0A59 GURMUKHI LETTER KHHA * 0A16 GURMUKHI LETTER KHA * 0A5A GURMUKHI LETTER GHHA * 0A17 GURMUKHI LETTER GA * 0A5B GURMUKHI LETTER ZA * 0A1C GURMUKHI LETTER JA * 0A5E GURMUKHI LETTER FA * 0A2B GURMUKHI LETTER PHA * 0A70 GURMUKHI TIPPI * 0000 * 0A71 GURMUKHI ADDAK * 0000 * 0A75 GURMUKHI SIGN YAKASH * 0000 * 0A81 GUJARATI SIGN CANDRABINDU * 0000 * 0A82 GUJARATI SIGN ANUSVARA * 0000 * 0A83 GUJARATI SIGN VISARGA * 0000 * 0ABC GUJARATI SIGN NUKTA * 0000 * 0ABE GUJARATI VOWEL SIGN AA * 0000 * 0ABF GUJARATI VOWEL SIGN I * 0000 * 0AC0 GUJARATI VOWEL SIGN II * 0000 * 0AC1 GUJARATI VOWEL SIGN U * 0000 * 0AC2 GUJARATI VOWEL SIGN UU * 0000 * 0AC3 GUJARATI VOWEL SIGN VOCALIC R * 0000 * 0AC4 GUJARATI VOWEL SIGN VOCALIC RR * 0000 * 0AC5 GUJARATI VOWEL SIGN CANDRA E * 0000 * 0AC7 GUJARATI VOWEL SIGN E * 0000 * 0AC8 GUJARATI VOWEL SIGN AI * 0000 * 0AC9 GUJARATI VOWEL SIGN CANDRA O * 0000 * 0ACB GUJARATI VOWEL SIGN O * 0000 * 0ACC GUJARATI VOWEL SIGN AU * 0000 * 0ACD GUJARATI SIGN VIRAMA * 0000 * 0AE2 GUJARATI VOWEL SIGN VOCALIC L * 0000 * 0AE3 GUJARATI VOWEL SIGN VOCALIC LL * 0000 * 0AFA GUJARATI SIGN SUKUN * 0000 * 0AFB GUJARATI SIGN SHADDA * 0000 * 0AFC GUJARATI SIGN MADDAH * 0000 * 0AFD GUJARATI SIGN THREE-DOT NUKTA ABOVE * 0000 * 0AFE GUJARATI SIGN CIRCLE NUKTA ABOVE * 0000 * 0AFF GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE * 0000 * 0B01 ORIYA SIGN CANDRABINDU * 0000 * 0B02 ORIYA SIGN ANUSVARA * 0000 * 0B03 ORIYA SIGN VISARGA * 0000 * 0B3C ORIYA SIGN NUKTA * 0000 * 0B3E ORIYA VOWEL SIGN AA * 0000 * 0B3F ORIYA VOWEL SIGN I * 0000 * 0B40 ORIYA VOWEL SIGN II * 0000 * 0B41 ORIYA VOWEL SIGN U * 0000 * 0B42 ORIYA VOWEL SIGN UU * 0000 * 0B43 ORIYA VOWEL SIGN VOCALIC R * 0000 * 0B44 ORIYA VOWEL SIGN VOCALIC RR * 0000 * 0B47 ORIYA VOWEL SIGN E * 0000 * 0B48 ORIYA VOWEL SIGN AI * 0000 * 0B4B ORIYA VOWEL SIGN O * 0000 * 0B4C ORIYA VOWEL SIGN AU * 0000 * 0B4D ORIYA SIGN VIRAMA * 0000 * 0B55 ORIYA SIGN OVERLINE * 0000 * 0B56 ORIYA AI LENGTH MARK * 0000 * 0B57 ORIYA AU LENGTH MARK * 0000 * 0B5C ORIYA LETTER RRA * 0B21 ORIYA LETTER DDA * 0B5D ORIYA LETTER RHA * 0B22 ORIYA LETTER DDHA * 0B62 ORIYA VOWEL SIGN VOCALIC L * 0000 * 0B63 ORIYA VOWEL SIGN VOCALIC LL * 0000 * 0C00 TELUGU SIGN COMBINING CANDRABINDU ABOVE * 0000 * 0C01 TELUGU SIGN CANDRABINDU * 0000 * 0C02 TELUGU SIGN ANUSVARA * 0000 * 0C03 TELUGU SIGN VISARGA * 0000 * 0C04 TELUGU SIGN COMBINING ANUSVARA ABOVE * 0000 * 0C3C TELUGU SIGN NUKTA * 0000 * 0C3E TELUGU VOWEL SIGN AA * 0000 * 0C3F TELUGU VOWEL SIGN I * 0000 * 0C40 TELUGU VOWEL SIGN II * 0000 * 0C41 TELUGU VOWEL SIGN U * 0000 * 0C42 TELUGU VOWEL SIGN UU * 0000 * 0C43 TELUGU VOWEL SIGN VOCALIC R * 0000 * 0C44 TELUGU VOWEL SIGN VOCALIC RR * 0000 * 0C46 TELUGU VOWEL SIGN E * 0000 * 0C47 TELUGU VOWEL SIGN EE * 0000 * 0C48 TELUGU VOWEL SIGN AI * 0000 * 0C4A TELUGU VOWEL SIGN O * 0000 * 0C4B TELUGU VOWEL SIGN OO * 0000 * 0C4C TELUGU VOWEL SIGN AU * 0000 * 0C4D TELUGU SIGN VIRAMA * 0000 * 0C55 TELUGU LENGTH MARK * 0000 * 0C56 TELUGU AI LENGTH MARK * 0000 * 0C62 TELUGU VOWEL SIGN VOCALIC L * 0000 * 0C63 TELUGU VOWEL SIGN VOCALIC LL * 0000 * 0C81 KANNADA SIGN CANDRABINDU * 0000 * 0C82 KANNADA SIGN ANUSVARA * 0000 * 0C83 KANNADA SIGN VISARGA * 0000 * 0CBC KANNADA SIGN NUKTA * 0000 * 0CBE KANNADA VOWEL SIGN AA * 0000 * 0CBF KANNADA VOWEL SIGN I * 0000 * 0CC0 KANNADA VOWEL SIGN II * 0000 * 0CC1 KANNADA VOWEL SIGN U * 0000 * 0CC2 KANNADA VOWEL SIGN UU * 0000 * 0CC3 KANNADA VOWEL SIGN VOCALIC R * 0000 * 0CC4 KANNADA VOWEL SIGN VOCALIC RR * 0000 * 0CC6 KANNADA VOWEL SIGN E * 0000 * 0CC7 KANNADA VOWEL SIGN EE * 0000 * 0CC8 KANNADA VOWEL SIGN AI * 0000 * 0CCA KANNADA VOWEL SIGN O * 0000 * 0CCB KANNADA VOWEL SIGN OO * 0000 * 0CCC KANNADA VOWEL SIGN AU * 0000 * 0CCD KANNADA SIGN VIRAMA * 0000 * 0CD5 KANNADA LENGTH MARK * 0000 * 0CD6 KANNADA AI LENGTH MARK * 0000 * 0CE2 KANNADA VOWEL SIGN VOCALIC L * 0000 * 0CE3 KANNADA VOWEL SIGN VOCALIC LL * 0000 * 0D00 MALAYALAM SIGN COMBINING ANUSVARA ABOVE * 0000 * 0D01 MALAYALAM SIGN CANDRABINDU * 0000 * 0D02 MALAYALAM SIGN ANUSVARA * 0000 * 0D03 MALAYALAM SIGN VISARGA * 0000 * 0D3B MALAYALAM SIGN VERTICAL BAR VIRAMA * 0000 * 0D3C MALAYALAM SIGN CIRCULAR VIRAMA * 0000 * 0D3E MALAYALAM VOWEL SIGN AA * 0000 * 0D3F MALAYALAM VOWEL SIGN I * 0000 * 0D40 MALAYALAM VOWEL SIGN II * 0000 * 0D41 MALAYALAM VOWEL SIGN U * 0000 * 0D42 MALAYALAM VOWEL SIGN UU * 0000 * 0D43 MALAYALAM VOWEL SIGN VOCALIC R * 0000 * 0D44 MALAYALAM VOWEL SIGN VOCALIC RR * 0000 * 0D46 MALAYALAM VOWEL SIGN E * 0000 * 0D47 MALAYALAM VOWEL SIGN EE * 0000 * 0D48 MALAYALAM VOWEL SIGN AI * 0000 * 0D4A MALAYALAM VOWEL SIGN O * 0000 * 0D4B MALAYALAM VOWEL SIGN OO * 0000 * 0D4C MALAYALAM VOWEL SIGN AU * 0000 * 0D4D MALAYALAM SIGN VIRAMA * 0000 * 0D57 MALAYALAM AU LENGTH MARK * 0000 * 0D62 MALAYALAM VOWEL SIGN VOCALIC L * 0000 * 0D63 MALAYALAM VOWEL SIGN VOCALIC LL * 0000 * 0D81 SINHALA SIGN CANDRABINDU * 0000 * 0D82 SINHALA SIGN ANUSVARAYA * 0000 * 0D83 SINHALA SIGN VISARGAYA * 0000 * 0DCA SINHALA SIGN AL-LAKUNA * 0000 * 0DCF SINHALA VOWEL SIGN AELA-PILLA * 0000 * 0DD0 SINHALA VOWEL SIGN KETTI AEDA-PILLA * 0000 * 0DD1 SINHALA VOWEL SIGN DIGA AEDA-PILLA * 0000 * 0DD2 SINHALA VOWEL SIGN KETTI IS-PILLA * 0000 * 0DD3 SINHALA VOWEL SIGN DIGA IS-PILLA * 0000 * 0DD4 SINHALA VOWEL SIGN KETTI PAA-PILLA * 0000 * 0DD6 SINHALA VOWEL SIGN DIGA PAA-PILLA * 0000 * 0DD8 SINHALA VOWEL SIGN GAETTA-PILLA * 0000 * 0DD9 SINHALA VOWEL SIGN KOMBUVA * 0000 * 0DDA SINHALA VOWEL SIGN DIGA KOMBUVA * 0000 * 0DDB SINHALA VOWEL SIGN KOMBU DEKA * 0000 * 0DDC SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA * 0000 * 0DDD SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA * 0000 * 0DDE SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA * 0000 * 0DDF SINHALA VOWEL SIGN GAYANUKITTA * 0000 * 0DF2 SINHALA VOWEL SIGN DIGA GAETTA-PILLA * 0000 * 0DF3 SINHALA VOWEL SIGN DIGA GAYANUKITTA * 0000 * 0E31 THAI CHARACTER MAI HAN-AKAT * 0000 * 0E33 THAI CHARACTER SARA AM * 0E32 THAI CHARACTER SARA AA * 0E34 THAI CHARACTER SARA I * 0000 * 0E35 THAI CHARACTER SARA II * 0000 * 0E36 THAI CHARACTER SARA UE * 0000 * 0E37 THAI CHARACTER SARA UEE * 0000 * 0E38 THAI CHARACTER SARA U * 0000 * 0E39 THAI CHARACTER SARA UU * 0000 * 0E3A THAI CHARACTER PHINTHU * 0000 * 0E47 THAI CHARACTER MAITAIKHU * 0000 * 0E48 THAI CHARACTER MAI EK * 0000 * 0E49 THAI CHARACTER MAI THO * 0000 * 0E4A THAI CHARACTER MAI TRI * 0000 * 0E4B THAI CHARACTER MAI CHATTAWA * 0000 * 0E4C THAI CHARACTER THANTHAKHAT * 0000 * 0E4D THAI CHARACTER NIKHAHIT * 0000 * 0E4E THAI CHARACTER YAMAKKAN * 0000 * 0EB1 LAO VOWEL SIGN MAI KAN * 0000 * 0EB3 LAO VOWEL SIGN AM * 0EB2 LAO VOWEL SIGN AA * 0EB4 LAO VOWEL SIGN I * 0000 * 0EB5 LAO VOWEL SIGN II * 0000 * 0EB6 LAO VOWEL SIGN Y * 0000 * 0EB7 LAO VOWEL SIGN YY * 0000 * 0EB8 LAO VOWEL SIGN U * 0000 * 0EB9 LAO VOWEL SIGN UU * 0000 * 0EBA LAO SIGN PALI VIRAMA * 0000 * 0EBB LAO VOWEL SIGN MAI KON * 0000 * 0EBC LAO SEMIVOWEL SIGN LO * 0000 * 0EC8 LAO TONE MAI EK * 0000 * 0EC9 LAO TONE MAI THO * 0000 * 0ECA LAO TONE MAI TI * 0000 * 0ECB LAO TONE MAI CATAWA * 0000 * 0ECC LAO CANCELLATION MARK * 0000 * 0ECD LAO NIGGAHITA * 0000 * 0EDC LAO HO NO * 0EAB LAO LETTER HO SUNG * 0E99 LAO LETTER NO * 0EDD LAO HO MO * 0EAB LAO LETTER HO SUNG * 0EA1 LAO LETTER MO * 0F0C TIBETAN MARK DELIMITER TSHEG BSTAR * 0F0B TIBETAN MARK INTERSYLLABIC TSHEG * 0F18 TIBETAN ASTROLOGICAL SIGN -KHYUD PA * 0000 * 0F19 TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS * 0000 * 0F35 TIBETAN MARK NGAS BZUNG NYI ZLA * 0000 * 0F37 TIBETAN MARK NGAS BZUNG SGOR RTAGS * 0000 * 0F39 TIBETAN MARK TSA -PHRU * 0000 * 0F3E TIBETAN SIGN YAR TSHES * 0000 * 0F3F TIBETAN SIGN MAR TSHES * 0000 * 0F43 TIBETAN LETTER GHA * 0F42 TIBETAN LETTER GA * 0F4D TIBETAN LETTER DDHA * 0F4C TIBETAN LETTER DDA * 0F52 TIBETAN LETTER DHA * 0F51 TIBETAN LETTER DA * 0F57 TIBETAN LETTER BHA * 0F56 TIBETAN LETTER BA * 0F5C TIBETAN LETTER DZHA * 0F5B TIBETAN LETTER DZA * 0F69 TIBETAN LETTER KSSA * 0F40 TIBETAN LETTER KA * 0F71 TIBETAN VOWEL SIGN AA * 0000 * 0F72 TIBETAN VOWEL SIGN I * 0000 * 0F73 TIBETAN VOWEL SIGN II * 0000 * 0F74 TIBETAN VOWEL SIGN U * 0000 * 0F75 TIBETAN VOWEL SIGN UU * 0000 * 0F76 TIBETAN VOWEL SIGN VOCALIC R * 0000 * 0F77 TIBETAN VOWEL SIGN VOCALIC RR * 0000 * 0F78 TIBETAN VOWEL SIGN VOCALIC L * 0000 * 0F79 TIBETAN VOWEL SIGN VOCALIC LL * 0000 * 0F7A TIBETAN VOWEL SIGN E * 0000 * 0F7B TIBETAN VOWEL SIGN EE * 0000 * 0F7C TIBETAN VOWEL SIGN O * 0000 * 0F7D TIBETAN VOWEL SIGN OO * 0000 * 0F7E TIBETAN SIGN RJES SU NGA RO * 0000 * 0F7F TIBETAN SIGN RNAM BCAD * 0000 * 0F80 TIBETAN VOWEL SIGN REVERSED I * 0000 * 0F81 TIBETAN VOWEL SIGN REVERSED II * 0000 * 0F82 TIBETAN SIGN NYI ZLA NAA DA * 0000 * 0F83 TIBETAN SIGN SNA LDAN * 0000 * 0F84 TIBETAN MARK HALANTA * 0000 * 0F86 TIBETAN SIGN LCI RTAGS * 0000 * 0F87 TIBETAN SIGN YANG RTAGS * 0000 * 0F8D TIBETAN SUBJOINED SIGN LCE TSA CAN * 0000 * 0F8E TIBETAN SUBJOINED SIGN MCHU CAN * 0000 * 0F8F TIBETAN SUBJOINED SIGN INVERTED MCHU CAN * 0000 * 0F90 TIBETAN SUBJOINED LETTER KA * 0000 * 0F91 TIBETAN SUBJOINED LETTER KHA * 0000 * 0F92 TIBETAN SUBJOINED LETTER GA * 0000 * 0F93 TIBETAN SUBJOINED LETTER GHA * 0000 * 0F94 TIBETAN SUBJOINED LETTER NGA * 0000 * 0F95 TIBETAN SUBJOINED LETTER CA * 0000 * 0F96 TIBETAN SUBJOINED LETTER CHA * 0000 * 0F97 TIBETAN SUBJOINED LETTER JA * 0000 * 0F99 TIBETAN SUBJOINED LETTER NYA * 0000 * 0F9A TIBETAN SUBJOINED LETTER TTA * 0000 * 0F9B TIBETAN SUBJOINED LETTER TTHA * 0000 * 0F9C TIBETAN SUBJOINED LETTER DDA * 0000 * 0F9D TIBETAN SUBJOINED LETTER DDHA * 0000 * 0F9E TIBETAN SUBJOINED LETTER NNA * 0000 * 0F9F TIBETAN SUBJOINED LETTER TA * 0000 * 0FA0 TIBETAN SUBJOINED LETTER THA * 0000 * 0FA1 TIBETAN SUBJOINED LETTER DA * 0000 * 0FA2 TIBETAN SUBJOINED LETTER DHA * 0000 * 0FA3 TIBETAN SUBJOINED LETTER NA * 0000 * 0FA4 TIBETAN SUBJOINED LETTER PA * 0000 * 0FA5 TIBETAN SUBJOINED LETTER PHA * 0000 * 0FA6 TIBETAN SUBJOINED LETTER BA * 0000 * 0FA7 TIBETAN SUBJOINED LETTER BHA * 0000 * 0FA8 TIBETAN SUBJOINED LETTER MA * 0000 * 0FA9 TIBETAN SUBJOINED LETTER TSA * 0000 * 0FAA TIBETAN SUBJOINED LETTER TSHA * 0000 * 0FAB TIBETAN SUBJOINED LETTER DZA * 0000 * 0FAC TIBETAN SUBJOINED LETTER DZHA * 0000 * 0FAD TIBETAN SUBJOINED LETTER WA * 0000 * 0FAE TIBETAN SUBJOINED LETTER ZHA * 0000 * 0FAF TIBETAN SUBJOINED LETTER ZA * 0000 * 0FB0 TIBETAN SUBJOINED LETTER -A * 0000 * 0FB1 TIBETAN SUBJOINED LETTER YA * 0000 * 0FB2 TIBETAN SUBJOINED LETTER RA * 0000 * 0FB3 TIBETAN SUBJOINED LETTER LA * 0000 * 0FB4 TIBETAN SUBJOINED LETTER SHA * 0000 * 0FB5 TIBETAN SUBJOINED LETTER SSA * 0000 * 0FB6 TIBETAN SUBJOINED LETTER SA * 0000 * 0FB7 TIBETAN SUBJOINED LETTER HA * 0000 * 0FB8 TIBETAN SUBJOINED LETTER A * 0000 * 0FB9 TIBETAN SUBJOINED LETTER KSSA * 0000 * 0FBA TIBETAN SUBJOINED LETTER FIXED-FORM WA * 0000 * 0FBB TIBETAN SUBJOINED LETTER FIXED-FORM YA * 0000 * 0FBC TIBETAN SUBJOINED LETTER FIXED-FORM RA * 0000 * 0FC6 TIBETAN SYMBOL PADMA GDAN * 0000 * 1026 MYANMAR LETTER UU * 1025 MYANMAR LETTER U * 102B MYANMAR VOWEL SIGN TALL AA * 0000 * 102C MYANMAR VOWEL SIGN AA * 0000 * 102D MYANMAR VOWEL SIGN I * 0000 * 102E MYANMAR VOWEL SIGN II * 0000 * 102F MYANMAR VOWEL SIGN U * 0000 * 1030 MYANMAR VOWEL SIGN UU * 0000 * 1031 MYANMAR VOWEL SIGN E * 0000 * 1032 MYANMAR VOWEL SIGN AI * 0000 * 1033 MYANMAR VOWEL SIGN MON II * 0000 * 1034 MYANMAR VOWEL SIGN MON O * 0000 * 1035 MYANMAR VOWEL SIGN E ABOVE * 0000 * 1036 MYANMAR SIGN ANUSVARA * 0000 * 1037 MYANMAR SIGN DOT BELOW * 0000 * 1038 MYANMAR SIGN VISARGA * 0000 * 1039 MYANMAR SIGN VIRAMA * 0000 * 103A MYANMAR SIGN ASAT * 0000 * 103B MYANMAR CONSONANT SIGN MEDIAL YA * 0000 * 103C MYANMAR CONSONANT SIGN MEDIAL RA * 0000 * 103D MYANMAR CONSONANT SIGN MEDIAL WA * 0000 * 103E MYANMAR CONSONANT SIGN MEDIAL HA * 0000 * 1056 MYANMAR VOWEL SIGN VOCALIC R * 0000 * 1057 MYANMAR VOWEL SIGN VOCALIC RR * 0000 * 1058 MYANMAR VOWEL SIGN VOCALIC L * 0000 * 1059 MYANMAR VOWEL SIGN VOCALIC LL * 0000 * 105E MYANMAR CONSONANT SIGN MON MEDIAL NA * 0000 * 105F MYANMAR CONSONANT SIGN MON MEDIAL MA * 0000 * 1060 MYANMAR CONSONANT SIGN MON MEDIAL LA * 0000 * 1062 MYANMAR VOWEL SIGN SGAW KAREN EU * 0000 * 1063 MYANMAR TONE MARK SGAW KAREN HATHI * 0000 * 1064 MYANMAR TONE MARK SGAW KAREN KE PHO * 0000 * 1067 MYANMAR VOWEL SIGN WESTERN PWO KAREN EU * 0000 * 1068 MYANMAR VOWEL SIGN WESTERN PWO KAREN UE * 0000 * 1069 MYANMAR SIGN WESTERN PWO KAREN TONE-1 * 0000 * 106A MYANMAR SIGN WESTERN PWO KAREN TONE-2 * 0000 * 106B MYANMAR SIGN WESTERN PWO KAREN TONE-3 * 0000 * 106C MYANMAR SIGN WESTERN PWO KAREN TONE-4 * 0000 * 106D MYANMAR SIGN WESTERN PWO KAREN TONE-5 * 0000 * 1071 MYANMAR VOWEL SIGN GEBA KAREN I * 0000 * 1072 MYANMAR VOWEL SIGN KAYAH OE * 0000 * 1073 MYANMAR VOWEL SIGN KAYAH U * 0000 * 1074 MYANMAR VOWEL SIGN KAYAH EE * 0000 * 1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA * 0000 * 1083 MYANMAR VOWEL SIGN SHAN AA * 0000 * 1084 MYANMAR VOWEL SIGN SHAN E * 0000 * 1085 MYANMAR VOWEL SIGN SHAN E ABOVE * 0000 * 1086 MYANMAR VOWEL SIGN SHAN FINAL Y * 0000 * 1087 MYANMAR SIGN SHAN TONE-2 * 0000 * 1088 MYANMAR SIGN SHAN TONE-3 * 0000 * 1089 MYANMAR SIGN SHAN TONE-5 * 0000 * 108A MYANMAR SIGN SHAN TONE-6 * 0000 * 108B MYANMAR SIGN SHAN COUNCIL TONE-2 * 0000 * 108C MYANMAR SIGN SHAN COUNCIL TONE-3 * 0000 * 108D MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE * 0000 * 108F MYANMAR SIGN RUMAI PALAUNG TONE-5 * 0000 * 109A MYANMAR SIGN KHAMTI TONE-1 * 0000 * 109B MYANMAR SIGN KHAMTI TONE-3 * 0000 * 109C MYANMAR VOWEL SIGN AITON A * 0000 * 109D MYANMAR VOWEL SIGN AITON AI * 0000 * 10FC MODIFIER LETTER GEORGIAN NAR * 10DC GEORGIAN LETTER NAR * 135D ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK * 0000 * 135E ETHIOPIC COMBINING VOWEL LENGTH MARK * 0000 * 135F ETHIOPIC COMBINING GEMINATION MARK * 0000 * 1712 TAGALOG VOWEL SIGN I * 0000 * 1713 TAGALOG VOWEL SIGN U * 0000 * 1714 TAGALOG SIGN VIRAMA * 0000 * 1715 TAGALOG SIGN PAMUDPOD * 0000 * 1732 HANUNOO VOWEL SIGN I * 0000 * 1733 HANUNOO VOWEL SIGN U * 0000 * 1734 HANUNOO SIGN PAMUDPOD * 0000 * 1752 BUHID VOWEL SIGN I * 0000 * 1753 BUHID VOWEL SIGN U * 0000 * 1772 TAGBANWA VOWEL SIGN I * 0000 * 1773 TAGBANWA VOWEL SIGN U * 0000 * 17B4 KHMER VOWEL INHERENT AQ * 0000 * 17B5 KHMER VOWEL INHERENT AA * 0000 * 17B6 KHMER VOWEL SIGN AA * 0000 * 17B7 KHMER VOWEL SIGN I * 0000 * 17B8 KHMER VOWEL SIGN II * 0000 * 17B9 KHMER VOWEL SIGN Y * 0000 * 17BA KHMER VOWEL SIGN YY * 0000 * 17BB KHMER VOWEL SIGN U * 0000 * 17BC KHMER VOWEL SIGN UU * 0000 * 17BD KHMER VOWEL SIGN UA * 0000 * 17BE KHMER VOWEL SIGN OE * 0000 * 17BF KHMER VOWEL SIGN YA * 0000 * 17C0 KHMER VOWEL SIGN IE * 0000 * 17C1 KHMER VOWEL SIGN E * 0000 * 17C2 KHMER VOWEL SIGN AE * 0000 * 17C3 KHMER VOWEL SIGN AI * 0000 * 17C4 KHMER VOWEL SIGN OO * 0000 * 17C5 KHMER VOWEL SIGN AU * 0000 * 17C6 KHMER SIGN NIKAHIT * 0000 * 17C7 KHMER SIGN REAHMUK * 0000 * 17C8 KHMER SIGN YUUKALEAPINTU * 0000 * 17C9 KHMER SIGN MUUSIKATOAN * 0000 * 17CA KHMER SIGN TRIISAP * 0000 * 17CB KHMER SIGN BANTOC * 0000 * 17CC KHMER SIGN ROBAT * 0000 * 17CD KHMER SIGN TOANDAKHIAT * 0000 * 17CE KHMER SIGN KAKABAT * 0000 * 17CF KHMER SIGN AHSDA * 0000 * 17D0 KHMER SIGN SAMYOK SANNYA * 0000 * 17D1 KHMER SIGN VIRIAM * 0000 * 17D2 KHMER SIGN COENG * 0000 * 17D3 KHMER SIGN BATHAMASAT * 0000 * 17DD KHMER SIGN ATTHACAN * 0000 * 180B MONGOLIAN FREE VARIATION SELECTOR ONE * 0000 * 180C MONGOLIAN FREE VARIATION SELECTOR TWO * 0000 * 180D MONGOLIAN FREE VARIATION SELECTOR THREE * 0000 * 180F MONGOLIAN FREE VARIATION SELECTOR FOUR * 0000 * 1885 MONGOLIAN LETTER ALI GALI BALUDA * 0000 * 1886 MONGOLIAN LETTER ALI GALI THREE BALUDA * 0000 * 18A9 MONGOLIAN LETTER ALI GALI DAGALGA * 0000 * 1920 LIMBU VOWEL SIGN A * 0000 * 1921 LIMBU VOWEL SIGN I * 0000 * 1922 LIMBU VOWEL SIGN U * 0000 * 1923 LIMBU VOWEL SIGN EE * 0000 * 1924 LIMBU VOWEL SIGN AI * 0000 * 1925 LIMBU VOWEL SIGN OO * 0000 * 1926 LIMBU VOWEL SIGN AU * 0000 * 1927 LIMBU VOWEL SIGN E * 0000 * 1928 LIMBU VOWEL SIGN O * 0000 * 1929 LIMBU SUBJOINED LETTER YA * 0000 * 192A LIMBU SUBJOINED LETTER RA * 0000 * 192B LIMBU SUBJOINED LETTER WA * 0000 * 1930 LIMBU SMALL LETTER KA * 0000 * 1931 LIMBU SMALL LETTER NGA * 0000 * 1932 LIMBU SMALL LETTER ANUSVARA * 0000 * 1933 LIMBU SMALL LETTER TA * 0000 * 1934 LIMBU SMALL LETTER NA * 0000 * 1935 LIMBU SMALL LETTER PA * 0000 * 1936 LIMBU SMALL LETTER MA * 0000 * 1937 LIMBU SMALL LETTER RA * 0000 * 1938 LIMBU SMALL LETTER LA * 0000 * 1939 LIMBU SIGN MUKPHRENG * 0000 * 193A LIMBU SIGN KEMPHRENG * 0000 * 193B LIMBU SIGN SA-I * 0000 * 1A17 BUGINESE VOWEL SIGN I * 0000 * 1A18 BUGINESE VOWEL SIGN U * 0000 * 1A19 BUGINESE VOWEL SIGN E * 0000 * 1A1A BUGINESE VOWEL SIGN O * 0000 * 1A1B BUGINESE VOWEL SIGN AE * 0000 * 1A55 TAI THAM CONSONANT SIGN MEDIAL RA * 0000 * 1A56 TAI THAM CONSONANT SIGN MEDIAL LA * 0000 * 1A57 TAI THAM CONSONANT SIGN LA TANG LAI * 0000 * 1A58 TAI THAM SIGN MAI KANG LAI * 0000 * 1A59 TAI THAM CONSONANT SIGN FINAL NGA * 0000 * 1A5A TAI THAM CONSONANT SIGN LOW PA * 0000 * 1A5B TAI THAM CONSONANT SIGN HIGH RATHA OR LOW PA * 0000 * 1A5C TAI THAM CONSONANT SIGN MA * 0000 * 1A5D TAI THAM CONSONANT SIGN BA * 0000 * 1A5E TAI THAM CONSONANT SIGN SA * 0000 * 1A60 TAI THAM SIGN SAKOT * 0000 * 1A61 TAI THAM VOWEL SIGN A * 0000 * 1A62 TAI THAM VOWEL SIGN MAI SAT * 0000 * 1A63 TAI THAM VOWEL SIGN AA * 0000 * 1A64 TAI THAM VOWEL SIGN TALL AA * 0000 * 1A65 TAI THAM VOWEL SIGN I * 0000 * 1A66 TAI THAM VOWEL SIGN II * 0000 * 1A67 TAI THAM VOWEL SIGN UE * 0000 * 1A68 TAI THAM VOWEL SIGN UUE * 0000 * 1A69 TAI THAM VOWEL SIGN U * 0000 * 1A6A TAI THAM VOWEL SIGN UU * 0000 * 1A6B TAI THAM VOWEL SIGN O * 0000 * 1A6C TAI THAM VOWEL SIGN OA BELOW * 0000 * 1A6D TAI THAM VOWEL SIGN OY * 0000 * 1A6E TAI THAM VOWEL SIGN E * 0000 * 1A6F TAI THAM VOWEL SIGN AE * 0000 * 1A70 TAI THAM VOWEL SIGN OO * 0000 * 1A71 TAI THAM VOWEL SIGN AI * 0000 * 1A72 TAI THAM VOWEL SIGN THAM AI * 0000 * 1A73 TAI THAM VOWEL SIGN OA ABOVE * 0000 * 1A74 TAI THAM SIGN MAI KANG * 0000 * 1A75 TAI THAM SIGN TONE-1 * 0000 * 1A76 TAI THAM SIGN TONE-2 * 0000 * 1A77 TAI THAM SIGN KHUEN TONE-3 * 0000 * 1A78 TAI THAM SIGN KHUEN TONE-4 * 0000 * 1A79 TAI THAM SIGN KHUEN TONE-5 * 0000 * 1A7A TAI THAM SIGN RA HAAM * 0000 * 1A7B TAI THAM SIGN MAI SAM * 0000 * 1A7C TAI THAM SIGN KHUEN-LUE KARAN * 0000 * 1A7F TAI THAM COMBINING CRYPTOGRAMMIC DOT * 0000 * 1AB0 COMBINING DOUBLED CIRCUMFLEX ACCENT * 0000 * 1AB1 COMBINING DIAERESIS-RING * 0000 * 1AB2 COMBINING INFINITY * 0000 * 1AB3 COMBINING DOWNWARDS ARROW * 0000 * 1AB4 COMBINING TRIPLE DOT * 0000 * 1AB5 COMBINING X-X BELOW * 0000 * 1AB6 COMBINING WIGGLY LINE BELOW * 0000 * 1AB7 COMBINING OPEN MARK BELOW * 0000 * 1AB8 COMBINING DOUBLE OPEN MARK BELOW * 0000 * 1AB9 COMBINING LIGHT CENTRALIZATION STROKE BELOW * 0000 * 1ABA COMBINING STRONG CENTRALIZATION STROKE BELOW * 0000 * 1ABB COMBINING PARENTHESES ABOVE * 0000 * 1ABC COMBINING DOUBLE PARENTHESES ABOVE * 0000 * 1ABD COMBINING PARENTHESES BELOW * 0000 * 1ABE COMBINING PARENTHESES OVERLAY * 0000 * 1ABF COMBINING LATIN SMALL LETTER W BELOW * 0000 * 1AC0 COMBINING LATIN SMALL LETTER TURNED W BELOW * 0000 * 1AC1 COMBINING LEFT PARENTHESIS ABOVE LEFT * 0000 * 1AC2 COMBINING RIGHT PARENTHESIS ABOVE RIGHT * 0000 * 1AC3 COMBINING LEFT PARENTHESIS BELOW LEFT * 0000 * 1AC4 COMBINING RIGHT PARENTHESIS BELOW RIGHT * 0000 * 1AC5 COMBINING SQUARE BRACKETS ABOVE * 0000 * 1AC6 COMBINING NUMBER SIGN ABOVE * 0000 * 1AC7 COMBINING INVERTED DOUBLE ARCH ABOVE * 0000 * 1AC8 COMBINING PLUS SIGN ABOVE * 0000 * 1AC9 COMBINING DOUBLE PLUS SIGN ABOVE * 0000 * 1ACA COMBINING DOUBLE PLUS SIGN BELOW * 0000 * 1ACB COMBINING TRIPLE ACUTE ACCENT * 0000 * 1ACC COMBINING LATIN SMALL LETTER INSULAR G * 0000 * 1ACD COMBINING LATIN SMALL LETTER INSULAR R * 0000 * 1ACE COMBINING LATIN SMALL LETTER INSULAR T * 0000 * 1B00 BALINESE SIGN ULU RICEM * 0000 * 1B01 BALINESE SIGN ULU CANDRA * 0000 * 1B02 BALINESE SIGN CECEK * 0000 * 1B03 BALINESE SIGN SURANG * 0000 * 1B04 BALINESE SIGN BISAH * 0000 * 1B06 BALINESE LETTER AKARA TEDUNG * 1B05 BALINESE LETTER AKARA * 1B08 BALINESE LETTER IKARA TEDUNG * 1B07 BALINESE LETTER IKARA * 1B0A BALINESE LETTER UKARA TEDUNG * 1B09 BALINESE LETTER UKARA * 1B0C BALINESE LETTER RA REPA TEDUNG * 1B0B BALINESE LETTER RA REPA * 1B0E BALINESE LETTER LA LENGA TEDUNG * 1B0D BALINESE LETTER LA LENGA * 1B12 BALINESE LETTER OKARA TEDUNG * 1B11 BALINESE LETTER OKARA * 1B34 BALINESE SIGN REREKAN * 0000 * 1B35 BALINESE VOWEL SIGN TEDUNG * 0000 * 1B36 BALINESE VOWEL SIGN ULU * 0000 * 1B37 BALINESE VOWEL SIGN ULU SARI * 0000 * 1B38 BALINESE VOWEL SIGN SUKU * 0000 * 1B39 BALINESE VOWEL SIGN SUKU ILUT * 0000 * 1B3A BALINESE VOWEL SIGN RA REPA * 0000 * 1B3B BALINESE VOWEL SIGN RA REPA TEDUNG * 0000 * 1B3C BALINESE VOWEL SIGN LA LENGA * 0000 * 1B3D BALINESE VOWEL SIGN LA LENGA TEDUNG * 0000 * 1B3E BALINESE VOWEL SIGN TALING * 0000 * 1B3F BALINESE VOWEL SIGN TALING REPA * 0000 * 1B40 BALINESE VOWEL SIGN TALING TEDUNG * 0000 * 1B41 BALINESE VOWEL SIGN TALING REPA TEDUNG * 0000 * 1B42 BALINESE VOWEL SIGN PEPET * 0000 * 1B43 BALINESE VOWEL SIGN PEPET TEDUNG * 0000 * 1B44 BALINESE ADEG ADEG * 0000 * 1B6B BALINESE MUSICAL SYMBOL COMBINING TEGEH * 0000 * 1B6C BALINESE MUSICAL SYMBOL COMBINING ENDEP * 0000 * 1B6D BALINESE MUSICAL SYMBOL COMBINING KEMPUL * 0000 * 1B6E BALINESE MUSICAL SYMBOL COMBINING KEMPLI * 0000 * 1B6F BALINESE MUSICAL SYMBOL COMBINING JEGOGAN * 0000 * 1B70 BALINESE MUSICAL SYMBOL COMBINING KEMPUL WITH JEGOGAN * 0000 * 1B71 BALINESE MUSICAL SYMBOL COMBINING KEMPLI WITH JEGOGAN * 0000 * 1B72 BALINESE MUSICAL SYMBOL COMBINING BENDE * 0000 * 1B73 BALINESE MUSICAL SYMBOL COMBINING GONG * 0000 * 1B80 SUNDANESE SIGN PANYECEK * 0000 * 1B81 SUNDANESE SIGN PANGLAYAR * 0000 * 1B82 SUNDANESE SIGN PANGWISAD * 0000 * 1BA1 SUNDANESE CONSONANT SIGN PAMINGKAL * 0000 * 1BA2 SUNDANESE CONSONANT SIGN PANYAKRA * 0000 * 1BA3 SUNDANESE CONSONANT SIGN PANYIKU * 0000 * 1BA4 SUNDANESE VOWEL SIGN PANGHULU * 0000 * 1BA5 SUNDANESE VOWEL SIGN PANYUKU * 0000 * 1BA6 SUNDANESE VOWEL SIGN PANAELAENG * 0000 * 1BA7 SUNDANESE VOWEL SIGN PANOLONG * 0000 * 1BA8 SUNDANESE VOWEL SIGN PAMEPET * 0000 * 1BA9 SUNDANESE VOWEL SIGN PANEULEUNG * 0000 * 1BAA SUNDANESE SIGN PAMAAEH * 0000 * 1BAB SUNDANESE SIGN VIRAMA * 0000 * 1BAC SUNDANESE CONSONANT SIGN PASANGAN MA * 0000 * 1BAD SUNDANESE CONSONANT SIGN PASANGAN WA * 0000 * 1BE6 BATAK SIGN TOMPI * 0000 * 1BE7 BATAK VOWEL SIGN E * 0000 * 1BE8 BATAK VOWEL SIGN PAKPAK E * 0000 * 1BE9 BATAK VOWEL SIGN EE * 0000 * 1BEA BATAK VOWEL SIGN I * 0000 * 1BEB BATAK VOWEL SIGN KARO I * 0000 * 1BEC BATAK VOWEL SIGN O * 0000 * 1BED BATAK VOWEL SIGN KARO O * 0000 * 1BEE BATAK VOWEL SIGN U * 0000 * 1BEF BATAK VOWEL SIGN U FOR SIMALUNGUN SA * 0000 * 1BF0 BATAK CONSONANT SIGN NG * 0000 * 1BF1 BATAK CONSONANT SIGN H * 0000 * 1BF2 BATAK PANGOLAT * 0000 * 1BF3 BATAK PANONGONAN * 0000 * 1C24 LEPCHA SUBJOINED LETTER YA * 0000 * 1C25 LEPCHA SUBJOINED LETTER RA * 0000 * 1C26 LEPCHA VOWEL SIGN AA * 0000 * 1C27 LEPCHA VOWEL SIGN I * 0000 * 1C28 LEPCHA VOWEL SIGN O * 0000 * 1C29 LEPCHA VOWEL SIGN OO * 0000 * 1C2A LEPCHA VOWEL SIGN U * 0000 * 1C2B LEPCHA VOWEL SIGN UU * 0000 * 1C2C LEPCHA VOWEL SIGN E * 0000 * 1C2D LEPCHA CONSONANT SIGN K * 0000 * 1C2E LEPCHA CONSONANT SIGN M * 0000 * 1C2F LEPCHA CONSONANT SIGN L * 0000 * 1C30 LEPCHA CONSONANT SIGN N * 0000 * 1C31 LEPCHA CONSONANT SIGN P * 0000 * 1C32 LEPCHA CONSONANT SIGN R * 0000 * 1C33 LEPCHA CONSONANT SIGN T * 0000 * 1C34 LEPCHA CONSONANT SIGN NYIN-DO * 0000 * 1C35 LEPCHA CONSONANT SIGN KANG * 0000 * 1C36 LEPCHA SIGN RAN * 0000 * 1C37 LEPCHA SIGN NUKTA * 0000 * 1CD0 VEDIC TONE KARSHANA * 0000 * 1CD1 VEDIC TONE SHARA * 0000 * 1CD2 VEDIC TONE PRENKHA * 0000 * 1CD4 VEDIC SIGN YAJURVEDIC MIDLINE SVARITA * 0000 * 1CD5 VEDIC TONE YAJURVEDIC AGGRAVATED INDEPENDENT SVARITA * 0000 * 1CD6 VEDIC TONE YAJURVEDIC INDEPENDENT SVARITA * 0000 * 1CD7 VEDIC TONE YAJURVEDIC KATHAKA INDEPENDENT SVARITA * 0000 * 1CD8 VEDIC TONE CANDRA BELOW * 0000 * 1CD9 VEDIC TONE YAJURVEDIC KATHAKA INDEPENDENT SVARITA SCHROEDER * 0000 * 1CDA VEDIC TONE DOUBLE SVARITA * 0000 * 1CDB VEDIC TONE TRIPLE SVARITA * 0000 * 1CDC VEDIC TONE KATHAKA ANUDATTA * 0000 * 1CDD VEDIC TONE DOT BELOW * 0000 * 1CDE VEDIC TONE TWO DOTS BELOW * 0000 * 1CDF VEDIC TONE THREE DOTS BELOW * 0000 * 1CE0 VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA * 0000 * 1CE1 VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA * 0000 * 1CE2 VEDIC SIGN VISARGA SVARITA * 0000 * 1CE3 VEDIC SIGN VISARGA UDATTA * 0000 * 1CE4 VEDIC SIGN REVERSED VISARGA UDATTA * 0000 * 1CE5 VEDIC SIGN VISARGA ANUDATTA * 0000 * 1CE6 VEDIC SIGN REVERSED VISARGA ANUDATTA * 0000 * 1CE7 VEDIC SIGN VISARGA UDATTA WITH TAIL * 0000 * 1CE8 VEDIC SIGN VISARGA ANUDATTA WITH TAIL * 0000 * 1CED VEDIC SIGN TIRYAK * 0000 * 1CF4 VEDIC TONE CANDRA ABOVE * 0000 * 1CF7 VEDIC SIGN ATIKRAMA * 0000 * 1CF8 VEDIC TONE RING ABOVE * 0000 * 1CF9 VEDIC TONE DOUBLE RING ABOVE * 0000 * 1D2C MODIFIER LETTER CAPITAL A * 0041 LATIN CAPITAL LETTER A * 1D2D MODIFIER LETTER CAPITAL AE * 00C6 LATIN CAPITAL LETTER AE * 1D2E MODIFIER LETTER CAPITAL B * 0042 LATIN CAPITAL LETTER B * 1D30 MODIFIER LETTER CAPITAL D * 0044 LATIN CAPITAL LETTER D * 1D31 MODIFIER LETTER CAPITAL E * 0045 LATIN CAPITAL LETTER E * 1D32 MODIFIER LETTER CAPITAL REVERSED E * 018E LATIN CAPITAL LETTER REVERSED E * 1D33 MODIFIER LETTER CAPITAL G * 0047 LATIN CAPITAL LETTER G * 1D34 MODIFIER LETTER CAPITAL H * 0048 LATIN CAPITAL LETTER H * 1D35 MODIFIER LETTER CAPITAL I * 0049 LATIN CAPITAL LETTER I * 1D36 MODIFIER LETTER CAPITAL J * 004A LATIN CAPITAL LETTER J * 1D37 MODIFIER LETTER CAPITAL K * 004B LATIN CAPITAL LETTER K * 1D38 MODIFIER LETTER CAPITAL L * 004C LATIN CAPITAL LETTER L * 1D39 MODIFIER LETTER CAPITAL M * 004D LATIN CAPITAL LETTER M * 1D3A MODIFIER LETTER CAPITAL N * 004E LATIN CAPITAL LETTER N * 1D3C MODIFIER LETTER CAPITAL O * 004F LATIN CAPITAL LETTER O * 1D3D MODIFIER LETTER CAPITAL OU * 0222 LATIN CAPITAL LETTER OU * 1D3E MODIFIER LETTER CAPITAL P * 0050 LATIN CAPITAL LETTER P * 1D3F MODIFIER LETTER CAPITAL R * 0052 LATIN CAPITAL LETTER R * 1D40 MODIFIER LETTER CAPITAL T * 0054 LATIN CAPITAL LETTER T * 1D41 MODIFIER LETTER CAPITAL U * 0055 LATIN CAPITAL LETTER U * 1D42 MODIFIER LETTER CAPITAL W * 0057 LATIN CAPITAL LETTER W * 1D43 MODIFIER LETTER SMALL A * 0061 LATIN SMALL LETTER A * 1D44 MODIFIER LETTER SMALL TURNED A * 0250 LATIN SMALL LETTER TURNED A * 1D45 MODIFIER LETTER SMALL ALPHA * 0251 LATIN SMALL LETTER ALPHA * 1D46 MODIFIER LETTER SMALL TURNED AE * 1D02 LATIN SMALL LETTER TURNED AE * 1D47 MODIFIER LETTER SMALL B * 0062 LATIN SMALL LETTER B * 1D48 MODIFIER LETTER SMALL D * 0064 LATIN SMALL LETTER D * 1D49 MODIFIER LETTER SMALL E * 0065 LATIN SMALL LETTER E * 1D4A MODIFIER LETTER SMALL SCHWA * 0259 LATIN SMALL LETTER SCHWA * 1D4B MODIFIER LETTER SMALL OPEN E * 025B LATIN SMALL LETTER OPEN E * 1D4C MODIFIER LETTER SMALL TURNED OPEN E * 025C LATIN SMALL LETTER REVERSED OPEN E * 1D4D MODIFIER LETTER SMALL G * 0067 LATIN SMALL LETTER G * 1D4F MODIFIER LETTER SMALL K * 006B LATIN SMALL LETTER K * 1D50 MODIFIER LETTER SMALL M * 006D LATIN SMALL LETTER M * 1D51 MODIFIER LETTER SMALL ENG * 014B LATIN SMALL LETTER ENG * 1D52 MODIFIER LETTER SMALL O * 006F LATIN SMALL LETTER O * 1D53 MODIFIER LETTER SMALL OPEN O * 0254 LATIN SMALL LETTER OPEN O * 1D54 MODIFIER LETTER SMALL TOP HALF O * 1D16 LATIN SMALL LETTER TOP HALF O * 1D55 MODIFIER LETTER SMALL BOTTOM HALF O * 1D17 LATIN SMALL LETTER BOTTOM HALF O * 1D56 MODIFIER LETTER SMALL P * 0070 LATIN SMALL LETTER P * 1D57 MODIFIER LETTER SMALL T * 0074 LATIN SMALL LETTER T * 1D58 MODIFIER LETTER SMALL U * 0075 LATIN SMALL LETTER U * 1D59 MODIFIER LETTER SMALL SIDEWAYS U * 1D1D LATIN SMALL LETTER SIDEWAYS U * 1D5A MODIFIER LETTER SMALL TURNED M * 026F LATIN SMALL LETTER TURNED M * 1D5B MODIFIER LETTER SMALL V * 0076 LATIN SMALL LETTER V * 1D5C MODIFIER LETTER SMALL AIN * 1D25 LATIN LETTER AIN * 1D5D MODIFIER LETTER SMALL BETA * 03B2 GREEK SMALL LETTER BETA * 1D5E MODIFIER LETTER SMALL GREEK GAMMA * 03B3 GREEK SMALL LETTER GAMMA * 1D5F MODIFIER LETTER SMALL DELTA * 03B4 GREEK SMALL LETTER DELTA * 1D60 MODIFIER LETTER SMALL GREEK PHI * 03C6 GREEK SMALL LETTER PHI * 1D61 MODIFIER LETTER SMALL CHI * 03C7 GREEK SMALL LETTER CHI * 1D62 LATIN SUBSCRIPT SMALL LETTER I * 0069 LATIN SMALL LETTER I * 1D63 LATIN SUBSCRIPT SMALL LETTER R * 0072 LATIN SMALL LETTER R * 1D64 LATIN SUBSCRIPT SMALL LETTER U * 0075 LATIN SMALL LETTER U * 1D65 LATIN SUBSCRIPT SMALL LETTER V * 0076 LATIN SMALL LETTER V * 1D66 GREEK SUBSCRIPT SMALL LETTER BETA * 03B2 GREEK SMALL LETTER BETA * 1D67 GREEK SUBSCRIPT SMALL LETTER GAMMA * 03B3 GREEK SMALL LETTER GAMMA * 1D68 GREEK SUBSCRIPT SMALL LETTER RHO * 03C1 GREEK SMALL LETTER RHO * 1D69 GREEK SUBSCRIPT SMALL LETTER PHI * 03C6 GREEK SMALL LETTER PHI * 1D6A GREEK SUBSCRIPT SMALL LETTER CHI * 03C7 GREEK SMALL LETTER CHI * 1D78 MODIFIER LETTER CYRILLIC EN * 043D CYRILLIC SMALL LETTER EN * 1D9B MODIFIER LETTER SMALL TURNED ALPHA * 0252 LATIN SMALL LETTER TURNED ALPHA * 1D9C MODIFIER LETTER SMALL C * 0063 LATIN SMALL LETTER C * 1D9D MODIFIER LETTER SMALL C WITH CURL * 0255 LATIN SMALL LETTER C WITH CURL * 1D9E MODIFIER LETTER SMALL ETH * 00F0 LATIN SMALL LETTER ETH * 1D9F MODIFIER LETTER SMALL REVERSED OPEN E * 025C LATIN SMALL LETTER REVERSED OPEN E * 1DA0 MODIFIER LETTER SMALL F * 0066 LATIN SMALL LETTER F * 1DA1 MODIFIER LETTER SMALL DOTLESS J WITH STROKE * 025F LATIN SMALL LETTER DOTLESS J WITH STROKE * 1DA2 MODIFIER LETTER SMALL SCRIPT G * 0261 LATIN SMALL LETTER SCRIPT G * 1DA3 MODIFIER LETTER SMALL TURNED H * 0265 LATIN SMALL LETTER TURNED H * 1DA4 MODIFIER LETTER SMALL I WITH STROKE * 0268 LATIN SMALL LETTER I WITH STROKE * 1DA5 MODIFIER LETTER SMALL IOTA * 0269 LATIN SMALL LETTER IOTA * 1DA6 MODIFIER LETTER SMALL CAPITAL I * 026A LATIN LETTER SMALL CAPITAL I * 1DA7 MODIFIER LETTER SMALL CAPITAL I WITH STROKE * 1D7B LATIN SMALL CAPITAL LETTER I WITH STROKE * 1DA8 MODIFIER LETTER SMALL J WITH CROSSED-TAIL * 029D LATIN SMALL LETTER J WITH CROSSED-TAIL * 1DA9 MODIFIER LETTER SMALL L WITH RETROFLEX HOOK * 026D LATIN SMALL LETTER L WITH RETROFLEX HOOK * 1DAA MODIFIER LETTER SMALL L WITH PALATAL HOOK * 1D85 LATIN SMALL LETTER L WITH PALATAL HOOK * 1DAB MODIFIER LETTER SMALL CAPITAL L * 029F LATIN LETTER SMALL CAPITAL L * 1DAC MODIFIER LETTER SMALL M WITH HOOK * 0271 LATIN SMALL LETTER M WITH HOOK * 1DAD MODIFIER LETTER SMALL TURNED M WITH LONG LEG * 0270 LATIN SMALL LETTER TURNED M WITH LONG LEG * 1DAE MODIFIER LETTER SMALL N WITH LEFT HOOK * 0272 LATIN SMALL LETTER N WITH LEFT HOOK * 1DAF MODIFIER LETTER SMALL N WITH RETROFLEX HOOK * 0273 LATIN SMALL LETTER N WITH RETROFLEX HOOK * 1DB0 MODIFIER LETTER SMALL CAPITAL N * 0274 LATIN LETTER SMALL CAPITAL N * 1DB1 MODIFIER LETTER SMALL BARRED O * 0275 LATIN SMALL LETTER BARRED O * 1DB2 MODIFIER LETTER SMALL PHI * 0278 LATIN SMALL LETTER PHI * 1DB3 MODIFIER LETTER SMALL S WITH HOOK * 0282 LATIN SMALL LETTER S WITH HOOK * 1DB4 MODIFIER LETTER SMALL ESH * 0283 LATIN SMALL LETTER ESH * 1DB5 MODIFIER LETTER SMALL T WITH PALATAL HOOK * 01AB LATIN SMALL LETTER T WITH PALATAL HOOK * 1DB6 MODIFIER LETTER SMALL U BAR * 0289 LATIN SMALL LETTER U BAR * 1DB7 MODIFIER LETTER SMALL UPSILON * 028A LATIN SMALL LETTER UPSILON * 1DB8 MODIFIER LETTER SMALL CAPITAL U * 1D1C LATIN LETTER SMALL CAPITAL U * 1DB9 MODIFIER LETTER SMALL V WITH HOOK * 028B LATIN SMALL LETTER V WITH HOOK * 1DBA MODIFIER LETTER SMALL TURNED V * 028C LATIN SMALL LETTER TURNED V * 1DBB MODIFIER LETTER SMALL Z * 007A LATIN SMALL LETTER Z * 1DBC MODIFIER LETTER SMALL Z WITH RETROFLEX HOOK * 0290 LATIN SMALL LETTER Z WITH RETROFLEX HOOK * 1DBD MODIFIER LETTER SMALL Z WITH CURL * 0291 LATIN SMALL LETTER Z WITH CURL * 1DBE MODIFIER LETTER SMALL EZH * 0292 LATIN SMALL LETTER EZH * 1DBF MODIFIER LETTER SMALL THETA * 03B8 GREEK SMALL LETTER THETA * 1DC0 COMBINING DOTTED GRAVE ACCENT * 0000 * 1DC1 COMBINING DOTTED ACUTE ACCENT * 0000 * 1DC2 COMBINING SNAKE BELOW * 0000 * 1DC3 COMBINING SUSPENSION MARK * 0000 * 1DC4 COMBINING MACRON-ACUTE * 0000 * 1DC5 COMBINING GRAVE-MACRON * 0000 * 1DC6 COMBINING MACRON-GRAVE * 0000 * 1DC7 COMBINING ACUTE-MACRON * 0000 * 1DC8 COMBINING GRAVE-ACUTE-GRAVE * 0000 * 1DC9 COMBINING ACUTE-GRAVE-ACUTE * 0000 * 1DCA COMBINING LATIN SMALL LETTER R BELOW * 0000 * 1DCB COMBINING BREVE-MACRON * 0000 * 1DCC COMBINING MACRON-BREVE * 0000 * 1DCD COMBINING DOUBLE CIRCUMFLEX ABOVE * 0000 * 1DCE COMBINING OGONEK ABOVE * 0000 * 1DCF COMBINING ZIGZAG BELOW * 0000 * 1DD0 COMBINING IS BELOW * 0000 * 1DD1 COMBINING UR ABOVE * 0000 * 1DD2 COMBINING US ABOVE * 0000 * 1DD3 COMBINING LATIN SMALL LETTER FLATTENED OPEN A ABOVE * 0000 * 1DD4 COMBINING LATIN SMALL LETTER AE * 0000 * 1DD5 COMBINING LATIN SMALL LETTER AO * 0000 * 1DD6 COMBINING LATIN SMALL LETTER AV * 0000 * 1DD7 COMBINING LATIN SMALL LETTER C CEDILLA * 0000 * 1DD8 COMBINING LATIN SMALL LETTER INSULAR D * 0000 * 1DD9 COMBINING LATIN SMALL LETTER ETH * 0000 * 1DDA COMBINING LATIN SMALL LETTER G * 0000 * 1DDB COMBINING LATIN LETTER SMALL CAPITAL G * 0000 * 1DDC COMBINING LATIN SMALL LETTER K * 0000 * 1DDD COMBINING LATIN SMALL LETTER L * 0000 * 1DDE COMBINING LATIN LETTER SMALL CAPITAL L * 0000 * 1DDF COMBINING LATIN LETTER SMALL CAPITAL M * 0000 * 1DE0 COMBINING LATIN SMALL LETTER N * 0000 * 1DE1 COMBINING LATIN LETTER SMALL CAPITAL N * 0000 * 1DE2 COMBINING LATIN LETTER SMALL CAPITAL R * 0000 * 1DE3 COMBINING LATIN SMALL LETTER R ROTUNDA * 0000 * 1DE4 COMBINING LATIN SMALL LETTER S * 0000 * 1DE5 COMBINING LATIN SMALL LETTER LONG S * 0000 * 1DE6 COMBINING LATIN SMALL LETTER Z * 0000 * 1DE7 COMBINING LATIN SMALL LETTER ALPHA * 0000 * 1DE8 COMBINING LATIN SMALL LETTER B * 0000 * 1DE9 COMBINING LATIN SMALL LETTER BETA * 0000 * 1DEA COMBINING LATIN SMALL LETTER SCHWA * 0000 * 1DEB COMBINING LATIN SMALL LETTER F * 0000 * 1DEC COMBINING LATIN SMALL LETTER L WITH DOUBLE MIDDLE TILDE * 0000 * 1DED COMBINING LATIN SMALL LETTER O WITH LIGHT CENTRALIZATION STROKE * 0000 * 1DEE COMBINING LATIN SMALL LETTER P * 0000 * 1DEF COMBINING LATIN SMALL LETTER ESH * 0000 * 1DF0 COMBINING LATIN SMALL LETTER U WITH LIGHT CENTRALIZATION STROKE * 0000 * 1DF1 COMBINING LATIN SMALL LETTER W * 0000 * 1DF2 COMBINING LATIN SMALL LETTER A WITH DIAERESIS * 0000 * 1DF3 COMBINING LATIN SMALL LETTER O WITH DIAERESIS * 0000 * 1DF4 COMBINING LATIN SMALL LETTER U WITH DIAERESIS * 0000 * 1DF5 COMBINING UP TACK ABOVE * 0000 * 1DF6 COMBINING KAVYKA ABOVE RIGHT * 0000 * 1DF7 COMBINING KAVYKA ABOVE LEFT * 0000 * 1DF8 COMBINING DOT ABOVE LEFT * 0000 * 1DF9 COMBINING WIDE INVERTED BRIDGE BELOW * 0000 * 1DFA COMBINING DOT BELOW LEFT * 0000 * 1DFB COMBINING DELETION MARK * 0000 * 1DFC COMBINING DOUBLE INVERTED BREVE BELOW * 0000 * 1DFD COMBINING ALMOST EQUAL TO BELOW * 0000 * 1DFE COMBINING LEFT ARROWHEAD ABOVE * 0000 * 1DFF COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW * 0000 * 1E00 LATIN CAPITAL LETTER A WITH RING BELOW * 0041 LATIN CAPITAL LETTER A * 1E01 LATIN SMALL LETTER A WITH RING BELOW * 0061 LATIN SMALL LETTER A * 1E02 LATIN CAPITAL LETTER B WITH DOT ABOVE * 0042 LATIN CAPITAL LETTER B * 1E03 LATIN SMALL LETTER B WITH DOT ABOVE * 0062 LATIN SMALL LETTER B * 1E04 LATIN CAPITAL LETTER B WITH DOT BELOW * 0042 LATIN CAPITAL LETTER B * 1E05 LATIN SMALL LETTER B WITH DOT BELOW * 0062 LATIN SMALL LETTER B * 1E06 LATIN CAPITAL LETTER B WITH LINE BELOW * 0042 LATIN CAPITAL LETTER B * 1E07 LATIN SMALL LETTER B WITH LINE BELOW * 0062 LATIN SMALL LETTER B * 1E08 LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE * 0043 LATIN CAPITAL LETTER C * 1E09 LATIN SMALL LETTER C WITH CEDILLA AND ACUTE * 0063 LATIN SMALL LETTER C * 1E0A LATIN CAPITAL LETTER D WITH DOT ABOVE * 0044 LATIN CAPITAL LETTER D * 1E0B LATIN SMALL LETTER D WITH DOT ABOVE * 0064 LATIN SMALL LETTER D * 1E0C LATIN CAPITAL LETTER D WITH DOT BELOW * 0044 LATIN CAPITAL LETTER D * 1E0D LATIN SMALL LETTER D WITH DOT BELOW * 0064 LATIN SMALL LETTER D * 1E0E LATIN CAPITAL LETTER D WITH LINE BELOW * 0044 LATIN CAPITAL LETTER D * 1E0F LATIN SMALL LETTER D WITH LINE BELOW * 0064 LATIN SMALL LETTER D * 1E10 LATIN CAPITAL LETTER D WITH CEDILLA * 0044 LATIN CAPITAL LETTER D * 1E11 LATIN SMALL LETTER D WITH CEDILLA * 0064 LATIN SMALL LETTER D * 1E12 LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW * 0044 LATIN CAPITAL LETTER D * 1E13 LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW * 0064 LATIN SMALL LETTER D * 1E14 LATIN CAPITAL LETTER E WITH MACRON AND GRAVE * 0045 LATIN CAPITAL LETTER E * 1E15 LATIN SMALL LETTER E WITH MACRON AND GRAVE * 0065 LATIN SMALL LETTER E * 1E16 LATIN CAPITAL LETTER E WITH MACRON AND ACUTE * 0045 LATIN CAPITAL LETTER E * 1E17 LATIN SMALL LETTER E WITH MACRON AND ACUTE * 0065 LATIN SMALL LETTER E * 1E18 LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW * 0045 LATIN CAPITAL LETTER E * 1E19 LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW * 0065 LATIN SMALL LETTER E * 1E1A LATIN CAPITAL LETTER E WITH TILDE BELOW * 0045 LATIN CAPITAL LETTER E * 1E1B LATIN SMALL LETTER E WITH TILDE BELOW * 0065 LATIN SMALL LETTER E * 1E1C LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE * 0045 LATIN CAPITAL LETTER E * 1E1D LATIN SMALL LETTER E WITH CEDILLA AND BREVE * 0065 LATIN SMALL LETTER E * 1E1E LATIN CAPITAL LETTER F WITH DOT ABOVE * 0046 LATIN CAPITAL LETTER F * 1E1F LATIN SMALL LETTER F WITH DOT ABOVE * 0066 LATIN SMALL LETTER F * 1E20 LATIN CAPITAL LETTER G WITH MACRON * 0047 LATIN CAPITAL LETTER G * 1E21 LATIN SMALL LETTER G WITH MACRON * 0067 LATIN SMALL LETTER G * 1E22 LATIN CAPITAL LETTER H WITH DOT ABOVE * 0048 LATIN CAPITAL LETTER H * 1E23 LATIN SMALL LETTER H WITH DOT ABOVE * 0068 LATIN SMALL LETTER H * 1E24 LATIN CAPITAL LETTER H WITH DOT BELOW * 0048 LATIN CAPITAL LETTER H * 1E25 LATIN SMALL LETTER H WITH DOT BELOW * 0068 LATIN SMALL LETTER H * 1E26 LATIN CAPITAL LETTER H WITH DIAERESIS * 0048 LATIN CAPITAL LETTER H * 1E27 LATIN SMALL LETTER H WITH DIAERESIS * 0068 LATIN SMALL LETTER H * 1E28 LATIN CAPITAL LETTER H WITH CEDILLA * 0048 LATIN CAPITAL LETTER H * 1E29 LATIN SMALL LETTER H WITH CEDILLA * 0068 LATIN SMALL LETTER H * 1E2A LATIN CAPITAL LETTER H WITH BREVE BELOW * 0048 LATIN CAPITAL LETTER H * 1E2B LATIN SMALL LETTER H WITH BREVE BELOW * 0068 LATIN SMALL LETTER H * 1E2C LATIN CAPITAL LETTER I WITH TILDE BELOW * 0049 LATIN CAPITAL LETTER I * 1E2D LATIN SMALL LETTER I WITH TILDE BELOW * 0069 LATIN SMALL LETTER I * 1E2E LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE * 0049 LATIN CAPITAL LETTER I * 1E2F LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE * 0069 LATIN SMALL LETTER I * 1E30 LATIN CAPITAL LETTER K WITH ACUTE * 004B LATIN CAPITAL LETTER K * 1E31 LATIN SMALL LETTER K WITH ACUTE * 006B LATIN SMALL LETTER K * 1E32 LATIN CAPITAL LETTER K WITH DOT BELOW * 004B LATIN CAPITAL LETTER K * 1E33 LATIN SMALL LETTER K WITH DOT BELOW * 006B LATIN SMALL LETTER K * 1E34 LATIN CAPITAL LETTER K WITH LINE BELOW * 004B LATIN CAPITAL LETTER K * 1E35 LATIN SMALL LETTER K WITH LINE BELOW * 006B LATIN SMALL LETTER K * 1E36 LATIN CAPITAL LETTER L WITH DOT BELOW * 004C LATIN CAPITAL LETTER L * 1E37 LATIN SMALL LETTER L WITH DOT BELOW * 006C LATIN SMALL LETTER L * 1E38 LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON * 004C LATIN CAPITAL LETTER L * 1E39 LATIN SMALL LETTER L WITH DOT BELOW AND MACRON * 006C LATIN SMALL LETTER L * 1E3A LATIN CAPITAL LETTER L WITH LINE BELOW * 004C LATIN CAPITAL LETTER L * 1E3B LATIN SMALL LETTER L WITH LINE BELOW * 006C LATIN SMALL LETTER L * 1E3C LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW * 004C LATIN CAPITAL LETTER L * 1E3D LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW * 006C LATIN SMALL LETTER L * 1E3E LATIN CAPITAL LETTER M WITH ACUTE * 004D LATIN CAPITAL LETTER M * 1E3F LATIN SMALL LETTER M WITH ACUTE * 006D LATIN SMALL LETTER M * 1E40 LATIN CAPITAL LETTER M WITH DOT ABOVE * 004D LATIN CAPITAL LETTER M * 1E41 LATIN SMALL LETTER M WITH DOT ABOVE * 006D LATIN SMALL LETTER M * 1E42 LATIN CAPITAL LETTER M WITH DOT BELOW * 004D LATIN CAPITAL LETTER M * 1E43 LATIN SMALL LETTER M WITH DOT BELOW * 006D LATIN SMALL LETTER M * 1E44 LATIN CAPITAL LETTER N WITH DOT ABOVE * 004E LATIN CAPITAL LETTER N * 1E45 LATIN SMALL LETTER N WITH DOT ABOVE * 006E LATIN SMALL LETTER N * 1E46 LATIN CAPITAL LETTER N WITH DOT BELOW * 004E LATIN CAPITAL LETTER N * 1E47 LATIN SMALL LETTER N WITH DOT BELOW * 006E LATIN SMALL LETTER N * 1E48 LATIN CAPITAL LETTER N WITH LINE BELOW * 004E LATIN CAPITAL LETTER N * 1E49 LATIN SMALL LETTER N WITH LINE BELOW * 006E LATIN SMALL LETTER N * 1E4A LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW * 004E LATIN CAPITAL LETTER N * 1E4B LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW * 006E LATIN SMALL LETTER N * 1E4C LATIN CAPITAL LETTER O WITH TILDE AND ACUTE * 004F LATIN CAPITAL LETTER O * 1E4D LATIN SMALL LETTER O WITH TILDE AND ACUTE * 006F LATIN SMALL LETTER O * 1E4E LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS * 004F LATIN CAPITAL LETTER O * 1E4F LATIN SMALL LETTER O WITH TILDE AND DIAERESIS * 006F LATIN SMALL LETTER O * 1E50 LATIN CAPITAL LETTER O WITH MACRON AND GRAVE * 004F LATIN CAPITAL LETTER O * 1E51 LATIN SMALL LETTER O WITH MACRON AND GRAVE * 006F LATIN SMALL LETTER O * 1E52 LATIN CAPITAL LETTER O WITH MACRON AND ACUTE * 004F LATIN CAPITAL LETTER O * 1E53 LATIN SMALL LETTER O WITH MACRON AND ACUTE * 006F LATIN SMALL LETTER O * 1E54 LATIN CAPITAL LETTER P WITH ACUTE * 0050 LATIN CAPITAL LETTER P * 1E55 LATIN SMALL LETTER P WITH ACUTE * 0070 LATIN SMALL LETTER P * 1E56 LATIN CAPITAL LETTER P WITH DOT ABOVE * 0050 LATIN CAPITAL LETTER P * 1E57 LATIN SMALL LETTER P WITH DOT ABOVE * 0070 LATIN SMALL LETTER P * 1E58 LATIN CAPITAL LETTER R WITH DOT ABOVE * 0052 LATIN CAPITAL LETTER R * 1E59 LATIN SMALL LETTER R WITH DOT ABOVE * 0072 LATIN SMALL LETTER R * 1E5A LATIN CAPITAL LETTER R WITH DOT BELOW * 0052 LATIN CAPITAL LETTER R * 1E5B LATIN SMALL LETTER R WITH DOT BELOW * 0072 LATIN SMALL LETTER R * 1E5C LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON * 0052 LATIN CAPITAL LETTER R * 1E5D LATIN SMALL LETTER R WITH DOT BELOW AND MACRON * 0072 LATIN SMALL LETTER R * 1E5E LATIN CAPITAL LETTER R WITH LINE BELOW * 0052 LATIN CAPITAL LETTER R * 1E5F LATIN SMALL LETTER R WITH LINE BELOW * 0072 LATIN SMALL LETTER R * 1E60 LATIN CAPITAL LETTER S WITH DOT ABOVE * 0053 LATIN CAPITAL LETTER S * 1E61 LATIN SMALL LETTER S WITH DOT ABOVE * 0073 LATIN SMALL LETTER S * 1E62 LATIN CAPITAL LETTER S WITH DOT BELOW * 0053 LATIN CAPITAL LETTER S * 1E63 LATIN SMALL LETTER S WITH DOT BELOW * 0073 LATIN SMALL LETTER S * 1E64 LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE * 0053 LATIN CAPITAL LETTER S * 1E65 LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE * 0073 LATIN SMALL LETTER S * 1E66 LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE * 0053 LATIN CAPITAL LETTER S * 1E67 LATIN SMALL LETTER S WITH CARON AND DOT ABOVE * 0073 LATIN SMALL LETTER S * 1E68 LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE * 0053 LATIN CAPITAL LETTER S * 1E69 LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE * 0073 LATIN SMALL LETTER S * 1E6A LATIN CAPITAL LETTER T WITH DOT ABOVE * 0054 LATIN CAPITAL LETTER T * 1E6B LATIN SMALL LETTER T WITH DOT ABOVE * 0074 LATIN SMALL LETTER T * 1E6C LATIN CAPITAL LETTER T WITH DOT BELOW * 0054 LATIN CAPITAL LETTER T * 1E6D LATIN SMALL LETTER T WITH DOT BELOW * 0074 LATIN SMALL LETTER T * 1E6E LATIN CAPITAL LETTER T WITH LINE BELOW * 0054 LATIN CAPITAL LETTER T * 1E6F LATIN SMALL LETTER T WITH LINE BELOW * 0074 LATIN SMALL LETTER T * 1E70 LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW * 0054 LATIN CAPITAL LETTER T * 1E71 LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW * 0074 LATIN SMALL LETTER T * 1E72 LATIN CAPITAL LETTER U WITH DIAERESIS BELOW * 0055 LATIN CAPITAL LETTER U * 1E73 LATIN SMALL LETTER U WITH DIAERESIS BELOW * 0075 LATIN SMALL LETTER U * 1E74 LATIN CAPITAL LETTER U WITH TILDE BELOW * 0055 LATIN CAPITAL LETTER U * 1E75 LATIN SMALL LETTER U WITH TILDE BELOW * 0075 LATIN SMALL LETTER U * 1E76 LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW * 0055 LATIN CAPITAL LETTER U * 1E77 LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW * 0075 LATIN SMALL LETTER U * 1E78 LATIN CAPITAL LETTER U WITH TILDE AND ACUTE * 0055 LATIN CAPITAL LETTER U * 1E79 LATIN SMALL LETTER U WITH TILDE AND ACUTE * 0075 LATIN SMALL LETTER U * 1E7A LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS * 0055 LATIN CAPITAL LETTER U * 1E7B LATIN SMALL LETTER U WITH MACRON AND DIAERESIS * 0075 LATIN SMALL LETTER U * 1E7C LATIN CAPITAL LETTER V WITH TILDE * 0056 LATIN CAPITAL LETTER V * 1E7D LATIN SMALL LETTER V WITH TILDE * 0076 LATIN SMALL LETTER V * 1E7E LATIN CAPITAL LETTER V WITH DOT BELOW * 0056 LATIN CAPITAL LETTER V * 1E7F LATIN SMALL LETTER V WITH DOT BELOW * 0076 LATIN SMALL LETTER V * 1E80 LATIN CAPITAL LETTER W WITH GRAVE * 0057 LATIN CAPITAL LETTER W * 1E81 LATIN SMALL LETTER W WITH GRAVE * 0077 LATIN SMALL LETTER W * 1E82 LATIN CAPITAL LETTER W WITH ACUTE * 0057 LATIN CAPITAL LETTER W * 1E83 LATIN SMALL LETTER W WITH ACUTE * 0077 LATIN SMALL LETTER W * 1E84 LATIN CAPITAL LETTER W WITH DIAERESIS * 0057 LATIN CAPITAL LETTER W * 1E85 LATIN SMALL LETTER W WITH DIAERESIS * 0077 LATIN SMALL LETTER W * 1E86 LATIN CAPITAL LETTER W WITH DOT ABOVE * 0057 LATIN CAPITAL LETTER W * 1E87 LATIN SMALL LETTER W WITH DOT ABOVE * 0077 LATIN SMALL LETTER W * 1E88 LATIN CAPITAL LETTER W WITH DOT BELOW * 0057 LATIN CAPITAL LETTER W * 1E89 LATIN SMALL LETTER W WITH DOT BELOW * 0077 LATIN SMALL LETTER W * 1E8A LATIN CAPITAL LETTER X WITH DOT ABOVE * 0058 LATIN CAPITAL LETTER X * 1E8B LATIN SMALL LETTER X WITH DOT ABOVE * 0078 LATIN SMALL LETTER X * 1E8C LATIN CAPITAL LETTER X WITH DIAERESIS * 0058 LATIN CAPITAL LETTER X * 1E8D LATIN SMALL LETTER X WITH DIAERESIS * 0078 LATIN SMALL LETTER X * 1E8E LATIN CAPITAL LETTER Y WITH DOT ABOVE * 0059 LATIN CAPITAL LETTER Y * 1E8F LATIN SMALL LETTER Y WITH DOT ABOVE * 0079 LATIN SMALL LETTER Y * 1E90 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX * 005A LATIN CAPITAL LETTER Z * 1E91 LATIN SMALL LETTER Z WITH CIRCUMFLEX * 007A LATIN SMALL LETTER Z * 1E92 LATIN CAPITAL LETTER Z WITH DOT BELOW * 005A LATIN CAPITAL LETTER Z * 1E93 LATIN SMALL LETTER Z WITH DOT BELOW * 007A LATIN SMALL LETTER Z * 1E94 LATIN CAPITAL LETTER Z WITH LINE BELOW * 005A LATIN CAPITAL LETTER Z * 1E95 LATIN SMALL LETTER Z WITH LINE BELOW * 007A LATIN SMALL LETTER Z * 1E96 LATIN SMALL LETTER H WITH LINE BELOW * 0068 LATIN SMALL LETTER H * 1E97 LATIN SMALL LETTER T WITH DIAERESIS * 0074 LATIN SMALL LETTER T * 1E98 LATIN SMALL LETTER W WITH RING ABOVE * 0077 LATIN SMALL LETTER W * 1E99 LATIN SMALL LETTER Y WITH RING ABOVE * 0079 LATIN SMALL LETTER Y * 1E9A LATIN SMALL LETTER A WITH RIGHT HALF RING * 0061 LATIN SMALL LETTER A * 02BE MODIFIER LETTER RIGHT HALF RING * 1E9B LATIN SMALL LETTER LONG S WITH DOT ABOVE * 0073 LATIN SMALL LETTER S * 1EA0 LATIN CAPITAL LETTER A WITH DOT BELOW * 0041 LATIN CAPITAL LETTER A * 1EA1 LATIN SMALL LETTER A WITH DOT BELOW * 0061 LATIN SMALL LETTER A * 1EA2 LATIN CAPITAL LETTER A WITH HOOK ABOVE * 0041 LATIN CAPITAL LETTER A * 1EA3 LATIN SMALL LETTER A WITH HOOK ABOVE * 0061 LATIN SMALL LETTER A * 1EA4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE * 0041 LATIN CAPITAL LETTER A * 1EA5 LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE * 0061 LATIN SMALL LETTER A * 1EA6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE * 0041 LATIN CAPITAL LETTER A * 1EA7 LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE * 0061 LATIN SMALL LETTER A * 1EA8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE * 0041 LATIN CAPITAL LETTER A * 1EA9 LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE * 0061 LATIN SMALL LETTER A * 1EAA LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE * 0041 LATIN CAPITAL LETTER A * 1EAB LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE * 0061 LATIN SMALL LETTER A * 1EAC LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW * 0041 LATIN CAPITAL LETTER A * 1EAD LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW * 0061 LATIN SMALL LETTER A * 1EAE LATIN CAPITAL LETTER A WITH BREVE AND ACUTE * 0041 LATIN CAPITAL LETTER A * 1EAF LATIN SMALL LETTER A WITH BREVE AND ACUTE * 0061 LATIN SMALL LETTER A * 1EB0 LATIN CAPITAL LETTER A WITH BREVE AND GRAVE * 0041 LATIN CAPITAL LETTER A * 1EB1 LATIN SMALL LETTER A WITH BREVE AND GRAVE * 0061 LATIN SMALL LETTER A * 1EB2 LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE * 0041 LATIN CAPITAL LETTER A * 1EB3 LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE * 0061 LATIN SMALL LETTER A * 1EB4 LATIN CAPITAL LETTER A WITH BREVE AND TILDE * 0041 LATIN CAPITAL LETTER A * 1EB5 LATIN SMALL LETTER A WITH BREVE AND TILDE * 0061 LATIN SMALL LETTER A * 1EB6 LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW * 0041 LATIN CAPITAL LETTER A * 1EB7 LATIN SMALL LETTER A WITH BREVE AND DOT BELOW * 0061 LATIN SMALL LETTER A * 1EB8 LATIN CAPITAL LETTER E WITH DOT BELOW * 0045 LATIN CAPITAL LETTER E * 1EB9 LATIN SMALL LETTER E WITH DOT BELOW * 0065 LATIN SMALL LETTER E * 1EBA LATIN CAPITAL LETTER E WITH HOOK ABOVE * 0045 LATIN CAPITAL LETTER E * 1EBB LATIN SMALL LETTER E WITH HOOK ABOVE * 0065 LATIN SMALL LETTER E * 1EBC LATIN CAPITAL LETTER E WITH TILDE * 0045 LATIN CAPITAL LETTER E * 1EBD LATIN SMALL LETTER E WITH TILDE * 0065 LATIN SMALL LETTER E * 1EBE LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE * 0045 LATIN CAPITAL LETTER E * 1EBF LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE * 0065 LATIN SMALL LETTER E * 1EC0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE * 0045 LATIN CAPITAL LETTER E * 1EC1 LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE * 0065 LATIN SMALL LETTER E * 1EC2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE * 0045 LATIN CAPITAL LETTER E * 1EC3 LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE * 0065 LATIN SMALL LETTER E * 1EC4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE * 0045 LATIN CAPITAL LETTER E * 1EC5 LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE * 0065 LATIN SMALL LETTER E * 1EC6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW * 0045 LATIN CAPITAL LETTER E * 1EC7 LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW * 0065 LATIN SMALL LETTER E * 1EC8 LATIN CAPITAL LETTER I WITH HOOK ABOVE * 0049 LATIN CAPITAL LETTER I * 1EC9 LATIN SMALL LETTER I WITH HOOK ABOVE * 0069 LATIN SMALL LETTER I * 1ECA LATIN CAPITAL LETTER I WITH DOT BELOW * 0049 LATIN CAPITAL LETTER I * 1ECB LATIN SMALL LETTER I WITH DOT BELOW * 0069 LATIN SMALL LETTER I * 1ECC LATIN CAPITAL LETTER O WITH DOT BELOW * 004F LATIN CAPITAL LETTER O * 1ECD LATIN SMALL LETTER O WITH DOT BELOW * 006F LATIN SMALL LETTER O * 1ECE LATIN CAPITAL LETTER O WITH HOOK ABOVE * 004F LATIN CAPITAL LETTER O * 1ECF LATIN SMALL LETTER O WITH HOOK ABOVE * 006F LATIN SMALL LETTER O * 1ED0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE * 004F LATIN CAPITAL LETTER O * 1ED1 LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE * 006F LATIN SMALL LETTER O * 1ED2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE * 004F LATIN CAPITAL LETTER O * 1ED3 LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE * 006F LATIN SMALL LETTER O * 1ED4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE * 004F LATIN CAPITAL LETTER O * 1ED5 LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE * 006F LATIN SMALL LETTER O * 1ED6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE * 004F LATIN CAPITAL LETTER O * 1ED7 LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE * 006F LATIN SMALL LETTER O * 1ED8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW * 004F LATIN CAPITAL LETTER O * 1ED9 LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW * 006F LATIN SMALL LETTER O * 1EDA LATIN CAPITAL LETTER O WITH HORN AND ACUTE * 004F LATIN CAPITAL LETTER O * 1EDB LATIN SMALL LETTER O WITH HORN AND ACUTE * 006F LATIN SMALL LETTER O * 1EDC LATIN CAPITAL LETTER O WITH HORN AND GRAVE * 004F LATIN CAPITAL LETTER O * 1EDD LATIN SMALL LETTER O WITH HORN AND GRAVE * 006F LATIN SMALL LETTER O * 1EDE LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE * 004F LATIN CAPITAL LETTER O * 1EDF LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE * 006F LATIN SMALL LETTER O * 1EE0 LATIN CAPITAL LETTER O WITH HORN AND TILDE * 004F LATIN CAPITAL LETTER O * 1EE1 LATIN SMALL LETTER O WITH HORN AND TILDE * 006F LATIN SMALL LETTER O * 1EE2 LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW * 004F LATIN CAPITAL LETTER O * 1EE3 LATIN SMALL LETTER O WITH HORN AND DOT BELOW * 006F LATIN SMALL LETTER O * 1EE4 LATIN CAPITAL LETTER U WITH DOT BELOW * 0055 LATIN CAPITAL LETTER U * 1EE5 LATIN SMALL LETTER U WITH DOT BELOW * 0075 LATIN SMALL LETTER U * 1EE6 LATIN CAPITAL LETTER U WITH HOOK ABOVE * 0055 LATIN CAPITAL LETTER U * 1EE7 LATIN SMALL LETTER U WITH HOOK ABOVE * 0075 LATIN SMALL LETTER U * 1EE8 LATIN CAPITAL LETTER U WITH HORN AND ACUTE * 0055 LATIN CAPITAL LETTER U * 1EE9 LATIN SMALL LETTER U WITH HORN AND ACUTE * 0075 LATIN SMALL LETTER U * 1EEA LATIN CAPITAL LETTER U WITH HORN AND GRAVE * 0055 LATIN CAPITAL LETTER U * 1EEB LATIN SMALL LETTER U WITH HORN AND GRAVE * 0075 LATIN SMALL LETTER U * 1EEC LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE * 0055 LATIN CAPITAL LETTER U * 1EED LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE * 0075 LATIN SMALL LETTER U * 1EEE LATIN CAPITAL LETTER U WITH HORN AND TILDE * 0055 LATIN CAPITAL LETTER U * 1EEF LATIN SMALL LETTER U WITH HORN AND TILDE * 0075 LATIN SMALL LETTER U * 1EF0 LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW * 0055 LATIN CAPITAL LETTER U * 1EF1 LATIN SMALL LETTER U WITH HORN AND DOT BELOW * 0075 LATIN SMALL LETTER U * 1EF2 LATIN CAPITAL LETTER Y WITH GRAVE * 0059 LATIN CAPITAL LETTER Y * 1EF3 LATIN SMALL LETTER Y WITH GRAVE * 0079 LATIN SMALL LETTER Y * 1EF4 LATIN CAPITAL LETTER Y WITH DOT BELOW * 0059 LATIN CAPITAL LETTER Y * 1EF5 LATIN SMALL LETTER Y WITH DOT BELOW * 0079 LATIN SMALL LETTER Y * 1EF6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE * 0059 LATIN CAPITAL LETTER Y * 1EF7 LATIN SMALL LETTER Y WITH HOOK ABOVE * 0079 LATIN SMALL LETTER Y * 1EF8 LATIN CAPITAL LETTER Y WITH TILDE * 0059 LATIN CAPITAL LETTER Y * 1EF9 LATIN SMALL LETTER Y WITH TILDE * 0079 LATIN SMALL LETTER Y * 1F00 GREEK SMALL LETTER ALPHA WITH PSILI * 03B1 GREEK SMALL LETTER ALPHA * 1F01 GREEK SMALL LETTER ALPHA WITH DASIA * 03B1 GREEK SMALL LETTER ALPHA * 1F02 GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA * 03B1 GREEK SMALL LETTER ALPHA * 1F03 GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA * 03B1 GREEK SMALL LETTER ALPHA * 1F04 GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA * 03B1 GREEK SMALL LETTER ALPHA * 1F05 GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA * 03B1 GREEK SMALL LETTER ALPHA * 1F06 GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F07 GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F08 GREEK CAPITAL LETTER ALPHA WITH PSILI * 0391 GREEK CAPITAL LETTER ALPHA * 1F09 GREEK CAPITAL LETTER ALPHA WITH DASIA * 0391 GREEK CAPITAL LETTER ALPHA * 1F0A GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA * 0391 GREEK CAPITAL LETTER ALPHA * 1F0B GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA * 0391 GREEK CAPITAL LETTER ALPHA * 1F0C GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA * 0391 GREEK CAPITAL LETTER ALPHA * 1F0D GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA * 0391 GREEK CAPITAL LETTER ALPHA * 1F0E GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F0F GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F10 GREEK SMALL LETTER EPSILON WITH PSILI * 03B5 GREEK SMALL LETTER EPSILON * 1F11 GREEK SMALL LETTER EPSILON WITH DASIA * 03B5 GREEK SMALL LETTER EPSILON * 1F12 GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA * 03B5 GREEK SMALL LETTER EPSILON * 1F13 GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA * 03B5 GREEK SMALL LETTER EPSILON * 1F14 GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA * 03B5 GREEK SMALL LETTER EPSILON * 1F15 GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA * 03B5 GREEK SMALL LETTER EPSILON * 1F18 GREEK CAPITAL LETTER EPSILON WITH PSILI * 0395 GREEK CAPITAL LETTER EPSILON * 1F19 GREEK CAPITAL LETTER EPSILON WITH DASIA * 0395 GREEK CAPITAL LETTER EPSILON * 1F1A GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA * 0395 GREEK CAPITAL LETTER EPSILON * 1F1B GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA * 0395 GREEK CAPITAL LETTER EPSILON * 1F1C GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA * 0395 GREEK CAPITAL LETTER EPSILON * 1F1D GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA * 0395 GREEK CAPITAL LETTER EPSILON * 1F20 GREEK SMALL LETTER ETA WITH PSILI * 03B7 GREEK SMALL LETTER ETA * 1F21 GREEK SMALL LETTER ETA WITH DASIA * 03B7 GREEK SMALL LETTER ETA * 1F22 GREEK SMALL LETTER ETA WITH PSILI AND VARIA * 03B7 GREEK SMALL LETTER ETA * 1F23 GREEK SMALL LETTER ETA WITH DASIA AND VARIA * 03B7 GREEK SMALL LETTER ETA * 1F24 GREEK SMALL LETTER ETA WITH PSILI AND OXIA * 03B7 GREEK SMALL LETTER ETA * 1F25 GREEK SMALL LETTER ETA WITH DASIA AND OXIA * 03B7 GREEK SMALL LETTER ETA * 1F26 GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI * 03B7 GREEK SMALL LETTER ETA * 1F27 GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI * 03B7 GREEK SMALL LETTER ETA * 1F28 GREEK CAPITAL LETTER ETA WITH PSILI * 0397 GREEK CAPITAL LETTER ETA * 1F29 GREEK CAPITAL LETTER ETA WITH DASIA * 0397 GREEK CAPITAL LETTER ETA * 1F2A GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA * 0397 GREEK CAPITAL LETTER ETA * 1F2B GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA * 0397 GREEK CAPITAL LETTER ETA * 1F2C GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA * 0397 GREEK CAPITAL LETTER ETA * 1F2D GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA * 0397 GREEK CAPITAL LETTER ETA * 1F2E GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI * 0397 GREEK CAPITAL LETTER ETA * 1F2F GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI * 0397 GREEK CAPITAL LETTER ETA * 1F30 GREEK SMALL LETTER IOTA WITH PSILI * 03B9 GREEK SMALL LETTER IOTA * 1F31 GREEK SMALL LETTER IOTA WITH DASIA * 03B9 GREEK SMALL LETTER IOTA * 1F32 GREEK SMALL LETTER IOTA WITH PSILI AND VARIA * 03B9 GREEK SMALL LETTER IOTA * 1F33 GREEK SMALL LETTER IOTA WITH DASIA AND VARIA * 03B9 GREEK SMALL LETTER IOTA * 1F34 GREEK SMALL LETTER IOTA WITH PSILI AND OXIA * 03B9 GREEK SMALL LETTER IOTA * 1F35 GREEK SMALL LETTER IOTA WITH DASIA AND OXIA * 03B9 GREEK SMALL LETTER IOTA * 1F36 GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI * 03B9 GREEK SMALL LETTER IOTA * 1F37 GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI * 03B9 GREEK SMALL LETTER IOTA * 1F38 GREEK CAPITAL LETTER IOTA WITH PSILI * 0399 GREEK CAPITAL LETTER IOTA * 1F39 GREEK CAPITAL LETTER IOTA WITH DASIA * 0399 GREEK CAPITAL LETTER IOTA * 1F3A GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA * 0399 GREEK CAPITAL LETTER IOTA * 1F3B GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA * 0399 GREEK CAPITAL LETTER IOTA * 1F3C GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA * 0399 GREEK CAPITAL LETTER IOTA * 1F3D GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA * 0399 GREEK CAPITAL LETTER IOTA * 1F3E GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI * 0399 GREEK CAPITAL LETTER IOTA * 1F3F GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI * 0399 GREEK CAPITAL LETTER IOTA * 1F40 GREEK SMALL LETTER OMICRON WITH PSILI * 03BF GREEK SMALL LETTER OMICRON * 1F41 GREEK SMALL LETTER OMICRON WITH DASIA * 03BF GREEK SMALL LETTER OMICRON * 1F42 GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA * 03BF GREEK SMALL LETTER OMICRON * 1F43 GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA * 03BF GREEK SMALL LETTER OMICRON * 1F44 GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA * 03BF GREEK SMALL LETTER OMICRON * 1F45 GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA * 03BF GREEK SMALL LETTER OMICRON * 1F48 GREEK CAPITAL LETTER OMICRON WITH PSILI * 039F GREEK CAPITAL LETTER OMICRON * 1F49 GREEK CAPITAL LETTER OMICRON WITH DASIA * 039F GREEK CAPITAL LETTER OMICRON * 1F4A GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA * 039F GREEK CAPITAL LETTER OMICRON * 1F4B GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA * 039F GREEK CAPITAL LETTER OMICRON * 1F4C GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA * 039F GREEK CAPITAL LETTER OMICRON * 1F4D GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA * 039F GREEK CAPITAL LETTER OMICRON * 1F50 GREEK SMALL LETTER UPSILON WITH PSILI * 03C5 GREEK SMALL LETTER UPSILON * 1F51 GREEK SMALL LETTER UPSILON WITH DASIA * 03C5 GREEK SMALL LETTER UPSILON * 1F52 GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA * 03C5 GREEK SMALL LETTER UPSILON * 1F53 GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA * 03C5 GREEK SMALL LETTER UPSILON * 1F54 GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA * 03C5 GREEK SMALL LETTER UPSILON * 1F55 GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA * 03C5 GREEK SMALL LETTER UPSILON * 1F56 GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI * 03C5 GREEK SMALL LETTER UPSILON * 1F57 GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI * 03C5 GREEK SMALL LETTER UPSILON * 1F59 GREEK CAPITAL LETTER UPSILON WITH DASIA * 03A5 GREEK CAPITAL LETTER UPSILON * 1F5B GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA * 03A5 GREEK CAPITAL LETTER UPSILON * 1F5D GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA * 03A5 GREEK CAPITAL LETTER UPSILON * 1F5F GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI * 03A5 GREEK CAPITAL LETTER UPSILON * 1F60 GREEK SMALL LETTER OMEGA WITH PSILI * 03C9 GREEK SMALL LETTER OMEGA * 1F61 GREEK SMALL LETTER OMEGA WITH DASIA * 03C9 GREEK SMALL LETTER OMEGA * 1F62 GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA * 03C9 GREEK SMALL LETTER OMEGA * 1F63 GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA * 03C9 GREEK SMALL LETTER OMEGA * 1F64 GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA * 03C9 GREEK SMALL LETTER OMEGA * 1F65 GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA * 03C9 GREEK SMALL LETTER OMEGA * 1F66 GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI * 03C9 GREEK SMALL LETTER OMEGA * 1F67 GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI * 03C9 GREEK SMALL LETTER OMEGA * 1F68 GREEK CAPITAL LETTER OMEGA WITH PSILI * 03A9 GREEK CAPITAL LETTER OMEGA * 1F69 GREEK CAPITAL LETTER OMEGA WITH DASIA * 03A9 GREEK CAPITAL LETTER OMEGA * 1F6A GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA * 03A9 GREEK CAPITAL LETTER OMEGA * 1F6B GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA * 03A9 GREEK CAPITAL LETTER OMEGA * 1F6C GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA * 03A9 GREEK CAPITAL LETTER OMEGA * 1F6D GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA * 03A9 GREEK CAPITAL LETTER OMEGA * 1F6E GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1F6F GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1F70 GREEK SMALL LETTER ALPHA WITH VARIA * 03B1 GREEK SMALL LETTER ALPHA * 1F71 GREEK SMALL LETTER ALPHA WITH OXIA * 03B1 GREEK SMALL LETTER ALPHA * 1F72 GREEK SMALL LETTER EPSILON WITH VARIA * 03B5 GREEK SMALL LETTER EPSILON * 1F73 GREEK SMALL LETTER EPSILON WITH OXIA * 03B5 GREEK SMALL LETTER EPSILON * 1F74 GREEK SMALL LETTER ETA WITH VARIA * 03B7 GREEK SMALL LETTER ETA * 1F75 GREEK SMALL LETTER ETA WITH OXIA * 03B7 GREEK SMALL LETTER ETA * 1F76 GREEK SMALL LETTER IOTA WITH VARIA * 03B9 GREEK SMALL LETTER IOTA * 1F77 GREEK SMALL LETTER IOTA WITH OXIA * 03B9 GREEK SMALL LETTER IOTA * 1F78 GREEK SMALL LETTER OMICRON WITH VARIA * 03BF GREEK SMALL LETTER OMICRON * 1F79 GREEK SMALL LETTER OMICRON WITH OXIA * 03BF GREEK SMALL LETTER OMICRON * 1F7A GREEK SMALL LETTER UPSILON WITH VARIA * 03C5 GREEK SMALL LETTER UPSILON * 1F7B GREEK SMALL LETTER UPSILON WITH OXIA * 03C5 GREEK SMALL LETTER UPSILON * 1F7C GREEK SMALL LETTER OMEGA WITH VARIA * 03C9 GREEK SMALL LETTER OMEGA * 1F7D GREEK SMALL LETTER OMEGA WITH OXIA * 03C9 GREEK SMALL LETTER OMEGA * 1F80 GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F81 GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F82 GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F83 GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F84 GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F85 GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F86 GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F87 GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1F88 GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F89 GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F8A GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F8B GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F8C GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F8D GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F8E GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F8F GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1F90 GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1F91 GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1F92 GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1F93 GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1F94 GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1F95 GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1F96 GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1F97 GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1F98 GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1F99 GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1F9A GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1F9B GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1F9C GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1F9D GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1F9E GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1F9F GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1FA0 GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FA1 GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FA2 GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FA3 GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FA4 GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FA5 GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FA6 GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FA7 GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FA8 GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FA9 GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FAA GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FAB GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FAC GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FAD GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FAE GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FAF GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FB0 GREEK SMALL LETTER ALPHA WITH VRACHY * 03B1 GREEK SMALL LETTER ALPHA * 1FB1 GREEK SMALL LETTER ALPHA WITH MACRON * 03B1 GREEK SMALL LETTER ALPHA * 1FB2 GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1FB3 GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1FB4 GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1FB6 GREEK SMALL LETTER ALPHA WITH PERISPOMENI * 03B1 GREEK SMALL LETTER ALPHA * 1FB7 GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI * 03B1 GREEK SMALL LETTER ALPHA * 1FB8 GREEK CAPITAL LETTER ALPHA WITH VRACHY * 0391 GREEK CAPITAL LETTER ALPHA * 1FB9 GREEK CAPITAL LETTER ALPHA WITH MACRON * 0391 GREEK CAPITAL LETTER ALPHA * 1FBA GREEK CAPITAL LETTER ALPHA WITH VARIA * 0391 GREEK CAPITAL LETTER ALPHA * 1FBB GREEK CAPITAL LETTER ALPHA WITH OXIA * 0391 GREEK CAPITAL LETTER ALPHA * 1FBC GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI * 0391 GREEK CAPITAL LETTER ALPHA * 1FBD GREEK KORONIS * 0020 SPACE * 1FBE GREEK PROSGEGRAMMENI * 03B9 GREEK SMALL LETTER IOTA * 1FBF GREEK PSILI * 0020 SPACE * 1FC0 GREEK PERISPOMENI * 0020 SPACE * 1FC1 GREEK DIALYTIKA AND PERISPOMENI * 0020 SPACE * 1FC2 GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1FC3 GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1FC4 GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1FC6 GREEK SMALL LETTER ETA WITH PERISPOMENI * 03B7 GREEK SMALL LETTER ETA * 1FC7 GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI * 03B7 GREEK SMALL LETTER ETA * 1FC8 GREEK CAPITAL LETTER EPSILON WITH VARIA * 0395 GREEK CAPITAL LETTER EPSILON * 1FC9 GREEK CAPITAL LETTER EPSILON WITH OXIA * 0395 GREEK CAPITAL LETTER EPSILON * 1FCA GREEK CAPITAL LETTER ETA WITH VARIA * 0397 GREEK CAPITAL LETTER ETA * 1FCB GREEK CAPITAL LETTER ETA WITH OXIA * 0397 GREEK CAPITAL LETTER ETA * 1FCC GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI * 0397 GREEK CAPITAL LETTER ETA * 1FCD GREEK PSILI AND VARIA * 0020 SPACE * 1FCE GREEK PSILI AND OXIA * 0020 SPACE * 1FCF GREEK PSILI AND PERISPOMENI * 0020 SPACE * 1FD0 GREEK SMALL LETTER IOTA WITH VRACHY * 03B9 GREEK SMALL LETTER IOTA * 1FD1 GREEK SMALL LETTER IOTA WITH MACRON * 03B9 GREEK SMALL LETTER IOTA * 1FD2 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA * 03B9 GREEK SMALL LETTER IOTA * 1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA * 03B9 GREEK SMALL LETTER IOTA * 1FD6 GREEK SMALL LETTER IOTA WITH PERISPOMENI * 03B9 GREEK SMALL LETTER IOTA * 1FD7 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI * 03B9 GREEK SMALL LETTER IOTA * 1FD8 GREEK CAPITAL LETTER IOTA WITH VRACHY * 0399 GREEK CAPITAL LETTER IOTA * 1FD9 GREEK CAPITAL LETTER IOTA WITH MACRON * 0399 GREEK CAPITAL LETTER IOTA * 1FDA GREEK CAPITAL LETTER IOTA WITH VARIA * 0399 GREEK CAPITAL LETTER IOTA * 1FDB GREEK CAPITAL LETTER IOTA WITH OXIA * 0399 GREEK CAPITAL LETTER IOTA * 1FDD GREEK DASIA AND VARIA * 0020 SPACE * 1FDE GREEK DASIA AND OXIA * 0020 SPACE * 1FDF GREEK DASIA AND PERISPOMENI * 0020 SPACE * 1FE0 GREEK SMALL LETTER UPSILON WITH VRACHY * 03C5 GREEK SMALL LETTER UPSILON * 1FE1 GREEK SMALL LETTER UPSILON WITH MACRON * 03C5 GREEK SMALL LETTER UPSILON * 1FE2 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA * 03C5 GREEK SMALL LETTER UPSILON * 1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA * 03C5 GREEK SMALL LETTER UPSILON * 1FE4 GREEK SMALL LETTER RHO WITH PSILI * 03C1 GREEK SMALL LETTER RHO * 1FE5 GREEK SMALL LETTER RHO WITH DASIA * 03C1 GREEK SMALL LETTER RHO * 1FE6 GREEK SMALL LETTER UPSILON WITH PERISPOMENI * 03C5 GREEK SMALL LETTER UPSILON * 1FE7 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI * 03C5 GREEK SMALL LETTER UPSILON * 1FE8 GREEK CAPITAL LETTER UPSILON WITH VRACHY * 03A5 GREEK CAPITAL LETTER UPSILON * 1FE9 GREEK CAPITAL LETTER UPSILON WITH MACRON * 03A5 GREEK CAPITAL LETTER UPSILON * 1FEA GREEK CAPITAL LETTER UPSILON WITH VARIA * 03A5 GREEK CAPITAL LETTER UPSILON * 1FEB GREEK CAPITAL LETTER UPSILON WITH OXIA * 03A5 GREEK CAPITAL LETTER UPSILON * 1FEC GREEK CAPITAL LETTER RHO WITH DASIA * 03A1 GREEK CAPITAL LETTER RHO * 1FED GREEK DIALYTIKA AND VARIA * 0020 SPACE * 1FEE GREEK DIALYTIKA AND OXIA * 0020 SPACE * 1FEF GREEK VARIA * 0060 GRAVE ACCENT * 1FF2 GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FF3 GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FF4 GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FF6 GREEK SMALL LETTER OMEGA WITH PERISPOMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FF7 GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI * 03C9 GREEK SMALL LETTER OMEGA * 1FF8 GREEK CAPITAL LETTER OMICRON WITH VARIA * 039F GREEK CAPITAL LETTER OMICRON * 1FF9 GREEK CAPITAL LETTER OMICRON WITH OXIA * 039F GREEK CAPITAL LETTER OMICRON * 1FFA GREEK CAPITAL LETTER OMEGA WITH VARIA * 03A9 GREEK CAPITAL LETTER OMEGA * 1FFB GREEK CAPITAL LETTER OMEGA WITH OXIA * 03A9 GREEK CAPITAL LETTER OMEGA * 1FFC GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI * 03A9 GREEK CAPITAL LETTER OMEGA * 1FFD GREEK OXIA * 0020 SPACE * 1FFE GREEK DASIA * 0020 SPACE * 2000 EN QUAD * 0020 SPACE * 2001 EM QUAD * 0020 SPACE * 2002 EN SPACE * 0020 SPACE * 2003 EM SPACE * 0020 SPACE * 2004 THREE-PER-EM SPACE * 0020 SPACE * 2005 FOUR-PER-EM SPACE * 0020 SPACE * 2006 SIX-PER-EM SPACE * 0020 SPACE * 2007 FIGURE SPACE * 0020 SPACE * 2008 PUNCTUATION SPACE * 0020 SPACE * 2009 THIN SPACE * 0020 SPACE * 200A HAIR SPACE * 0020 SPACE * 2011 NON-BREAKING HYPHEN * 2010 HYPHEN * 2017 DOUBLE LOW LINE * 0020 SPACE * 2024 ONE DOT LEADER * 002E FULL STOP * 2025 TWO DOT LEADER * 002E FULL STOP * 002E FULL STOP * 2026 HORIZONTAL ELLIPSIS * 002E FULL STOP * 002E FULL STOP * 002E FULL STOP * 202F NARROW NO-BREAK SPACE * 0020 SPACE * 2033 DOUBLE PRIME * 2032 PRIME * 2032 PRIME * 2034 TRIPLE PRIME * 2032 PRIME * 2032 PRIME * 2032 PRIME * 2036 REVERSED DOUBLE PRIME * 2035 REVERSED PRIME * 2035 REVERSED PRIME * 2037 REVERSED TRIPLE PRIME * 2035 REVERSED PRIME * 2035 REVERSED PRIME * 2035 REVERSED PRIME * 203C DOUBLE EXCLAMATION MARK * 0021 EXCLAMATION MARK * 0021 EXCLAMATION MARK * 203E OVERLINE * 0020 SPACE * 2047 DOUBLE QUESTION MARK * 003F QUESTION MARK * 003F QUESTION MARK * 2048 QUESTION EXCLAMATION MARK * 003F QUESTION MARK * 0021 EXCLAMATION MARK * 2049 EXCLAMATION QUESTION MARK * 0021 EXCLAMATION MARK * 003F QUESTION MARK * 2057 QUADRUPLE PRIME * 2032 PRIME * 2032 PRIME * 2032 PRIME * 2032 PRIME * 205F MEDIUM MATHEMATICAL SPACE * 0020 SPACE * 2070 SUPERSCRIPT ZERO * 0030 DIGIT ZERO * 2071 SUPERSCRIPT LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 2074 SUPERSCRIPT FOUR * 0034 DIGIT FOUR * 2075 SUPERSCRIPT FIVE * 0035 DIGIT FIVE * 2076 SUPERSCRIPT SIX * 0036 DIGIT SIX * 2077 SUPERSCRIPT SEVEN * 0037 DIGIT SEVEN * 2078 SUPERSCRIPT EIGHT * 0038 DIGIT EIGHT * 2079 SUPERSCRIPT NINE * 0039 DIGIT NINE * 207A SUPERSCRIPT PLUS SIGN * 002B PLUS SIGN * 207B SUPERSCRIPT MINUS * 2212 MINUS SIGN * 207C SUPERSCRIPT EQUALS SIGN * 003D EQUALS SIGN * 207D SUPERSCRIPT LEFT PARENTHESIS * 0028 LEFT PARENTHESIS * 207E SUPERSCRIPT RIGHT PARENTHESIS * 0029 RIGHT PARENTHESIS * 207F SUPERSCRIPT LATIN SMALL LETTER N * 006E LATIN SMALL LETTER N * 2080 SUBSCRIPT ZERO * 0030 DIGIT ZERO * 2081 SUBSCRIPT ONE * 0031 DIGIT ONE * 2082 SUBSCRIPT TWO * 0032 DIGIT TWO * 2083 SUBSCRIPT THREE * 0033 DIGIT THREE * 2084 SUBSCRIPT FOUR * 0034 DIGIT FOUR * 2085 SUBSCRIPT FIVE * 0035 DIGIT FIVE * 2086 SUBSCRIPT SIX * 0036 DIGIT SIX * 2087 SUBSCRIPT SEVEN * 0037 DIGIT SEVEN * 2088 SUBSCRIPT EIGHT * 0038 DIGIT EIGHT * 2089 SUBSCRIPT NINE * 0039 DIGIT NINE * 208A SUBSCRIPT PLUS SIGN * 002B PLUS SIGN * 208B SUBSCRIPT MINUS * 2212 MINUS SIGN * 208C SUBSCRIPT EQUALS SIGN * 003D EQUALS SIGN * 208D SUBSCRIPT LEFT PARENTHESIS * 0028 LEFT PARENTHESIS * 208E SUBSCRIPT RIGHT PARENTHESIS * 0029 RIGHT PARENTHESIS * 2090 LATIN SUBSCRIPT SMALL LETTER A * 0061 LATIN SMALL LETTER A * 2091 LATIN SUBSCRIPT SMALL LETTER E * 0065 LATIN SMALL LETTER E * 2092 LATIN SUBSCRIPT SMALL LETTER O * 006F LATIN SMALL LETTER O * 2093 LATIN SUBSCRIPT SMALL LETTER X * 0078 LATIN SMALL LETTER X * 2094 LATIN SUBSCRIPT SMALL LETTER SCHWA * 0259 LATIN SMALL LETTER SCHWA * 2095 LATIN SUBSCRIPT SMALL LETTER H * 0068 LATIN SMALL LETTER H * 2096 LATIN SUBSCRIPT SMALL LETTER K * 006B LATIN SMALL LETTER K * 2097 LATIN SUBSCRIPT SMALL LETTER L * 006C LATIN SMALL LETTER L * 2098 LATIN SUBSCRIPT SMALL LETTER M * 006D LATIN SMALL LETTER M * 2099 LATIN SUBSCRIPT SMALL LETTER N * 006E LATIN SMALL LETTER N * 209A LATIN SUBSCRIPT SMALL LETTER P * 0070 LATIN SMALL LETTER P * 209B LATIN SUBSCRIPT SMALL LETTER S * 0073 LATIN SMALL LETTER S * 209C LATIN SUBSCRIPT SMALL LETTER T * 0074 LATIN SMALL LETTER T * 20A8 RUPEE SIGN * 0052 LATIN CAPITAL LETTER R * 0073 LATIN SMALL LETTER S * 20D0 COMBINING LEFT HARPOON ABOVE * 0000 * 20D1 COMBINING RIGHT HARPOON ABOVE * 0000 * 20D2 COMBINING LONG VERTICAL LINE OVERLAY * 0000 * 20D3 COMBINING SHORT VERTICAL LINE OVERLAY * 0000 * 20D4 COMBINING ANTICLOCKWISE ARROW ABOVE * 0000 * 20D5 COMBINING CLOCKWISE ARROW ABOVE * 0000 * 20D6 COMBINING LEFT ARROW ABOVE * 0000 * 20D7 COMBINING RIGHT ARROW ABOVE * 0000 * 20D8 COMBINING RING OVERLAY * 0000 * 20D9 COMBINING CLOCKWISE RING OVERLAY * 0000 * 20DA COMBINING ANTICLOCKWISE RING OVERLAY * 0000 * 20DB COMBINING THREE DOTS ABOVE * 0000 * 20DC COMBINING FOUR DOTS ABOVE * 0000 * 20DD COMBINING ENCLOSING CIRCLE * 0000 * 20DE COMBINING ENCLOSING SQUARE * 0000 * 20DF COMBINING ENCLOSING DIAMOND * 0000 * 20E0 COMBINING ENCLOSING CIRCLE BACKSLASH * 0000 * 20E1 COMBINING LEFT RIGHT ARROW ABOVE * 0000 * 20E2 COMBINING ENCLOSING SCREEN * 0000 * 20E3 COMBINING ENCLOSING KEYCAP * 0000 * 20E4 COMBINING ENCLOSING UPWARD POINTING TRIANGLE * 0000 * 20E5 COMBINING REVERSE SOLIDUS OVERLAY * 0000 * 20E6 COMBINING DOUBLE VERTICAL STROKE OVERLAY * 0000 * 20E7 COMBINING ANNUITY SYMBOL * 0000 * 20E8 COMBINING TRIPLE UNDERDOT * 0000 * 20E9 COMBINING WIDE BRIDGE ABOVE * 0000 * 20EA COMBINING LEFTWARDS ARROW OVERLAY * 0000 * 20EB COMBINING LONG DOUBLE SOLIDUS OVERLAY * 0000 * 20EC COMBINING RIGHTWARDS HARPOON WITH BARB DOWNWARDS * 0000 * 20ED COMBINING LEFTWARDS HARPOON WITH BARB DOWNWARDS * 0000 * 20EE COMBINING LEFT ARROW BELOW * 0000 * 20EF COMBINING RIGHT ARROW BELOW * 0000 * 20F0 COMBINING ASTERISK ABOVE * 0000 * 2100 ACCOUNT OF * 0061 LATIN SMALL LETTER A * 002F SOLIDUS * 0063 LATIN SMALL LETTER C * 2101 ADDRESSED TO THE SUBJECT * 0061 LATIN SMALL LETTER A * 002F SOLIDUS * 0073 LATIN SMALL LETTER S * 2102 DOUBLE-STRUCK CAPITAL C * 0043 LATIN CAPITAL LETTER C * 2103 DEGREE CELSIUS * 00B0 DEGREE SIGN * 0043 LATIN CAPITAL LETTER C * 2105 CARE OF * 0063 LATIN SMALL LETTER C * 002F SOLIDUS * 006F LATIN SMALL LETTER O * 2106 CADA UNA * 0063 LATIN SMALL LETTER C * 002F SOLIDUS * 0075 LATIN SMALL LETTER U * 2107 EULER CONSTANT * 0190 LATIN CAPITAL LETTER OPEN E * 2109 DEGREE FAHRENHEIT * 00B0 DEGREE SIGN * 0046 LATIN CAPITAL LETTER F * 210A SCRIPT SMALL G * 0067 LATIN SMALL LETTER G * 210B SCRIPT CAPITAL H * 0048 LATIN CAPITAL LETTER H * 210C BLACK-LETTER CAPITAL H * 0048 LATIN CAPITAL LETTER H * 210D DOUBLE-STRUCK CAPITAL H * 0048 LATIN CAPITAL LETTER H * 210E PLANCK CONSTANT * 0068 LATIN SMALL LETTER H * 210F PLANCK CONSTANT OVER TWO PI * 0127 LATIN SMALL LETTER H WITH STROKE * 2110 SCRIPT CAPITAL I * 0049 LATIN CAPITAL LETTER I * 2111 BLACK-LETTER CAPITAL I * 0049 LATIN CAPITAL LETTER I * 2112 SCRIPT CAPITAL L * 004C LATIN CAPITAL LETTER L * 2113 SCRIPT SMALL L * 006C LATIN SMALL LETTER L * 2115 DOUBLE-STRUCK CAPITAL N * 004E LATIN CAPITAL LETTER N * 2116 NUMERO SIGN * 004E LATIN CAPITAL LETTER N * 006F LATIN SMALL LETTER O * 2119 DOUBLE-STRUCK CAPITAL P * 0050 LATIN CAPITAL LETTER P * 211A DOUBLE-STRUCK CAPITAL Q * 0051 LATIN CAPITAL LETTER Q * 211B SCRIPT CAPITAL R * 0052 LATIN CAPITAL LETTER R * 211C BLACK-LETTER CAPITAL R * 0052 LATIN CAPITAL LETTER R * 211D DOUBLE-STRUCK CAPITAL R * 0052 LATIN CAPITAL LETTER R * 2120 SERVICE MARK * 0053 LATIN CAPITAL LETTER S * 004D LATIN CAPITAL LETTER M * 2121 TELEPHONE SIGN * 0054 LATIN CAPITAL LETTER T * 0045 LATIN CAPITAL LETTER E * 004C LATIN CAPITAL LETTER L * 2122 TRADE MARK SIGN * 0054 LATIN CAPITAL LETTER T * 004D LATIN CAPITAL LETTER M * 2124 DOUBLE-STRUCK CAPITAL Z * 005A LATIN CAPITAL LETTER Z * 2126 OHM SIGN * 03A9 GREEK CAPITAL LETTER OMEGA * 2128 BLACK-LETTER CAPITAL Z * 005A LATIN CAPITAL LETTER Z * 212A KELVIN SIGN * 004B LATIN CAPITAL LETTER K * 212B ANGSTROM SIGN * 0041 LATIN CAPITAL LETTER A * 212C SCRIPT CAPITAL B * 0042 LATIN CAPITAL LETTER B * 212D BLACK-LETTER CAPITAL C * 0043 LATIN CAPITAL LETTER C * 212F SCRIPT SMALL E * 0065 LATIN SMALL LETTER E * 2130 SCRIPT CAPITAL E * 0045 LATIN CAPITAL LETTER E * 2131 SCRIPT CAPITAL F * 0046 LATIN CAPITAL LETTER F * 2133 SCRIPT CAPITAL M * 004D LATIN CAPITAL LETTER M * 2134 SCRIPT SMALL O * 006F LATIN SMALL LETTER O * 2135 ALEF SYMBOL * 05D0 HEBREW LETTER ALEF * 2136 BET SYMBOL * 05D1 HEBREW LETTER BET * 2137 GIMEL SYMBOL * 05D2 HEBREW LETTER GIMEL * 2138 DALET SYMBOL * 05D3 HEBREW LETTER DALET * 2139 INFORMATION SOURCE * 0069 LATIN SMALL LETTER I * 213B FACSIMILE SIGN * 0046 LATIN CAPITAL LETTER F * 0041 LATIN CAPITAL LETTER A * 0058 LATIN CAPITAL LETTER X * 213C DOUBLE-STRUCK SMALL PI * 03C0 GREEK SMALL LETTER PI * 213D DOUBLE-STRUCK SMALL GAMMA * 03B3 GREEK SMALL LETTER GAMMA * 213E DOUBLE-STRUCK CAPITAL GAMMA * 0393 GREEK CAPITAL LETTER GAMMA * 213F DOUBLE-STRUCK CAPITAL PI * 03A0 GREEK CAPITAL LETTER PI * 2140 DOUBLE-STRUCK N-ARY SUMMATION * 2211 N-ARY SUMMATION * 2145 DOUBLE-STRUCK ITALIC CAPITAL D * 0044 LATIN CAPITAL LETTER D * 2146 DOUBLE-STRUCK ITALIC SMALL D * 0064 LATIN SMALL LETTER D * 2147 DOUBLE-STRUCK ITALIC SMALL E * 0065 LATIN SMALL LETTER E * 2148 DOUBLE-STRUCK ITALIC SMALL I * 0069 LATIN SMALL LETTER I * 2149 DOUBLE-STRUCK ITALIC SMALL J * 006A LATIN SMALL LETTER J * 2150 VULGAR FRACTION ONE SEVENTH * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0037 DIGIT SEVEN * 2151 VULGAR FRACTION ONE NINTH * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0039 DIGIT NINE * 2152 VULGAR FRACTION ONE TENTH * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0031 DIGIT ONE * 0030 DIGIT ZERO * 2153 VULGAR FRACTION ONE THIRD * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0033 DIGIT THREE * 2154 VULGAR FRACTION TWO THIRDS * 0032 DIGIT TWO * 2044 FRACTION SLASH * 0033 DIGIT THREE * 2155 VULGAR FRACTION ONE FIFTH * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0035 DIGIT FIVE * 2156 VULGAR FRACTION TWO FIFTHS * 0032 DIGIT TWO * 2044 FRACTION SLASH * 0035 DIGIT FIVE * 2157 VULGAR FRACTION THREE FIFTHS * 0033 DIGIT THREE * 2044 FRACTION SLASH * 0035 DIGIT FIVE * 2158 VULGAR FRACTION FOUR FIFTHS * 0034 DIGIT FOUR * 2044 FRACTION SLASH * 0035 DIGIT FIVE * 2159 VULGAR FRACTION ONE SIXTH * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0036 DIGIT SIX * 215A VULGAR FRACTION FIVE SIXTHS * 0035 DIGIT FIVE * 2044 FRACTION SLASH * 0036 DIGIT SIX * 215B VULGAR FRACTION ONE EIGHTH * 0031 DIGIT ONE * 2044 FRACTION SLASH * 0038 DIGIT EIGHT * 215C VULGAR FRACTION THREE EIGHTHS * 0033 DIGIT THREE * 2044 FRACTION SLASH * 0038 DIGIT EIGHT * 215D VULGAR FRACTION FIVE EIGHTHS * 0035 DIGIT FIVE * 2044 FRACTION SLASH * 0038 DIGIT EIGHT * 215E VULGAR FRACTION SEVEN EIGHTHS * 0037 DIGIT SEVEN * 2044 FRACTION SLASH * 0038 DIGIT EIGHT * 215F FRACTION NUMERATOR ONE * 0031 DIGIT ONE * 2044 FRACTION SLASH * 2160 ROMAN NUMERAL ONE * 0049 LATIN CAPITAL LETTER I * 2161 ROMAN NUMERAL TWO * 0049 LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * 2162 ROMAN NUMERAL THREE * 0049 LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * 2163 ROMAN NUMERAL FOUR * 0049 LATIN CAPITAL LETTER I * 0056 LATIN CAPITAL LETTER V * 2164 ROMAN NUMERAL FIVE * 0056 LATIN CAPITAL LETTER V * 2165 ROMAN NUMERAL SIX * 0056 LATIN CAPITAL LETTER V * 0049 LATIN CAPITAL LETTER I * 2166 ROMAN NUMERAL SEVEN * 0056 LATIN CAPITAL LETTER V * 0049 LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * 2167 ROMAN NUMERAL EIGHT * 0056 LATIN CAPITAL LETTER V * 0049 LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * 2168 ROMAN NUMERAL NINE * 0049 LATIN CAPITAL LETTER I * 0058 LATIN CAPITAL LETTER X * 2169 ROMAN NUMERAL TEN * 0058 LATIN CAPITAL LETTER X * 216A ROMAN NUMERAL ELEVEN * 0058 LATIN CAPITAL LETTER X * 0049 LATIN CAPITAL LETTER I * 216B ROMAN NUMERAL TWELVE * 0058 LATIN CAPITAL LETTER X * 0049 LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * 216C ROMAN NUMERAL FIFTY * 004C LATIN CAPITAL LETTER L * 216D ROMAN NUMERAL ONE HUNDRED * 0043 LATIN CAPITAL LETTER C * 216E ROMAN NUMERAL FIVE HUNDRED * 0044 LATIN CAPITAL LETTER D * 216F ROMAN NUMERAL ONE THOUSAND * 004D LATIN CAPITAL LETTER M * 2170 SMALL ROMAN NUMERAL ONE * 0069 LATIN SMALL LETTER I * 2171 SMALL ROMAN NUMERAL TWO * 0069 LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 2172 SMALL ROMAN NUMERAL THREE * 0069 LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 2173 SMALL ROMAN NUMERAL FOUR * 0069 LATIN SMALL LETTER I * 0076 LATIN SMALL LETTER V * 2174 SMALL ROMAN NUMERAL FIVE * 0076 LATIN SMALL LETTER V * 2175 SMALL ROMAN NUMERAL SIX * 0076 LATIN SMALL LETTER V * 0069 LATIN SMALL LETTER I * 2176 SMALL ROMAN NUMERAL SEVEN * 0076 LATIN SMALL LETTER V * 0069 LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 2177 SMALL ROMAN NUMERAL EIGHT * 0076 LATIN SMALL LETTER V * 0069 LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 2178 SMALL ROMAN NUMERAL NINE * 0069 LATIN SMALL LETTER I * 0078 LATIN SMALL LETTER X * 2179 SMALL ROMAN NUMERAL TEN * 0078 LATIN SMALL LETTER X * 217A SMALL ROMAN NUMERAL ELEVEN * 0078 LATIN SMALL LETTER X * 0069 LATIN SMALL LETTER I * 217B SMALL ROMAN NUMERAL TWELVE * 0078 LATIN SMALL LETTER X * 0069 LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 217C SMALL ROMAN NUMERAL FIFTY * 006C LATIN SMALL LETTER L * 217D SMALL ROMAN NUMERAL ONE HUNDRED * 0063 LATIN SMALL LETTER C * 217E SMALL ROMAN NUMERAL FIVE HUNDRED * 0064 LATIN SMALL LETTER D * 217F SMALL ROMAN NUMERAL ONE THOUSAND * 006D LATIN SMALL LETTER M * 2189 VULGAR FRACTION ZERO THIRDS * 0030 DIGIT ZERO * 2044 FRACTION SLASH * 0033 DIGIT THREE * 219A LEFTWARDS ARROW WITH STROKE * 2190 LEFTWARDS ARROW * 219B RIGHTWARDS ARROW WITH STROKE * 2192 RIGHTWARDS ARROW * 21AE LEFT RIGHT ARROW WITH STROKE * 2194 LEFT RIGHT ARROW * 21CD LEFTWARDS DOUBLE ARROW WITH STROKE * 21D0 LEFTWARDS DOUBLE ARROW * 21CE LEFT RIGHT DOUBLE ARROW WITH STROKE * 21D4 LEFT RIGHT DOUBLE ARROW * 21CF RIGHTWARDS DOUBLE ARROW WITH STROKE * 21D2 RIGHTWARDS DOUBLE ARROW * 2204 THERE DOES NOT EXIST * 2203 THERE EXISTS * 2209 NOT AN ELEMENT OF * 2208 ELEMENT OF * 220C DOES NOT CONTAIN AS MEMBER * 220B CONTAINS AS MEMBER * 2224 DOES NOT DIVIDE * 2223 DIVIDES * 2226 NOT PARALLEL TO * 2225 PARALLEL TO * 222C DOUBLE INTEGRAL * 222B INTEGRAL * 222B INTEGRAL * 222D TRIPLE INTEGRAL * 222B INTEGRAL * 222B INTEGRAL * 222B INTEGRAL * 222F SURFACE INTEGRAL * 222E CONTOUR INTEGRAL * 222E CONTOUR INTEGRAL * 2230 VOLUME INTEGRAL * 222E CONTOUR INTEGRAL * 222E CONTOUR INTEGRAL * 222E CONTOUR INTEGRAL * 2241 NOT TILDE * 223C TILDE OPERATOR * 2244 NOT ASYMPTOTICALLY EQUAL TO * 2243 ASYMPTOTICALLY EQUAL TO * 2247 NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO * 2245 APPROXIMATELY EQUAL TO * 2249 NOT ALMOST EQUAL TO * 2248 ALMOST EQUAL TO * 2260 NOT EQUAL TO * 003D EQUALS SIGN * 2262 NOT IDENTICAL TO * 2261 IDENTICAL TO * 226D NOT EQUIVALENT TO * 224D EQUIVALENT TO * 226E NOT LESS-THAN * 003C LESS-THAN SIGN * 226F NOT GREATER-THAN * 003E GREATER-THAN SIGN * 2270 NEITHER LESS-THAN NOR EQUAL TO * 2264 LESS-THAN OR EQUAL TO * 2271 NEITHER GREATER-THAN NOR EQUAL TO * 2265 GREATER-THAN OR EQUAL TO * 2274 NEITHER LESS-THAN NOR EQUIVALENT TO * 2272 LESS-THAN OR EQUIVALENT TO * 2275 NEITHER GREATER-THAN NOR EQUIVALENT TO * 2273 GREATER-THAN OR EQUIVALENT TO * 2278 NEITHER LESS-THAN NOR GREATER-THAN * 2276 LESS-THAN OR GREATER-THAN * 2279 NEITHER GREATER-THAN NOR LESS-THAN * 2277 GREATER-THAN OR LESS-THAN * 2280 DOES NOT PRECEDE * 227A PRECEDES * 2281 DOES NOT SUCCEED * 227B SUCCEEDS * 2284 NOT A SUBSET OF * 2282 SUBSET OF * 2285 NOT A SUPERSET OF * 2283 SUPERSET OF * 2288 NEITHER A SUBSET OF NOR EQUAL TO * 2286 SUBSET OF OR EQUAL TO * 2289 NEITHER A SUPERSET OF NOR EQUAL TO * 2287 SUPERSET OF OR EQUAL TO * 22AC DOES NOT PROVE * 22A2 RIGHT TACK * 22AD NOT TRUE * 22A8 TRUE * 22AE DOES NOT FORCE * 22A9 FORCES * 22AF NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE * 22AB DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE * 22E0 DOES NOT PRECEDE OR EQUAL * 227C PRECEDES OR EQUAL TO * 22E1 DOES NOT SUCCEED OR EQUAL * 227D SUCCEEDS OR EQUAL TO * 22E2 NOT SQUARE IMAGE OF OR EQUAL TO * 2291 SQUARE IMAGE OF OR EQUAL TO * 22E3 NOT SQUARE ORIGINAL OF OR EQUAL TO * 2292 SQUARE ORIGINAL OF OR EQUAL TO * 22EA NOT NORMAL SUBGROUP OF * 22B2 NORMAL SUBGROUP OF * 22EB DOES NOT CONTAIN AS NORMAL SUBGROUP * 22B3 CONTAINS AS NORMAL SUBGROUP * 22EC NOT NORMAL SUBGROUP OF OR EQUAL TO * 22B4 NORMAL SUBGROUP OF OR EQUAL TO * 22ED DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL * 22B5 CONTAINS AS NORMAL SUBGROUP OR EQUAL TO * 2329 LEFT-POINTING ANGLE BRACKET * 3008 LEFT ANGLE BRACKET * 232A RIGHT-POINTING ANGLE BRACKET * 3009 RIGHT ANGLE BRACKET * 2460 CIRCLED DIGIT ONE * 0031 DIGIT ONE * 2461 CIRCLED DIGIT TWO * 0032 DIGIT TWO * 2462 CIRCLED DIGIT THREE * 0033 DIGIT THREE * 2463 CIRCLED DIGIT FOUR * 0034 DIGIT FOUR * 2464 CIRCLED DIGIT FIVE * 0035 DIGIT FIVE * 2465 CIRCLED DIGIT SIX * 0036 DIGIT SIX * 2466 CIRCLED DIGIT SEVEN * 0037 DIGIT SEVEN * 2467 CIRCLED DIGIT EIGHT * 0038 DIGIT EIGHT * 2468 CIRCLED DIGIT NINE * 0039 DIGIT NINE * 2469 CIRCLED NUMBER TEN * 0031 DIGIT ONE * 0030 DIGIT ZERO * 246A CIRCLED NUMBER ELEVEN * 0031 DIGIT ONE * 0031 DIGIT ONE * 246B CIRCLED NUMBER TWELVE * 0031 DIGIT ONE * 0032 DIGIT TWO * 246C CIRCLED NUMBER THIRTEEN * 0031 DIGIT ONE * 0033 DIGIT THREE * 246D CIRCLED NUMBER FOURTEEN * 0031 DIGIT ONE * 0034 DIGIT FOUR * 246E CIRCLED NUMBER FIFTEEN * 0031 DIGIT ONE * 0035 DIGIT FIVE * 246F CIRCLED NUMBER SIXTEEN * 0031 DIGIT ONE * 0036 DIGIT SIX * 2470 CIRCLED NUMBER SEVENTEEN * 0031 DIGIT ONE * 0037 DIGIT SEVEN * 2471 CIRCLED NUMBER EIGHTEEN * 0031 DIGIT ONE * 0038 DIGIT EIGHT * 2472 CIRCLED NUMBER NINETEEN * 0031 DIGIT ONE * 0039 DIGIT NINE * 2473 CIRCLED NUMBER TWENTY * 0032 DIGIT TWO * 0030 DIGIT ZERO * 2474 PARENTHESIZED DIGIT ONE * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0029 RIGHT PARENTHESIS * 2475 PARENTHESIZED DIGIT TWO * 0028 LEFT PARENTHESIS * 0032 DIGIT TWO * 0029 RIGHT PARENTHESIS * 2476 PARENTHESIZED DIGIT THREE * 0028 LEFT PARENTHESIS * 0033 DIGIT THREE * 0029 RIGHT PARENTHESIS * 2477 PARENTHESIZED DIGIT FOUR * 0028 LEFT PARENTHESIS * 0034 DIGIT FOUR * 0029 RIGHT PARENTHESIS * 2478 PARENTHESIZED DIGIT FIVE * 0028 LEFT PARENTHESIS * 0035 DIGIT FIVE * 0029 RIGHT PARENTHESIS * 2479 PARENTHESIZED DIGIT SIX * 0028 LEFT PARENTHESIS * 0036 DIGIT SIX * 0029 RIGHT PARENTHESIS * 247A PARENTHESIZED DIGIT SEVEN * 0028 LEFT PARENTHESIS * 0037 DIGIT SEVEN * 0029 RIGHT PARENTHESIS * 247B PARENTHESIZED DIGIT EIGHT * 0028 LEFT PARENTHESIS * 0038 DIGIT EIGHT * 0029 RIGHT PARENTHESIS * 247C PARENTHESIZED DIGIT NINE * 0028 LEFT PARENTHESIS * 0039 DIGIT NINE * 0029 RIGHT PARENTHESIS * 247D PARENTHESIZED NUMBER TEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0030 DIGIT ZERO * 0029 RIGHT PARENTHESIS * 247E PARENTHESIZED NUMBER ELEVEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0031 DIGIT ONE * 0029 RIGHT PARENTHESIS * 247F PARENTHESIZED NUMBER TWELVE * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0032 DIGIT TWO * 0029 RIGHT PARENTHESIS * 2480 PARENTHESIZED NUMBER THIRTEEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0033 DIGIT THREE * 0029 RIGHT PARENTHESIS * 2481 PARENTHESIZED NUMBER FOURTEEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0034 DIGIT FOUR * 0029 RIGHT PARENTHESIS * 2482 PARENTHESIZED NUMBER FIFTEEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0035 DIGIT FIVE * 0029 RIGHT PARENTHESIS * 2483 PARENTHESIZED NUMBER SIXTEEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0036 DIGIT SIX * 0029 RIGHT PARENTHESIS * 2484 PARENTHESIZED NUMBER SEVENTEEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0037 DIGIT SEVEN * 0029 RIGHT PARENTHESIS * 2485 PARENTHESIZED NUMBER EIGHTEEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0038 DIGIT EIGHT * 0029 RIGHT PARENTHESIS * 2486 PARENTHESIZED NUMBER NINETEEN * 0028 LEFT PARENTHESIS * 0031 DIGIT ONE * 0039 DIGIT NINE * 0029 RIGHT PARENTHESIS * 2487 PARENTHESIZED NUMBER TWENTY * 0028 LEFT PARENTHESIS * 0032 DIGIT TWO * 0030 DIGIT ZERO * 0029 RIGHT PARENTHESIS * 2488 DIGIT ONE FULL STOP * 0031 DIGIT ONE * 002E FULL STOP * 2489 DIGIT TWO FULL STOP * 0032 DIGIT TWO * 002E FULL STOP * 248A DIGIT THREE FULL STOP * 0033 DIGIT THREE * 002E FULL STOP * 248B DIGIT FOUR FULL STOP * 0034 DIGIT FOUR * 002E FULL STOP * 248C DIGIT FIVE FULL STOP * 0035 DIGIT FIVE * 002E FULL STOP * 248D DIGIT SIX FULL STOP * 0036 DIGIT SIX * 002E FULL STOP * 248E DIGIT SEVEN FULL STOP * 0037 DIGIT SEVEN * 002E FULL STOP * 248F DIGIT EIGHT FULL STOP * 0038 DIGIT EIGHT * 002E FULL STOP * 2490 DIGIT NINE FULL STOP * 0039 DIGIT NINE * 002E FULL STOP * 2491 NUMBER TEN FULL STOP * 0031 DIGIT ONE * 0030 DIGIT ZERO * 002E FULL STOP * 2492 NUMBER ELEVEN FULL STOP * 0031 DIGIT ONE * 0031 DIGIT ONE * 002E FULL STOP * 2493 NUMBER TWELVE FULL STOP * 0031 DIGIT ONE * 0032 DIGIT TWO * 002E FULL STOP * 2494 NUMBER THIRTEEN FULL STOP * 0031 DIGIT ONE * 0033 DIGIT THREE * 002E FULL STOP * 2495 NUMBER FOURTEEN FULL STOP * 0031 DIGIT ONE * 0034 DIGIT FOUR * 002E FULL STOP * 2496 NUMBER FIFTEEN FULL STOP * 0031 DIGIT ONE * 0035 DIGIT FIVE * 002E FULL STOP * 2497 NUMBER SIXTEEN FULL STOP * 0031 DIGIT ONE * 0036 DIGIT SIX * 002E FULL STOP * 2498 NUMBER SEVENTEEN FULL STOP * 0031 DIGIT ONE * 0037 DIGIT SEVEN * 002E FULL STOP * 2499 NUMBER EIGHTEEN FULL STOP * 0031 DIGIT ONE * 0038 DIGIT EIGHT * 002E FULL STOP * 249A NUMBER NINETEEN FULL STOP * 0031 DIGIT ONE * 0039 DIGIT NINE * 002E FULL STOP * 249B NUMBER TWENTY FULL STOP * 0032 DIGIT TWO * 0030 DIGIT ZERO * 002E FULL STOP * 249C PARENTHESIZED LATIN SMALL LETTER A * 0028 LEFT PARENTHESIS * 0061 LATIN SMALL LETTER A * 0029 RIGHT PARENTHESIS * 249D PARENTHESIZED LATIN SMALL LETTER B * 0028 LEFT PARENTHESIS * 0062 LATIN SMALL LETTER B * 0029 RIGHT PARENTHESIS * 249E PARENTHESIZED LATIN SMALL LETTER C * 0028 LEFT PARENTHESIS * 0063 LATIN SMALL LETTER C * 0029 RIGHT PARENTHESIS * 249F PARENTHESIZED LATIN SMALL LETTER D * 0028 LEFT PARENTHESIS * 0064 LATIN SMALL LETTER D * 0029 RIGHT PARENTHESIS * 24A0 PARENTHESIZED LATIN SMALL LETTER E * 0028 LEFT PARENTHESIS * 0065 LATIN SMALL LETTER E * 0029 RIGHT PARENTHESIS * 24A1 PARENTHESIZED LATIN SMALL LETTER F * 0028 LEFT PARENTHESIS * 0066 LATIN SMALL LETTER F * 0029 RIGHT PARENTHESIS * 24A2 PARENTHESIZED LATIN SMALL LETTER G * 0028 LEFT PARENTHESIS * 0067 LATIN SMALL LETTER G * 0029 RIGHT PARENTHESIS * 24A3 PARENTHESIZED LATIN SMALL LETTER H * 0028 LEFT PARENTHESIS * 0068 LATIN SMALL LETTER H * 0029 RIGHT PARENTHESIS * 24A4 PARENTHESIZED LATIN SMALL LETTER I * 0028 LEFT PARENTHESIS * 0069 LATIN SMALL LETTER I * 0029 RIGHT PARENTHESIS * 24A5 PARENTHESIZED LATIN SMALL LETTER J * 0028 LEFT PARENTHESIS * 006A LATIN SMALL LETTER J * 0029 RIGHT PARENTHESIS * 24A6 PARENTHESIZED LATIN SMALL LETTER K * 0028 LEFT PARENTHESIS * 006B LATIN SMALL LETTER K * 0029 RIGHT PARENTHESIS * 24A7 PARENTHESIZED LATIN SMALL LETTER L * 0028 LEFT PARENTHESIS * 006C LATIN SMALL LETTER L * 0029 RIGHT PARENTHESIS * 24A8 PARENTHESIZED LATIN SMALL LETTER M * 0028 LEFT PARENTHESIS * 006D LATIN SMALL LETTER M * 0029 RIGHT PARENTHESIS * 24A9 PARENTHESIZED LATIN SMALL LETTER N * 0028 LEFT PARENTHESIS * 006E LATIN SMALL LETTER N * 0029 RIGHT PARENTHESIS * 24AA PARENTHESIZED LATIN SMALL LETTER O * 0028 LEFT PARENTHESIS * 006F LATIN SMALL LETTER O * 0029 RIGHT PARENTHESIS * 24AB PARENTHESIZED LATIN SMALL LETTER P * 0028 LEFT PARENTHESIS * 0070 LATIN SMALL LETTER P * 0029 RIGHT PARENTHESIS * 24AC PARENTHESIZED LATIN SMALL LETTER Q * 0028 LEFT PARENTHESIS * 0071 LATIN SMALL LETTER Q * 0029 RIGHT PARENTHESIS * 24AD PARENTHESIZED LATIN SMALL LETTER R * 0028 LEFT PARENTHESIS * 0072 LATIN SMALL LETTER R * 0029 RIGHT PARENTHESIS * 24AE PARENTHESIZED LATIN SMALL LETTER S * 0028 LEFT PARENTHESIS * 0073 LATIN SMALL LETTER S * 0029 RIGHT PARENTHESIS * 24AF PARENTHESIZED LATIN SMALL LETTER T * 0028 LEFT PARENTHESIS * 0074 LATIN SMALL LETTER T * 0029 RIGHT PARENTHESIS * 24B0 PARENTHESIZED LATIN SMALL LETTER U * 0028 LEFT PARENTHESIS * 0075 LATIN SMALL LETTER U * 0029 RIGHT PARENTHESIS * 24B1 PARENTHESIZED LATIN SMALL LETTER V * 0028 LEFT PARENTHESIS * 0076 LATIN SMALL LETTER V * 0029 RIGHT PARENTHESIS * 24B2 PARENTHESIZED LATIN SMALL LETTER W * 0028 LEFT PARENTHESIS * 0077 LATIN SMALL LETTER W * 0029 RIGHT PARENTHESIS * 24B3 PARENTHESIZED LATIN SMALL LETTER X * 0028 LEFT PARENTHESIS * 0078 LATIN SMALL LETTER X * 0029 RIGHT PARENTHESIS * 24B4 PARENTHESIZED LATIN SMALL LETTER Y * 0028 LEFT PARENTHESIS * 0079 LATIN SMALL LETTER Y * 0029 RIGHT PARENTHESIS * 24B5 PARENTHESIZED LATIN SMALL LETTER Z * 0028 LEFT PARENTHESIS * 007A LATIN SMALL LETTER Z * 0029 RIGHT PARENTHESIS * 24B6 CIRCLED LATIN CAPITAL LETTER A * 0041 LATIN CAPITAL LETTER A * 24B7 CIRCLED LATIN CAPITAL LETTER B * 0042 LATIN CAPITAL LETTER B * 24B8 CIRCLED LATIN CAPITAL LETTER C * 0043 LATIN CAPITAL LETTER C * 24B9 CIRCLED LATIN CAPITAL LETTER D * 0044 LATIN CAPITAL LETTER D * 24BA CIRCLED LATIN CAPITAL LETTER E * 0045 LATIN CAPITAL LETTER E * 24BB CIRCLED LATIN CAPITAL LETTER F * 0046 LATIN CAPITAL LETTER F * 24BC CIRCLED LATIN CAPITAL LETTER G * 0047 LATIN CAPITAL LETTER G * 24BD CIRCLED LATIN CAPITAL LETTER H * 0048 LATIN CAPITAL LETTER H * 24BE CIRCLED LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * 24BF CIRCLED LATIN CAPITAL LETTER J * 004A LATIN CAPITAL LETTER J * 24C0 CIRCLED LATIN CAPITAL LETTER K * 004B LATIN CAPITAL LETTER K * 24C1 CIRCLED LATIN CAPITAL LETTER L * 004C LATIN CAPITAL LETTER L * 24C2 CIRCLED LATIN CAPITAL LETTER M * 004D LATIN CAPITAL LETTER M * 24C3 CIRCLED LATIN CAPITAL LETTER N * 004E LATIN CAPITAL LETTER N * 24C4 CIRCLED LATIN CAPITAL LETTER O * 004F LATIN CAPITAL LETTER O * 24C5 CIRCLED LATIN CAPITAL LETTER P * 0050 LATIN CAPITAL LETTER P * 24C6 CIRCLED LATIN CAPITAL LETTER Q * 0051 LATIN CAPITAL LETTER Q * 24C7 CIRCLED LATIN CAPITAL LETTER R * 0052 LATIN CAPITAL LETTER R * 24C8 CIRCLED LATIN CAPITAL LETTER S * 0053 LATIN CAPITAL LETTER S * 24C9 CIRCLED LATIN CAPITAL LETTER T * 0054 LATIN CAPITAL LETTER T * 24CA CIRCLED LATIN CAPITAL LETTER U * 0055 LATIN CAPITAL LETTER U * 24CB CIRCLED LATIN CAPITAL LETTER V * 0056 LATIN CAPITAL LETTER V * 24CC CIRCLED LATIN CAPITAL LETTER W * 0057 LATIN CAPITAL LETTER W * 24CD CIRCLED LATIN CAPITAL LETTER X * 0058 LATIN CAPITAL LETTER X * 24CE CIRCLED LATIN CAPITAL LETTER Y * 0059 LATIN CAPITAL LETTER Y * 24CF CIRCLED LATIN CAPITAL LETTER Z * 005A LATIN CAPITAL LETTER Z * 24D0 CIRCLED LATIN SMALL LETTER A * 0061 LATIN SMALL LETTER A * 24D1 CIRCLED LATIN SMALL LETTER B * 0062 LATIN SMALL LETTER B * 24D2 CIRCLED LATIN SMALL LETTER C * 0063 LATIN SMALL LETTER C * 24D3 CIRCLED LATIN SMALL LETTER D * 0064 LATIN SMALL LETTER D * 24D4 CIRCLED LATIN SMALL LETTER E * 0065 LATIN SMALL LETTER E * 24D5 CIRCLED LATIN SMALL LETTER F * 0066 LATIN SMALL LETTER F * 24D6 CIRCLED LATIN SMALL LETTER G * 0067 LATIN SMALL LETTER G * 24D7 CIRCLED LATIN SMALL LETTER H * 0068 LATIN SMALL LETTER H * 24D8 CIRCLED LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * 24D9 CIRCLED LATIN SMALL LETTER J * 006A LATIN SMALL LETTER J * 24DA CIRCLED LATIN SMALL LETTER K * 006B LATIN SMALL LETTER K * 24DB CIRCLED LATIN SMALL LETTER L * 006C LATIN SMALL LETTER L * 24DC CIRCLED LATIN SMALL LETTER M * 006D LATIN SMALL LETTER M * 24DD CIRCLED LATIN SMALL LETTER N * 006E LATIN SMALL LETTER N * 24DE CIRCLED LATIN SMALL LETTER O * 006F LATIN SMALL LETTER O * 24DF CIRCLED LATIN SMALL LETTER P * 0070 LATIN SMALL LETTER P * 24E0 CIRCLED LATIN SMALL LETTER Q * 0071 LATIN SMALL LETTER Q * 24E1 CIRCLED LATIN SMALL LETTER R * 0072 LATIN SMALL LETTER R * 24E2 CIRCLED LATIN SMALL LETTER S * 0073 LATIN SMALL LETTER S * 24E3 CIRCLED LATIN SMALL LETTER T * 0074 LATIN SMALL LETTER T * 24E4 CIRCLED LATIN SMALL LETTER U * 0075 LATIN SMALL LETTER U * 24E5 CIRCLED LATIN SMALL LETTER V * 0076 LATIN SMALL LETTER V * 24E6 CIRCLED LATIN SMALL LETTER W * 0077 LATIN SMALL LETTER W * 24E7 CIRCLED LATIN SMALL LETTER X * 0078 LATIN SMALL LETTER X * 24E8 CIRCLED LATIN SMALL LETTER Y * 0079 LATIN SMALL LETTER Y * 24E9 CIRCLED LATIN SMALL LETTER Z * 007A LATIN SMALL LETTER Z * 24EA CIRCLED DIGIT ZERO * 0030 DIGIT ZERO * 2A0C QUADRUPLE INTEGRAL OPERATOR * 222B INTEGRAL * 222B INTEGRAL * 222B INTEGRAL * 222B INTEGRAL * 2A74 DOUBLE COLON EQUAL * 003A COLON * 003A COLON * 003D EQUALS SIGN * 2A75 TWO CONSECUTIVE EQUALS SIGNS * 003D EQUALS SIGN * 003D EQUALS SIGN * 2A76 THREE CONSECUTIVE EQUALS SIGNS * 003D EQUALS SIGN * 003D EQUALS SIGN * 003D EQUALS SIGN * 2ADC FORKING * 2ADD NONFORKING * 2C7C LATIN SUBSCRIPT SMALL LETTER J * 006A LATIN SMALL LETTER J * 2C7D MODIFIER LETTER CAPITAL V * 0056 LATIN CAPITAL LETTER V * 2CEF COPTIC COMBINING NI ABOVE * 0000 * 2CF0 COPTIC COMBINING SPIRITUS ASPER * 0000 * 2CF1 COPTIC COMBINING SPIRITUS LENIS * 0000 * 2D6F TIFINAGH MODIFIER LETTER LABIALIZATION MARK * 2D61 TIFINAGH LETTER YAW * 2D7F TIFINAGH CONSONANT JOINER * 0000 * 2DE0 COMBINING CYRILLIC LETTER BE * 0000 * 2DE1 COMBINING CYRILLIC LETTER VE * 0000 * 2DE2 COMBINING CYRILLIC LETTER GHE * 0000 * 2DE3 COMBINING CYRILLIC LETTER DE * 0000 * 2DE4 COMBINING CYRILLIC LETTER ZHE * 0000 * 2DE5 COMBINING CYRILLIC LETTER ZE * 0000 * 2DE6 COMBINING CYRILLIC LETTER KA * 0000 * 2DE7 COMBINING CYRILLIC LETTER EL * 0000 * 2DE8 COMBINING CYRILLIC LETTER EM * 0000 * 2DE9 COMBINING CYRILLIC LETTER EN * 0000 * 2DEA COMBINING CYRILLIC LETTER O * 0000 * 2DEB COMBINING CYRILLIC LETTER PE * 0000 * 2DEC COMBINING CYRILLIC LETTER ER * 0000 * 2DED COMBINING CYRILLIC LETTER ES * 0000 * 2DEE COMBINING CYRILLIC LETTER TE * 0000 * 2DEF COMBINING CYRILLIC LETTER HA * 0000 * 2DF0 COMBINING CYRILLIC LETTER TSE * 0000 * 2DF1 COMBINING CYRILLIC LETTER CHE * 0000 * 2DF2 COMBINING CYRILLIC LETTER SHA * 0000 * 2DF3 COMBINING CYRILLIC LETTER SHCHA * 0000 * 2DF4 COMBINING CYRILLIC LETTER FITA * 0000 * 2DF5 COMBINING CYRILLIC LETTER ES-TE * 0000 * 2DF6 COMBINING CYRILLIC LETTER A * 0000 * 2DF7 COMBINING CYRILLIC LETTER IE * 0000 * 2DF8 COMBINING CYRILLIC LETTER DJERV * 0000 * 2DF9 COMBINING CYRILLIC LETTER MONOGRAPH UK * 0000 * 2DFA COMBINING CYRILLIC LETTER YAT * 0000 * 2DFB COMBINING CYRILLIC LETTER YU * 0000 * 2DFC COMBINING CYRILLIC LETTER IOTIFIED A * 0000 * 2DFD COMBINING CYRILLIC LETTER LITTLE YUS * 0000 * 2DFE COMBINING CYRILLIC LETTER BIG YUS * 0000 * 2DFF COMBINING CYRILLIC LETTER IOTIFIED BIG YUS * 0000 * 2E9F CJK RADICAL MOTHER * 6BCD ?? * 2EF3 CJK RADICAL C-SIMPLIFIED TURTLE * 9F9F ?? * 2F00 KANGXI RADICAL ONE * 4E00 * 2F01 KANGXI RADICAL LINE * 4E28 ?? * 2F02 KANGXI RADICAL DOT * 4E36 ?? * 2F03 KANGXI RADICAL SLASH * 4E3F ?? * 2F04 KANGXI RADICAL SECOND * 4E59 ?? * 2F05 KANGXI RADICAL HOOK * 4E85 ?? * 2F06 KANGXI RADICAL TWO * 4E8C ?? * 2F07 KANGXI RADICAL LID * 4EA0 ?? * 2F08 KANGXI RADICAL MAN * 4EBA ?? * 2F09 KANGXI RADICAL LEGS * 513F ?? * 2F0A KANGXI RADICAL ENTER * 5165 ?? * 2F0B KANGXI RADICAL EIGHT * 516B ?? * 2F0C KANGXI RADICAL DOWN BOX * 5182 ?? * 2F0D KANGXI RADICAL COVER * 5196 ?? * 2F0E KANGXI RADICAL ICE * 51AB ?? * 2F0F KANGXI RADICAL TABLE * 51E0 ?? * 2F10 KANGXI RADICAL OPEN BOX * 51F5 ?? * 2F11 KANGXI RADICAL KNIFE * 5200 ?? * 2F12 KANGXI RADICAL POWER * 529B ?? * 2F13 KANGXI RADICAL WRAP * 52F9 ?? * 2F14 KANGXI RADICAL SPOON * 5315 ?? * 2F15 KANGXI RADICAL RIGHT OPEN BOX * 531A ?? * 2F16 KANGXI RADICAL HIDING ENCLOSURE * 5338 ?? * 2F17 KANGXI RADICAL TEN * 5341 ?? * 2F18 KANGXI RADICAL DIVINATION * 535C ?? * 2F19 KANGXI RADICAL SEAL * 5369 ?? * 2F1A KANGXI RADICAL CLIFF * 5382 ?? * 2F1B KANGXI RADICAL PRIVATE * 53B6 ?? * 2F1C KANGXI RADICAL AGAIN * 53C8 ?? * 2F1D KANGXI RADICAL MOUTH * 53E3 ?? * 2F1E KANGXI RADICAL ENCLOSURE * 56D7 ?? * 2F1F KANGXI RADICAL EARTH * 571F ?? * 2F20 KANGXI RADICAL SCHOLAR * 58EB ?? * 2F21 KANGXI RADICAL GO * 5902 ?? * 2F22 KANGXI RADICAL GO SLOWLY * 590A ?? * 2F23 KANGXI RADICAL EVENING * 5915 ?? * 2F24 KANGXI RADICAL BIG * 5927 ?? * 2F25 KANGXI RADICAL WOMAN * 5973 ?? * 2F26 KANGXI RADICAL CHILD * 5B50 ?? * 2F27 KANGXI RADICAL ROOF * 5B80 ?? * 2F28 KANGXI RADICAL INCH * 5BF8 ?? * 2F29 KANGXI RADICAL SMALL * 5C0F ?? * 2F2A KANGXI RADICAL LAME * 5C22 ?? * 2F2B KANGXI RADICAL CORPSE * 5C38 ?? * 2F2C KANGXI RADICAL SPROUT * 5C6E ?? * 2F2D KANGXI RADICAL MOUNTAIN * 5C71 ?? * 2F2E KANGXI RADICAL RIVER * 5DDB ?? * 2F2F KANGXI RADICAL WORK * 5DE5 ?? * 2F30 KANGXI RADICAL ONESELF * 5DF1 ?? * 2F31 KANGXI RADICAL TURBAN * 5DFE ?? * 2F32 KANGXI RADICAL DRY * 5E72 ?? * 2F33 KANGXI RADICAL SHORT THREAD * 5E7A ?? * 2F34 KANGXI RADICAL DOTTED CLIFF * 5E7F ?? * 2F35 KANGXI RADICAL LONG STRIDE * 5EF4 ?? * 2F36 KANGXI RADICAL TWO HANDS * 5EFE ?? * 2F37 KANGXI RADICAL SHOOT * 5F0B ?? * 2F38 KANGXI RADICAL BOW * 5F13 ?? * 2F39 KANGXI RADICAL SNOUT * 5F50 ?? * 2F3A KANGXI RADICAL BRISTLE * 5F61 ?? * 2F3B KANGXI RADICAL STEP * 5F73 ?? * 2F3C KANGXI RADICAL HEART * 5FC3 ?? * 2F3D KANGXI RADICAL HALBERD * 6208 ?? * 2F3E KANGXI RADICAL DOOR * 6236 ?? * 2F3F KANGXI RADICAL HAND * 624B ?? * 2F40 KANGXI RADICAL BRANCH * 652F ?? * 2F41 KANGXI RADICAL RAP * 6534 ?? * 2F42 KANGXI RADICAL SCRIPT * 6587 ?? * 2F43 KANGXI RADICAL DIPPER * 6597 ?? * 2F44 KANGXI RADICAL AXE * 65A4 ?? * 2F45 KANGXI RADICAL SQUARE * 65B9 ?? * 2F46 KANGXI RADICAL NOT * 65E0 ?? * 2F47 KANGXI RADICAL SUN * 65E5 ?? * 2F48 KANGXI RADICAL SAY * 66F0 ?? * 2F49 KANGXI RADICAL MOON * 6708 ?? * 2F4A KANGXI RADICAL TREE * 6728 ?? * 2F4B KANGXI RADICAL LACK * 6B20 ?? * 2F4C KANGXI RADICAL STOP * 6B62 ?? * 2F4D KANGXI RADICAL DEATH * 6B79 ?? * 2F4E KANGXI RADICAL WEAPON * 6BB3 ?? * 2F4F KANGXI RADICAL DO NOT * 6BCB ?? * 2F50 KANGXI RADICAL COMPARE * 6BD4 ?? * 2F51 KANGXI RADICAL FUR * 6BDB ?? * 2F52 KANGXI RADICAL CLAN * 6C0F ?? * 2F53 KANGXI RADICAL STEAM * 6C14 ?? * 2F54 KANGXI RADICAL WATER * 6C34 ?? * 2F55 KANGXI RADICAL FIRE * 706B ?? * 2F56 KANGXI RADICAL CLAW * 722A ?? * 2F57 KANGXI RADICAL FATHER * 7236 ?? * 2F58 KANGXI RADICAL DOUBLE X * 723B ?? * 2F59 KANGXI RADICAL HALF TREE TRUNK * 723F ?? * 2F5A KANGXI RADICAL SLICE * 7247 ?? * 2F5B KANGXI RADICAL FANG * 7259 ?? * 2F5C KANGXI RADICAL COW * 725B ?? * 2F5D KANGXI RADICAL DOG * 72AC ?? * 2F5E KANGXI RADICAL PROFOUND * 7384 ?? * 2F5F KANGXI RADICAL JADE * 7389 ?? * 2F60 KANGXI RADICAL MELON * 74DC ?? * 2F61 KANGXI RADICAL TILE * 74E6 ?? * 2F62 KANGXI RADICAL SWEET * 7518 ?? * 2F63 KANGXI RADICAL LIFE * 751F ?? * 2F64 KANGXI RADICAL USE * 7528 ?? * 2F65 KANGXI RADICAL FIELD * 7530 ?? * 2F66 KANGXI RADICAL BOLT OF CLOTH * 758B ?? * 2F67 KANGXI RADICAL SICKNESS * 7592 ?? * 2F68 KANGXI RADICAL DOTTED TENT * 7676 ?? * 2F69 KANGXI RADICAL WHITE * 767D ?? * 2F6A KANGXI RADICAL SKIN * 76AE ?? * 2F6B KANGXI RADICAL DISH * 76BF ?? * 2F6C KANGXI RADICAL EYE * 76EE ?? * 2F6D KANGXI RADICAL SPEAR * 77DB ?? * 2F6E KANGXI RADICAL ARROW * 77E2 ?? * 2F6F KANGXI RADICAL STONE * 77F3 ?? * 2F70 KANGXI RADICAL SPIRIT * 793A ?? * 2F71 KANGXI RADICAL TRACK * 79B8 ?? * 2F72 KANGXI RADICAL GRAIN * 79BE ?? * 2F73 KANGXI RADICAL CAVE * 7A74 ?? * 2F74 KANGXI RADICAL STAND * 7ACB ?? * 2F75 KANGXI RADICAL BAMBOO * 7AF9 ?? * 2F76 KANGXI RADICAL RICE * 7C73 ?? * 2F77 KANGXI RADICAL SILK * 7CF8 ?? * 2F78 KANGXI RADICAL JAR * 7F36 ?? * 2F79 KANGXI RADICAL NET * 7F51 ?? * 2F7A KANGXI RADICAL SHEEP * 7F8A ?? * 2F7B KANGXI RADICAL FEATHER * 7FBD ?? * 2F7C KANGXI RADICAL OLD * 8001 ?? * 2F7D KANGXI RADICAL AND * 800C ?? * 2F7E KANGXI RADICAL PLOW * 8012 ?? * 2F7F KANGXI RADICAL EAR * 8033 ?? * 2F80 KANGXI RADICAL BRUSH * 807F ?? * 2F81 KANGXI RADICAL MEAT * 8089 ?? * 2F82 KANGXI RADICAL MINISTER * 81E3 ?? * 2F83 KANGXI RADICAL SELF * 81EA ?? * 2F84 KANGXI RADICAL ARRIVE * 81F3 ?? * 2F85 KANGXI RADICAL MORTAR * 81FC ?? * 2F86 KANGXI RADICAL TONGUE * 820C ?? * 2F87 KANGXI RADICAL OPPOSE * 821B ?? * 2F88 KANGXI RADICAL BOAT * 821F ?? * 2F89 KANGXI RADICAL STOPPING * 826E ?? * 2F8A KANGXI RADICAL COLOR * 8272 ?? * 2F8B KANGXI RADICAL GRASS * 8278 ?? * 2F8C KANGXI RADICAL TIGER * 864D ?? * 2F8D KANGXI RADICAL INSECT * 866B ?? * 2F8E KANGXI RADICAL BLOOD * 8840 ?? * 2F8F KANGXI RADICAL WALK ENCLOSURE * 884C ?? * 2F90 KANGXI RADICAL CLOTHES * 8863 ?? * 2F91 KANGXI RADICAL WEST * 897E ?? * 2F92 KANGXI RADICAL SEE * 898B ?? * 2F93 KANGXI RADICAL HORN * 89D2 ?? * 2F94 KANGXI RADICAL SPEECH * 8A00 ?? * 2F95 KANGXI RADICAL VALLEY * 8C37 ?? * 2F96 KANGXI RADICAL BEAN * 8C46 ?? * 2F97 KANGXI RADICAL PIG * 8C55 ?? * 2F98 KANGXI RADICAL BADGER * 8C78 ?? * 2F99 KANGXI RADICAL SHELL * 8C9D ?? * 2F9A KANGXI RADICAL RED * 8D64 ?? * 2F9B KANGXI RADICAL RUN * 8D70 ?? * 2F9C KANGXI RADICAL FOOT * 8DB3 ?? * 2F9D KANGXI RADICAL BODY * 8EAB ?? * 2F9E KANGXI RADICAL CART * 8ECA ?? * 2F9F KANGXI RADICAL BITTER * 8F9B ?? * 2FA0 KANGXI RADICAL MORNING * 8FB0 ?? * 2FA1 KANGXI RADICAL WALK * 8FB5 ?? * 2FA2 KANGXI RADICAL CITY * 9091 ?? * 2FA3 KANGXI RADICAL WINE * 9149 ?? * 2FA4 KANGXI RADICAL DISTINGUISH * 91C6 ?? * 2FA5 KANGXI RADICAL VILLAGE * 91CC ?? * 2FA6 KANGXI RADICAL GOLD * 91D1 ?? * 2FA7 KANGXI RADICAL LONG * 9577 ?? * 2FA8 KANGXI RADICAL GATE * 9580 ?? * 2FA9 KANGXI RADICAL MOUND * 961C ?? * 2FAA KANGXI RADICAL SLAVE * 96B6 ?? * 2FAB KANGXI RADICAL SHORT TAILED BIRD * 96B9 ?? * 2FAC KANGXI RADICAL RAIN * 96E8 ?? * 2FAD KANGXI RADICAL BLUE * 9751 ?? * 2FAE KANGXI RADICAL WRONG * 975E ?? * 2FAF KANGXI RADICAL FACE * 9762 ?? * 2FB0 KANGXI RADICAL LEATHER * 9769 ?? * 2FB1 KANGXI RADICAL TANNED LEATHER * 97CB ?? * 2FB2 KANGXI RADICAL LEEK * 97ED ?? * 2FB3 KANGXI RADICAL SOUND * 97F3 ?? * 2FB4 KANGXI RADICAL LEAF * 9801 ?? * 2FB5 KANGXI RADICAL WIND * 98A8 ?? * 2FB6 KANGXI RADICAL FLY * 98DB ?? * 2FB7 KANGXI RADICAL EAT * 98DF ?? * 2FB8 KANGXI RADICAL HEAD * 9996 ?? * 2FB9 KANGXI RADICAL FRAGRANT * 9999 ?? * 2FBA KANGXI RADICAL HORSE * 99AC ?? * 2FBB KANGXI RADICAL BONE * 9AA8 ?? * 2FBC KANGXI RADICAL TALL * 9AD8 ?? * 2FBD KANGXI RADICAL HAIR * 9ADF ?? * 2FBE KANGXI RADICAL FIGHT * 9B25 ?? * 2FBF KANGXI RADICAL SACRIFICIAL WINE * 9B2F ?? * 2FC0 KANGXI RADICAL CAULDRON * 9B32 ?? * 2FC1 KANGXI RADICAL GHOST * 9B3C ?? * 2FC2 KANGXI RADICAL FISH * 9B5A ?? * 2FC3 KANGXI RADICAL BIRD * 9CE5 ?? * 2FC4 KANGXI RADICAL SALT * 9E75 ?? * 2FC5 KANGXI RADICAL DEER * 9E7F ?? * 2FC6 KANGXI RADICAL WHEAT * 9EA5 ?? * 2FC7 KANGXI RADICAL HEMP * 9EBB ?? * 2FC8 KANGXI RADICAL YELLOW * 9EC3 ?? * 2FC9 KANGXI RADICAL MILLET * 9ECD ?? * 2FCA KANGXI RADICAL BLACK * 9ED1 ?? * 2FCB KANGXI RADICAL EMBROIDERY * 9EF9 ?? * 2FCC KANGXI RADICAL FROG * 9EFD ?? * 2FCD KANGXI RADICAL TRIPOD * 9F0E ?? * 2FCE KANGXI RADICAL DRUM * 9F13 ?? * 2FCF KANGXI RADICAL RAT * 9F20 ?? * 2FD0 KANGXI RADICAL NOSE * 9F3B ?? * 2FD1 KANGXI RADICAL EVEN * 9F4A ?? * 2FD2 KANGXI RADICAL TOOTH * 9F52 ?? * 2FD3 KANGXI RADICAL DRAGON * 9F8D ?? * 2FD4 KANGXI RADICAL TURTLE * 9F9C ?? * 2FD5 KANGXI RADICAL FLUTE * 9FA0 ?? * 3000 IDEOGRAPHIC SPACE * 0020 SPACE * 302A IDEOGRAPHIC LEVEL TONE MARK * 0000 * 302B IDEOGRAPHIC RISING TONE MARK * 0000 * 302C IDEOGRAPHIC DEPARTING TONE MARK * 0000 * 302D IDEOGRAPHIC ENTERING TONE MARK * 0000 * 302E HANGUL SINGLE DOT TONE MARK * 0000 * 302F HANGUL DOUBLE DOT TONE MARK * 0000 * 3036 CIRCLED POSTAL MARK * 3012 POSTAL MARK * 3038 HANGZHOU NUMERAL TEN * 5341 ?? * 3039 HANGZHOU NUMERAL TWENTY * 5344 ?? * 303A HANGZHOU NUMERAL THIRTY * 5345 ?? * 3131 HANGUL LETTER KIYEOK * 1100 HANGUL CHOSEONG KIYEOK * 3132 HANGUL LETTER SSANGKIYEOK * 1101 HANGUL CHOSEONG SSANGKIYEOK * 3133 HANGUL LETTER KIYEOK-SIOS * 11AA HANGUL JONGSEONG KIYEOK-SIOS * 3134 HANGUL LETTER NIEUN * 1102 HANGUL CHOSEONG NIEUN * 3135 HANGUL LETTER NIEUN-CIEUC * 11AC HANGUL JONGSEONG NIEUN-CIEUC * 3136 HANGUL LETTER NIEUN-HIEUH * 11AD HANGUL JONGSEONG NIEUN-HIEUH * 3137 HANGUL LETTER TIKEUT * 1103 HANGUL CHOSEONG TIKEUT * 3138 HANGUL LETTER SSANGTIKEUT * 1104 HANGUL CHOSEONG SSANGTIKEUT * 3139 HANGUL LETTER RIEUL * 1105 HANGUL CHOSEONG RIEUL * 313A HANGUL LETTER RIEUL-KIYEOK * 11B0 HANGUL JONGSEONG RIEUL-KIYEOK * 313B HANGUL LETTER RIEUL-MIEUM * 11B1 HANGUL JONGSEONG RIEUL-MIEUM * 313C HANGUL LETTER RIEUL-PIEUP * 11B2 HANGUL JONGSEONG RIEUL-PIEUP * 313D HANGUL LETTER RIEUL-SIOS * 11B3 HANGUL JONGSEONG RIEUL-SIOS * 313E HANGUL LETTER RIEUL-THIEUTH * 11B4 HANGUL JONGSEONG RIEUL-THIEUTH * 313F HANGUL LETTER RIEUL-PHIEUPH * 11B5 HANGUL JONGSEONG RIEUL-PHIEUPH * 3140 HANGUL LETTER RIEUL-HIEUH * 111A HANGUL CHOSEONG RIEUL-HIEUH * 3141 HANGUL LETTER MIEUM * 1106 HANGUL CHOSEONG MIEUM * 3142 HANGUL LETTER PIEUP * 1107 HANGUL CHOSEONG PIEUP * 3143 HANGUL LETTER SSANGPIEUP * 1108 HANGUL CHOSEONG SSANGPIEUP * 3144 HANGUL LETTER PIEUP-SIOS * 1121 HANGUL CHOSEONG PIEUP-SIOS * 3145 HANGUL LETTER SIOS * 1109 HANGUL CHOSEONG SIOS * 3146 HANGUL LETTER SSANGSIOS * 110A HANGUL CHOSEONG SSANGSIOS * 3147 HANGUL LETTER IEUNG * 110B HANGUL CHOSEONG IEUNG * 3148 HANGUL LETTER CIEUC * 110C HANGUL CHOSEONG CIEUC * 3149 HANGUL LETTER SSANGCIEUC * 110D HANGUL CHOSEONG SSANGCIEUC * 314A HANGUL LETTER CHIEUCH * 110E HANGUL CHOSEONG CHIEUCH * 314B HANGUL LETTER KHIEUKH * 110F HANGUL CHOSEONG KHIEUKH * 314C HANGUL LETTER THIEUTH * 1110 HANGUL CHOSEONG THIEUTH * 314D HANGUL LETTER PHIEUPH * 1111 HANGUL CHOSEONG PHIEUPH * 314E HANGUL LETTER HIEUH * 1112 HANGUL CHOSEONG HIEUH * 314F HANGUL LETTER A * 1161 HANGUL JUNGSEONG A * 3150 HANGUL LETTER AE * 1162 HANGUL JUNGSEONG AE * 3151 HANGUL LETTER YA * 1163 HANGUL JUNGSEONG YA * 3152 HANGUL LETTER YAE * 1164 HANGUL JUNGSEONG YAE * 3153 HANGUL LETTER EO * 1165 HANGUL JUNGSEONG EO * 3154 HANGUL LETTER E * 1166 HANGUL JUNGSEONG E * 3155 HANGUL LETTER YEO * 1167 HANGUL JUNGSEONG YEO * 3156 HANGUL LETTER YE * 1168 HANGUL JUNGSEONG YE * 3157 HANGUL LETTER O * 1169 HANGUL JUNGSEONG O * 3158 HANGUL LETTER WA * 116A HANGUL JUNGSEONG WA * 3159 HANGUL LETTER WAE * 116B HANGUL JUNGSEONG WAE * 315A HANGUL LETTER OE * 116C HANGUL JUNGSEONG OE * 315B HANGUL LETTER YO * 116D HANGUL JUNGSEONG YO * 315C HANGUL LETTER U * 116E HANGUL JUNGSEONG U * 315D HANGUL LETTER WEO * 116F HANGUL JUNGSEONG WEO * 315E HANGUL LETTER WE * 1170 HANGUL JUNGSEONG WE * 315F HANGUL LETTER WI * 1171 HANGUL JUNGSEONG WI * 3160 HANGUL LETTER YU * 1172 HANGUL JUNGSEONG YU * 3161 HANGUL LETTER EU * 1173 HANGUL JUNGSEONG EU * 3162 HANGUL LETTER YI * 1174 HANGUL JUNGSEONG YI * 3163 HANGUL LETTER I * 1175 HANGUL JUNGSEONG I * 3164 HANGUL FILLER * 1160 HANGUL JUNGSEONG FILLER * 3165 HANGUL LETTER SSANGNIEUN * 1114 HANGUL CHOSEONG SSANGNIEUN * 3166 HANGUL LETTER NIEUN-TIKEUT * 1115 HANGUL CHOSEONG NIEUN-TIKEUT * 3167 HANGUL LETTER NIEUN-SIOS * 11C7 HANGUL JONGSEONG NIEUN-SIOS * 3168 HANGUL LETTER NIEUN-PANSIOS * 11C8 HANGUL JONGSEONG NIEUN-PANSIOS * 3169 HANGUL LETTER RIEUL-KIYEOK-SIOS * 11CC HANGUL JONGSEONG RIEUL-KIYEOK-SIOS * 316A HANGUL LETTER RIEUL-TIKEUT * 11CE HANGUL JONGSEONG RIEUL-TIKEUT * 316B HANGUL LETTER RIEUL-PIEUP-SIOS * 11D3 HANGUL JONGSEONG RIEUL-PIEUP-SIOS * 316C HANGUL LETTER RIEUL-PANSIOS * 11D7 HANGUL JONGSEONG RIEUL-PANSIOS * 316D HANGUL LETTER RIEUL-YEORINHIEUH * 11D9 HANGUL JONGSEONG RIEUL-YEORINHIEUH * 316E HANGUL LETTER MIEUM-PIEUP * 111C HANGUL CHOSEONG MIEUM-PIEUP * 316F HANGUL LETTER MIEUM-SIOS * 11DD HANGUL JONGSEONG MIEUM-SIOS * 3170 HANGUL LETTER MIEUM-PANSIOS * 11DF HANGUL JONGSEONG MIEUM-PANSIOS * 3171 HANGUL LETTER KAPYEOUNMIEUM * 111D HANGUL CHOSEONG KAPYEOUNMIEUM * 3172 HANGUL LETTER PIEUP-KIYEOK * 111E HANGUL CHOSEONG PIEUP-KIYEOK * 3173 HANGUL LETTER PIEUP-TIKEUT * 1120 HANGUL CHOSEONG PIEUP-TIKEUT * 3174 HANGUL LETTER PIEUP-SIOS-KIYEOK * 1122 HANGUL CHOSEONG PIEUP-SIOS-KIYEOK * 3175 HANGUL LETTER PIEUP-SIOS-TIKEUT * 1123 HANGUL CHOSEONG PIEUP-SIOS-TIKEUT * 3176 HANGUL LETTER PIEUP-CIEUC * 1127 HANGUL CHOSEONG PIEUP-CIEUC * 3177 HANGUL LETTER PIEUP-THIEUTH * 1129 HANGUL CHOSEONG PIEUP-THIEUTH * 3178 HANGUL LETTER KAPYEOUNPIEUP * 112B HANGUL CHOSEONG KAPYEOUNPIEUP * 3179 HANGUL LETTER KAPYEOUNSSANGPIEUP * 112C HANGUL CHOSEONG KAPYEOUNSSANGPIEUP * 317A HANGUL LETTER SIOS-KIYEOK * 112D HANGUL CHOSEONG SIOS-KIYEOK * 317B HANGUL LETTER SIOS-NIEUN * 112E HANGUL CHOSEONG SIOS-NIEUN * 317C HANGUL LETTER SIOS-TIKEUT * 112F HANGUL CHOSEONG SIOS-TIKEUT * 317D HANGUL LETTER SIOS-PIEUP * 1132 HANGUL CHOSEONG SIOS-PIEUP * 317E HANGUL LETTER SIOS-CIEUC * 1136 HANGUL CHOSEONG SIOS-CIEUC * 317F HANGUL LETTER PANSIOS * 1140 HANGUL CHOSEONG PANSIOS * 3180 HANGUL LETTER SSANGIEUNG * 1147 HANGUL CHOSEONG SSANGIEUNG * 3181 HANGUL LETTER YESIEUNG * 114C HANGUL CHOSEONG YESIEUNG * 3182 HANGUL LETTER YESIEUNG-SIOS * 11F1 HANGUL JONGSEONG YESIEUNG-SIOS * 3183 HANGUL LETTER YESIEUNG-PANSIOS * 11F2 HANGUL JONGSEONG YESIEUNG-PANSIOS * 3184 HANGUL LETTER KAPYEOUNPHIEUPH * 1157 HANGUL CHOSEONG KAPYEOUNPHIEUPH * 3185 HANGUL LETTER SSANGHIEUH * 1158 HANGUL CHOSEONG SSANGHIEUH * 3186 HANGUL LETTER YEORINHIEUH * 1159 HANGUL CHOSEONG YEORINHIEUH * 3187 HANGUL LETTER YO-YA * 1184 HANGUL JUNGSEONG YO-YA * 3188 HANGUL LETTER YO-YAE * 1185 HANGUL JUNGSEONG YO-YAE * 3189 HANGUL LETTER YO-I * 1188 HANGUL JUNGSEONG YO-I * 318A HANGUL LETTER YU-YEO * 1191 HANGUL JUNGSEONG YU-YEO * 318B HANGUL LETTER YU-YE * 1192 HANGUL JUNGSEONG YU-YE * 318C HANGUL LETTER YU-I * 1194 HANGUL JUNGSEONG YU-I * 318D HANGUL LETTER ARAEA * 119E HANGUL JUNGSEONG ARAEA * 318E HANGUL LETTER ARAEAE * 11A1 HANGUL JUNGSEONG ARAEA-I * 3192 IDEOGRAPHIC ANNOTATION ONE MARK * 4E00 * 3193 IDEOGRAPHIC ANNOTATION TWO MARK * 4E8C ?? * 3194 IDEOGRAPHIC ANNOTATION THREE MARK * 4E09 ?? * 3195 IDEOGRAPHIC ANNOTATION FOUR MARK * 56DB ?? * 3196 IDEOGRAPHIC ANNOTATION TOP MARK * 4E0A ?? * 3197 IDEOGRAPHIC ANNOTATION MIDDLE MARK * 4E2D ?? * 3198 IDEOGRAPHIC ANNOTATION BOTTOM MARK * 4E0B ?? * 3199 IDEOGRAPHIC ANNOTATION FIRST MARK * 7532 ?? * 319A IDEOGRAPHIC ANNOTATION SECOND MARK * 4E59 ?? * 319B IDEOGRAPHIC ANNOTATION THIRD MARK * 4E19 ?? * 319C IDEOGRAPHIC ANNOTATION FOURTH MARK * 4E01 ?? * 319D IDEOGRAPHIC ANNOTATION HEAVEN MARK * 5929 ?? * 319E IDEOGRAPHIC ANNOTATION EARTH MARK * 5730 ?? * 319F IDEOGRAPHIC ANNOTATION MAN MARK * 4EBA ?? * 3200 PARENTHESIZED HANGUL KIYEOK * 0028 LEFT PARENTHESIS * 1100 HANGUL CHOSEONG KIYEOK * 0029 RIGHT PARENTHESIS * 3201 PARENTHESIZED HANGUL NIEUN * 0028 LEFT PARENTHESIS * 1102 HANGUL CHOSEONG NIEUN * 0029 RIGHT PARENTHESIS * 3202 PARENTHESIZED HANGUL TIKEUT * 0028 LEFT PARENTHESIS * 1103 HANGUL CHOSEONG TIKEUT * 0029 RIGHT PARENTHESIS * 3203 PARENTHESIZED HANGUL RIEUL * 0028 LEFT PARENTHESIS * 1105 HANGUL CHOSEONG RIEUL * 0029 RIGHT PARENTHESIS * 3204 PARENTHESIZED HANGUL MIEUM * 0028 LEFT PARENTHESIS * 1106 HANGUL CHOSEONG MIEUM * 0029 RIGHT PARENTHESIS * 3205 PARENTHESIZED HANGUL PIEUP * 0028 LEFT PARENTHESIS * 1107 HANGUL CHOSEONG PIEUP * 0029 RIGHT PARENTHESIS * 3206 PARENTHESIZED HANGUL SIOS * 0028 LEFT PARENTHESIS * 1109 HANGUL CHOSEONG SIOS * 0029 RIGHT PARENTHESIS * 3207 PARENTHESIZED HANGUL IEUNG * 0028 LEFT PARENTHESIS * 110B HANGUL CHOSEONG IEUNG * 0029 RIGHT PARENTHESIS * 3208 PARENTHESIZED HANGUL CIEUC * 0028 LEFT PARENTHESIS * 110C HANGUL CHOSEONG CIEUC * 0029 RIGHT PARENTHESIS * 3209 PARENTHESIZED HANGUL CHIEUCH * 0028 LEFT PARENTHESIS * 110E HANGUL CHOSEONG CHIEUCH * 0029 RIGHT PARENTHESIS * 320A PARENTHESIZED HANGUL KHIEUKH * 0028 LEFT PARENTHESIS * 110F HANGUL CHOSEONG KHIEUKH * 0029 RIGHT PARENTHESIS * 320B PARENTHESIZED HANGUL THIEUTH * 0028 LEFT PARENTHESIS * 1110 HANGUL CHOSEONG THIEUTH * 0029 RIGHT PARENTHESIS * 320C PARENTHESIZED HANGUL PHIEUPH * 0028 LEFT PARENTHESIS * 1111 HANGUL CHOSEONG PHIEUPH * 0029 RIGHT PARENTHESIS * 320D PARENTHESIZED HANGUL HIEUH * 0028 LEFT PARENTHESIS * 1112 HANGUL CHOSEONG HIEUH * 0029 RIGHT PARENTHESIS * 320E PARENTHESIZED HANGUL KIYEOK A * 0028 LEFT PARENTHESIS * 1100 HANGUL CHOSEONG KIYEOK * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 320F PARENTHESIZED HANGUL NIEUN A * 0028 LEFT PARENTHESIS * 1102 HANGUL CHOSEONG NIEUN * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3210 PARENTHESIZED HANGUL TIKEUT A * 0028 LEFT PARENTHESIS * 1103 HANGUL CHOSEONG TIKEUT * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3211 PARENTHESIZED HANGUL RIEUL A * 0028 LEFT PARENTHESIS * 1105 HANGUL CHOSEONG RIEUL * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3212 PARENTHESIZED HANGUL MIEUM A * 0028 LEFT PARENTHESIS * 1106 HANGUL CHOSEONG MIEUM * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3213 PARENTHESIZED HANGUL PIEUP A * 0028 LEFT PARENTHESIS * 1107 HANGUL CHOSEONG PIEUP * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3214 PARENTHESIZED HANGUL SIOS A * 0028 LEFT PARENTHESIS * 1109 HANGUL CHOSEONG SIOS * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3215 PARENTHESIZED HANGUL IEUNG A * 0028 LEFT PARENTHESIS * 110B HANGUL CHOSEONG IEUNG * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3216 PARENTHESIZED HANGUL CIEUC A * 0028 LEFT PARENTHESIS * 110C HANGUL CHOSEONG CIEUC * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3217 PARENTHESIZED HANGUL CHIEUCH A * 0028 LEFT PARENTHESIS * 110E HANGUL CHOSEONG CHIEUCH * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3218 PARENTHESIZED HANGUL KHIEUKH A * 0028 LEFT PARENTHESIS * 110F HANGUL CHOSEONG KHIEUKH * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 3219 PARENTHESIZED HANGUL THIEUTH A * 0028 LEFT PARENTHESIS * 1110 HANGUL CHOSEONG THIEUTH * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 321A PARENTHESIZED HANGUL PHIEUPH A * 0028 LEFT PARENTHESIS * 1111 HANGUL CHOSEONG PHIEUPH * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 321B PARENTHESIZED HANGUL HIEUH A * 0028 LEFT PARENTHESIS * 1112 HANGUL CHOSEONG HIEUH * 1161 HANGUL JUNGSEONG A * 0029 RIGHT PARENTHESIS * 321C PARENTHESIZED HANGUL CIEUC U * 0028 LEFT PARENTHESIS * 110C HANGUL CHOSEONG CIEUC * 116E HANGUL JUNGSEONG U * 0029 RIGHT PARENTHESIS * 321D PARENTHESIZED KOREAN CHARACTER OJEON * 0028 LEFT PARENTHESIS * 110B HANGUL CHOSEONG IEUNG * 1169 HANGUL JUNGSEONG O * 110C HANGUL CHOSEONG CIEUC * 1165 HANGUL JUNGSEONG EO * 11AB HANGUL JONGSEONG NIEUN * 0029 RIGHT PARENTHESIS * 321E PARENTHESIZED KOREAN CHARACTER O HU * 0028 LEFT PARENTHESIS * 110B HANGUL CHOSEONG IEUNG * 1169 HANGUL JUNGSEONG O * 1112 HANGUL CHOSEONG HIEUH * 116E HANGUL JUNGSEONG U * 0029 RIGHT PARENTHESIS * 3220 PARENTHESIZED IDEOGRAPH ONE * 0028 LEFT PARENTHESIS * 4E00 * 0029 RIGHT PARENTHESIS * 3221 PARENTHESIZED IDEOGRAPH TWO * 0028 LEFT PARENTHESIS * 4E8C ?? * 0029 RIGHT PARENTHESIS * 3222 PARENTHESIZED IDEOGRAPH THREE * 0028 LEFT PARENTHESIS * 4E09 ?? * 0029 RIGHT PARENTHESIS * 3223 PARENTHESIZED IDEOGRAPH FOUR * 0028 LEFT PARENTHESIS * 56DB ?? * 0029 RIGHT PARENTHESIS * 3224 PARENTHESIZED IDEOGRAPH FIVE * 0028 LEFT PARENTHESIS * 4E94 ?? * 0029 RIGHT PARENTHESIS * 3225 PARENTHESIZED IDEOGRAPH SIX * 0028 LEFT PARENTHESIS * 516D ?? * 0029 RIGHT PARENTHESIS * 3226 PARENTHESIZED IDEOGRAPH SEVEN * 0028 LEFT PARENTHESIS * 4E03 ?? * 0029 RIGHT PARENTHESIS * 3227 PARENTHESIZED IDEOGRAPH EIGHT * 0028 LEFT PARENTHESIS * 516B ?? * 0029 RIGHT PARENTHESIS * 3228 PARENTHESIZED IDEOGRAPH NINE * 0028 LEFT PARENTHESIS * 4E5D ?? * 0029 RIGHT PARENTHESIS * 3229 PARENTHESIZED IDEOGRAPH TEN * 0028 LEFT PARENTHESIS * 5341 ?? * 0029 RIGHT PARENTHESIS * 322A PARENTHESIZED IDEOGRAPH MOON * 0028 LEFT PARENTHESIS * 6708 ?? * 0029 RIGHT PARENTHESIS * 322B PARENTHESIZED IDEOGRAPH FIRE * 0028 LEFT PARENTHESIS * 706B ?? * 0029 RIGHT PARENTHESIS * 322C PARENTHESIZED IDEOGRAPH WATER * 0028 LEFT PARENTHESIS * 6C34 ?? * 0029 RIGHT PARENTHESIS * 322D PARENTHESIZED IDEOGRAPH WOOD * 0028 LEFT PARENTHESIS * 6728 ?? * 0029 RIGHT PARENTHESIS * 322E PARENTHESIZED IDEOGRAPH METAL * 0028 LEFT PARENTHESIS * 91D1 ?? * 0029 RIGHT PARENTHESIS * 322F PARENTHESIZED IDEOGRAPH EARTH * 0028 LEFT PARENTHESIS * 571F ?? * 0029 RIGHT PARENTHESIS * 3230 PARENTHESIZED IDEOGRAPH SUN * 0028 LEFT PARENTHESIS * 65E5 ?? * 0029 RIGHT PARENTHESIS * 3231 PARENTHESIZED IDEOGRAPH STOCK * 0028 LEFT PARENTHESIS * 682A ?? * 0029 RIGHT PARENTHESIS * 3232 PARENTHESIZED IDEOGRAPH HAVE * 0028 LEFT PARENTHESIS * 6709 ?? * 0029 RIGHT PARENTHESIS * 3233 PARENTHESIZED IDEOGRAPH SOCIETY * 0028 LEFT PARENTHESIS * 793E ?? * 0029 RIGHT PARENTHESIS * 3234 PARENTHESIZED IDEOGRAPH NAME * 0028 LEFT PARENTHESIS * 540D ?? * 0029 RIGHT PARENTHESIS * 3235 PARENTHESIZED IDEOGRAPH SPECIAL * 0028 LEFT PARENTHESIS * 7279 ?? * 0029 RIGHT PARENTHESIS * 3236 PARENTHESIZED IDEOGRAPH FINANCIAL * 0028 LEFT PARENTHESIS * 8CA1 ?? * 0029 RIGHT PARENTHESIS * 3237 PARENTHESIZED IDEOGRAPH CONGRATULATION * 0028 LEFT PARENTHESIS * 795D ?? * 0029 RIGHT PARENTHESIS * 3238 PARENTHESIZED IDEOGRAPH LABOR * 0028 LEFT PARENTHESIS * 52B4 ?? * 0029 RIGHT PARENTHESIS * 3239 PARENTHESIZED IDEOGRAPH REPRESENT * 0028 LEFT PARENTHESIS * 4EE3 ?? * 0029 RIGHT PARENTHESIS * 323A PARENTHESIZED IDEOGRAPH CALL * 0028 LEFT PARENTHESIS * 547C ?? * 0029 RIGHT PARENTHESIS * 323B PARENTHESIZED IDEOGRAPH STUDY * 0028 LEFT PARENTHESIS * 5B66 ?? * 0029 RIGHT PARENTHESIS * 323C PARENTHESIZED IDEOGRAPH SUPERVISE * 0028 LEFT PARENTHESIS * 76E3 ?? * 0029 RIGHT PARENTHESIS * 323D PARENTHESIZED IDEOGRAPH ENTERPRISE * 0028 LEFT PARENTHESIS * 4F01 ?? * 0029 RIGHT PARENTHESIS * 323E PARENTHESIZED IDEOGRAPH RESOURCE * 0028 LEFT PARENTHESIS * 8CC7 ?? * 0029 RIGHT PARENTHESIS * 323F PARENTHESIZED IDEOGRAPH ALLIANCE * 0028 LEFT PARENTHESIS * 5354 ?? * 0029 RIGHT PARENTHESIS * 3240 PARENTHESIZED IDEOGRAPH FESTIVAL * 0028 LEFT PARENTHESIS * 796D ?? * 0029 RIGHT PARENTHESIS * 3241 PARENTHESIZED IDEOGRAPH REST * 0028 LEFT PARENTHESIS * 4F11 ?? * 0029 RIGHT PARENTHESIS * 3242 PARENTHESIZED IDEOGRAPH SELF * 0028 LEFT PARENTHESIS * 81EA ?? * 0029 RIGHT PARENTHESIS * 3243 PARENTHESIZED IDEOGRAPH REACH * 0028 LEFT PARENTHESIS * 81F3 ?? * 0029 RIGHT PARENTHESIS * 3244 CIRCLED IDEOGRAPH QUESTION * 554F ?? * 3245 CIRCLED IDEOGRAPH KINDERGARTEN * 5E7C ?? * 3246 CIRCLED IDEOGRAPH SCHOOL * 6587 ?? * 3247 CIRCLED IDEOGRAPH KOTO * 7B8F ?? * 3250 PARTNERSHIP SIGN * 0050 LATIN CAPITAL LETTER P * 0054 LATIN CAPITAL LETTER T * 0045 LATIN CAPITAL LETTER E * 3251 CIRCLED NUMBER TWENTY ONE * 0032 DIGIT TWO * 0031 DIGIT ONE * 3252 CIRCLED NUMBER TWENTY TWO * 0032 DIGIT TWO * 0032 DIGIT TWO * 3253 CIRCLED NUMBER TWENTY THREE * 0032 DIGIT TWO * 0033 DIGIT THREE * 3254 CIRCLED NUMBER TWENTY FOUR * 0032 DIGIT TWO * 0034 DIGIT FOUR * 3255 CIRCLED NUMBER TWENTY FIVE * 0032 DIGIT TWO * 0035 DIGIT FIVE * 3256 CIRCLED NUMBER TWENTY SIX * 0032 DIGIT TWO * 0036 DIGIT SIX * 3257 CIRCLED NUMBER TWENTY SEVEN * 0032 DIGIT TWO * 0037 DIGIT SEVEN * 3258 CIRCLED NUMBER TWENTY EIGHT * 0032 DIGIT TWO * 0038 DIGIT EIGHT * 3259 CIRCLED NUMBER TWENTY NINE * 0032 DIGIT TWO * 0039 DIGIT NINE * 325A CIRCLED NUMBER THIRTY * 0033 DIGIT THREE * 0030 DIGIT ZERO * 325B CIRCLED NUMBER THIRTY ONE * 0033 DIGIT THREE * 0031 DIGIT ONE * 325C CIRCLED NUMBER THIRTY TWO * 0033 DIGIT THREE * 0032 DIGIT TWO * 325D CIRCLED NUMBER THIRTY THREE * 0033 DIGIT THREE * 0033 DIGIT THREE * 325E CIRCLED NUMBER THIRTY FOUR * 0033 DIGIT THREE * 0034 DIGIT FOUR * 325F CIRCLED NUMBER THIRTY FIVE * 0033 DIGIT THREE * 0035 DIGIT FIVE * 3260 CIRCLED HANGUL KIYEOK * 1100 HANGUL CHOSEONG KIYEOK * 3261 CIRCLED HANGUL NIEUN * 1102 HANGUL CHOSEONG NIEUN * 3262 CIRCLED HANGUL TIKEUT * 1103 HANGUL CHOSEONG TIKEUT * 3263 CIRCLED HANGUL RIEUL * 1105 HANGUL CHOSEONG RIEUL * 3264 CIRCLED HANGUL MIEUM * 1106 HANGUL CHOSEONG MIEUM * 3265 CIRCLED HANGUL PIEUP * 1107 HANGUL CHOSEONG PIEUP * 3266 CIRCLED HANGUL SIOS * 1109 HANGUL CHOSEONG SIOS * 3267 CIRCLED HANGUL IEUNG * 110B HANGUL CHOSEONG IEUNG * 3268 CIRCLED HANGUL CIEUC * 110C HANGUL CHOSEONG CIEUC * 3269 CIRCLED HANGUL CHIEUCH * 110E HANGUL CHOSEONG CHIEUCH * 326A CIRCLED HANGUL KHIEUKH * 110F HANGUL CHOSEONG KHIEUKH * 326B CIRCLED HANGUL THIEUTH * 1110 HANGUL CHOSEONG THIEUTH * 326C CIRCLED HANGUL PHIEUPH * 1111 HANGUL CHOSEONG PHIEUPH * 326D CIRCLED HANGUL HIEUH * 1112 HANGUL CHOSEONG HIEUH * 326E CIRCLED HANGUL KIYEOK A * 1100 HANGUL CHOSEONG KIYEOK * 1161 HANGUL JUNGSEONG A * 326F CIRCLED HANGUL NIEUN A * 1102 HANGUL CHOSEONG NIEUN * 1161 HANGUL JUNGSEONG A * 3270 CIRCLED HANGUL TIKEUT A * 1103 HANGUL CHOSEONG TIKEUT * 1161 HANGUL JUNGSEONG A * 3271 CIRCLED HANGUL RIEUL A * 1105 HANGUL CHOSEONG RIEUL * 1161 HANGUL JUNGSEONG A * 3272 CIRCLED HANGUL MIEUM A * 1106 HANGUL CHOSEONG MIEUM * 1161 HANGUL JUNGSEONG A * 3273 CIRCLED HANGUL PIEUP A * 1107 HANGUL CHOSEONG PIEUP * 1161 HANGUL JUNGSEONG A * 3274 CIRCLED HANGUL SIOS A * 1109 HANGUL CHOSEONG SIOS * 1161 HANGUL JUNGSEONG A * 3275 CIRCLED HANGUL IEUNG A * 110B HANGUL CHOSEONG IEUNG * 1161 HANGUL JUNGSEONG A * 3276 CIRCLED HANGUL CIEUC A * 110C HANGUL CHOSEONG CIEUC * 1161 HANGUL JUNGSEONG A * 3277 CIRCLED HANGUL CHIEUCH A * 110E HANGUL CHOSEONG CHIEUCH * 1161 HANGUL JUNGSEONG A * 3278 CIRCLED HANGUL KHIEUKH A * 110F HANGUL CHOSEONG KHIEUKH * 1161 HANGUL JUNGSEONG A * 3279 CIRCLED HANGUL THIEUTH A * 1110 HANGUL CHOSEONG THIEUTH * 1161 HANGUL JUNGSEONG A * 327A CIRCLED HANGUL PHIEUPH A * 1111 HANGUL CHOSEONG PHIEUPH * 1161 HANGUL JUNGSEONG A * 327B CIRCLED HANGUL HIEUH A * 1112 HANGUL CHOSEONG HIEUH * 1161 HANGUL JUNGSEONG A * 327C CIRCLED KOREAN CHARACTER CHAMKO * 110E HANGUL CHOSEONG CHIEUCH * 1161 HANGUL JUNGSEONG A * 11B7 HANGUL JONGSEONG MIEUM * 1100 HANGUL CHOSEONG KIYEOK * 1169 HANGUL JUNGSEONG O * 327D CIRCLED KOREAN CHARACTER JUEUI * 110C HANGUL CHOSEONG CIEUC * 116E HANGUL JUNGSEONG U * 110B HANGUL CHOSEONG IEUNG * 1174 HANGUL JUNGSEONG YI * 327E CIRCLED HANGUL IEUNG U * 110B HANGUL CHOSEONG IEUNG * 116E HANGUL JUNGSEONG U * 3280 CIRCLED IDEOGRAPH ONE * 4E00 * 3281 CIRCLED IDEOGRAPH TWO * 4E8C ?? * 3282 CIRCLED IDEOGRAPH THREE * 4E09 ?? * 3283 CIRCLED IDEOGRAPH FOUR * 56DB ?? * 3284 CIRCLED IDEOGRAPH FIVE * 4E94 ?? * 3285 CIRCLED IDEOGRAPH SIX * 516D ?? * 3286 CIRCLED IDEOGRAPH SEVEN * 4E03 ?? * 3287 CIRCLED IDEOGRAPH EIGHT * 516B ?? * 3288 CIRCLED IDEOGRAPH NINE * 4E5D ?? * 3289 CIRCLED IDEOGRAPH TEN * 5341 ?? * 328A CIRCLED IDEOGRAPH MOON * 6708 ?? * 328B CIRCLED IDEOGRAPH FIRE * 706B ?? * 328C CIRCLED IDEOGRAPH WATER * 6C34 ?? * 328D CIRCLED IDEOGRAPH WOOD * 6728 ?? * 328E CIRCLED IDEOGRAPH METAL * 91D1 ?? * 328F CIRCLED IDEOGRAPH EARTH * 571F ?? * 3290 CIRCLED IDEOGRAPH SUN * 65E5 ?? * 3291 CIRCLED IDEOGRAPH STOCK * 682A ?? * 3292 CIRCLED IDEOGRAPH HAVE * 6709 ?? * 3293 CIRCLED IDEOGRAPH SOCIETY * 793E ?? * 3294 CIRCLED IDEOGRAPH NAME * 540D ?? * 3295 CIRCLED IDEOGRAPH SPECIAL * 7279 ?? * 3296 CIRCLED IDEOGRAPH FINANCIAL * 8CA1 ?? * 3297 CIRCLED IDEOGRAPH CONGRATULATION * 795D ?? * 3298 CIRCLED IDEOGRAPH LABOR * 52B4 ?? * 3299 CIRCLED IDEOGRAPH SECRET * 79D8 ?? * 329A CIRCLED IDEOGRAPH MALE * 7537 ?? * 329B CIRCLED IDEOGRAPH FEMALE * 5973 ?? * 329C CIRCLED IDEOGRAPH SUITABLE * 9069 ?? * 329D CIRCLED IDEOGRAPH EXCELLENT * 512A ?? * 329E CIRCLED IDEOGRAPH PRINT * 5370 ?? * 329F CIRCLED IDEOGRAPH ATTENTION * 6CE8 ?? * 32A0 CIRCLED IDEOGRAPH ITEM * 9805 ?? * 32A1 CIRCLED IDEOGRAPH REST * 4F11 ?? * 32A2 CIRCLED IDEOGRAPH COPY * 5199 ?? * 32A3 CIRCLED IDEOGRAPH CORRECT * 6B63 ?? * 32A4 CIRCLED IDEOGRAPH HIGH * 4E0A ?? * 32A5 CIRCLED IDEOGRAPH CENTRE * 4E2D ?? * 32A6 CIRCLED IDEOGRAPH LOW * 4E0B ?? * 32A7 CIRCLED IDEOGRAPH LEFT * 5DE6 ?? * 32A8 CIRCLED IDEOGRAPH RIGHT * 53F3 ?? * 32A9 CIRCLED IDEOGRAPH MEDICINE * 533B ?? * 32AA CIRCLED IDEOGRAPH RELIGION * 5B97 ?? * 32AB CIRCLED IDEOGRAPH STUDY * 5B66 ?? * 32AC CIRCLED IDEOGRAPH SUPERVISE * 76E3 ?? * 32AD CIRCLED IDEOGRAPH ENTERPRISE * 4F01 ?? * 32AE CIRCLED IDEOGRAPH RESOURCE * 8CC7 ?? * 32AF CIRCLED IDEOGRAPH ALLIANCE * 5354 ?? * 32B0 CIRCLED IDEOGRAPH NIGHT * 591C ?? * 32B1 CIRCLED NUMBER THIRTY SIX * 0033 DIGIT THREE * 0036 DIGIT SIX * 32B2 CIRCLED NUMBER THIRTY SEVEN * 0033 DIGIT THREE * 0037 DIGIT SEVEN * 32B3 CIRCLED NUMBER THIRTY EIGHT * 0033 DIGIT THREE * 0038 DIGIT EIGHT * 32B4 CIRCLED NUMBER THIRTY NINE * 0033 DIGIT THREE * 0039 DIGIT NINE * 32B5 CIRCLED NUMBER FORTY * 0034 DIGIT FOUR * 0030 DIGIT ZERO * 32B6 CIRCLED NUMBER FORTY ONE * 0034 DIGIT FOUR * 0031 DIGIT ONE * 32B7 CIRCLED NUMBER FORTY TWO * 0034 DIGIT FOUR * 0032 DIGIT TWO * 32B8 CIRCLED NUMBER FORTY THREE * 0034 DIGIT FOUR * 0033 DIGIT THREE * 32B9 CIRCLED NUMBER FORTY FOUR * 0034 DIGIT FOUR * 0034 DIGIT FOUR * 32BA CIRCLED NUMBER FORTY FIVE * 0034 DIGIT FOUR * 0035 DIGIT FIVE * 32BB CIRCLED NUMBER FORTY SIX * 0034 DIGIT FOUR * 0036 DIGIT SIX * 32BC CIRCLED NUMBER FORTY SEVEN * 0034 DIGIT FOUR * 0037 DIGIT SEVEN * 32BD CIRCLED NUMBER FORTY EIGHT * 0034 DIGIT FOUR * 0038 DIGIT EIGHT * 32BE CIRCLED NUMBER FORTY NINE * 0034 DIGIT FOUR * 0039 DIGIT NINE * 32BF CIRCLED NUMBER FIFTY * 0035 DIGIT FIVE * 0030 DIGIT ZERO * 32C0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY * 0031 DIGIT ONE * 6708 ?? * 32C1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY * 0032 DIGIT TWO * 6708 ?? * 32C2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH * 0033 DIGIT THREE * 6708 ?? * 32C3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL * 0034 DIGIT FOUR * 6708 ?? * 32C4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY * 0035 DIGIT FIVE * 6708 ?? * 32C5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE * 0036 DIGIT SIX * 6708 ?? * 32C6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY * 0037 DIGIT SEVEN * 6708 ?? * 32C7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST * 0038 DIGIT EIGHT * 6708 ?? * 32C8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER * 0039 DIGIT NINE * 6708 ?? * 32C9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER * 0031 DIGIT ONE * 0030 DIGIT ZERO * 6708 ?? * 32CA IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER * 0031 DIGIT ONE * 0031 DIGIT ONE * 6708 ?? * 32CB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER * 0031 DIGIT ONE * 0032 DIGIT TWO * 6708 ?? * 32CC SQUARE HG * 0048 LATIN CAPITAL LETTER H * 0067 LATIN SMALL LETTER G * 32CD SQUARE ERG * 0065 LATIN SMALL LETTER E * 0072 LATIN SMALL LETTER R * 0067 LATIN SMALL LETTER G * 32CE SQUARE EV * 0065 LATIN SMALL LETTER E * 0056 LATIN CAPITAL LETTER V * 32CF LIMITED LIABILITY SIGN * 004C LATIN CAPITAL LETTER L * 0054 LATIN CAPITAL LETTER T * 0044 LATIN CAPITAL LETTER D * 32D0 CIRCLED KATAKANA A * 30A2 KATAKANA LETTER A * 32D1 CIRCLED KATAKANA I * 30A4 KATAKANA LETTER I * 32D2 CIRCLED KATAKANA U * 30A6 KATAKANA LETTER U * 32D3 CIRCLED KATAKANA E * 30A8 KATAKANA LETTER E * 32D4 CIRCLED KATAKANA O * 30AA KATAKANA LETTER O * 32D5 CIRCLED KATAKANA KA * 30AB KATAKANA LETTER KA * 32D6 CIRCLED KATAKANA KI * 30AD KATAKANA LETTER KI * 32D7 CIRCLED KATAKANA KU * 30AF KATAKANA LETTER KU * 32D8 CIRCLED KATAKANA KE * 30B1 KATAKANA LETTER KE * 32D9 CIRCLED KATAKANA KO * 30B3 KATAKANA LETTER KO * 32DA CIRCLED KATAKANA SA * 30B5 KATAKANA LETTER SA * 32DB CIRCLED KATAKANA SI * 30B7 KATAKANA LETTER SI * 32DC CIRCLED KATAKANA SU * 30B9 KATAKANA LETTER SU * 32DD CIRCLED KATAKANA SE * 30BB KATAKANA LETTER SE * 32DE CIRCLED KATAKANA SO * 30BD KATAKANA LETTER SO * 32DF CIRCLED KATAKANA TA * 30BF KATAKANA LETTER TA * 32E0 CIRCLED KATAKANA TI * 30C1 KATAKANA LETTER TI * 32E1 CIRCLED KATAKANA TU * 30C4 KATAKANA LETTER TU * 32E2 CIRCLED KATAKANA TE * 30C6 KATAKANA LETTER TE * 32E3 CIRCLED KATAKANA TO * 30C8 KATAKANA LETTER TO * 32E4 CIRCLED KATAKANA NA * 30CA KATAKANA LETTER NA * 32E5 CIRCLED KATAKANA NI * 30CB KATAKANA LETTER NI * 32E6 CIRCLED KATAKANA NU * 30CC KATAKANA LETTER NU * 32E7 CIRCLED KATAKANA NE * 30CD KATAKANA LETTER NE * 32E8 CIRCLED KATAKANA NO * 30CE KATAKANA LETTER NO * 32E9 CIRCLED KATAKANA HA * 30CF KATAKANA LETTER HA * 32EA CIRCLED KATAKANA HI * 30D2 KATAKANA LETTER HI * 32EB CIRCLED KATAKANA HU * 30D5 KATAKANA LETTER HU * 32EC CIRCLED KATAKANA HE * 30D8 KATAKANA LETTER HE * 32ED CIRCLED KATAKANA HO * 30DB KATAKANA LETTER HO * 32EE CIRCLED KATAKANA MA * 30DE KATAKANA LETTER MA * 32EF CIRCLED KATAKANA MI * 30DF KATAKANA LETTER MI * 32F0 CIRCLED KATAKANA MU * 30E0 KATAKANA LETTER MU * 32F1 CIRCLED KATAKANA ME * 30E1 KATAKANA LETTER ME * 32F2 CIRCLED KATAKANA MO * 30E2 KATAKANA LETTER MO * 32F3 CIRCLED KATAKANA YA * 30E4 KATAKANA LETTER YA * 32F4 CIRCLED KATAKANA YU * 30E6 KATAKANA LETTER YU * 32F5 CIRCLED KATAKANA YO * 30E8 KATAKANA LETTER YO * 32F6 CIRCLED KATAKANA RA * 30E9 KATAKANA LETTER RA * 32F7 CIRCLED KATAKANA RI * 30EA KATAKANA LETTER RI * 32F8 CIRCLED KATAKANA RU * 30EB KATAKANA LETTER RU * 32F9 CIRCLED KATAKANA RE * 30EC KATAKANA LETTER RE * 32FA CIRCLED KATAKANA RO * 30ED KATAKANA LETTER RO * 32FB CIRCLED KATAKANA WA * 30EF KATAKANA LETTER WA * 32FC CIRCLED KATAKANA WI * 30F0 KATAKANA LETTER WI * 32FD CIRCLED KATAKANA WE * 30F1 KATAKANA LETTER WE * 32FE CIRCLED KATAKANA WO * 30F2 KATAKANA LETTER WO * 32FF SQUARE ERA NAME REIWA * 4EE4 ?? * 548C ?? * 3300 SQUARE APAATO * 30A2 KATAKANA LETTER A * 30D1 KATAKANA LETTER PA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30C8 KATAKANA LETTER TO * 3301 SQUARE ARUHUA * 30A2 KATAKANA LETTER A * 30EB KATAKANA LETTER RU * 30D5 KATAKANA LETTER HU * 30A1 KATAKANA LETTER SMALL A * 3302 SQUARE ANPEA * 30A2 KATAKANA LETTER A * 30F3 KATAKANA LETTER N * 30DA KATAKANA LETTER PE * 30A2 KATAKANA LETTER A * 3303 SQUARE AARU * 30A2 KATAKANA LETTER A * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30EB KATAKANA LETTER RU * 3304 SQUARE ININGU * 30A4 KATAKANA LETTER I * 30CB KATAKANA LETTER NI * 30F3 KATAKANA LETTER N * 30B0 KATAKANA LETTER GU * 3305 SQUARE INTI * 30A4 KATAKANA LETTER I * 30F3 KATAKANA LETTER N * 30C1 KATAKANA LETTER TI * 3306 SQUARE UON * 30A6 KATAKANA LETTER U * 30A9 KATAKANA LETTER SMALL O * 30F3 KATAKANA LETTER N * 3307 SQUARE ESUKUUDO * 30A8 KATAKANA LETTER E * 30B9 KATAKANA LETTER SU * 30AF KATAKANA LETTER KU * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30C9 KATAKANA LETTER DO * 3308 SQUARE EEKAA * 30A8 KATAKANA LETTER E * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30AB KATAKANA LETTER KA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 3309 SQUARE ONSU * 30AA KATAKANA LETTER O * 30F3 KATAKANA LETTER N * 30B9 KATAKANA LETTER SU * 330A SQUARE OOMU * 30AA KATAKANA LETTER O * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30E0 KATAKANA LETTER MU * 330B SQUARE KAIRI * 30AB KATAKANA LETTER KA * 30A4 KATAKANA LETTER I * 30EA KATAKANA LETTER RI * 330C SQUARE KARATTO * 30AB KATAKANA LETTER KA * 30E9 KATAKANA LETTER RA * 30C3 KATAKANA LETTER SMALL TU * 30C8 KATAKANA LETTER TO * 330D SQUARE KARORII * 30AB KATAKANA LETTER KA * 30ED KATAKANA LETTER RO * 30EA KATAKANA LETTER RI * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 330E SQUARE GARON * 30AC KATAKANA LETTER GA * 30ED KATAKANA LETTER RO * 30F3 KATAKANA LETTER N * 330F SQUARE GANMA * 30AC KATAKANA LETTER GA * 30F3 KATAKANA LETTER N * 30DE KATAKANA LETTER MA * 3310 SQUARE GIGA * 30AE KATAKANA LETTER GI * 30AC KATAKANA LETTER GA * 3311 SQUARE GINII * 30AE KATAKANA LETTER GI * 30CB KATAKANA LETTER NI * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 3312 SQUARE KYURII * 30AD KATAKANA LETTER KI * 30E5 KATAKANA LETTER SMALL YU * 30EA KATAKANA LETTER RI * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 3313 SQUARE GIRUDAA * 30AE KATAKANA LETTER GI * 30EB KATAKANA LETTER RU * 30C0 KATAKANA LETTER DA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 3314 SQUARE KIRO * 30AD KATAKANA LETTER KI * 30ED KATAKANA LETTER RO * 3315 SQUARE KIROGURAMU * 30AD KATAKANA LETTER KI * 30ED KATAKANA LETTER RO * 30B0 KATAKANA LETTER GU * 30E9 KATAKANA LETTER RA * 30E0 KATAKANA LETTER MU * 3316 SQUARE KIROMEETORU * 30AD KATAKANA LETTER KI * 30ED KATAKANA LETTER RO * 30E1 KATAKANA LETTER ME * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30C8 KATAKANA LETTER TO * 30EB KATAKANA LETTER RU * 3317 SQUARE KIROWATTO * 30AD KATAKANA LETTER KI * 30ED KATAKANA LETTER RO * 30EF KATAKANA LETTER WA * 30C3 KATAKANA LETTER SMALL TU * 30C8 KATAKANA LETTER TO * 3318 SQUARE GURAMU * 30B0 KATAKANA LETTER GU * 30E9 KATAKANA LETTER RA * 30E0 KATAKANA LETTER MU * 3319 SQUARE GURAMUTON * 30B0 KATAKANA LETTER GU * 30E9 KATAKANA LETTER RA * 30E0 KATAKANA LETTER MU * 30C8 KATAKANA LETTER TO * 30F3 KATAKANA LETTER N * 331A SQUARE KURUZEIRO * 30AF KATAKANA LETTER KU * 30EB KATAKANA LETTER RU * 30BC KATAKANA LETTER ZE * 30A4 KATAKANA LETTER I * 30ED KATAKANA LETTER RO * 331B SQUARE KUROONE * 30AF KATAKANA LETTER KU * 30ED KATAKANA LETTER RO * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30CD KATAKANA LETTER NE * 331C SQUARE KEESU * 30B1 KATAKANA LETTER KE * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30B9 KATAKANA LETTER SU * 331D SQUARE KORUNA * 30B3 KATAKANA LETTER KO * 30EB KATAKANA LETTER RU * 30CA KATAKANA LETTER NA * 331E SQUARE KOOPO * 30B3 KATAKANA LETTER KO * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30DD KATAKANA LETTER PO * 331F SQUARE SAIKURU * 30B5 KATAKANA LETTER SA * 30A4 KATAKANA LETTER I * 30AF KATAKANA LETTER KU * 30EB KATAKANA LETTER RU * 3320 SQUARE SANTIIMU * 30B5 KATAKANA LETTER SA * 30F3 KATAKANA LETTER N * 30C1 KATAKANA LETTER TI * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30E0 KATAKANA LETTER MU * 3321 SQUARE SIRINGU * 30B7 KATAKANA LETTER SI * 30EA KATAKANA LETTER RI * 30F3 KATAKANA LETTER N * 30B0 KATAKANA LETTER GU * 3322 SQUARE SENTI * 30BB KATAKANA LETTER SE * 30F3 KATAKANA LETTER N * 30C1 KATAKANA LETTER TI * 3323 SQUARE SENTO * 30BB KATAKANA LETTER SE * 30F3 KATAKANA LETTER N * 30C8 KATAKANA LETTER TO * 3324 SQUARE DAASU * 30C0 KATAKANA LETTER DA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30B9 KATAKANA LETTER SU * 3325 SQUARE DESI * 30C7 KATAKANA LETTER DE * 30B7 KATAKANA LETTER SI * 3326 SQUARE DORU * 30C9 KATAKANA LETTER DO * 30EB KATAKANA LETTER RU * 3327 SQUARE TON * 30C8 KATAKANA LETTER TO * 30F3 KATAKANA LETTER N * 3328 SQUARE NANO * 30CA KATAKANA LETTER NA * 30CE KATAKANA LETTER NO * 3329 SQUARE NOTTO * 30CE KATAKANA LETTER NO * 30C3 KATAKANA LETTER SMALL TU * 30C8 KATAKANA LETTER TO * 332A SQUARE HAITU * 30CF KATAKANA LETTER HA * 30A4 KATAKANA LETTER I * 30C4 KATAKANA LETTER TU * 332B SQUARE PAASENTO * 30D1 KATAKANA LETTER PA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30BB KATAKANA LETTER SE * 30F3 KATAKANA LETTER N * 30C8 KATAKANA LETTER TO * 332C SQUARE PAATU * 30D1 KATAKANA LETTER PA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30C4 KATAKANA LETTER TU * 332D SQUARE BAARERU * 30D0 KATAKANA LETTER BA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30EC KATAKANA LETTER RE * 30EB KATAKANA LETTER RU * 332E SQUARE PIASUTORU * 30D4 KATAKANA LETTER PI * 30A2 KATAKANA LETTER A * 30B9 KATAKANA LETTER SU * 30C8 KATAKANA LETTER TO * 30EB KATAKANA LETTER RU * 332F SQUARE PIKURU * 30D4 KATAKANA LETTER PI * 30AF KATAKANA LETTER KU * 30EB KATAKANA LETTER RU * 3330 SQUARE PIKO * 30D4 KATAKANA LETTER PI * 30B3 KATAKANA LETTER KO * 3331 SQUARE BIRU * 30D3 KATAKANA LETTER BI * 30EB KATAKANA LETTER RU * 3332 SQUARE HUARADDO * 30D5 KATAKANA LETTER HU * 30A1 KATAKANA LETTER SMALL A * 30E9 KATAKANA LETTER RA * 30C3 KATAKANA LETTER SMALL TU * 30C9 KATAKANA LETTER DO * 3333 SQUARE HUIITO * 30D5 KATAKANA LETTER HU * 30A3 KATAKANA LETTER SMALL I * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30C8 KATAKANA LETTER TO * 3334 SQUARE BUSSYERU * 30D6 KATAKANA LETTER BU * 30C3 KATAKANA LETTER SMALL TU * 30B7 KATAKANA LETTER SI * 30A7 KATAKANA LETTER SMALL E * 30EB KATAKANA LETTER RU * 3335 SQUARE HURAN * 30D5 KATAKANA LETTER HU * 30E9 KATAKANA LETTER RA * 30F3 KATAKANA LETTER N * 3336 SQUARE HEKUTAARU * 30D8 KATAKANA LETTER HE * 30AF KATAKANA LETTER KU * 30BF KATAKANA LETTER TA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30EB KATAKANA LETTER RU * 3337 SQUARE PESO * 30DA KATAKANA LETTER PE * 30BD KATAKANA LETTER SO * 3338 SQUARE PENIHI * 30DA KATAKANA LETTER PE * 30CB KATAKANA LETTER NI * 30D2 KATAKANA LETTER HI * 3339 SQUARE HERUTU * 30D8 KATAKANA LETTER HE * 30EB KATAKANA LETTER RU * 30C4 KATAKANA LETTER TU * 333A SQUARE PENSU * 30DA KATAKANA LETTER PE * 30F3 KATAKANA LETTER N * 30B9 KATAKANA LETTER SU * 333B SQUARE PEEZI * 30DA KATAKANA LETTER PE * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30B8 KATAKANA LETTER ZI * 333C SQUARE BEETA * 30D9 KATAKANA LETTER BE * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30BF KATAKANA LETTER TA * 333D SQUARE POINTO * 30DD KATAKANA LETTER PO * 30A4 KATAKANA LETTER I * 30F3 KATAKANA LETTER N * 30C8 KATAKANA LETTER TO * 333E SQUARE BORUTO * 30DC KATAKANA LETTER BO * 30EB KATAKANA LETTER RU * 30C8 KATAKANA LETTER TO * 333F SQUARE HON * 30DB KATAKANA LETTER HO * 30F3 KATAKANA LETTER N * 3340 SQUARE PONDO * 30DD KATAKANA LETTER PO * 30F3 KATAKANA LETTER N * 30C9 KATAKANA LETTER DO * 3341 SQUARE HOORU * 30DB KATAKANA LETTER HO * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30EB KATAKANA LETTER RU * 3342 SQUARE HOON * 30DB KATAKANA LETTER HO * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30F3 KATAKANA LETTER N * 3343 SQUARE MAIKURO * 30DE KATAKANA LETTER MA * 30A4 KATAKANA LETTER I * 30AF KATAKANA LETTER KU * 30ED KATAKANA LETTER RO * 3344 SQUARE MAIRU * 30DE KATAKANA LETTER MA * 30A4 KATAKANA LETTER I * 30EB KATAKANA LETTER RU * 3345 SQUARE MAHHA * 30DE KATAKANA LETTER MA * 30C3 KATAKANA LETTER SMALL TU * 30CF KATAKANA LETTER HA * 3346 SQUARE MARUKU * 30DE KATAKANA LETTER MA * 30EB KATAKANA LETTER RU * 30AF KATAKANA LETTER KU * 3347 SQUARE MANSYON * 30DE KATAKANA LETTER MA * 30F3 KATAKANA LETTER N * 30B7 KATAKANA LETTER SI * 30E7 KATAKANA LETTER SMALL YO * 30F3 KATAKANA LETTER N * 3348 SQUARE MIKURON * 30DF KATAKANA LETTER MI * 30AF KATAKANA LETTER KU * 30ED KATAKANA LETTER RO * 30F3 KATAKANA LETTER N * 3349 SQUARE MIRI * 30DF KATAKANA LETTER MI * 30EA KATAKANA LETTER RI * 334A SQUARE MIRIBAARU * 30DF KATAKANA LETTER MI * 30EA KATAKANA LETTER RI * 30D0 KATAKANA LETTER BA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30EB KATAKANA LETTER RU * 334B SQUARE MEGA * 30E1 KATAKANA LETTER ME * 30AC KATAKANA LETTER GA * 334C SQUARE MEGATON * 30E1 KATAKANA LETTER ME * 30AC KATAKANA LETTER GA * 30C8 KATAKANA LETTER TO * 30F3 KATAKANA LETTER N * 334D SQUARE MEETORU * 30E1 KATAKANA LETTER ME * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30C8 KATAKANA LETTER TO * 30EB KATAKANA LETTER RU * 334E SQUARE YAADO * 30E4 KATAKANA LETTER YA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30C9 KATAKANA LETTER DO * 334F SQUARE YAARU * 30E4 KATAKANA LETTER YA * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30EB KATAKANA LETTER RU * 3350 SQUARE YUAN * 30E6 KATAKANA LETTER YU * 30A2 KATAKANA LETTER A * 30F3 KATAKANA LETTER N * 3351 SQUARE RITTORU * 30EA KATAKANA LETTER RI * 30C3 KATAKANA LETTER SMALL TU * 30C8 KATAKANA LETTER TO * 30EB KATAKANA LETTER RU * 3352 SQUARE RIRA * 30EA KATAKANA LETTER RI * 30E9 KATAKANA LETTER RA * 3353 SQUARE RUPII * 30EB KATAKANA LETTER RU * 30D4 KATAKANA LETTER PI * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 3354 SQUARE RUUBURU * 30EB KATAKANA LETTER RU * 30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK * 30D6 KATAKANA LETTER BU * 30EB KATAKANA LETTER RU * 3355 SQUARE REMU * 30EC KATAKANA LETTER RE * 30E0 KATAKANA LETTER MU * 3356 SQUARE RENTOGEN * 30EC KATAKANA LETTER RE * 30F3 KATAKANA LETTER N * 30C8 KATAKANA LETTER TO * 30B2 KATAKANA LETTER GE * 30F3 KATAKANA LETTER N * 3357 SQUARE WATTO * 30EF KATAKANA LETTER WA * 30C3 KATAKANA LETTER SMALL TU * 30C8 KATAKANA LETTER TO * 3358 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO * 0030 DIGIT ZERO * 70B9 ?? * 3359 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE * 0031 DIGIT ONE * 70B9 ?? * 335A IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO * 0032 DIGIT TWO * 70B9 ?? * 335B IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE * 0033 DIGIT THREE * 70B9 ?? * 335C IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR * 0034 DIGIT FOUR * 70B9 ?? * 335D IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE * 0035 DIGIT FIVE * 70B9 ?? * 335E IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX * 0036 DIGIT SIX * 70B9 ?? * 335F IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN * 0037 DIGIT SEVEN * 70B9 ?? * 3360 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT * 0038 DIGIT EIGHT * 70B9 ?? * 3361 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE * 0039 DIGIT NINE * 70B9 ?? * 3362 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN * 0031 DIGIT ONE * 0030 DIGIT ZERO * 70B9 ?? * 3363 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN * 0031 DIGIT ONE * 0031 DIGIT ONE * 70B9 ?? * 3364 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE * 0031 DIGIT ONE * 0032 DIGIT TWO * 70B9 ?? * 3365 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN * 0031 DIGIT ONE * 0033 DIGIT THREE * 70B9 ?? * 3366 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN * 0031 DIGIT ONE * 0034 DIGIT FOUR * 70B9 ?? * 3367 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN * 0031 DIGIT ONE * 0035 DIGIT FIVE * 70B9 ?? * 3368 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN * 0031 DIGIT ONE * 0036 DIGIT SIX * 70B9 ?? * 3369 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN * 0031 DIGIT ONE * 0037 DIGIT SEVEN * 70B9 ?? * 336A IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN * 0031 DIGIT ONE * 0038 DIGIT EIGHT * 70B9 ?? * 336B IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN * 0031 DIGIT ONE * 0039 DIGIT NINE * 70B9 ?? * 336C IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY * 0032 DIGIT TWO * 0030 DIGIT ZERO * 70B9 ?? * 336D IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE * 0032 DIGIT TWO * 0031 DIGIT ONE * 70B9 ?? * 336E IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO * 0032 DIGIT TWO * 0032 DIGIT TWO * 70B9 ?? * 336F IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE * 0032 DIGIT TWO * 0033 DIGIT THREE * 70B9 ?? * 3370 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR * 0032 DIGIT TWO * 0034 DIGIT FOUR * 70B9 ?? * 3371 SQUARE HPA * 0068 LATIN SMALL LETTER H * 0050 LATIN CAPITAL LETTER P * 0061 LATIN SMALL LETTER A * 3372 SQUARE DA * 0064 LATIN SMALL LETTER D * 0061 LATIN SMALL LETTER A * 3373 SQUARE AU * 0041 LATIN CAPITAL LETTER A * 0055 LATIN CAPITAL LETTER U * 3374 SQUARE BAR * 0062 LATIN SMALL LETTER B * 0061 LATIN SMALL LETTER A * 0072 LATIN SMALL LETTER R * 3375 SQUARE OV * 006F LATIN SMALL LETTER O * 0056 LATIN CAPITAL LETTER V * 3376 SQUARE PC * 0070 LATIN SMALL LETTER P * 0063 LATIN SMALL LETTER C * 3377 SQUARE DM * 0064 LATIN SMALL LETTER D * 006D LATIN SMALL LETTER M * 3378 SQUARE DM SQUARED * 0064 LATIN SMALL LETTER D * 006D LATIN SMALL LETTER M * 0032 DIGIT TWO * 3379 SQUARE DM CUBED * 0064 LATIN SMALL LETTER D * 006D LATIN SMALL LETTER M * 0033 DIGIT THREE * 337A SQUARE IU * 0049 LATIN CAPITAL LETTER I * 0055 LATIN CAPITAL LETTER U * 337B SQUARE ERA NAME HEISEI * 5E73 ?? * 6210 ?? * 337C SQUARE ERA NAME SYOUWA * 662D ?? * 548C ?? * 337D SQUARE ERA NAME TAISYOU * 5927 ?? * 6B63 ?? * 337E SQUARE ERA NAME MEIZI * 660E ?? * 6CBB ?? * 337F SQUARE CORPORATION * 682A ?? * 5F0F ?? * 4F1A ?? * 793E ?? * 3380 SQUARE PA AMPS * 0070 LATIN SMALL LETTER P * 0041 LATIN CAPITAL LETTER A * 3381 SQUARE NA * 006E LATIN SMALL LETTER N * 0041 LATIN CAPITAL LETTER A * 3382 SQUARE MU A * 03BC GREEK SMALL LETTER MU * 0041 LATIN CAPITAL LETTER A * 3383 SQUARE MA * 006D LATIN SMALL LETTER M * 0041 LATIN CAPITAL LETTER A * 3384 SQUARE KA * 006B LATIN SMALL LETTER K * 0041 LATIN CAPITAL LETTER A * 3385 SQUARE KB * 004B LATIN CAPITAL LETTER K * 0042 LATIN CAPITAL LETTER B * 3386 SQUARE MB * 004D LATIN CAPITAL LETTER M * 0042 LATIN CAPITAL LETTER B * 3387 SQUARE GB * 0047 LATIN CAPITAL LETTER G * 0042 LATIN CAPITAL LETTER B * 3388 SQUARE CAL * 0063 LATIN SMALL LETTER C * 0061 LATIN SMALL LETTER A * 006C LATIN SMALL LETTER L * 3389 SQUARE KCAL * 006B LATIN SMALL LETTER K * 0063 LATIN SMALL LETTER C * 0061 LATIN SMALL LETTER A * 006C LATIN SMALL LETTER L * 338A SQUARE PF * 0070 LATIN SMALL LETTER P * 0046 LATIN CAPITAL LETTER F * 338B SQUARE NF * 006E LATIN SMALL LETTER N * 0046 LATIN CAPITAL LETTER F * 338C SQUARE MU F * 03BC GREEK SMALL LETTER MU * 0046 LATIN CAPITAL LETTER F * 338D SQUARE MU G * 03BC GREEK SMALL LETTER MU * 0067 LATIN SMALL LETTER G * 338E SQUARE MG * 006D LATIN SMALL LETTER M * 0067 LATIN SMALL LETTER G * 338F SQUARE KG * 006B LATIN SMALL LETTER K * 0067 LATIN SMALL LETTER G * 3390 SQUARE HZ * 0048 LATIN CAPITAL LETTER H * 007A LATIN SMALL LETTER Z * 3391 SQUARE KHZ * 006B LATIN SMALL LETTER K * 0048 LATIN CAPITAL LETTER H * 007A LATIN SMALL LETTER Z * 3392 SQUARE MHZ * 004D LATIN CAPITAL LETTER M * 0048 LATIN CAPITAL LETTER H * 007A LATIN SMALL LETTER Z * 3393 SQUARE GHZ * 0047 LATIN CAPITAL LETTER G * 0048 LATIN CAPITAL LETTER H * 007A LATIN SMALL LETTER Z * 3394 SQUARE THZ * 0054 LATIN CAPITAL LETTER T * 0048 LATIN CAPITAL LETTER H * 007A LATIN SMALL LETTER Z * 3395 SQUARE MU L * 03BC GREEK SMALL LETTER MU * 006C LATIN SMALL LETTER L * 3396 SQUARE ML * 006D LATIN SMALL LETTER M * 006C LATIN SMALL LETTER L * 3397 SQUARE DL * 0064 LATIN SMALL LETTER D * 006C LATIN SMALL LETTER L * 3398 SQUARE KL * 006B LATIN SMALL LETTER K * 006C LATIN SMALL LETTER L * 3399 SQUARE FM * 0066 LATIN SMALL LETTER F * 006D LATIN SMALL LETTER M * 339A SQUARE NM * 006E LATIN SMALL LETTER N * 006D LATIN SMALL LETTER M * 339B SQUARE MU M * 03BC GREEK SMALL LETTER MU * 006D LATIN SMALL LETTER M * 339C SQUARE MM * 006D LATIN SMALL LETTER M * 006D LATIN SMALL LETTER M * 339D SQUARE CM * 0063 LATIN SMALL LETTER C * 006D LATIN SMALL LETTER M * 339E SQUARE KM * 006B LATIN SMALL LETTER K * 006D LATIN SMALL LETTER M * 339F SQUARE MM SQUARED * 006D LATIN SMALL LETTER M * 006D LATIN SMALL LETTER M * 0032 DIGIT TWO * 33A0 SQUARE CM SQUARED * 0063 LATIN SMALL LETTER C * 006D LATIN SMALL LETTER M * 0032 DIGIT TWO * 33A1 SQUARE M SQUARED * 006D LATIN SMALL LETTER M * 0032 DIGIT TWO * 33A2 SQUARE KM SQUARED * 006B LATIN SMALL LETTER K * 006D LATIN SMALL LETTER M * 0032 DIGIT TWO * 33A3 SQUARE MM CUBED * 006D LATIN SMALL LETTER M * 006D LATIN SMALL LETTER M * 0033 DIGIT THREE * 33A4 SQUARE CM CUBED * 0063 LATIN SMALL LETTER C * 006D LATIN SMALL LETTER M * 0033 DIGIT THREE * 33A5 SQUARE M CUBED * 006D LATIN SMALL LETTER M * 0033 DIGIT THREE * 33A6 SQUARE KM CUBED * 006B LATIN SMALL LETTER K * 006D LATIN SMALL LETTER M * 0033 DIGIT THREE * 33A7 SQUARE M OVER S * 006D LATIN SMALL LETTER M * 2215 DIVISION SLASH * 0073 LATIN SMALL LETTER S * 33A8 SQUARE M OVER S SQUARED * 006D LATIN SMALL LETTER M * 2215 DIVISION SLASH * 0073 LATIN SMALL LETTER S * 0032 DIGIT TWO * 33A9 SQUARE PA * 0050 LATIN CAPITAL LETTER P * 0061 LATIN SMALL LETTER A * 33AA SQUARE KPA * 006B LATIN SMALL LETTER K * 0050 LATIN CAPITAL LETTER P * 0061 LATIN SMALL LETTER A * 33AB SQUARE MPA * 004D LATIN CAPITAL LETTER M * 0050 LATIN CAPITAL LETTER P * 0061 LATIN SMALL LETTER A * 33AC SQUARE GPA * 0047 LATIN CAPITAL LETTER G * 0050 LATIN CAPITAL LETTER P * 0061 LATIN SMALL LETTER A * 33AD SQUARE RAD * 0072 LATIN SMALL LETTER R * 0061 LATIN SMALL LETTER A * 0064 LATIN SMALL LETTER D * 33AE SQUARE RAD OVER S * 0072 LATIN SMALL LETTER R * 0061 LATIN SMALL LETTER A * 0064 LATIN SMALL LETTER D * 2215 DIVISION SLASH * 0073 LATIN SMALL LETTER S * 33AF SQUARE RAD OVER S SQUARED * 0072 LATIN SMALL LETTER R * 0061 LATIN SMALL LETTER A * 0064 LATIN SMALL LETTER D * 2215 DIVISION SLASH * 0073 LATIN SMALL LETTER S * 0032 DIGIT TWO * 33B0 SQUARE PS * 0070 LATIN SMALL LETTER P * 0073 LATIN SMALL LETTER S * 33B1 SQUARE NS * 006E LATIN SMALL LETTER N * 0073 LATIN SMALL LETTER S * 33B2 SQUARE MU S * 03BC GREEK SMALL LETTER MU * 0073 LATIN SMALL LETTER S * 33B3 SQUARE MS * 006D LATIN SMALL LETTER M * 0073 LATIN SMALL LETTER S * 33B4 SQUARE PV * 0070 LATIN SMALL LETTER P * 0056 LATIN CAPITAL LETTER V * 33B5 SQUARE NV * 006E LATIN SMALL LETTER N * 0056 LATIN CAPITAL LETTER V * 33B6 SQUARE MU V * 03BC GREEK SMALL LETTER MU * 0056 LATIN CAPITAL LETTER V * 33B7 SQUARE MV * 006D LATIN SMALL LETTER M * 0056 LATIN CAPITAL LETTER V * 33B8 SQUARE KV * 006B LATIN SMALL LETTER K * 0056 LATIN CAPITAL LETTER V * 33B9 SQUARE MV MEGA * 004D LATIN CAPITAL LETTER M * 0056 LATIN CAPITAL LETTER V * 33BA SQUARE PW * 0070 LATIN SMALL LETTER P * 0057 LATIN CAPITAL LETTER W * 33BB SQUARE NW * 006E LATIN SMALL LETTER N * 0057 LATIN CAPITAL LETTER W * 33BC SQUARE MU W * 03BC GREEK SMALL LETTER MU * 0057 LATIN CAPITAL LETTER W * 33BD SQUARE MW * 006D LATIN SMALL LETTER M * 0057 LATIN CAPITAL LETTER W * 33BE SQUARE KW * 006B LATIN SMALL LETTER K * 0057 LATIN CAPITAL LETTER W * 33BF SQUARE MW MEGA * 004D LATIN CAPITAL LETTER M * 0057 LATIN CAPITAL LETTER W * 33C0 SQUARE K OHM * 006B LATIN SMALL LETTER K * 03A9 GREEK CAPITAL LETTER OMEGA * 33C1 SQUARE M OHM * 004D LATIN CAPITAL LETTER M * 03A9 GREEK CAPITAL LETTER OMEGA * 33C2 SQUARE AM * 0061 LATIN SMALL LETTER A * 002E FULL STOP * 006D LATIN SMALL LETTER M * 002E FULL STOP * 33C3 SQUARE BQ * 0042 LATIN CAPITAL LETTER B * 0071 LATIN SMALL LETTER Q * 33C4 SQUARE CC * 0063 LATIN SMALL LETTER C * 0063 LATIN SMALL LETTER C * 33C5 SQUARE CD * 0063 LATIN SMALL LETTER C * 0064 LATIN SMALL LETTER D * 33C6 SQUARE C OVER KG * 0043 LATIN CAPITAL LETTER C * 2215 DIVISION SLASH * 006B LATIN SMALL LETTER K * 0067 LATIN SMALL LETTER G * 33C7 SQUARE CO * 0043 LATIN CAPITAL LETTER C * 006F LATIN SMALL LETTER O * 002E FULL STOP * 33C8 SQUARE DB * 0064 LATIN SMALL LETTER D * 0042 LATIN CAPITAL LETTER B * 33C9 SQUARE GY * 0047 LATIN CAPITAL LETTER G * 0079 LATIN SMALL LETTER Y * 33CA SQUARE HA * 0068 LATIN SMALL LETTER H * 0061 LATIN SMALL LETTER A * 33CB SQUARE HP * 0048 LATIN CAPITAL LETTER H * 0050 LATIN CAPITAL LETTER P * 33CC SQUARE IN * 0069 LATIN SMALL LETTER I * 006E LATIN SMALL LETTER N * 33CD SQUARE KK * 004B LATIN CAPITAL LETTER K * 004B LATIN CAPITAL LETTER K * 33CE SQUARE KM CAPITAL * 004B LATIN CAPITAL LETTER K * 004D LATIN CAPITAL LETTER M * 33CF SQUARE KT * 006B LATIN SMALL LETTER K * 0074 LATIN SMALL LETTER T * 33D0 SQUARE LM * 006C LATIN SMALL LETTER L * 006D LATIN SMALL LETTER M * 33D1 SQUARE LN * 006C LATIN SMALL LETTER L * 006E LATIN SMALL LETTER N * 33D2 SQUARE LOG * 006C LATIN SMALL LETTER L * 006F LATIN SMALL LETTER O * 0067 LATIN SMALL LETTER G * 33D3 SQUARE LX * 006C LATIN SMALL LETTER L * 0078 LATIN SMALL LETTER X * 33D4 SQUARE MB SMALL * 006D LATIN SMALL LETTER M * 0062 LATIN SMALL LETTER B * 33D5 SQUARE MIL * 006D LATIN SMALL LETTER M * 0069 LATIN SMALL LETTER I * 006C LATIN SMALL LETTER L * 33D6 SQUARE MOL * 006D LATIN SMALL LETTER M * 006F LATIN SMALL LETTER O * 006C LATIN SMALL LETTER L * 33D7 SQUARE PH * 0050 LATIN CAPITAL LETTER P * 0048 LATIN CAPITAL LETTER H * 33D8 SQUARE PM * 0070 LATIN SMALL LETTER P * 002E FULL STOP * 006D LATIN SMALL LETTER M * 002E FULL STOP * 33D9 SQUARE PPM * 0050 LATIN CAPITAL LETTER P * 0050 LATIN CAPITAL LETTER P * 004D LATIN CAPITAL LETTER M * 33DA SQUARE PR * 0050 LATIN CAPITAL LETTER P * 0052 LATIN CAPITAL LETTER R * 33DB SQUARE SR * 0073 LATIN SMALL LETTER S * 0072 LATIN SMALL LETTER R * 33DC SQUARE SV * 0053 LATIN CAPITAL LETTER S * 0076 LATIN SMALL LETTER V * 33DD SQUARE WB * 0057 LATIN CAPITAL LETTER W * 0062 LATIN SMALL LETTER B * 33DE SQUARE V OVER M * 0056 LATIN CAPITAL LETTER V * 2215 DIVISION SLASH * 006D LATIN SMALL LETTER M * 33DF SQUARE A OVER M * 0041 LATIN CAPITAL LETTER A * 2215 DIVISION SLASH * 006D LATIN SMALL LETTER M * 33E0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE * 0031 DIGIT ONE * 65E5 ?? * 33E1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO * 0032 DIGIT TWO * 65E5 ?? * 33E2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE * 0033 DIGIT THREE * 65E5 ?? * 33E3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR * 0034 DIGIT FOUR * 65E5 ?? * 33E4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE * 0035 DIGIT FIVE * 65E5 ?? * 33E5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX * 0036 DIGIT SIX * 65E5 ?? * 33E6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN * 0037 DIGIT SEVEN * 65E5 ?? * 33E7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT * 0038 DIGIT EIGHT * 65E5 ?? * 33E8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE * 0039 DIGIT NINE * 65E5 ?? * 33E9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN * 0031 DIGIT ONE * 0030 DIGIT ZERO * 65E5 ?? * 33EA IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN * 0031 DIGIT ONE * 0031 DIGIT ONE * 65E5 ?? * 33EB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE * 0031 DIGIT ONE * 0032 DIGIT TWO * 65E5 ?? * 33EC IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN * 0031 DIGIT ONE * 0033 DIGIT THREE * 65E5 ?? * 33ED IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN * 0031 DIGIT ONE * 0034 DIGIT FOUR * 65E5 ?? * 33EE IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN * 0031 DIGIT ONE * 0035 DIGIT FIVE * 65E5 ?? * 33EF IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN * 0031 DIGIT ONE * 0036 DIGIT SIX * 65E5 ?? * 33F0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN * 0031 DIGIT ONE * 0037 DIGIT SEVEN * 65E5 ?? * 33F1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN * 0031 DIGIT ONE * 0038 DIGIT EIGHT * 65E5 ?? * 33F2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN * 0031 DIGIT ONE * 0039 DIGIT NINE * 65E5 ?? * 33F3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY * 0032 DIGIT TWO * 0030 DIGIT ZERO * 65E5 ?? * 33F4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE * 0032 DIGIT TWO * 0031 DIGIT ONE * 65E5 ?? * 33F5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO * 0032 DIGIT TWO * 0032 DIGIT TWO * 65E5 ?? * 33F6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE * 0032 DIGIT TWO * 0033 DIGIT THREE * 65E5 ?? * 33F7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR * 0032 DIGIT TWO * 0034 DIGIT FOUR * 65E5 ?? * 33F8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE * 0032 DIGIT TWO * 0035 DIGIT FIVE * 65E5 ?? * 33F9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX * 0032 DIGIT TWO * 0036 DIGIT SIX * 65E5 ?? * 33FA IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN * 0032 DIGIT TWO * 0037 DIGIT SEVEN * 65E5 ?? * 33FB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT * 0032 DIGIT TWO * 0038 DIGIT EIGHT * 65E5 ?? * 33FC IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE * 0032 DIGIT TWO * 0039 DIGIT NINE * 65E5 ?? * 33FD IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY * 0033 DIGIT THREE * 0030 DIGIT ZERO * 65E5 ?? * 33FE IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE * 0033 DIGIT THREE * 0031 DIGIT ONE * 65E5 ?? * 33FF SQUARE GAL * 0067 LATIN SMALL LETTER G * 0061 LATIN SMALL LETTER A * 006C LATIN SMALL LETTER L * A66F COMBINING CYRILLIC VZMET * 0000 * A670 COMBINING CYRILLIC TEN MILLIONS SIGN * 0000 * A671 COMBINING CYRILLIC HUNDRED MILLIONS SIGN * 0000 * A672 COMBINING CYRILLIC THOUSAND MILLIONS SIGN * 0000 * A674 COMBINING CYRILLIC LETTER UKRAINIAN IE * 0000 * A675 COMBINING CYRILLIC LETTER I * 0000 * A676 COMBINING CYRILLIC LETTER YI * 0000 * A677 COMBINING CYRILLIC LETTER U * 0000 * A678 COMBINING CYRILLIC LETTER HARD SIGN * 0000 * A679 COMBINING CYRILLIC LETTER YERU * 0000 * A67A COMBINING CYRILLIC LETTER SOFT SIGN * 0000 * A67B COMBINING CYRILLIC LETTER OMEGA * 0000 * A67C COMBINING CYRILLIC KAVYKA * 0000 * A67D COMBINING CYRILLIC PAYEROK * 0000 * A69C MODIFIER LETTER CYRILLIC HARD SIGN * 044A CYRILLIC SMALL LETTER HARD SIGN * A69D MODIFIER LETTER CYRILLIC SOFT SIGN * 044C CYRILLIC SMALL LETTER SOFT SIGN * A69E COMBINING CYRILLIC LETTER EF * 0000 * A69F COMBINING CYRILLIC LETTER IOTIFIED E * 0000 * A6F0 BAMUM COMBINING MARK KOQNDON * 0000 * A6F1 BAMUM COMBINING MARK TUKWENTIS * 0000 * A770 MODIFIER LETTER US * A76F LATIN SMALL LETTER CON * A7F2 MODIFIER LETTER CAPITAL C * 0043 LATIN CAPITAL LETTER C * A7F3 MODIFIER LETTER CAPITAL F * 0046 LATIN CAPITAL LETTER F * A7F4 MODIFIER LETTER CAPITAL Q * 0051 LATIN CAPITAL LETTER Q * A7F8 MODIFIER LETTER CAPITAL H WITH STROKE * 0126 LATIN CAPITAL LETTER H WITH STROKE * A7F9 MODIFIER LETTER SMALL LIGATURE OE * 0153 LATIN SMALL LIGATURE OE * A802 SYLOTI NAGRI SIGN DVISVARA * 0000 * A806 SYLOTI NAGRI SIGN HASANTA * 0000 * A80B SYLOTI NAGRI SIGN ANUSVARA * 0000 * A823 SYLOTI NAGRI VOWEL SIGN A * 0000 * A824 SYLOTI NAGRI VOWEL SIGN I * 0000 * A825 SYLOTI NAGRI VOWEL SIGN U * 0000 * A826 SYLOTI NAGRI VOWEL SIGN E * 0000 * A827 SYLOTI NAGRI VOWEL SIGN OO * 0000 * A82C SYLOTI NAGRI SIGN ALTERNATE HASANTA * 0000 * A880 SAURASHTRA SIGN ANUSVARA * 0000 * A881 SAURASHTRA SIGN VISARGA * 0000 * A8B4 SAURASHTRA CONSONANT SIGN HAARU * 0000 * A8B5 SAURASHTRA VOWEL SIGN AA * 0000 * A8B6 SAURASHTRA VOWEL SIGN I * 0000 * A8B7 SAURASHTRA VOWEL SIGN II * 0000 * A8B8 SAURASHTRA VOWEL SIGN U * 0000 * A8B9 SAURASHTRA VOWEL SIGN UU * 0000 * A8BA SAURASHTRA VOWEL SIGN VOCALIC R * 0000 * A8BB SAURASHTRA VOWEL SIGN VOCALIC RR * 0000 * A8BC SAURASHTRA VOWEL SIGN VOCALIC L * 0000 * A8BD SAURASHTRA VOWEL SIGN VOCALIC LL * 0000 * A8BE SAURASHTRA VOWEL SIGN E * 0000 * A8BF SAURASHTRA VOWEL SIGN EE * 0000 * A8C0 SAURASHTRA VOWEL SIGN AI * 0000 * A8C1 SAURASHTRA VOWEL SIGN O * 0000 * A8C2 SAURASHTRA VOWEL SIGN OO * 0000 * A8C3 SAURASHTRA VOWEL SIGN AU * 0000 * A8C4 SAURASHTRA SIGN VIRAMA * 0000 * A8C5 SAURASHTRA SIGN CANDRABINDU * 0000 * A926 KAYAH LI VOWEL UE * 0000 * A927 KAYAH LI VOWEL E * 0000 * A928 KAYAH LI VOWEL U * 0000 * A929 KAYAH LI VOWEL EE * 0000 * A92A KAYAH LI VOWEL O * 0000 * A92B KAYAH LI TONE PLOPHU * 0000 * A92C KAYAH LI TONE CALYA * 0000 * A92D KAYAH LI TONE CALYA PLOPHU * 0000 * A947 REJANG VOWEL SIGN I * 0000 * A948 REJANG VOWEL SIGN U * 0000 * A949 REJANG VOWEL SIGN E * 0000 * A94A REJANG VOWEL SIGN AI * 0000 * A94B REJANG VOWEL SIGN O * 0000 * A94C REJANG VOWEL SIGN AU * 0000 * A94D REJANG VOWEL SIGN EU * 0000 * A94E REJANG VOWEL SIGN EA * 0000 * A94F REJANG CONSONANT SIGN NG * 0000 * A950 REJANG CONSONANT SIGN N * 0000 * A951 REJANG CONSONANT SIGN R * 0000 * A952 REJANG CONSONANT SIGN H * 0000 * A953 REJANG VIRAMA * 0000 * A980 JAVANESE SIGN PANYANGGA * 0000 * A981 JAVANESE SIGN CECAK * 0000 * A982 JAVANESE SIGN LAYAR * 0000 * A983 JAVANESE SIGN WIGNYAN * 0000 * A9B3 JAVANESE SIGN CECAK TELU * 0000 * A9B4 JAVANESE VOWEL SIGN TARUNG * 0000 * A9B5 JAVANESE VOWEL SIGN TOLONG * 0000 * A9B6 JAVANESE VOWEL SIGN WULU * 0000 * A9B7 JAVANESE VOWEL SIGN WULU MELIK * 0000 * A9B8 JAVANESE VOWEL SIGN SUKU * 0000 * A9B9 JAVANESE VOWEL SIGN SUKU MENDUT * 0000 * A9BA JAVANESE VOWEL SIGN TALING * 0000 * A9BB JAVANESE VOWEL SIGN DIRGA MURE * 0000 * A9BC JAVANESE VOWEL SIGN PEPET * 0000 * A9BD JAVANESE CONSONANT SIGN KERET * 0000 * A9BE JAVANESE CONSONANT SIGN PENGKAL * 0000 * A9BF JAVANESE CONSONANT SIGN CAKRA * 0000 * A9C0 JAVANESE PANGKON * 0000 * A9E5 MYANMAR SIGN SHAN SAW * 0000 * AA29 CHAM VOWEL SIGN AA * 0000 * AA2A CHAM VOWEL SIGN I * 0000 * AA2B CHAM VOWEL SIGN II * 0000 * AA2C CHAM VOWEL SIGN EI * 0000 * AA2D CHAM VOWEL SIGN U * 0000 * AA2E CHAM VOWEL SIGN OE * 0000 * AA2F CHAM VOWEL SIGN O * 0000 * AA30 CHAM VOWEL SIGN AI * 0000 * AA31 CHAM VOWEL SIGN AU * 0000 * AA32 CHAM VOWEL SIGN UE * 0000 * AA33 CHAM CONSONANT SIGN YA * 0000 * AA34 CHAM CONSONANT SIGN RA * 0000 * AA35 CHAM CONSONANT SIGN LA * 0000 * AA36 CHAM CONSONANT SIGN WA * 0000 * AA43 CHAM CONSONANT SIGN FINAL NG * 0000 * AA4C CHAM CONSONANT SIGN FINAL M * 0000 * AA4D CHAM CONSONANT SIGN FINAL H * 0000 * AA7B MYANMAR SIGN PAO KAREN TONE * 0000 * AA7C MYANMAR SIGN TAI LAING TONE-2 * 0000 * AA7D MYANMAR SIGN TAI LAING TONE-5 * 0000 * AAB0 TAI VIET MAI KANG * 0000 * AAB2 TAI VIET VOWEL I * 0000 * AAB3 TAI VIET VOWEL UE * 0000 * AAB4 TAI VIET VOWEL U * 0000 * AAB7 TAI VIET MAI KHIT * 0000 * AAB8 TAI VIET VOWEL IA * 0000 * AABE TAI VIET VOWEL AM * 0000 * AABF TAI VIET TONE MAI EK * 0000 * AAC1 TAI VIET TONE MAI THO * 0000 * AAEB MEETEI MAYEK VOWEL SIGN II * 0000 * AAEC MEETEI MAYEK VOWEL SIGN UU * 0000 * AAED MEETEI MAYEK VOWEL SIGN AAI * 0000 * AAEE MEETEI MAYEK VOWEL SIGN AU * 0000 * AAEF MEETEI MAYEK VOWEL SIGN AAU * 0000 * AAF5 MEETEI MAYEK VOWEL SIGN VISARGA * 0000 * AAF6 MEETEI MAYEK VIRAMA * 0000 * AB5C MODIFIER LETTER SMALL HENG * A727 LATIN SMALL LETTER HENG * AB5D MODIFIER LETTER SMALL L WITH INVERTED LAZY S * AB37 LATIN SMALL LETTER L WITH INVERTED LAZY S * AB5E MODIFIER LETTER SMALL L WITH MIDDLE TILDE * 026B LATIN SMALL LETTER L WITH MIDDLE TILDE * AB5F MODIFIER LETTER SMALL U WITH LEFT HOOK * AB52 LATIN SMALL LETTER U WITH LEFT HOOK * AB69 MODIFIER LETTER SMALL TURNED W * 028D LATIN SMALL LETTER TURNED W * ABE3 MEETEI MAYEK VOWEL SIGN ONAP * 0000 * ABE4 MEETEI MAYEK VOWEL SIGN INAP * 0000 * ABE5 MEETEI MAYEK VOWEL SIGN ANAP * 0000 * ABE6 MEETEI MAYEK VOWEL SIGN YENAP * 0000 * ABE7 MEETEI MAYEK VOWEL SIGN SOUNAP * 0000 * ABE8 MEETEI MAYEK VOWEL SIGN UNAP * 0000 * ABE9 MEETEI MAYEK VOWEL SIGN CHEINAP * 0000 * ABEA MEETEI MAYEK VOWEL SIGN NUNG * 0000 * ABEC MEETEI MAYEK LUM IYEK * 0000 * ABED MEETEI MAYEK APUN IYEK * 0000 * F900 CJK COMPATIBILITY IDEOGRAPH-F900 * 8C48 ?? * F901 CJK COMPATIBILITY IDEOGRAPH-F901 * 66F4 ?? * F902 CJK COMPATIBILITY IDEOGRAPH-F902 * 8ECA ?? * F903 CJK COMPATIBILITY IDEOGRAPH-F903 * 8CC8 ?? * F904 CJK COMPATIBILITY IDEOGRAPH-F904 * 6ED1 ?? * F905 CJK COMPATIBILITY IDEOGRAPH-F905 * 4E32 ?? * F906 CJK COMPATIBILITY IDEOGRAPH-F906 * 53E5 ?? * F907 CJK COMPATIBILITY IDEOGRAPH-F907 * 9F9C ?? * F908 CJK COMPATIBILITY IDEOGRAPH-F908 * 9F9C ?? * F909 CJK COMPATIBILITY IDEOGRAPH-F909 * 5951 ?? * F90A CJK COMPATIBILITY IDEOGRAPH-F90A * 91D1 ?? * F90B CJK COMPATIBILITY IDEOGRAPH-F90B * 5587 ?? * F90C CJK COMPATIBILITY IDEOGRAPH-F90C * 5948 ?? * F90D CJK COMPATIBILITY IDEOGRAPH-F90D * 61F6 ?? * F90E CJK COMPATIBILITY IDEOGRAPH-F90E * 7669 ?? * F90F CJK COMPATIBILITY IDEOGRAPH-F90F * 7F85 ?? * F910 CJK COMPATIBILITY IDEOGRAPH-F910 * 863F ?? * F911 CJK COMPATIBILITY IDEOGRAPH-F911 * 87BA ?? * F912 CJK COMPATIBILITY IDEOGRAPH-F912 * 88F8 ?? * F913 CJK COMPATIBILITY IDEOGRAPH-F913 * 908F ?? * F914 CJK COMPATIBILITY IDEOGRAPH-F914 * 6A02 ?? * F915 CJK COMPATIBILITY IDEOGRAPH-F915 * 6D1B ?? * F916 CJK COMPATIBILITY IDEOGRAPH-F916 * 70D9 ?? * F917 CJK COMPATIBILITY IDEOGRAPH-F917 * 73DE ?? * F918 CJK COMPATIBILITY IDEOGRAPH-F918 * 843D ?? * F919 CJK COMPATIBILITY IDEOGRAPH-F919 * 916A ?? * F91A CJK COMPATIBILITY IDEOGRAPH-F91A * 99F1 ?? * F91B CJK COMPATIBILITY IDEOGRAPH-F91B * 4E82 ?? * F91C CJK COMPATIBILITY IDEOGRAPH-F91C * 5375 ?? * F91D CJK COMPATIBILITY IDEOGRAPH-F91D * 6B04 ?? * F91E CJK COMPATIBILITY IDEOGRAPH-F91E * 721B ?? * F91F CJK COMPATIBILITY IDEOGRAPH-F91F * 862D ?? * F920 CJK COMPATIBILITY IDEOGRAPH-F920 * 9E1E ?? * F921 CJK COMPATIBILITY IDEOGRAPH-F921 * 5D50 ?? * F922 CJK COMPATIBILITY IDEOGRAPH-F922 * 6FEB ?? * F923 CJK COMPATIBILITY IDEOGRAPH-F923 * 85CD ?? * F924 CJK COMPATIBILITY IDEOGRAPH-F924 * 8964 ?? * F925 CJK COMPATIBILITY IDEOGRAPH-F925 * 62C9 ?? * F926 CJK COMPATIBILITY IDEOGRAPH-F926 * 81D8 ?? * F927 CJK COMPATIBILITY IDEOGRAPH-F927 * 881F ?? * F928 CJK COMPATIBILITY IDEOGRAPH-F928 * 5ECA ?? * F929 CJK COMPATIBILITY IDEOGRAPH-F929 * 6717 ?? * F92A CJK COMPATIBILITY IDEOGRAPH-F92A * 6D6A ?? * F92B CJK COMPATIBILITY IDEOGRAPH-F92B * 72FC ?? * F92C CJK COMPATIBILITY IDEOGRAPH-F92C * 90CE ?? * F92D CJK COMPATIBILITY IDEOGRAPH-F92D * 4F86 ?? * F92E CJK COMPATIBILITY IDEOGRAPH-F92E * 51B7 ?? * F92F CJK COMPATIBILITY IDEOGRAPH-F92F * 52DE ?? * F930 CJK COMPATIBILITY IDEOGRAPH-F930 * 64C4 ?? * F931 CJK COMPATIBILITY IDEOGRAPH-F931 * 6AD3 ?? * F932 CJK COMPATIBILITY IDEOGRAPH-F932 * 7210 ?? * F933 CJK COMPATIBILITY IDEOGRAPH-F933 * 76E7 ?? * F934 CJK COMPATIBILITY IDEOGRAPH-F934 * 8001 ?? * F935 CJK COMPATIBILITY IDEOGRAPH-F935 * 8606 ?? * F936 CJK COMPATIBILITY IDEOGRAPH-F936 * 865C ?? * F937 CJK COMPATIBILITY IDEOGRAPH-F937 * 8DEF ?? * F938 CJK COMPATIBILITY IDEOGRAPH-F938 * 9732 ?? * F939 CJK COMPATIBILITY IDEOGRAPH-F939 * 9B6F ?? * F93A CJK COMPATIBILITY IDEOGRAPH-F93A * 9DFA ?? * F93B CJK COMPATIBILITY IDEOGRAPH-F93B * 788C ?? * F93C CJK COMPATIBILITY IDEOGRAPH-F93C * 797F ?? * F93D CJK COMPATIBILITY IDEOGRAPH-F93D * 7DA0 ?? * F93E CJK COMPATIBILITY IDEOGRAPH-F93E * 83C9 ?? * F93F CJK COMPATIBILITY IDEOGRAPH-F93F * 9304 ?? * F940 CJK COMPATIBILITY IDEOGRAPH-F940 * 9E7F ?? * F941 CJK COMPATIBILITY IDEOGRAPH-F941 * 8AD6 ?? * F942 CJK COMPATIBILITY IDEOGRAPH-F942 * 58DF ?? * F943 CJK COMPATIBILITY IDEOGRAPH-F943 * 5F04 ?? * F944 CJK COMPATIBILITY IDEOGRAPH-F944 * 7C60 ?? * F945 CJK COMPATIBILITY IDEOGRAPH-F945 * 807E ?? * F946 CJK COMPATIBILITY IDEOGRAPH-F946 * 7262 ?? * F947 CJK COMPATIBILITY IDEOGRAPH-F947 * 78CA ?? * F948 CJK COMPATIBILITY IDEOGRAPH-F948 * 8CC2 ?? * F949 CJK COMPATIBILITY IDEOGRAPH-F949 * 96F7 ?? * F94A CJK COMPATIBILITY IDEOGRAPH-F94A * 58D8 ?? * F94B CJK COMPATIBILITY IDEOGRAPH-F94B * 5C62 ?? * F94C CJK COMPATIBILITY IDEOGRAPH-F94C * 6A13 ?? * F94D CJK COMPATIBILITY IDEOGRAPH-F94D * 6DDA ?? * F94E CJK COMPATIBILITY IDEOGRAPH-F94E * 6F0F ?? * F94F CJK COMPATIBILITY IDEOGRAPH-F94F * 7D2F ?? * F950 CJK COMPATIBILITY IDEOGRAPH-F950 * 7E37 ?? * F951 CJK COMPATIBILITY IDEOGRAPH-F951 * 964B ?? * F952 CJK COMPATIBILITY IDEOGRAPH-F952 * 52D2 ?? * F953 CJK COMPATIBILITY IDEOGRAPH-F953 * 808B ?? * F954 CJK COMPATIBILITY IDEOGRAPH-F954 * 51DC ?? * F955 CJK COMPATIBILITY IDEOGRAPH-F955 * 51CC ?? * F956 CJK COMPATIBILITY IDEOGRAPH-F956 * 7A1C ?? * F957 CJK COMPATIBILITY IDEOGRAPH-F957 * 7DBE ?? * F958 CJK COMPATIBILITY IDEOGRAPH-F958 * 83F1 ?? * F959 CJK COMPATIBILITY IDEOGRAPH-F959 * 9675 ?? * F95A CJK COMPATIBILITY IDEOGRAPH-F95A * 8B80 ?? * F95B CJK COMPATIBILITY IDEOGRAPH-F95B * 62CF ?? * F95C CJK COMPATIBILITY IDEOGRAPH-F95C * 6A02 ?? * F95D CJK COMPATIBILITY IDEOGRAPH-F95D * 8AFE ?? * F95E CJK COMPATIBILITY IDEOGRAPH-F95E * 4E39 ?? * F95F CJK COMPATIBILITY IDEOGRAPH-F95F * 5BE7 ?? * F960 CJK COMPATIBILITY IDEOGRAPH-F960 * 6012 ?? * F961 CJK COMPATIBILITY IDEOGRAPH-F961 * 7387 ?? * F962 CJK COMPATIBILITY IDEOGRAPH-F962 * 7570 ?? * F963 CJK COMPATIBILITY IDEOGRAPH-F963 * 5317 ?? * F964 CJK COMPATIBILITY IDEOGRAPH-F964 * 78FB ?? * F965 CJK COMPATIBILITY IDEOGRAPH-F965 * 4FBF ?? * F966 CJK COMPATIBILITY IDEOGRAPH-F966 * 5FA9 ?? * F967 CJK COMPATIBILITY IDEOGRAPH-F967 * 4E0D ?? * F968 CJK COMPATIBILITY IDEOGRAPH-F968 * 6CCC ?? * F969 CJK COMPATIBILITY IDEOGRAPH-F969 * 6578 ?? * F96A CJK COMPATIBILITY IDEOGRAPH-F96A * 7D22 ?? * F96B CJK COMPATIBILITY IDEOGRAPH-F96B * 53C3 ?? * F96C CJK COMPATIBILITY IDEOGRAPH-F96C * 585E ?? * F96D CJK COMPATIBILITY IDEOGRAPH-F96D * 7701 ?? * F96E CJK COMPATIBILITY IDEOGRAPH-F96E * 8449 ?? * F96F CJK COMPATIBILITY IDEOGRAPH-F96F * 8AAA ?? * F970 CJK COMPATIBILITY IDEOGRAPH-F970 * 6BBA ?? * F971 CJK COMPATIBILITY IDEOGRAPH-F971 * 8FB0 ?? * F972 CJK COMPATIBILITY IDEOGRAPH-F972 * 6C88 ?? * F973 CJK COMPATIBILITY IDEOGRAPH-F973 * 62FE ?? * F974 CJK COMPATIBILITY IDEOGRAPH-F974 * 82E5 ?? * F975 CJK COMPATIBILITY IDEOGRAPH-F975 * 63A0 ?? * F976 CJK COMPATIBILITY IDEOGRAPH-F976 * 7565 ?? * F977 CJK COMPATIBILITY IDEOGRAPH-F977 * 4EAE ?? * F978 CJK COMPATIBILITY IDEOGRAPH-F978 * 5169 ?? * F979 CJK COMPATIBILITY IDEOGRAPH-F979 * 51C9 ?? * F97A CJK COMPATIBILITY IDEOGRAPH-F97A * 6881 ?? * F97B CJK COMPATIBILITY IDEOGRAPH-F97B * 7CE7 ?? * F97C CJK COMPATIBILITY IDEOGRAPH-F97C * 826F ?? * F97D CJK COMPATIBILITY IDEOGRAPH-F97D * 8AD2 ?? * F97E CJK COMPATIBILITY IDEOGRAPH-F97E * 91CF ?? * F97F CJK COMPATIBILITY IDEOGRAPH-F97F * 52F5 ?? * F980 CJK COMPATIBILITY IDEOGRAPH-F980 * 5442 ?? * F981 CJK COMPATIBILITY IDEOGRAPH-F981 * 5973 ?? * F982 CJK COMPATIBILITY IDEOGRAPH-F982 * 5EEC ?? * F983 CJK COMPATIBILITY IDEOGRAPH-F983 * 65C5 ?? * F984 CJK COMPATIBILITY IDEOGRAPH-F984 * 6FFE ?? * F985 CJK COMPATIBILITY IDEOGRAPH-F985 * 792A ?? * F986 CJK COMPATIBILITY IDEOGRAPH-F986 * 95AD ?? * F987 CJK COMPATIBILITY IDEOGRAPH-F987 * 9A6A ?? * F988 CJK COMPATIBILITY IDEOGRAPH-F988 * 9E97 ?? * F989 CJK COMPATIBILITY IDEOGRAPH-F989 * 9ECE ?? * F98A CJK COMPATIBILITY IDEOGRAPH-F98A * 529B ?? * F98B CJK COMPATIBILITY IDEOGRAPH-F98B * 66C6 ?? * F98C CJK COMPATIBILITY IDEOGRAPH-F98C * 6B77 ?? * F98D CJK COMPATIBILITY IDEOGRAPH-F98D * 8F62 ?? * F98E CJK COMPATIBILITY IDEOGRAPH-F98E * 5E74 ?? * F98F CJK COMPATIBILITY IDEOGRAPH-F98F * 6190 ?? * F990 CJK COMPATIBILITY IDEOGRAPH-F990 * 6200 ?? * F991 CJK COMPATIBILITY IDEOGRAPH-F991 * 649A ?? * F992 CJK COMPATIBILITY IDEOGRAPH-F992 * 6F23 ?? * F993 CJK COMPATIBILITY IDEOGRAPH-F993 * 7149 ?? * F994 CJK COMPATIBILITY IDEOGRAPH-F994 * 7489 ?? * F995 CJK COMPATIBILITY IDEOGRAPH-F995 * 79CA ?? * F996 CJK COMPATIBILITY IDEOGRAPH-F996 * 7DF4 ?? * F997 CJK COMPATIBILITY IDEOGRAPH-F997 * 806F ?? * F998 CJK COMPATIBILITY IDEOGRAPH-F998 * 8F26 ?? * F999 CJK COMPATIBILITY IDEOGRAPH-F999 * 84EE ?? * F99A CJK COMPATIBILITY IDEOGRAPH-F99A * 9023 ?? * F99B CJK COMPATIBILITY IDEOGRAPH-F99B * 934A ?? * F99C CJK COMPATIBILITY IDEOGRAPH-F99C * 5217 ?? * F99D CJK COMPATIBILITY IDEOGRAPH-F99D * 52A3 ?? * F99E CJK COMPATIBILITY IDEOGRAPH-F99E * 54BD ?? * F99F CJK COMPATIBILITY IDEOGRAPH-F99F * 70C8 ?? * F9A0 CJK COMPATIBILITY IDEOGRAPH-F9A0 * 88C2 ?? * F9A1 CJK COMPATIBILITY IDEOGRAPH-F9A1 * 8AAA ?? * F9A2 CJK COMPATIBILITY IDEOGRAPH-F9A2 * 5EC9 ?? * F9A3 CJK COMPATIBILITY IDEOGRAPH-F9A3 * 5FF5 ?? * F9A4 CJK COMPATIBILITY IDEOGRAPH-F9A4 * 637B ?? * F9A5 CJK COMPATIBILITY IDEOGRAPH-F9A5 * 6BAE ?? * F9A6 CJK COMPATIBILITY IDEOGRAPH-F9A6 * 7C3E ?? * F9A7 CJK COMPATIBILITY IDEOGRAPH-F9A7 * 7375 ?? * F9A8 CJK COMPATIBILITY IDEOGRAPH-F9A8 * 4EE4 ?? * F9A9 CJK COMPATIBILITY IDEOGRAPH-F9A9 * 56F9 ?? * F9AA CJK COMPATIBILITY IDEOGRAPH-F9AA * 5BE7 ?? * F9AB CJK COMPATIBILITY IDEOGRAPH-F9AB * 5DBA ?? * F9AC CJK COMPATIBILITY IDEOGRAPH-F9AC * 601C ?? * F9AD CJK COMPATIBILITY IDEOGRAPH-F9AD * 73B2 ?? * F9AE CJK COMPATIBILITY IDEOGRAPH-F9AE * 7469 ?? * F9AF CJK COMPATIBILITY IDEOGRAPH-F9AF * 7F9A ?? * F9B0 CJK COMPATIBILITY IDEOGRAPH-F9B0 * 8046 ?? * F9B1 CJK COMPATIBILITY IDEOGRAPH-F9B1 * 9234 ?? * F9B2 CJK COMPATIBILITY IDEOGRAPH-F9B2 * 96F6 ?? * F9B3 CJK COMPATIBILITY IDEOGRAPH-F9B3 * 9748 ?? * F9B4 CJK COMPATIBILITY IDEOGRAPH-F9B4 * 9818 ?? * F9B5 CJK COMPATIBILITY IDEOGRAPH-F9B5 * 4F8B ?? * F9B6 CJK COMPATIBILITY IDEOGRAPH-F9B6 * 79AE ?? * F9B7 CJK COMPATIBILITY IDEOGRAPH-F9B7 * 91B4 ?? * F9B8 CJK COMPATIBILITY IDEOGRAPH-F9B8 * 96B8 ?? * F9B9 CJK COMPATIBILITY IDEOGRAPH-F9B9 * 60E1 ?? * F9BA CJK COMPATIBILITY IDEOGRAPH-F9BA * 4E86 ?? * F9BB CJK COMPATIBILITY IDEOGRAPH-F9BB * 50DA ?? * F9BC CJK COMPATIBILITY IDEOGRAPH-F9BC * 5BEE ?? * F9BD CJK COMPATIBILITY IDEOGRAPH-F9BD * 5C3F ?? * F9BE CJK COMPATIBILITY IDEOGRAPH-F9BE * 6599 ?? * F9BF CJK COMPATIBILITY IDEOGRAPH-F9BF * 6A02 ?? * F9C0 CJK COMPATIBILITY IDEOGRAPH-F9C0 * 71CE ?? * F9C1 CJK COMPATIBILITY IDEOGRAPH-F9C1 * 7642 ?? * F9C2 CJK COMPATIBILITY IDEOGRAPH-F9C2 * 84FC ?? * F9C3 CJK COMPATIBILITY IDEOGRAPH-F9C3 * 907C ?? * F9C4 CJK COMPATIBILITY IDEOGRAPH-F9C4 * 9F8D ?? * F9C5 CJK COMPATIBILITY IDEOGRAPH-F9C5 * 6688 ?? * F9C6 CJK COMPATIBILITY IDEOGRAPH-F9C6 * 962E ?? * F9C7 CJK COMPATIBILITY IDEOGRAPH-F9C7 * 5289 ?? * F9C8 CJK COMPATIBILITY IDEOGRAPH-F9C8 * 677B ?? * F9C9 CJK COMPATIBILITY IDEOGRAPH-F9C9 * 67F3 ?? * F9CA CJK COMPATIBILITY IDEOGRAPH-F9CA * 6D41 ?? * F9CB CJK COMPATIBILITY IDEOGRAPH-F9CB * 6E9C ?? * F9CC CJK COMPATIBILITY IDEOGRAPH-F9CC * 7409 ?? * F9CD CJK COMPATIBILITY IDEOGRAPH-F9CD * 7559 ?? * F9CE CJK COMPATIBILITY IDEOGRAPH-F9CE * 786B ?? * F9CF CJK COMPATIBILITY IDEOGRAPH-F9CF * 7D10 ?? * F9D0 CJK COMPATIBILITY IDEOGRAPH-F9D0 * 985E ?? * F9D1 CJK COMPATIBILITY IDEOGRAPH-F9D1 * 516D ?? * F9D2 CJK COMPATIBILITY IDEOGRAPH-F9D2 * 622E ?? * F9D3 CJK COMPATIBILITY IDEOGRAPH-F9D3 * 9678 ?? * F9D4 CJK COMPATIBILITY IDEOGRAPH-F9D4 * 502B ?? * F9D5 CJK COMPATIBILITY IDEOGRAPH-F9D5 * 5D19 ?? * F9D6 CJK COMPATIBILITY IDEOGRAPH-F9D6 * 6DEA ?? * F9D7 CJK COMPATIBILITY IDEOGRAPH-F9D7 * 8F2A ?? * F9D8 CJK COMPATIBILITY IDEOGRAPH-F9D8 * 5F8B ?? * F9D9 CJK COMPATIBILITY IDEOGRAPH-F9D9 * 6144 ?? * F9DA CJK COMPATIBILITY IDEOGRAPH-F9DA * 6817 ?? * F9DB CJK COMPATIBILITY IDEOGRAPH-F9DB * 7387 ?? * F9DC CJK COMPATIBILITY IDEOGRAPH-F9DC * 9686 ?? * F9DD CJK COMPATIBILITY IDEOGRAPH-F9DD * 5229 ?? * F9DE CJK COMPATIBILITY IDEOGRAPH-F9DE * 540F ?? * F9DF CJK COMPATIBILITY IDEOGRAPH-F9DF * 5C65 ?? * F9E0 CJK COMPATIBILITY IDEOGRAPH-F9E0 * 6613 ?? * F9E1 CJK COMPATIBILITY IDEOGRAPH-F9E1 * 674E ?? * F9E2 CJK COMPATIBILITY IDEOGRAPH-F9E2 * 68A8 ?? * F9E3 CJK COMPATIBILITY IDEOGRAPH-F9E3 * 6CE5 ?? * F9E4 CJK COMPATIBILITY IDEOGRAPH-F9E4 * 7406 ?? * F9E5 CJK COMPATIBILITY IDEOGRAPH-F9E5 * 75E2 ?? * F9E6 CJK COMPATIBILITY IDEOGRAPH-F9E6 * 7F79 ?? * F9E7 CJK COMPATIBILITY IDEOGRAPH-F9E7 * 88CF ?? * F9E8 CJK COMPATIBILITY IDEOGRAPH-F9E8 * 88E1 ?? * F9E9 CJK COMPATIBILITY IDEOGRAPH-F9E9 * 91CC ?? * F9EA CJK COMPATIBILITY IDEOGRAPH-F9EA * 96E2 ?? * F9EB CJK COMPATIBILITY IDEOGRAPH-F9EB * 533F ?? * F9EC CJK COMPATIBILITY IDEOGRAPH-F9EC * 6EBA ?? * F9ED CJK COMPATIBILITY IDEOGRAPH-F9ED * 541D ?? * F9EE CJK COMPATIBILITY IDEOGRAPH-F9EE * 71D0 ?? * F9EF CJK COMPATIBILITY IDEOGRAPH-F9EF * 7498 ?? * F9F0 CJK COMPATIBILITY IDEOGRAPH-F9F0 * 85FA ?? * F9F1 CJK COMPATIBILITY IDEOGRAPH-F9F1 * 96A3 ?? * F9F2 CJK COMPATIBILITY IDEOGRAPH-F9F2 * 9C57 ?? * F9F3 CJK COMPATIBILITY IDEOGRAPH-F9F3 * 9E9F ?? * F9F4 CJK COMPATIBILITY IDEOGRAPH-F9F4 * 6797 ?? * F9F5 CJK COMPATIBILITY IDEOGRAPH-F9F5 * 6DCB ?? * F9F6 CJK COMPATIBILITY IDEOGRAPH-F9F6 * 81E8 ?? * F9F7 CJK COMPATIBILITY IDEOGRAPH-F9F7 * 7ACB ?? * F9F8 CJK COMPATIBILITY IDEOGRAPH-F9F8 * 7B20 ?? * F9F9 CJK COMPATIBILITY IDEOGRAPH-F9F9 * 7C92 ?? * F9FA CJK COMPATIBILITY IDEOGRAPH-F9FA * 72C0 ?? * F9FB CJK COMPATIBILITY IDEOGRAPH-F9FB * 7099 ?? * F9FC CJK COMPATIBILITY IDEOGRAPH-F9FC * 8B58 ?? * F9FD CJK COMPATIBILITY IDEOGRAPH-F9FD * 4EC0 ?? * F9FE CJK COMPATIBILITY IDEOGRAPH-F9FE * 8336 ?? * F9FF CJK COMPATIBILITY IDEOGRAPH-F9FF * 523A ?? * FA00 CJK COMPATIBILITY IDEOGRAPH-FA00 * 5207 ?? * FA01 CJK COMPATIBILITY IDEOGRAPH-FA01 * 5EA6 ?? * FA02 CJK COMPATIBILITY IDEOGRAPH-FA02 * 62D3 ?? * FA03 CJK COMPATIBILITY IDEOGRAPH-FA03 * 7CD6 ?? * FA04 CJK COMPATIBILITY IDEOGRAPH-FA04 * 5B85 ?? * FA05 CJK COMPATIBILITY IDEOGRAPH-FA05 * 6D1E ?? * FA06 CJK COMPATIBILITY IDEOGRAPH-FA06 * 66B4 ?? * FA07 CJK COMPATIBILITY IDEOGRAPH-FA07 * 8F3B ?? * FA08 CJK COMPATIBILITY IDEOGRAPH-FA08 * 884C ?? * FA09 CJK COMPATIBILITY IDEOGRAPH-FA09 * 964D ?? * FA0A CJK COMPATIBILITY IDEOGRAPH-FA0A * 898B ?? * FA0B CJK COMPATIBILITY IDEOGRAPH-FA0B * 5ED3 ?? * FA0C CJK COMPATIBILITY IDEOGRAPH-FA0C * 5140 ?? * FA0D CJK COMPATIBILITY IDEOGRAPH-FA0D * 55C0 ?? * FA10 CJK COMPATIBILITY IDEOGRAPH-FA10 * 585A ?? * FA12 CJK COMPATIBILITY IDEOGRAPH-FA12 * 6674 ?? * FA15 CJK COMPATIBILITY IDEOGRAPH-FA15 * 51DE ?? * FA16 CJK COMPATIBILITY IDEOGRAPH-FA16 * 732A ?? * FA17 CJK COMPATIBILITY IDEOGRAPH-FA17 * 76CA ?? * FA18 CJK COMPATIBILITY IDEOGRAPH-FA18 * 793C ?? * FA19 CJK COMPATIBILITY IDEOGRAPH-FA19 * 795E ?? * FA1A CJK COMPATIBILITY IDEOGRAPH-FA1A * 7965 ?? * FA1B CJK COMPATIBILITY IDEOGRAPH-FA1B * 798F ?? * FA1C CJK COMPATIBILITY IDEOGRAPH-FA1C * 9756 ?? * FA1D CJK COMPATIBILITY IDEOGRAPH-FA1D * 7CBE ?? * FA1E CJK COMPATIBILITY IDEOGRAPH-FA1E * 7FBD ?? * FA20 CJK COMPATIBILITY IDEOGRAPH-FA20 * 8612 ?? * FA22 CJK COMPATIBILITY IDEOGRAPH-FA22 * 8AF8 ?? * FA25 CJK COMPATIBILITY IDEOGRAPH-FA25 * 9038 ?? * FA26 CJK COMPATIBILITY IDEOGRAPH-FA26 * 90FD ?? * FA2A CJK COMPATIBILITY IDEOGRAPH-FA2A * 98EF ?? * FA2B CJK COMPATIBILITY IDEOGRAPH-FA2B * 98FC ?? * FA2C CJK COMPATIBILITY IDEOGRAPH-FA2C * 9928 ?? * FA2D CJK COMPATIBILITY IDEOGRAPH-FA2D * 9DB4 ?? * FA2E CJK COMPATIBILITY IDEOGRAPH-FA2E * 90DE ?? * FA2F CJK COMPATIBILITY IDEOGRAPH-FA2F * 96B7 ?? * FA30 CJK COMPATIBILITY IDEOGRAPH-FA30 * 4FAE ?? * FA31 CJK COMPATIBILITY IDEOGRAPH-FA31 * 50E7 ?? * FA32 CJK COMPATIBILITY IDEOGRAPH-FA32 * 514D ?? * FA33 CJK COMPATIBILITY IDEOGRAPH-FA33 * 52C9 ?? * FA34 CJK COMPATIBILITY IDEOGRAPH-FA34 * 52E4 ?? * FA35 CJK COMPATIBILITY IDEOGRAPH-FA35 * 5351 ?? * FA36 CJK COMPATIBILITY IDEOGRAPH-FA36 * 559D ?? * FA37 CJK COMPATIBILITY IDEOGRAPH-FA37 * 5606 ?? * FA38 CJK COMPATIBILITY IDEOGRAPH-FA38 * 5668 ?? * FA39 CJK COMPATIBILITY IDEOGRAPH-FA39 * 5840 ?? * FA3A CJK COMPATIBILITY IDEOGRAPH-FA3A * 58A8 ?? * FA3B CJK COMPATIBILITY IDEOGRAPH-FA3B * 5C64 ?? * FA3C CJK COMPATIBILITY IDEOGRAPH-FA3C * 5C6E ?? * FA3D CJK COMPATIBILITY IDEOGRAPH-FA3D * 6094 ?? * FA3E CJK COMPATIBILITY IDEOGRAPH-FA3E * 6168 ?? * FA3F CJK COMPATIBILITY IDEOGRAPH-FA3F * 618E ?? * FA40 CJK COMPATIBILITY IDEOGRAPH-FA40 * 61F2 ?? * FA41 CJK COMPATIBILITY IDEOGRAPH-FA41 * 654F ?? * FA42 CJK COMPATIBILITY IDEOGRAPH-FA42 * 65E2 ?? * FA43 CJK COMPATIBILITY IDEOGRAPH-FA43 * 6691 ?? * FA44 CJK COMPATIBILITY IDEOGRAPH-FA44 * 6885 ?? * FA45 CJK COMPATIBILITY IDEOGRAPH-FA45 * 6D77 ?? * FA46 CJK COMPATIBILITY IDEOGRAPH-FA46 * 6E1A ?? * FA47 CJK COMPATIBILITY IDEOGRAPH-FA47 * 6F22 ?? * FA48 CJK COMPATIBILITY IDEOGRAPH-FA48 * 716E ?? * FA49 CJK COMPATIBILITY IDEOGRAPH-FA49 * 722B ?? * FA4A CJK COMPATIBILITY IDEOGRAPH-FA4A * 7422 ?? * FA4B CJK COMPATIBILITY IDEOGRAPH-FA4B * 7891 ?? * FA4C CJK COMPATIBILITY IDEOGRAPH-FA4C * 793E ?? * FA4D CJK COMPATIBILITY IDEOGRAPH-FA4D * 7949 ?? * FA4E CJK COMPATIBILITY IDEOGRAPH-FA4E * 7948 ?? * FA4F CJK COMPATIBILITY IDEOGRAPH-FA4F * 7950 ?? * FA50 CJK COMPATIBILITY IDEOGRAPH-FA50 * 7956 ?? * FA51 CJK COMPATIBILITY IDEOGRAPH-FA51 * 795D ?? * FA52 CJK COMPATIBILITY IDEOGRAPH-FA52 * 798D ?? * FA53 CJK COMPATIBILITY IDEOGRAPH-FA53 * 798E ?? * FA54 CJK COMPATIBILITY IDEOGRAPH-FA54 * 7A40 ?? * FA55 CJK COMPATIBILITY IDEOGRAPH-FA55 * 7A81 ?? * FA56 CJK COMPATIBILITY IDEOGRAPH-FA56 * 7BC0 ?? * FA57 CJK COMPATIBILITY IDEOGRAPH-FA57 * 7DF4 ?? * FA58 CJK COMPATIBILITY IDEOGRAPH-FA58 * 7E09 ?? * FA59 CJK COMPATIBILITY IDEOGRAPH-FA59 * 7E41 ?? * FA5A CJK COMPATIBILITY IDEOGRAPH-FA5A * 7F72 ?? * FA5B CJK COMPATIBILITY IDEOGRAPH-FA5B * 8005 ?? * FA5C CJK COMPATIBILITY IDEOGRAPH-FA5C * 81ED ?? * FA5D CJK COMPATIBILITY IDEOGRAPH-FA5D * 8279 ?? * FA5E CJK COMPATIBILITY IDEOGRAPH-FA5E * 8279 ?? * FA5F CJK COMPATIBILITY IDEOGRAPH-FA5F * 8457 ?? * FA60 CJK COMPATIBILITY IDEOGRAPH-FA60 * 8910 ?? * FA61 CJK COMPATIBILITY IDEOGRAPH-FA61 * 8996 ?? * FA62 CJK COMPATIBILITY IDEOGRAPH-FA62 * 8B01 ?? * FA63 CJK COMPATIBILITY IDEOGRAPH-FA63 * 8B39 ?? * FA64 CJK COMPATIBILITY IDEOGRAPH-FA64 * 8CD3 ?? * FA65 CJK COMPATIBILITY IDEOGRAPH-FA65 * 8D08 ?? * FA66 CJK COMPATIBILITY IDEOGRAPH-FA66 * 8FB6 ?? * FA67 CJK COMPATIBILITY IDEOGRAPH-FA67 * 9038 ?? * FA68 CJK COMPATIBILITY IDEOGRAPH-FA68 * 96E3 ?? * FA69 CJK COMPATIBILITY IDEOGRAPH-FA69 * 97FF ?? * FA6A CJK COMPATIBILITY IDEOGRAPH-FA6A * 983B ?? * FA6B CJK COMPATIBILITY IDEOGRAPH-FA6B * 6075 ?? * FA6D CJK COMPATIBILITY IDEOGRAPH-FA6D * 8218 ?? * FA70 CJK COMPATIBILITY IDEOGRAPH-FA70 * 4E26 ?? * FA71 CJK COMPATIBILITY IDEOGRAPH-FA71 * 51B5 ?? * FA72 CJK COMPATIBILITY IDEOGRAPH-FA72 * 5168 ?? * FA73 CJK COMPATIBILITY IDEOGRAPH-FA73 * 4F80 ?? * FA74 CJK COMPATIBILITY IDEOGRAPH-FA74 * 5145 ?? * FA75 CJK COMPATIBILITY IDEOGRAPH-FA75 * 5180 ?? * FA76 CJK COMPATIBILITY IDEOGRAPH-FA76 * 52C7 ?? * FA77 CJK COMPATIBILITY IDEOGRAPH-FA77 * 52FA ?? * FA78 CJK COMPATIBILITY IDEOGRAPH-FA78 * 559D ?? * FA79 CJK COMPATIBILITY IDEOGRAPH-FA79 * 5555 ?? * FA7A CJK COMPATIBILITY IDEOGRAPH-FA7A * 5599 ?? * FA7B CJK COMPATIBILITY IDEOGRAPH-FA7B * 55E2 ?? * FA7C CJK COMPATIBILITY IDEOGRAPH-FA7C * 585A ?? * FA7D CJK COMPATIBILITY IDEOGRAPH-FA7D * 58B3 ?? * FA7E CJK COMPATIBILITY IDEOGRAPH-FA7E * 5944 ?? * FA7F CJK COMPATIBILITY IDEOGRAPH-FA7F * 5954 ?? * FA80 CJK COMPATIBILITY IDEOGRAPH-FA80 * 5A62 ?? * FA81 CJK COMPATIBILITY IDEOGRAPH-FA81 * 5B28 ?? * FA82 CJK COMPATIBILITY IDEOGRAPH-FA82 * 5ED2 ?? * FA83 CJK COMPATIBILITY IDEOGRAPH-FA83 * 5ED9 ?? * FA84 CJK COMPATIBILITY IDEOGRAPH-FA84 * 5F69 ?? * FA85 CJK COMPATIBILITY IDEOGRAPH-FA85 * 5FAD ?? * FA86 CJK COMPATIBILITY IDEOGRAPH-FA86 * 60D8 ?? * FA87 CJK COMPATIBILITY IDEOGRAPH-FA87 * 614E ?? * FA88 CJK COMPATIBILITY IDEOGRAPH-FA88 * 6108 ?? * FA89 CJK COMPATIBILITY IDEOGRAPH-FA89 * 618E ?? * FA8A CJK COMPATIBILITY IDEOGRAPH-FA8A * 6160 ?? * FA8B CJK COMPATIBILITY IDEOGRAPH-FA8B * 61F2 ?? * FA8C CJK COMPATIBILITY IDEOGRAPH-FA8C * 6234 ?? * FA8D CJK COMPATIBILITY IDEOGRAPH-FA8D * 63C4 ?? * FA8E CJK COMPATIBILITY IDEOGRAPH-FA8E * 641C ?? * FA8F CJK COMPATIBILITY IDEOGRAPH-FA8F * 6452 ?? * FA90 CJK COMPATIBILITY IDEOGRAPH-FA90 * 6556 ?? * FA91 CJK COMPATIBILITY IDEOGRAPH-FA91 * 6674 ?? * FA92 CJK COMPATIBILITY IDEOGRAPH-FA92 * 6717 ?? * FA93 CJK COMPATIBILITY IDEOGRAPH-FA93 * 671B ?? * FA94 CJK COMPATIBILITY IDEOGRAPH-FA94 * 6756 ?? * FA95 CJK COMPATIBILITY IDEOGRAPH-FA95 * 6B79 ?? * FA96 CJK COMPATIBILITY IDEOGRAPH-FA96 * 6BBA ?? * FA97 CJK COMPATIBILITY IDEOGRAPH-FA97 * 6D41 ?? * FA98 CJK COMPATIBILITY IDEOGRAPH-FA98 * 6EDB ?? * FA99 CJK COMPATIBILITY IDEOGRAPH-FA99 * 6ECB ?? * FA9A CJK COMPATIBILITY IDEOGRAPH-FA9A * 6F22 ?? * FA9B CJK COMPATIBILITY IDEOGRAPH-FA9B * 701E ?? * FA9C CJK COMPATIBILITY IDEOGRAPH-FA9C * 716E ?? * FA9D CJK COMPATIBILITY IDEOGRAPH-FA9D * 77A7 ?? * FA9E CJK COMPATIBILITY IDEOGRAPH-FA9E * 7235 ?? * FA9F CJK COMPATIBILITY IDEOGRAPH-FA9F * 72AF ?? * FAA0 CJK COMPATIBILITY IDEOGRAPH-FAA0 * 732A ?? * FAA1 CJK COMPATIBILITY IDEOGRAPH-FAA1 * 7471 ?? * FAA2 CJK COMPATIBILITY IDEOGRAPH-FAA2 * 7506 ?? * FAA3 CJK COMPATIBILITY IDEOGRAPH-FAA3 * 753B ?? * FAA4 CJK COMPATIBILITY IDEOGRAPH-FAA4 * 761D ?? * FAA5 CJK COMPATIBILITY IDEOGRAPH-FAA5 * 761F ?? * FAA6 CJK COMPATIBILITY IDEOGRAPH-FAA6 * 76CA ?? * FAA7 CJK COMPATIBILITY IDEOGRAPH-FAA7 * 76DB ?? * FAA8 CJK COMPATIBILITY IDEOGRAPH-FAA8 * 76F4 ?? * FAA9 CJK COMPATIBILITY IDEOGRAPH-FAA9 * 774A ?? * FAAA CJK COMPATIBILITY IDEOGRAPH-FAAA * 7740 ?? * FAAB CJK COMPATIBILITY IDEOGRAPH-FAAB * 78CC ?? * FAAC CJK COMPATIBILITY IDEOGRAPH-FAAC * 7AB1 ?? * FAAD CJK COMPATIBILITY IDEOGRAPH-FAAD * 7BC0 ?? * FAAE CJK COMPATIBILITY IDEOGRAPH-FAAE * 7C7B ?? * FAAF CJK COMPATIBILITY IDEOGRAPH-FAAF * 7D5B ?? * FAB0 CJK COMPATIBILITY IDEOGRAPH-FAB0 * 7DF4 ?? * FAB1 CJK COMPATIBILITY IDEOGRAPH-FAB1 * 7F3E ?? * FAB2 CJK COMPATIBILITY IDEOGRAPH-FAB2 * 8005 ?? * FAB3 CJK COMPATIBILITY IDEOGRAPH-FAB3 * 8352 ?? * FAB4 CJK COMPATIBILITY IDEOGRAPH-FAB4 * 83EF ?? * FAB5 CJK COMPATIBILITY IDEOGRAPH-FAB5 * 8779 ?? * FAB6 CJK COMPATIBILITY IDEOGRAPH-FAB6 * 8941 ?? * FAB7 CJK COMPATIBILITY IDEOGRAPH-FAB7 * 8986 ?? * FAB8 CJK COMPATIBILITY IDEOGRAPH-FAB8 * 8996 ?? * FAB9 CJK COMPATIBILITY IDEOGRAPH-FAB9 * 8ABF ?? * FABA CJK COMPATIBILITY IDEOGRAPH-FABA * 8AF8 ?? * FABB CJK COMPATIBILITY IDEOGRAPH-FABB * 8ACB ?? * FABC CJK COMPATIBILITY IDEOGRAPH-FABC * 8B01 ?? * FABD CJK COMPATIBILITY IDEOGRAPH-FABD * 8AFE ?? * FABE CJK COMPATIBILITY IDEOGRAPH-FABE * 8AED ?? * FABF CJK COMPATIBILITY IDEOGRAPH-FABF * 8B39 ?? * FAC0 CJK COMPATIBILITY IDEOGRAPH-FAC0 * 8B8A ?? * FAC1 CJK COMPATIBILITY IDEOGRAPH-FAC1 * 8D08 ?? * FAC2 CJK COMPATIBILITY IDEOGRAPH-FAC2 * 8F38 ?? * FAC3 CJK COMPATIBILITY IDEOGRAPH-FAC3 * 9072 ?? * FAC4 CJK COMPATIBILITY IDEOGRAPH-FAC4 * 9199 ?? * FAC5 CJK COMPATIBILITY IDEOGRAPH-FAC5 * 9276 ?? * FAC6 CJK COMPATIBILITY IDEOGRAPH-FAC6 * 967C ?? * FAC7 CJK COMPATIBILITY IDEOGRAPH-FAC7 * 96E3 ?? * FAC8 CJK COMPATIBILITY IDEOGRAPH-FAC8 * 9756 ?? * FAC9 CJK COMPATIBILITY IDEOGRAPH-FAC9 * 97DB ?? * FACA CJK COMPATIBILITY IDEOGRAPH-FACA * 97FF ?? * FACB CJK COMPATIBILITY IDEOGRAPH-FACB * 980B ?? * FACC CJK COMPATIBILITY IDEOGRAPH-FACC * 983B ?? * FACD CJK COMPATIBILITY IDEOGRAPH-FACD * 9B12 ?? * FACE CJK COMPATIBILITY IDEOGRAPH-FACE * 9F9C ?? * FAD2 CJK COMPATIBILITY IDEOGRAPH-FAD2 * 3B9D ?? * FAD3 CJK COMPATIBILITY IDEOGRAPH-FAD3 * 4018 ?? * FAD4 CJK COMPATIBILITY IDEOGRAPH-FAD4 * 4039 ?? * FAD8 CJK COMPATIBILITY IDEOGRAPH-FAD8 * 9F43 ?? * FAD9 CJK COMPATIBILITY IDEOGRAPH-FAD9 * 9F8E ?? * FB00 LATIN SMALL LIGATURE FF * 0066 LATIN SMALL LETTER F * 0066 LATIN SMALL LETTER F * FB01 LATIN SMALL LIGATURE FI * 0066 LATIN SMALL LETTER F * 0069 LATIN SMALL LETTER I * FB02 LATIN SMALL LIGATURE FL * 0066 LATIN SMALL LETTER F * 006C LATIN SMALL LETTER L * FB03 LATIN SMALL LIGATURE FFI * 0066 LATIN SMALL LETTER F * 0066 LATIN SMALL LETTER F * 0069 LATIN SMALL LETTER I * FB04 LATIN SMALL LIGATURE FFL * 0066 LATIN SMALL LETTER F * 0066 LATIN SMALL LETTER F * 006C LATIN SMALL LETTER L * FB05 LATIN SMALL LIGATURE LONG S T * 0074 LATIN SMALL LETTER T * 0073 LATIN SMALL LETTER S * FB06 LATIN SMALL LIGATURE ST * 0073 LATIN SMALL LETTER S * 0074 LATIN SMALL LETTER T * FB13 ARMENIAN SMALL LIGATURE MEN NOW * 0574 ARMENIAN SMALL LETTER MEN * 0576 ARMENIAN SMALL LETTER NOW * FB14 ARMENIAN SMALL LIGATURE MEN ECH * 0574 ARMENIAN SMALL LETTER MEN * 0565 ARMENIAN SMALL LETTER ECH * FB15 ARMENIAN SMALL LIGATURE MEN INI * 0574 ARMENIAN SMALL LETTER MEN * 056B ARMENIAN SMALL LETTER INI * FB16 ARMENIAN SMALL LIGATURE VEW NOW * 057E ARMENIAN SMALL LETTER VEW * 0576 ARMENIAN SMALL LETTER NOW * FB17 ARMENIAN SMALL LIGATURE MEN XEH * 0574 ARMENIAN SMALL LETTER MEN * 056D ARMENIAN SMALL LETTER XEH * FB1D HEBREW LETTER YOD WITH HIRIQ * 05D9 HEBREW LETTER YOD * FB1E HEBREW POINT JUDEO-SPANISH VARIKA * 0000 * FB1F HEBREW LIGATURE YIDDISH YOD YOD PATAH * 05F2 HEBREW LIGATURE YIDDISH DOUBLE YOD * FB20 HEBREW LETTER ALTERNATIVE AYIN * 05E2 HEBREW LETTER AYIN * FB21 HEBREW LETTER WIDE ALEF * 05D0 HEBREW LETTER ALEF * FB22 HEBREW LETTER WIDE DALET * 05D3 HEBREW LETTER DALET * FB23 HEBREW LETTER WIDE HE * 05D4 HEBREW LETTER HE * FB24 HEBREW LETTER WIDE KAF * 05DB HEBREW LETTER KAF * FB25 HEBREW LETTER WIDE LAMED * 05DC HEBREW LETTER LAMED * FB26 HEBREW LETTER WIDE FINAL MEM * 05DD HEBREW LETTER FINAL MEM * FB27 HEBREW LETTER WIDE RESH * 05E8 HEBREW LETTER RESH * FB28 HEBREW LETTER WIDE TAV * 05EA HEBREW LETTER TAV * FB29 HEBREW LETTER ALTERNATIVE PLUS SIGN * 002B PLUS SIGN * FB2A HEBREW LETTER SHIN WITH SHIN DOT * 05E9 HEBREW LETTER SHIN * FB2B HEBREW LETTER SHIN WITH SIN DOT * 05E9 HEBREW LETTER SHIN * FB2C HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT * 05E9 HEBREW LETTER SHIN * FB2D HEBREW LETTER SHIN WITH DAGESH AND SIN DOT * 05E9 HEBREW LETTER SHIN * FB2E HEBREW LETTER ALEF WITH PATAH * 05D0 HEBREW LETTER ALEF * FB2F HEBREW LETTER ALEF WITH QAMATS * 05D0 HEBREW LETTER ALEF * FB30 HEBREW LETTER ALEF WITH MAPIQ * 05D0 HEBREW LETTER ALEF * FB31 HEBREW LETTER BET WITH DAGESH * 05D1 HEBREW LETTER BET * FB32 HEBREW LETTER GIMEL WITH DAGESH * 05D2 HEBREW LETTER GIMEL * FB33 HEBREW LETTER DALET WITH DAGESH * 05D3 HEBREW LETTER DALET * FB34 HEBREW LETTER HE WITH MAPIQ * 05D4 HEBREW LETTER HE * FB35 HEBREW LETTER VAV WITH DAGESH * 05D5 HEBREW LETTER VAV * FB36 HEBREW LETTER ZAYIN WITH DAGESH * 05D6 HEBREW LETTER ZAYIN * FB38 HEBREW LETTER TET WITH DAGESH * 05D8 HEBREW LETTER TET * FB39 HEBREW LETTER YOD WITH DAGESH * 05D9 HEBREW LETTER YOD * FB3A HEBREW LETTER FINAL KAF WITH DAGESH * 05DA HEBREW LETTER FINAL KAF * FB3B HEBREW LETTER KAF WITH DAGESH * 05DB HEBREW LETTER KAF * FB3C HEBREW LETTER LAMED WITH DAGESH * 05DC HEBREW LETTER LAMED * FB3E HEBREW LETTER MEM WITH DAGESH * 05DE HEBREW LETTER MEM * FB40 HEBREW LETTER NUN WITH DAGESH * 05E0 HEBREW LETTER NUN * FB41 HEBREW LETTER SAMEKH WITH DAGESH * 05E1 HEBREW LETTER SAMEKH * FB43 HEBREW LETTER FINAL PE WITH DAGESH * 05E3 HEBREW LETTER FINAL PE * FB44 HEBREW LETTER PE WITH DAGESH * 05E4 HEBREW LETTER PE * FB46 HEBREW LETTER TSADI WITH DAGESH * 05E6 HEBREW LETTER TSADI * FB47 HEBREW LETTER QOF WITH DAGESH * 05E7 HEBREW LETTER QOF * FB48 HEBREW LETTER RESH WITH DAGESH * 05E8 HEBREW LETTER RESH * FB49 HEBREW LETTER SHIN WITH DAGESH * 05E9 HEBREW LETTER SHIN * FB4A HEBREW LETTER TAV WITH DAGESH * 05EA HEBREW LETTER TAV * FB4B HEBREW LETTER VAV WITH HOLAM * 05D5 HEBREW LETTER VAV * FB4C HEBREW LETTER BET WITH RAFE * 05D1 HEBREW LETTER BET * FB4D HEBREW LETTER KAF WITH RAFE * 05DB HEBREW LETTER KAF * FB4E HEBREW LETTER PE WITH RAFE * 05E4 HEBREW LETTER PE * FB4F HEBREW LIGATURE ALEF LAMED * 05D0 HEBREW LETTER ALEF * 05DC HEBREW LETTER LAMED * FB50 ARABIC LETTER ALEF WASLA ISOLATED FORM * 0671 ARABIC LETTER ALEF WASLA * FB51 ARABIC LETTER ALEF WASLA FINAL FORM * 0671 ARABIC LETTER ALEF WASLA * FB52 ARABIC LETTER BEEH ISOLATED FORM * 067B ARABIC LETTER BEEH * FB53 ARABIC LETTER BEEH FINAL FORM * 067B ARABIC LETTER BEEH * FB54 ARABIC LETTER BEEH INITIAL FORM * 067B ARABIC LETTER BEEH * FB55 ARABIC LETTER BEEH MEDIAL FORM * 067B ARABIC LETTER BEEH * FB56 ARABIC LETTER PEH ISOLATED FORM * 067E ARABIC LETTER PEH * FB57 ARABIC LETTER PEH FINAL FORM * 067E ARABIC LETTER PEH * FB58 ARABIC LETTER PEH INITIAL FORM * 067E ARABIC LETTER PEH * FB59 ARABIC LETTER PEH MEDIAL FORM * 067E ARABIC LETTER PEH * FB5A ARABIC LETTER BEHEH ISOLATED FORM * 0680 ARABIC LETTER BEHEH * FB5B ARABIC LETTER BEHEH FINAL FORM * 0680 ARABIC LETTER BEHEH * FB5C ARABIC LETTER BEHEH INITIAL FORM * 0680 ARABIC LETTER BEHEH * FB5D ARABIC LETTER BEHEH MEDIAL FORM * 0680 ARABIC LETTER BEHEH * FB5E ARABIC LETTER TTEHEH ISOLATED FORM * 067A ARABIC LETTER TTEHEH * FB5F ARABIC LETTER TTEHEH FINAL FORM * 067A ARABIC LETTER TTEHEH * FB60 ARABIC LETTER TTEHEH INITIAL FORM * 067A ARABIC LETTER TTEHEH * FB61 ARABIC LETTER TTEHEH MEDIAL FORM * 067A ARABIC LETTER TTEHEH * FB62 ARABIC LETTER TEHEH ISOLATED FORM * 067F ARABIC LETTER TEHEH * FB63 ARABIC LETTER TEHEH FINAL FORM * 067F ARABIC LETTER TEHEH * FB64 ARABIC LETTER TEHEH INITIAL FORM * 067F ARABIC LETTER TEHEH * FB65 ARABIC LETTER TEHEH MEDIAL FORM * 067F ARABIC LETTER TEHEH * FB66 ARABIC LETTER TTEH ISOLATED FORM * 0679 ARABIC LETTER TTEH * FB67 ARABIC LETTER TTEH FINAL FORM * 0679 ARABIC LETTER TTEH * FB68 ARABIC LETTER TTEH INITIAL FORM * 0679 ARABIC LETTER TTEH * FB69 ARABIC LETTER TTEH MEDIAL FORM * 0679 ARABIC LETTER TTEH * FB6A ARABIC LETTER VEH ISOLATED FORM * 06A4 ARABIC LETTER VEH * FB6B ARABIC LETTER VEH FINAL FORM * 06A4 ARABIC LETTER VEH * FB6C ARABIC LETTER VEH INITIAL FORM * 06A4 ARABIC LETTER VEH * FB6D ARABIC LETTER VEH MEDIAL FORM * 06A4 ARABIC LETTER VEH * FB6E ARABIC LETTER PEHEH ISOLATED FORM * 06A6 ARABIC LETTER PEHEH * FB6F ARABIC LETTER PEHEH FINAL FORM * 06A6 ARABIC LETTER PEHEH * FB70 ARABIC LETTER PEHEH INITIAL FORM * 06A6 ARABIC LETTER PEHEH * FB71 ARABIC LETTER PEHEH MEDIAL FORM * 06A6 ARABIC LETTER PEHEH * FB72 ARABIC LETTER DYEH ISOLATED FORM * 0684 ARABIC LETTER DYEH * FB73 ARABIC LETTER DYEH FINAL FORM * 0684 ARABIC LETTER DYEH * FB74 ARABIC LETTER DYEH INITIAL FORM * 0684 ARABIC LETTER DYEH * FB75 ARABIC LETTER DYEH MEDIAL FORM * 0684 ARABIC LETTER DYEH * FB76 ARABIC LETTER NYEH ISOLATED FORM * 0683 ARABIC LETTER NYEH * FB77 ARABIC LETTER NYEH FINAL FORM * 0683 ARABIC LETTER NYEH * FB78 ARABIC LETTER NYEH INITIAL FORM * 0683 ARABIC LETTER NYEH * FB79 ARABIC LETTER NYEH MEDIAL FORM * 0683 ARABIC LETTER NYEH * FB7A ARABIC LETTER TCHEH ISOLATED FORM * 0686 ARABIC LETTER TCHEH * FB7B ARABIC LETTER TCHEH FINAL FORM * 0686 ARABIC LETTER TCHEH * FB7C ARABIC LETTER TCHEH INITIAL FORM * 0686 ARABIC LETTER TCHEH * FB7D ARABIC LETTER TCHEH MEDIAL FORM * 0686 ARABIC LETTER TCHEH * FB7E ARABIC LETTER TCHEHEH ISOLATED FORM * 0687 ARABIC LETTER TCHEHEH * FB7F ARABIC LETTER TCHEHEH FINAL FORM * 0687 ARABIC LETTER TCHEHEH * FB80 ARABIC LETTER TCHEHEH INITIAL FORM * 0687 ARABIC LETTER TCHEHEH * FB81 ARABIC LETTER TCHEHEH MEDIAL FORM * 0687 ARABIC LETTER TCHEHEH * FB82 ARABIC LETTER DDAHAL ISOLATED FORM * 068D ARABIC LETTER DDAHAL * FB83 ARABIC LETTER DDAHAL FINAL FORM * 068D ARABIC LETTER DDAHAL * FB84 ARABIC LETTER DAHAL ISOLATED FORM * 068C ARABIC LETTER DAHAL * FB85 ARABIC LETTER DAHAL FINAL FORM * 068C ARABIC LETTER DAHAL * FB86 ARABIC LETTER DUL ISOLATED FORM * 068E ARABIC LETTER DUL * FB87 ARABIC LETTER DUL FINAL FORM * 068E ARABIC LETTER DUL * FB88 ARABIC LETTER DDAL ISOLATED FORM * 0688 ARABIC LETTER DDAL * FB89 ARABIC LETTER DDAL FINAL FORM * 0688 ARABIC LETTER DDAL * FB8A ARABIC LETTER JEH ISOLATED FORM * 0698 ARABIC LETTER JEH * FB8B ARABIC LETTER JEH FINAL FORM * 0698 ARABIC LETTER JEH * FB8C ARABIC LETTER RREH ISOLATED FORM * 0691 ARABIC LETTER RREH * FB8D ARABIC LETTER RREH FINAL FORM * 0691 ARABIC LETTER RREH * FB8E ARABIC LETTER KEHEH ISOLATED FORM * 06A9 ARABIC LETTER KEHEH * FB8F ARABIC LETTER KEHEH FINAL FORM * 06A9 ARABIC LETTER KEHEH * FB90 ARABIC LETTER KEHEH INITIAL FORM * 06A9 ARABIC LETTER KEHEH * FB91 ARABIC LETTER KEHEH MEDIAL FORM * 06A9 ARABIC LETTER KEHEH * FB92 ARABIC LETTER GAF ISOLATED FORM * 06AF ARABIC LETTER GAF * FB93 ARABIC LETTER GAF FINAL FORM * 06AF ARABIC LETTER GAF * FB94 ARABIC LETTER GAF INITIAL FORM * 06AF ARABIC LETTER GAF * FB95 ARABIC LETTER GAF MEDIAL FORM * 06AF ARABIC LETTER GAF * FB96 ARABIC LETTER GUEH ISOLATED FORM * 06B3 ARABIC LETTER GUEH * FB97 ARABIC LETTER GUEH FINAL FORM * 06B3 ARABIC LETTER GUEH * FB98 ARABIC LETTER GUEH INITIAL FORM * 06B3 ARABIC LETTER GUEH * FB99 ARABIC LETTER GUEH MEDIAL FORM * 06B3 ARABIC LETTER GUEH * FB9A ARABIC LETTER NGOEH ISOLATED FORM * 06B1 ARABIC LETTER NGOEH * FB9B ARABIC LETTER NGOEH FINAL FORM * 06B1 ARABIC LETTER NGOEH * FB9C ARABIC LETTER NGOEH INITIAL FORM * 06B1 ARABIC LETTER NGOEH * FB9D ARABIC LETTER NGOEH MEDIAL FORM * 06B1 ARABIC LETTER NGOEH * FB9E ARABIC LETTER NOON GHUNNA ISOLATED FORM * 06BA ARABIC LETTER NOON GHUNNA * FB9F ARABIC LETTER NOON GHUNNA FINAL FORM * 06BA ARABIC LETTER NOON GHUNNA * FBA0 ARABIC LETTER RNOON ISOLATED FORM * 06BB ARABIC LETTER RNOON * FBA1 ARABIC LETTER RNOON FINAL FORM * 06BB ARABIC LETTER RNOON * FBA2 ARABIC LETTER RNOON INITIAL FORM * 06BB ARABIC LETTER RNOON * FBA3 ARABIC LETTER RNOON MEDIAL FORM * 06BB ARABIC LETTER RNOON * FBA4 ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM * 06D5 ARABIC LETTER AE * FBA5 ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM * 06D5 ARABIC LETTER AE * FBA6 ARABIC LETTER HEH GOAL ISOLATED FORM * 06C1 ARABIC LETTER HEH GOAL * FBA7 ARABIC LETTER HEH GOAL FINAL FORM * 06C1 ARABIC LETTER HEH GOAL * FBA8 ARABIC LETTER HEH GOAL INITIAL FORM * 06C1 ARABIC LETTER HEH GOAL * FBA9 ARABIC LETTER HEH GOAL MEDIAL FORM * 06C1 ARABIC LETTER HEH GOAL * FBAA ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM * 06BE ARABIC LETTER HEH DOACHASHMEE * FBAB ARABIC LETTER HEH DOACHASHMEE FINAL FORM * 06BE ARABIC LETTER HEH DOACHASHMEE * FBAC ARABIC LETTER HEH DOACHASHMEE INITIAL FORM * 06BE ARABIC LETTER HEH DOACHASHMEE * FBAD ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM * 06BE ARABIC LETTER HEH DOACHASHMEE * FBAE ARABIC LETTER YEH BARREE ISOLATED FORM * 06D2 ARABIC LETTER YEH BARREE * FBAF ARABIC LETTER YEH BARREE FINAL FORM * 06D2 ARABIC LETTER YEH BARREE * FBB0 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM * 06D2 ARABIC LETTER YEH BARREE * FBB1 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM * 06D2 ARABIC LETTER YEH BARREE * FBD3 ARABIC LETTER NG ISOLATED FORM * 06AD ARABIC LETTER NG * FBD4 ARABIC LETTER NG FINAL FORM * 06AD ARABIC LETTER NG * FBD5 ARABIC LETTER NG INITIAL FORM * 06AD ARABIC LETTER NG * FBD6 ARABIC LETTER NG MEDIAL FORM * 06AD ARABIC LETTER NG * FBD7 ARABIC LETTER U ISOLATED FORM * 06C7 ARABIC LETTER U * FBD8 ARABIC LETTER U FINAL FORM * 06C7 ARABIC LETTER U * FBD9 ARABIC LETTER OE ISOLATED FORM * 06C6 ARABIC LETTER OE * FBDA ARABIC LETTER OE FINAL FORM * 06C6 ARABIC LETTER OE * FBDB ARABIC LETTER YU ISOLATED FORM * 06C8 ARABIC LETTER YU * FBDC ARABIC LETTER YU FINAL FORM * 06C8 ARABIC LETTER YU * FBDD ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM * 06C7 ARABIC LETTER U * 0674 ARABIC LETTER HIGH HAMZA * FBDE ARABIC LETTER VE ISOLATED FORM * 06CB ARABIC LETTER VE * FBDF ARABIC LETTER VE FINAL FORM * 06CB ARABIC LETTER VE * FBE0 ARABIC LETTER KIRGHIZ OE ISOLATED FORM * 06C5 ARABIC LETTER KIRGHIZ OE * FBE1 ARABIC LETTER KIRGHIZ OE FINAL FORM * 06C5 ARABIC LETTER KIRGHIZ OE * FBE2 ARABIC LETTER KIRGHIZ YU ISOLATED FORM * 06C9 ARABIC LETTER KIRGHIZ YU * FBE3 ARABIC LETTER KIRGHIZ YU FINAL FORM * 06C9 ARABIC LETTER KIRGHIZ YU * FBE4 ARABIC LETTER E ISOLATED FORM * 06D0 ARABIC LETTER E * FBE5 ARABIC LETTER E FINAL FORM * 06D0 ARABIC LETTER E * FBE6 ARABIC LETTER E INITIAL FORM * 06D0 ARABIC LETTER E * FBE7 ARABIC LETTER E MEDIAL FORM * 06D0 ARABIC LETTER E * FBE8 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM * 0649 ARABIC LETTER ALEF MAKSURA * FBE9 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM * 0649 ARABIC LETTER ALEF MAKSURA * FBEA ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM * 0627 ARABIC LETTER ALEF * 064A ARABIC LETTER YEH * FBEB ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM * 0627 ARABIC LETTER ALEF * 064A ARABIC LETTER YEH * FBEC ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM * 06D5 ARABIC LETTER AE * 064A ARABIC LETTER YEH * FBED ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM * 06D5 ARABIC LETTER AE * 064A ARABIC LETTER YEH * FBEE ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM * 0648 ARABIC LETTER WAW * 064A ARABIC LETTER YEH * FBEF ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM * 0648 ARABIC LETTER WAW * 064A ARABIC LETTER YEH * FBF0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM * 06C7 ARABIC LETTER U * 064A ARABIC LETTER YEH * FBF1 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM * 06C7 ARABIC LETTER U * 064A ARABIC LETTER YEH * FBF2 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM * 06C6 ARABIC LETTER OE * 064A ARABIC LETTER YEH * FBF3 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM * 06C6 ARABIC LETTER OE * 064A ARABIC LETTER YEH * FBF4 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM * 06C8 ARABIC LETTER YU * 064A ARABIC LETTER YEH * FBF5 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM * 06C8 ARABIC LETTER YU * 064A ARABIC LETTER YEH * FBF6 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM * 06D0 ARABIC LETTER E * 064A ARABIC LETTER YEH * FBF7 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM * 06D0 ARABIC LETTER E * 064A ARABIC LETTER YEH * FBF8 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM * 06D0 ARABIC LETTER E * 064A ARABIC LETTER YEH * FBF9 ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM * 0649 ARABIC LETTER ALEF MAKSURA * 064A ARABIC LETTER YEH * FBFA ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM * 0649 ARABIC LETTER ALEF MAKSURA * 064A ARABIC LETTER YEH * FBFB ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM * 0649 ARABIC LETTER ALEF MAKSURA * 064A ARABIC LETTER YEH * FBFC ARABIC LETTER FARSI YEH ISOLATED FORM * 06CC ARABIC LETTER FARSI YEH * FBFD ARABIC LETTER FARSI YEH FINAL FORM * 06CC ARABIC LETTER FARSI YEH * FBFE ARABIC LETTER FARSI YEH INITIAL FORM * 06CC ARABIC LETTER FARSI YEH * FBFF ARABIC LETTER FARSI YEH MEDIAL FORM * 06CC ARABIC LETTER FARSI YEH * FC00 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FC01 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FC02 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FC03 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM * 0649 ARABIC LETTER ALEF MAKSURA * 064A ARABIC LETTER YEH * FC04 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM * 064A ARABIC LETTER YEH * 064A ARABIC LETTER YEH * FC05 ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM * 0628 ARABIC LETTER BEH * 062C ARABIC LETTER JEEM * FC06 ARABIC LIGATURE BEH WITH HAH ISOLATED FORM * 0628 ARABIC LETTER BEH * 062D ARABIC LETTER HAH * FC07 ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM * 0628 ARABIC LETTER BEH * 062E ARABIC LETTER KHAH * FC08 ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM * 0628 ARABIC LETTER BEH * 0645 ARABIC LETTER MEEM * FC09 ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM * 0628 ARABIC LETTER BEH * 0649 ARABIC LETTER ALEF MAKSURA * FC0A ARABIC LIGATURE BEH WITH YEH ISOLATED FORM * 0628 ARABIC LETTER BEH * 064A ARABIC LETTER YEH * FC0B ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM * 062A ARABIC LETTER TEH * 062C ARABIC LETTER JEEM * FC0C ARABIC LIGATURE TEH WITH HAH ISOLATED FORM * 062A ARABIC LETTER TEH * 062D ARABIC LETTER HAH * FC0D ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM * 062A ARABIC LETTER TEH * 062E ARABIC LETTER KHAH * FC0E ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * FC0F ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM * 062A ARABIC LETTER TEH * 0649 ARABIC LETTER ALEF MAKSURA * FC10 ARABIC LIGATURE TEH WITH YEH ISOLATED FORM * 062A ARABIC LETTER TEH * 064A ARABIC LETTER YEH * FC11 ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM * 062B ARABIC LETTER THEH * 062C ARABIC LETTER JEEM * FC12 ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM * 062B ARABIC LETTER THEH * 0645 ARABIC LETTER MEEM * FC13 ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM * 062B ARABIC LETTER THEH * 0649 ARABIC LETTER ALEF MAKSURA * FC14 ARABIC LIGATURE THEH WITH YEH ISOLATED FORM * 062B ARABIC LETTER THEH * 064A ARABIC LETTER YEH * FC15 ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM * 062C ARABIC LETTER JEEM * 062D ARABIC LETTER HAH * FC16 ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FC17 ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM * 062D ARABIC LETTER HAH * 062C ARABIC LETTER JEEM * FC18 ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FC19 ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM * 062E ARABIC LETTER KHAH * 062C ARABIC LETTER JEEM * FC1A ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM * 062E ARABIC LETTER KHAH * 062D ARABIC LETTER HAH * FC1B ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FC1C ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM * 0633 ARABIC LETTER SEEN * 062C ARABIC LETTER JEEM * FC1D ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM * 0633 ARABIC LETTER SEEN * 062D ARABIC LETTER HAH * FC1E ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM * 0633 ARABIC LETTER SEEN * 062E ARABIC LETTER KHAH * FC1F ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM * 0633 ARABIC LETTER SEEN * 0645 ARABIC LETTER MEEM * FC20 ARABIC LIGATURE SAD WITH HAH ISOLATED FORM * 0635 ARABIC LETTER SAD * 062D ARABIC LETTER HAH * FC21 ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM * 0635 ARABIC LETTER SAD * 0645 ARABIC LETTER MEEM * FC22 ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM * 0636 ARABIC LETTER DAD * 062C ARABIC LETTER JEEM * FC23 ARABIC LIGATURE DAD WITH HAH ISOLATED FORM * 0636 ARABIC LETTER DAD * 062D ARABIC LETTER HAH * FC24 ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM * 0636 ARABIC LETTER DAD * 062E ARABIC LETTER KHAH * FC25 ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM * 0636 ARABIC LETTER DAD * 0645 ARABIC LETTER MEEM * FC26 ARABIC LIGATURE TAH WITH HAH ISOLATED FORM * 0637 ARABIC LETTER TAH * 062D ARABIC LETTER HAH * FC27 ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM * 0637 ARABIC LETTER TAH * 0645 ARABIC LETTER MEEM * FC28 ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM * 0638 ARABIC LETTER ZAH * 0645 ARABIC LETTER MEEM * FC29 ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM * 0639 ARABIC LETTER AIN * 062C ARABIC LETTER JEEM * FC2A ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM * 0639 ARABIC LETTER AIN * 0645 ARABIC LETTER MEEM * FC2B ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM * 063A ARABIC LETTER GHAIN * 062C ARABIC LETTER JEEM * FC2C ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM * 063A ARABIC LETTER GHAIN * 0645 ARABIC LETTER MEEM * FC2D ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM * 0641 ARABIC LETTER FEH * 062C ARABIC LETTER JEEM * FC2E ARABIC LIGATURE FEH WITH HAH ISOLATED FORM * 0641 ARABIC LETTER FEH * 062D ARABIC LETTER HAH * FC2F ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM * 0641 ARABIC LETTER FEH * 062E ARABIC LETTER KHAH * FC30 ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM * 0641 ARABIC LETTER FEH * 0645 ARABIC LETTER MEEM * FC31 ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM * 0641 ARABIC LETTER FEH * 0649 ARABIC LETTER ALEF MAKSURA * FC32 ARABIC LIGATURE FEH WITH YEH ISOLATED FORM * 0641 ARABIC LETTER FEH * 064A ARABIC LETTER YEH * FC33 ARABIC LIGATURE QAF WITH HAH ISOLATED FORM * 0642 ARABIC LETTER QAF * 062D ARABIC LETTER HAH * FC34 ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM * 0642 ARABIC LETTER QAF * 0645 ARABIC LETTER MEEM * FC35 ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM * 0642 ARABIC LETTER QAF * 0649 ARABIC LETTER ALEF MAKSURA * FC36 ARABIC LIGATURE QAF WITH YEH ISOLATED FORM * 0642 ARABIC LETTER QAF * 064A ARABIC LETTER YEH * FC37 ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM * 0643 ARABIC LETTER KAF * 0627 ARABIC LETTER ALEF * FC38 ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM * 0643 ARABIC LETTER KAF * 062C ARABIC LETTER JEEM * FC39 ARABIC LIGATURE KAF WITH HAH ISOLATED FORM * 0643 ARABIC LETTER KAF * 062D ARABIC LETTER HAH * FC3A ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM * 0643 ARABIC LETTER KAF * 062E ARABIC LETTER KHAH * FC3B ARABIC LIGATURE KAF WITH LAM ISOLATED FORM * 0643 ARABIC LETTER KAF * 0644 ARABIC LETTER LAM * FC3C ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM * 0643 ARABIC LETTER KAF * 0645 ARABIC LETTER MEEM * FC3D ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM * 0643 ARABIC LETTER KAF * 0649 ARABIC LETTER ALEF MAKSURA * FC3E ARABIC LIGATURE KAF WITH YEH ISOLATED FORM * 0643 ARABIC LETTER KAF * 064A ARABIC LETTER YEH * FC3F ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM * 0644 ARABIC LETTER LAM * 062C ARABIC LETTER JEEM * FC40 ARABIC LIGATURE LAM WITH HAH ISOLATED FORM * 0644 ARABIC LETTER LAM * 062D ARABIC LETTER HAH * FC41 ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM * 0644 ARABIC LETTER LAM * 062E ARABIC LETTER KHAH * FC42 ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * FC43 ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM * 0644 ARABIC LETTER LAM * 0649 ARABIC LETTER ALEF MAKSURA * FC44 ARABIC LIGATURE LAM WITH YEH ISOLATED FORM * 0644 ARABIC LETTER LAM * 064A ARABIC LETTER YEH * FC45 ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * FC46 ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FC47 ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM * 0645 ARABIC LETTER MEEM * 062E ARABIC LETTER KHAH * FC48 ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FC49 ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM * 0645 ARABIC LETTER MEEM * 0649 ARABIC LETTER ALEF MAKSURA * FC4A ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FC4B ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM * 0646 ARABIC LETTER NOON * 062C ARABIC LETTER JEEM * FC4C ARABIC LIGATURE NOON WITH HAH ISOLATED FORM * 0646 ARABIC LETTER NOON * 062D ARABIC LETTER HAH * FC4D ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM * 0646 ARABIC LETTER NOON * 062E ARABIC LETTER KHAH * FC4E ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM * 0646 ARABIC LETTER NOON * 0645 ARABIC LETTER MEEM * FC4F ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM * 0646 ARABIC LETTER NOON * 0649 ARABIC LETTER ALEF MAKSURA * FC50 ARABIC LIGATURE NOON WITH YEH ISOLATED FORM * 0646 ARABIC LETTER NOON * 064A ARABIC LETTER YEH * FC51 ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM * 0647 ARABIC LETTER HEH * 062C ARABIC LETTER JEEM * FC52 ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM * 0647 ARABIC LETTER HEH * 0645 ARABIC LETTER MEEM * FC53 ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM * 0647 ARABIC LETTER HEH * 0649 ARABIC LETTER ALEF MAKSURA * FC54 ARABIC LIGATURE HEH WITH YEH ISOLATED FORM * 0647 ARABIC LETTER HEH * 064A ARABIC LETTER YEH * FC55 ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM * 064A ARABIC LETTER YEH * 062C ARABIC LETTER JEEM * FC56 ARABIC LIGATURE YEH WITH HAH ISOLATED FORM * 064A ARABIC LETTER YEH * 062D ARABIC LETTER HAH * FC57 ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM * 064A ARABIC LETTER YEH * 062E ARABIC LETTER KHAH * FC58 ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM * 064A ARABIC LETTER YEH * 0645 ARABIC LETTER MEEM * FC59 ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM * 064A ARABIC LETTER YEH * 0649 ARABIC LETTER ALEF MAKSURA * FC5A ARABIC LIGATURE YEH WITH YEH ISOLATED FORM * 064A ARABIC LETTER YEH * 064A ARABIC LETTER YEH * FC5B ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM * 0630 ARABIC LETTER THAL * FC5C ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM * 0631 ARABIC LETTER REH * FC5D ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM * 0649 ARABIC LETTER ALEF MAKSURA * FC5E ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM * 0020 SPACE * FC5F ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM * 0020 SPACE * FC60 ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM * 0020 SPACE * FC61 ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM * 0020 SPACE * FC62 ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM * 0020 SPACE * FC63 ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM * 0020 SPACE * FC64 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM * 0631 ARABIC LETTER REH * 064A ARABIC LETTER YEH * FC65 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM * 0632 ARABIC LETTER ZAIN * 064A ARABIC LETTER YEH * FC66 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FC67 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM * 0646 ARABIC LETTER NOON * 064A ARABIC LETTER YEH * FC68 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM * 0649 ARABIC LETTER ALEF MAKSURA * 064A ARABIC LETTER YEH * FC69 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM * 064A ARABIC LETTER YEH * 064A ARABIC LETTER YEH * FC6A ARABIC LIGATURE BEH WITH REH FINAL FORM * 0628 ARABIC LETTER BEH * 0631 ARABIC LETTER REH * FC6B ARABIC LIGATURE BEH WITH ZAIN FINAL FORM * 0628 ARABIC LETTER BEH * 0632 ARABIC LETTER ZAIN * FC6C ARABIC LIGATURE BEH WITH MEEM FINAL FORM * 0628 ARABIC LETTER BEH * 0645 ARABIC LETTER MEEM * FC6D ARABIC LIGATURE BEH WITH NOON FINAL FORM * 0628 ARABIC LETTER BEH * 0646 ARABIC LETTER NOON * FC6E ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM * 0628 ARABIC LETTER BEH * 0649 ARABIC LETTER ALEF MAKSURA * FC6F ARABIC LIGATURE BEH WITH YEH FINAL FORM * 0628 ARABIC LETTER BEH * 064A ARABIC LETTER YEH * FC70 ARABIC LIGATURE TEH WITH REH FINAL FORM * 062A ARABIC LETTER TEH * 0631 ARABIC LETTER REH * FC71 ARABIC LIGATURE TEH WITH ZAIN FINAL FORM * 062A ARABIC LETTER TEH * 0632 ARABIC LETTER ZAIN * FC72 ARABIC LIGATURE TEH WITH MEEM FINAL FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * FC73 ARABIC LIGATURE TEH WITH NOON FINAL FORM * 062A ARABIC LETTER TEH * 0646 ARABIC LETTER NOON * FC74 ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM * 062A ARABIC LETTER TEH * 0649 ARABIC LETTER ALEF MAKSURA * FC75 ARABIC LIGATURE TEH WITH YEH FINAL FORM * 062A ARABIC LETTER TEH * 064A ARABIC LETTER YEH * FC76 ARABIC LIGATURE THEH WITH REH FINAL FORM * 062B ARABIC LETTER THEH * 0631 ARABIC LETTER REH * FC77 ARABIC LIGATURE THEH WITH ZAIN FINAL FORM * 062B ARABIC LETTER THEH * 0632 ARABIC LETTER ZAIN * FC78 ARABIC LIGATURE THEH WITH MEEM FINAL FORM * 062B ARABIC LETTER THEH * 0645 ARABIC LETTER MEEM * FC79 ARABIC LIGATURE THEH WITH NOON FINAL FORM * 062B ARABIC LETTER THEH * 0646 ARABIC LETTER NOON * FC7A ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM * 062B ARABIC LETTER THEH * 0649 ARABIC LETTER ALEF MAKSURA * FC7B ARABIC LIGATURE THEH WITH YEH FINAL FORM * 062B ARABIC LETTER THEH * 064A ARABIC LETTER YEH * FC7C ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM * 0641 ARABIC LETTER FEH * 0649 ARABIC LETTER ALEF MAKSURA * FC7D ARABIC LIGATURE FEH WITH YEH FINAL FORM * 0641 ARABIC LETTER FEH * 064A ARABIC LETTER YEH * FC7E ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM * 0642 ARABIC LETTER QAF * 0649 ARABIC LETTER ALEF MAKSURA * FC7F ARABIC LIGATURE QAF WITH YEH FINAL FORM * 0642 ARABIC LETTER QAF * 064A ARABIC LETTER YEH * FC80 ARABIC LIGATURE KAF WITH ALEF FINAL FORM * 0643 ARABIC LETTER KAF * 0627 ARABIC LETTER ALEF * FC81 ARABIC LIGATURE KAF WITH LAM FINAL FORM * 0643 ARABIC LETTER KAF * 0644 ARABIC LETTER LAM * FC82 ARABIC LIGATURE KAF WITH MEEM FINAL FORM * 0643 ARABIC LETTER KAF * 0645 ARABIC LETTER MEEM * FC83 ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM * 0643 ARABIC LETTER KAF * 0649 ARABIC LETTER ALEF MAKSURA * FC84 ARABIC LIGATURE KAF WITH YEH FINAL FORM * 0643 ARABIC LETTER KAF * 064A ARABIC LETTER YEH * FC85 ARABIC LIGATURE LAM WITH MEEM FINAL FORM * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * FC86 ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM * 0644 ARABIC LETTER LAM * 0649 ARABIC LETTER ALEF MAKSURA * FC87 ARABIC LIGATURE LAM WITH YEH FINAL FORM * 0644 ARABIC LETTER LAM * 064A ARABIC LETTER YEH * FC88 ARABIC LIGATURE MEEM WITH ALEF FINAL FORM * 0645 ARABIC LETTER MEEM * 0627 ARABIC LETTER ALEF * FC89 ARABIC LIGATURE MEEM WITH MEEM FINAL FORM * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FC8A ARABIC LIGATURE NOON WITH REH FINAL FORM * 0646 ARABIC LETTER NOON * 0631 ARABIC LETTER REH * FC8B ARABIC LIGATURE NOON WITH ZAIN FINAL FORM * 0646 ARABIC LETTER NOON * 0632 ARABIC LETTER ZAIN * FC8C ARABIC LIGATURE NOON WITH MEEM FINAL FORM * 0646 ARABIC LETTER NOON * 0645 ARABIC LETTER MEEM * FC8D ARABIC LIGATURE NOON WITH NOON FINAL FORM * 0646 ARABIC LETTER NOON * 0646 ARABIC LETTER NOON * FC8E ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM * 0646 ARABIC LETTER NOON * 0649 ARABIC LETTER ALEF MAKSURA * FC8F ARABIC LIGATURE NOON WITH YEH FINAL FORM * 0646 ARABIC LETTER NOON * 064A ARABIC LETTER YEH * FC90 ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM * 0649 ARABIC LETTER ALEF MAKSURA * FC91 ARABIC LIGATURE YEH WITH REH FINAL FORM * 064A ARABIC LETTER YEH * 0631 ARABIC LETTER REH * FC92 ARABIC LIGATURE YEH WITH ZAIN FINAL FORM * 064A ARABIC LETTER YEH * 0632 ARABIC LETTER ZAIN * FC93 ARABIC LIGATURE YEH WITH MEEM FINAL FORM * 064A ARABIC LETTER YEH * 0645 ARABIC LETTER MEEM * FC94 ARABIC LIGATURE YEH WITH NOON FINAL FORM * 064A ARABIC LETTER YEH * 0646 ARABIC LETTER NOON * FC95 ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM * 064A ARABIC LETTER YEH * 0649 ARABIC LETTER ALEF MAKSURA * FC96 ARABIC LIGATURE YEH WITH YEH FINAL FORM * 064A ARABIC LETTER YEH * 064A ARABIC LETTER YEH * FC97 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FC98 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FC99 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM * 062E ARABIC LETTER KHAH * 064A ARABIC LETTER YEH * FC9A ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FC9B ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM * 0647 ARABIC LETTER HEH * 064A ARABIC LETTER YEH * FC9C ARABIC LIGATURE BEH WITH JEEM INITIAL FORM * 0628 ARABIC LETTER BEH * 062C ARABIC LETTER JEEM * FC9D ARABIC LIGATURE BEH WITH HAH INITIAL FORM * 0628 ARABIC LETTER BEH * 062D ARABIC LETTER HAH * FC9E ARABIC LIGATURE BEH WITH KHAH INITIAL FORM * 0628 ARABIC LETTER BEH * 062E ARABIC LETTER KHAH * FC9F ARABIC LIGATURE BEH WITH MEEM INITIAL FORM * 0628 ARABIC LETTER BEH * 0645 ARABIC LETTER MEEM * FCA0 ARABIC LIGATURE BEH WITH HEH INITIAL FORM * 0628 ARABIC LETTER BEH * 0647 ARABIC LETTER HEH * FCA1 ARABIC LIGATURE TEH WITH JEEM INITIAL FORM * 062A ARABIC LETTER TEH * 062C ARABIC LETTER JEEM * FCA2 ARABIC LIGATURE TEH WITH HAH INITIAL FORM * 062A ARABIC LETTER TEH * 062D ARABIC LETTER HAH * FCA3 ARABIC LIGATURE TEH WITH KHAH INITIAL FORM * 062A ARABIC LETTER TEH * 062E ARABIC LETTER KHAH * FCA4 ARABIC LIGATURE TEH WITH MEEM INITIAL FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * FCA5 ARABIC LIGATURE TEH WITH HEH INITIAL FORM * 062A ARABIC LETTER TEH * 0647 ARABIC LETTER HEH * FCA6 ARABIC LIGATURE THEH WITH MEEM INITIAL FORM * 062B ARABIC LETTER THEH * 0645 ARABIC LETTER MEEM * FCA7 ARABIC LIGATURE JEEM WITH HAH INITIAL FORM * 062C ARABIC LETTER JEEM * 062D ARABIC LETTER HAH * FCA8 ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FCA9 ARABIC LIGATURE HAH WITH JEEM INITIAL FORM * 062D ARABIC LETTER HAH * 062C ARABIC LETTER JEEM * FCAA ARABIC LIGATURE HAH WITH MEEM INITIAL FORM * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FCAB ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM * 062E ARABIC LETTER KHAH * 062C ARABIC LETTER JEEM * FCAC ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FCAD ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM * 0633 ARABIC LETTER SEEN * 062C ARABIC LETTER JEEM * FCAE ARABIC LIGATURE SEEN WITH HAH INITIAL FORM * 0633 ARABIC LETTER SEEN * 062D ARABIC LETTER HAH * FCAF ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM * 0633 ARABIC LETTER SEEN * 062E ARABIC LETTER KHAH * FCB0 ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM * 0633 ARABIC LETTER SEEN * 0645 ARABIC LETTER MEEM * FCB1 ARABIC LIGATURE SAD WITH HAH INITIAL FORM * 0635 ARABIC LETTER SAD * 062D ARABIC LETTER HAH * FCB2 ARABIC LIGATURE SAD WITH KHAH INITIAL FORM * 0635 ARABIC LETTER SAD * 062E ARABIC LETTER KHAH * FCB3 ARABIC LIGATURE SAD WITH MEEM INITIAL FORM * 0635 ARABIC LETTER SAD * 0645 ARABIC LETTER MEEM * FCB4 ARABIC LIGATURE DAD WITH JEEM INITIAL FORM * 0636 ARABIC LETTER DAD * 062C ARABIC LETTER JEEM * FCB5 ARABIC LIGATURE DAD WITH HAH INITIAL FORM * 0636 ARABIC LETTER DAD * 062D ARABIC LETTER HAH * FCB6 ARABIC LIGATURE DAD WITH KHAH INITIAL FORM * 0636 ARABIC LETTER DAD * 062E ARABIC LETTER KHAH * FCB7 ARABIC LIGATURE DAD WITH MEEM INITIAL FORM * 0636 ARABIC LETTER DAD * 0645 ARABIC LETTER MEEM * FCB8 ARABIC LIGATURE TAH WITH HAH INITIAL FORM * 0637 ARABIC LETTER TAH * 062D ARABIC LETTER HAH * FCB9 ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM * 0638 ARABIC LETTER ZAH * 0645 ARABIC LETTER MEEM * FCBA ARABIC LIGATURE AIN WITH JEEM INITIAL FORM * 0639 ARABIC LETTER AIN * 062C ARABIC LETTER JEEM * FCBB ARABIC LIGATURE AIN WITH MEEM INITIAL FORM * 0639 ARABIC LETTER AIN * 0645 ARABIC LETTER MEEM * FCBC ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM * 063A ARABIC LETTER GHAIN * 062C ARABIC LETTER JEEM * FCBD ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM * 063A ARABIC LETTER GHAIN * 0645 ARABIC LETTER MEEM * FCBE ARABIC LIGATURE FEH WITH JEEM INITIAL FORM * 0641 ARABIC LETTER FEH * 062C ARABIC LETTER JEEM * FCBF ARABIC LIGATURE FEH WITH HAH INITIAL FORM * 0641 ARABIC LETTER FEH * 062D ARABIC LETTER HAH * FCC0 ARABIC LIGATURE FEH WITH KHAH INITIAL FORM * 0641 ARABIC LETTER FEH * 062E ARABIC LETTER KHAH * FCC1 ARABIC LIGATURE FEH WITH MEEM INITIAL FORM * 0641 ARABIC LETTER FEH * 0645 ARABIC LETTER MEEM * FCC2 ARABIC LIGATURE QAF WITH HAH INITIAL FORM * 0642 ARABIC LETTER QAF * 062D ARABIC LETTER HAH * FCC3 ARABIC LIGATURE QAF WITH MEEM INITIAL FORM * 0642 ARABIC LETTER QAF * 0645 ARABIC LETTER MEEM * FCC4 ARABIC LIGATURE KAF WITH JEEM INITIAL FORM * 0643 ARABIC LETTER KAF * 062C ARABIC LETTER JEEM * FCC5 ARABIC LIGATURE KAF WITH HAH INITIAL FORM * 0643 ARABIC LETTER KAF * 062D ARABIC LETTER HAH * FCC6 ARABIC LIGATURE KAF WITH KHAH INITIAL FORM * 0643 ARABIC LETTER KAF * 062E ARABIC LETTER KHAH * FCC7 ARABIC LIGATURE KAF WITH LAM INITIAL FORM * 0643 ARABIC LETTER KAF * 0644 ARABIC LETTER LAM * FCC8 ARABIC LIGATURE KAF WITH MEEM INITIAL FORM * 0643 ARABIC LETTER KAF * 0645 ARABIC LETTER MEEM * FCC9 ARABIC LIGATURE LAM WITH JEEM INITIAL FORM * 0644 ARABIC LETTER LAM * 062C ARABIC LETTER JEEM * FCCA ARABIC LIGATURE LAM WITH HAH INITIAL FORM * 0644 ARABIC LETTER LAM * 062D ARABIC LETTER HAH * FCCB ARABIC LIGATURE LAM WITH KHAH INITIAL FORM * 0644 ARABIC LETTER LAM * 062E ARABIC LETTER KHAH * FCCC ARABIC LIGATURE LAM WITH MEEM INITIAL FORM * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * FCCD ARABIC LIGATURE LAM WITH HEH INITIAL FORM * 0644 ARABIC LETTER LAM * 0647 ARABIC LETTER HEH * FCCE ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * FCCF ARABIC LIGATURE MEEM WITH HAH INITIAL FORM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FCD0 ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM * 0645 ARABIC LETTER MEEM * 062E ARABIC LETTER KHAH * FCD1 ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FCD2 ARABIC LIGATURE NOON WITH JEEM INITIAL FORM * 0646 ARABIC LETTER NOON * 062C ARABIC LETTER JEEM * FCD3 ARABIC LIGATURE NOON WITH HAH INITIAL FORM * 0646 ARABIC LETTER NOON * 062D ARABIC LETTER HAH * FCD4 ARABIC LIGATURE NOON WITH KHAH INITIAL FORM * 0646 ARABIC LETTER NOON * 062E ARABIC LETTER KHAH * FCD5 ARABIC LIGATURE NOON WITH MEEM INITIAL FORM * 0646 ARABIC LETTER NOON * 0645 ARABIC LETTER MEEM * FCD6 ARABIC LIGATURE NOON WITH HEH INITIAL FORM * 0646 ARABIC LETTER NOON * 0647 ARABIC LETTER HEH * FCD7 ARABIC LIGATURE HEH WITH JEEM INITIAL FORM * 0647 ARABIC LETTER HEH * 062C ARABIC LETTER JEEM * FCD8 ARABIC LIGATURE HEH WITH MEEM INITIAL FORM * 0647 ARABIC LETTER HEH * 0645 ARABIC LETTER MEEM * FCD9 ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM * 0647 ARABIC LETTER HEH * FCDA ARABIC LIGATURE YEH WITH JEEM INITIAL FORM * 064A ARABIC LETTER YEH * 062C ARABIC LETTER JEEM * FCDB ARABIC LIGATURE YEH WITH HAH INITIAL FORM * 064A ARABIC LETTER YEH * 062D ARABIC LETTER HAH * FCDC ARABIC LIGATURE YEH WITH KHAH INITIAL FORM * 064A ARABIC LETTER YEH * 062E ARABIC LETTER KHAH * FCDD ARABIC LIGATURE YEH WITH MEEM INITIAL FORM * 064A ARABIC LETTER YEH * 0645 ARABIC LETTER MEEM * FCDE ARABIC LIGATURE YEH WITH HEH INITIAL FORM * 064A ARABIC LETTER YEH * 0647 ARABIC LETTER HEH * FCDF ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FCE0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM * 0647 ARABIC LETTER HEH * 064A ARABIC LETTER YEH * FCE1 ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM * 0628 ARABIC LETTER BEH * 0645 ARABIC LETTER MEEM * FCE2 ARABIC LIGATURE BEH WITH HEH MEDIAL FORM * 0628 ARABIC LETTER BEH * 0647 ARABIC LETTER HEH * FCE3 ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * FCE4 ARABIC LIGATURE TEH WITH HEH MEDIAL FORM * 062A ARABIC LETTER TEH * 0647 ARABIC LETTER HEH * FCE5 ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM * 062B ARABIC LETTER THEH * 0645 ARABIC LETTER MEEM * FCE6 ARABIC LIGATURE THEH WITH HEH MEDIAL FORM * 062B ARABIC LETTER THEH * 0647 ARABIC LETTER HEH * FCE7 ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM * 0633 ARABIC LETTER SEEN * 0645 ARABIC LETTER MEEM * FCE8 ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM * 0633 ARABIC LETTER SEEN * 0647 ARABIC LETTER HEH * FCE9 ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM * 0634 ARABIC LETTER SHEEN * 0645 ARABIC LETTER MEEM * FCEA ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM * 0634 ARABIC LETTER SHEEN * 0647 ARABIC LETTER HEH * FCEB ARABIC LIGATURE KAF WITH LAM MEDIAL FORM * 0643 ARABIC LETTER KAF * 0644 ARABIC LETTER LAM * FCEC ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM * 0643 ARABIC LETTER KAF * 0645 ARABIC LETTER MEEM * FCED ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * FCEE ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM * 0646 ARABIC LETTER NOON * 0645 ARABIC LETTER MEEM * FCEF ARABIC LIGATURE NOON WITH HEH MEDIAL FORM * 0646 ARABIC LETTER NOON * 0647 ARABIC LETTER HEH * FCF0 ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM * 064A ARABIC LETTER YEH * 0645 ARABIC LETTER MEEM * FCF1 ARABIC LIGATURE YEH WITH HEH MEDIAL FORM * 064A ARABIC LETTER YEH * 0647 ARABIC LETTER HEH * FCF2 ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM * 0640 ARABIC TATWEEL * FCF3 ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM * 0640 ARABIC TATWEEL * FCF4 ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM * 0640 ARABIC TATWEEL * FCF5 ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM * 0637 ARABIC LETTER TAH * 0649 ARABIC LETTER ALEF MAKSURA * FCF6 ARABIC LIGATURE TAH WITH YEH ISOLATED FORM * 0637 ARABIC LETTER TAH * 064A ARABIC LETTER YEH * FCF7 ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM * 0639 ARABIC LETTER AIN * 0649 ARABIC LETTER ALEF MAKSURA * FCF8 ARABIC LIGATURE AIN WITH YEH ISOLATED FORM * 0639 ARABIC LETTER AIN * 064A ARABIC LETTER YEH * FCF9 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM * 063A ARABIC LETTER GHAIN * 0649 ARABIC LETTER ALEF MAKSURA * FCFA ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM * 063A ARABIC LETTER GHAIN * 064A ARABIC LETTER YEH * FCFB ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM * 0633 ARABIC LETTER SEEN * 0649 ARABIC LETTER ALEF MAKSURA * FCFC ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM * 0633 ARABIC LETTER SEEN * 064A ARABIC LETTER YEH * FCFD ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM * 0634 ARABIC LETTER SHEEN * 0649 ARABIC LETTER ALEF MAKSURA * FCFE ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM * 0634 ARABIC LETTER SHEEN * 064A ARABIC LETTER YEH * FCFF ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM * 062D ARABIC LETTER HAH * 0649 ARABIC LETTER ALEF MAKSURA * FD00 ARABIC LIGATURE HAH WITH YEH ISOLATED FORM * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FD01 ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM * 062C ARABIC LETTER JEEM * 0649 ARABIC LETTER ALEF MAKSURA * FD02 ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FD03 ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM * 062E ARABIC LETTER KHAH * 0649 ARABIC LETTER ALEF MAKSURA * FD04 ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM * 062E ARABIC LETTER KHAH * 064A ARABIC LETTER YEH * FD05 ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM * 0635 ARABIC LETTER SAD * 0649 ARABIC LETTER ALEF MAKSURA * FD06 ARABIC LIGATURE SAD WITH YEH ISOLATED FORM * 0635 ARABIC LETTER SAD * 064A ARABIC LETTER YEH * FD07 ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM * 0636 ARABIC LETTER DAD * 0649 ARABIC LETTER ALEF MAKSURA * FD08 ARABIC LIGATURE DAD WITH YEH ISOLATED FORM * 0636 ARABIC LETTER DAD * 064A ARABIC LETTER YEH * FD09 ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM * 0634 ARABIC LETTER SHEEN * 062C ARABIC LETTER JEEM * FD0A ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM * 0634 ARABIC LETTER SHEEN * 062D ARABIC LETTER HAH * FD0B ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM * 0634 ARABIC LETTER SHEEN * 062E ARABIC LETTER KHAH * FD0C ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM * 0634 ARABIC LETTER SHEEN * 0645 ARABIC LETTER MEEM * FD0D ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM * 0634 ARABIC LETTER SHEEN * 0631 ARABIC LETTER REH * FD0E ARABIC LIGATURE SEEN WITH REH ISOLATED FORM * 0633 ARABIC LETTER SEEN * 0631 ARABIC LETTER REH * FD0F ARABIC LIGATURE SAD WITH REH ISOLATED FORM * 0635 ARABIC LETTER SAD * 0631 ARABIC LETTER REH * FD10 ARABIC LIGATURE DAD WITH REH ISOLATED FORM * 0636 ARABIC LETTER DAD * 0631 ARABIC LETTER REH * FD11 ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM * 0637 ARABIC LETTER TAH * 0649 ARABIC LETTER ALEF MAKSURA * FD12 ARABIC LIGATURE TAH WITH YEH FINAL FORM * 0637 ARABIC LETTER TAH * 064A ARABIC LETTER YEH * FD13 ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM * 0639 ARABIC LETTER AIN * 0649 ARABIC LETTER ALEF MAKSURA * FD14 ARABIC LIGATURE AIN WITH YEH FINAL FORM * 0639 ARABIC LETTER AIN * 064A ARABIC LETTER YEH * FD15 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM * 063A ARABIC LETTER GHAIN * 0649 ARABIC LETTER ALEF MAKSURA * FD16 ARABIC LIGATURE GHAIN WITH YEH FINAL FORM * 063A ARABIC LETTER GHAIN * 064A ARABIC LETTER YEH * FD17 ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM * 0633 ARABIC LETTER SEEN * 0649 ARABIC LETTER ALEF MAKSURA * FD18 ARABIC LIGATURE SEEN WITH YEH FINAL FORM * 0633 ARABIC LETTER SEEN * 064A ARABIC LETTER YEH * FD19 ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM * 0634 ARABIC LETTER SHEEN * 0649 ARABIC LETTER ALEF MAKSURA * FD1A ARABIC LIGATURE SHEEN WITH YEH FINAL FORM * 0634 ARABIC LETTER SHEEN * 064A ARABIC LETTER YEH * FD1B ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM * 062D ARABIC LETTER HAH * 0649 ARABIC LETTER ALEF MAKSURA * FD1C ARABIC LIGATURE HAH WITH YEH FINAL FORM * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FD1D ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM * 062C ARABIC LETTER JEEM * 0649 ARABIC LETTER ALEF MAKSURA * FD1E ARABIC LIGATURE JEEM WITH YEH FINAL FORM * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FD1F ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM * 062E ARABIC LETTER KHAH * 0649 ARABIC LETTER ALEF MAKSURA * FD20 ARABIC LIGATURE KHAH WITH YEH FINAL FORM * 062E ARABIC LETTER KHAH * 064A ARABIC LETTER YEH * FD21 ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM * 0635 ARABIC LETTER SAD * 0649 ARABIC LETTER ALEF MAKSURA * FD22 ARABIC LIGATURE SAD WITH YEH FINAL FORM * 0635 ARABIC LETTER SAD * 064A ARABIC LETTER YEH * FD23 ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM * 0636 ARABIC LETTER DAD * 0649 ARABIC LETTER ALEF MAKSURA * FD24 ARABIC LIGATURE DAD WITH YEH FINAL FORM * 0636 ARABIC LETTER DAD * 064A ARABIC LETTER YEH * FD25 ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM * 0634 ARABIC LETTER SHEEN * 062C ARABIC LETTER JEEM * FD26 ARABIC LIGATURE SHEEN WITH HAH FINAL FORM * 0634 ARABIC LETTER SHEEN * 062D ARABIC LETTER HAH * FD27 ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM * 0634 ARABIC LETTER SHEEN * 062E ARABIC LETTER KHAH * FD28 ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM * 0634 ARABIC LETTER SHEEN * 0645 ARABIC LETTER MEEM * FD29 ARABIC LIGATURE SHEEN WITH REH FINAL FORM * 0634 ARABIC LETTER SHEEN * 0631 ARABIC LETTER REH * FD2A ARABIC LIGATURE SEEN WITH REH FINAL FORM * 0633 ARABIC LETTER SEEN * 0631 ARABIC LETTER REH * FD2B ARABIC LIGATURE SAD WITH REH FINAL FORM * 0635 ARABIC LETTER SAD * 0631 ARABIC LETTER REH * FD2C ARABIC LIGATURE DAD WITH REH FINAL FORM * 0636 ARABIC LETTER DAD * 0631 ARABIC LETTER REH * FD2D ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM * 0634 ARABIC LETTER SHEEN * 062C ARABIC LETTER JEEM * FD2E ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM * 0634 ARABIC LETTER SHEEN * 062D ARABIC LETTER HAH * FD2F ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM * 0634 ARABIC LETTER SHEEN * 062E ARABIC LETTER KHAH * FD30 ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM * 0634 ARABIC LETTER SHEEN * 0645 ARABIC LETTER MEEM * FD31 ARABIC LIGATURE SEEN WITH HEH INITIAL FORM * 0633 ARABIC LETTER SEEN * 0647 ARABIC LETTER HEH * FD32 ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM * 0634 ARABIC LETTER SHEEN * 0647 ARABIC LETTER HEH * FD33 ARABIC LIGATURE TAH WITH MEEM INITIAL FORM * 0637 ARABIC LETTER TAH * 0645 ARABIC LETTER MEEM * FD34 ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM * 0633 ARABIC LETTER SEEN * 062C ARABIC LETTER JEEM * FD35 ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM * 0633 ARABIC LETTER SEEN * 062D ARABIC LETTER HAH * FD36 ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM * 0633 ARABIC LETTER SEEN * 062E ARABIC LETTER KHAH * FD37 ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM * 0634 ARABIC LETTER SHEEN * 062C ARABIC LETTER JEEM * FD38 ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM * 0634 ARABIC LETTER SHEEN * 062D ARABIC LETTER HAH * FD39 ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM * 0634 ARABIC LETTER SHEEN * 062E ARABIC LETTER KHAH * FD3A ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM * 0637 ARABIC LETTER TAH * 0645 ARABIC LETTER MEEM * FD3B ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM * 0638 ARABIC LETTER ZAH * 0645 ARABIC LETTER MEEM * FD3C ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM * 0627 ARABIC LETTER ALEF * FD3D ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM * 0627 ARABIC LETTER ALEF * FD50 ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM * 062A ARABIC LETTER TEH * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FD51 ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM * 062A ARABIC LETTER TEH * 062D ARABIC LETTER HAH * 062C ARABIC LETTER JEEM * FD52 ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM * 062A ARABIC LETTER TEH * 062D ARABIC LETTER HAH * 062C ARABIC LETTER JEEM * FD53 ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM * 062A ARABIC LETTER TEH * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FD54 ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM * 062A ARABIC LETTER TEH * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FD55 ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * FD56 ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD57 ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * 062E ARABIC LETTER KHAH * FD58 ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD59 ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD5A ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FD5B ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * 0649 ARABIC LETTER ALEF MAKSURA * FD5C ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM * 0633 ARABIC LETTER SEEN * 062D ARABIC LETTER HAH * 062C ARABIC LETTER JEEM * FD5D ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM * 0633 ARABIC LETTER SEEN * 062C ARABIC LETTER JEEM * 062D ARABIC LETTER HAH * FD5E ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM * 0633 ARABIC LETTER SEEN * 062C ARABIC LETTER JEEM * 0649 ARABIC LETTER ALEF MAKSURA * FD5F ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM * 0633 ARABIC LETTER SEEN * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD60 ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM * 0633 ARABIC LETTER SEEN * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD61 ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM * 0633 ARABIC LETTER SEEN * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * FD62 ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM * 0633 ARABIC LETTER SEEN * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD63 ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM * 0633 ARABIC LETTER SEEN * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD64 ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM * 0635 ARABIC LETTER SAD * 062D ARABIC LETTER HAH * 062D ARABIC LETTER HAH * FD65 ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM * 0635 ARABIC LETTER SAD * 062D ARABIC LETTER HAH * 062D ARABIC LETTER HAH * FD66 ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM * 0635 ARABIC LETTER SAD * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD67 ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM * 0634 ARABIC LETTER SHEEN * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FD68 ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM * 0634 ARABIC LETTER SHEEN * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FD69 ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM * 0634 ARABIC LETTER SHEEN * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FD6A ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM * 0634 ARABIC LETTER SHEEN * 0645 ARABIC LETTER MEEM * 062E ARABIC LETTER KHAH * FD6B ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM * 0634 ARABIC LETTER SHEEN * 0645 ARABIC LETTER MEEM * 062E ARABIC LETTER KHAH * FD6C ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM * 0634 ARABIC LETTER SHEEN * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD6D ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM * 0634 ARABIC LETTER SHEEN * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD6E ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM * 0636 ARABIC LETTER DAD * 062D ARABIC LETTER HAH * 0649 ARABIC LETTER ALEF MAKSURA * FD6F ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM * 0636 ARABIC LETTER DAD * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FD70 ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM * 0636 ARABIC LETTER DAD * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FD71 ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM * 0637 ARABIC LETTER TAH * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD72 ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM * 0637 ARABIC LETTER TAH * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD73 ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM * 0637 ARABIC LETTER TAH * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD74 ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM * 0637 ARABIC LETTER TAH * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FD75 ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM * 0639 ARABIC LETTER AIN * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FD76 ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM * 0639 ARABIC LETTER AIN * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD77 ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM * 0639 ARABIC LETTER AIN * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD78 ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM * 0639 ARABIC LETTER AIN * 0645 ARABIC LETTER MEEM * 0649 ARABIC LETTER ALEF MAKSURA * FD79 ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM * 063A ARABIC LETTER GHAIN * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD7A ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM * 063A ARABIC LETTER GHAIN * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FD7B ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM * 063A ARABIC LETTER GHAIN * 0645 ARABIC LETTER MEEM * 0649 ARABIC LETTER ALEF MAKSURA * FD7C ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM * 0641 ARABIC LETTER FEH * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FD7D ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM * 0641 ARABIC LETTER FEH * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FD7E ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM * 0642 ARABIC LETTER QAF * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD7F ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM * 0642 ARABIC LETTER QAF * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD80 ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM * 0644 ARABIC LETTER LAM * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FD81 ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM * 0644 ARABIC LETTER LAM * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FD82 ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM * 0644 ARABIC LETTER LAM * 062D ARABIC LETTER HAH * 0649 ARABIC LETTER ALEF MAKSURA * FD83 ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM * 0644 ARABIC LETTER LAM * 062C ARABIC LETTER JEEM * 062C ARABIC LETTER JEEM * FD84 ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM * 0644 ARABIC LETTER LAM * 062C ARABIC LETTER JEEM * 062C ARABIC LETTER JEEM * FD85 ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM * 0644 ARABIC LETTER LAM * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FD86 ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM * 0644 ARABIC LETTER LAM * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FD87 ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD88 ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FD89 ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * 062C ARABIC LETTER JEEM * FD8A ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FD8B ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FD8C ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * 062D ARABIC LETTER HAH * FD8D ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FD8E ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * 062E ARABIC LETTER KHAH * 062C ARABIC LETTER JEEM * FD8F ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * 062E ARABIC LETTER KHAH * 0645 ARABIC LETTER MEEM * FD92 ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * 062E ARABIC LETTER KHAH * FD93 ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM * 0647 ARABIC LETTER HEH * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * FD94 ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM * 0647 ARABIC LETTER HEH * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD95 ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM * 0646 ARABIC LETTER NOON * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FD96 ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM * 0646 ARABIC LETTER NOON * 062D ARABIC LETTER HAH * 0649 ARABIC LETTER ALEF MAKSURA * FD97 ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM * 0646 ARABIC LETTER NOON * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FD98 ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM * 0646 ARABIC LETTER NOON * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FD99 ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM * 0646 ARABIC LETTER NOON * 062C ARABIC LETTER JEEM * 0649 ARABIC LETTER ALEF MAKSURA * FD9A ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM * 0646 ARABIC LETTER NOON * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FD9B ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM * 0646 ARABIC LETTER NOON * 0645 ARABIC LETTER MEEM * 0649 ARABIC LETTER ALEF MAKSURA * FD9C ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM * 064A ARABIC LETTER YEH * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD9D ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM * 064A ARABIC LETTER YEH * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FD9E ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM * 0628 ARABIC LETTER BEH * 062E ARABIC LETTER KHAH * 064A ARABIC LETTER YEH * FD9F ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM * 062A ARABIC LETTER TEH * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FDA0 ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM * 062A ARABIC LETTER TEH * 062C ARABIC LETTER JEEM * 0649 ARABIC LETTER ALEF MAKSURA * FDA1 ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM * 062A ARABIC LETTER TEH * 062E ARABIC LETTER KHAH * 064A ARABIC LETTER YEH * FDA2 ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM * 062A ARABIC LETTER TEH * 062E ARABIC LETTER KHAH * 0649 ARABIC LETTER ALEF MAKSURA * FDA3 ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDA4 ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM * 062A ARABIC LETTER TEH * 0645 ARABIC LETTER MEEM * 0649 ARABIC LETTER ALEF MAKSURA * FDA5 ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDA6 ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM * 062C ARABIC LETTER JEEM * 062D ARABIC LETTER HAH * 0649 ARABIC LETTER ALEF MAKSURA * FDA7 ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * 0649 ARABIC LETTER ALEF MAKSURA * FDA8 ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM * 0633 ARABIC LETTER SEEN * 062E ARABIC LETTER KHAH * 0649 ARABIC LETTER ALEF MAKSURA * FDA9 ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM * 0635 ARABIC LETTER SAD * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FDAA ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM * 0634 ARABIC LETTER SHEEN * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FDAB ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM * 0636 ARABIC LETTER DAD * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FDAC ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM * 0644 ARABIC LETTER LAM * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FDAD ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDAE ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM * 064A ARABIC LETTER YEH * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FDAF ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM * 064A ARABIC LETTER YEH * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FDB0 ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM * 064A ARABIC LETTER YEH * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDB1 ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDB2 ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM * 0642 ARABIC LETTER QAF * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDB3 ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM * 0646 ARABIC LETTER NOON * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FDB4 ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM * 0642 ARABIC LETTER QAF * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * FDB5 ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM * 0644 ARABIC LETTER LAM * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * FDB6 ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM * 0639 ARABIC LETTER AIN * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDB7 ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM * 0643 ARABIC LETTER KAF * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDB8 ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM * 0646 ARABIC LETTER NOON * 062C ARABIC LETTER JEEM * 062D ARABIC LETTER HAH * FDB9 ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM * 0645 ARABIC LETTER MEEM * 062E ARABIC LETTER KHAH * 064A ARABIC LETTER YEH * FDBA ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM * 0644 ARABIC LETTER LAM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FDBB ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM * 0643 ARABIC LETTER KAF * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FDBC ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM * 0644 ARABIC LETTER LAM * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FDBD ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM * 0646 ARABIC LETTER NOON * 062C ARABIC LETTER JEEM * 062D ARABIC LETTER HAH * FDBE ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM * 062C ARABIC LETTER JEEM * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FDBF ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM * 062D ARABIC LETTER HAH * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FDC0 ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM * 0645 ARABIC LETTER MEEM * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FDC1 ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM * 0641 ARABIC LETTER FEH * 0645 ARABIC LETTER MEEM * 064A ARABIC LETTER YEH * FDC2 ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM * 0628 ARABIC LETTER BEH * 062D ARABIC LETTER HAH * 064A ARABIC LETTER YEH * FDC3 ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM * 0643 ARABIC LETTER KAF * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FDC4 ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM * 0639 ARABIC LETTER AIN * 062C ARABIC LETTER JEEM * 0645 ARABIC LETTER MEEM * FDC5 ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM * 0635 ARABIC LETTER SAD * 0645 ARABIC LETTER MEEM * 0645 ARABIC LETTER MEEM * FDC6 ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM * 0633 ARABIC LETTER SEEN * 062E ARABIC LETTER KHAH * 064A ARABIC LETTER YEH * FDC7 ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM * 0646 ARABIC LETTER NOON * 062C ARABIC LETTER JEEM * 064A ARABIC LETTER YEH * FDF0 ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM * 0635 ARABIC LETTER SAD * 0644 ARABIC LETTER LAM * 06D2 ARABIC LETTER YEH BARREE * FDF1 ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM * 0642 ARABIC LETTER QAF * 0644 ARABIC LETTER LAM * 06D2 ARABIC LETTER YEH BARREE * FDF2 ARABIC LIGATURE ALLAH ISOLATED FORM * 0627 ARABIC LETTER ALEF * 0644 ARABIC LETTER LAM * 0644 ARABIC LETTER LAM * 0647 ARABIC LETTER HEH * FDF3 ARABIC LIGATURE AKBAR ISOLATED FORM * 0627 ARABIC LETTER ALEF * 0643 ARABIC LETTER KAF * 0628 ARABIC LETTER BEH * 0631 ARABIC LETTER REH * FDF4 ARABIC LIGATURE MOHAMMAD ISOLATED FORM * 0645 ARABIC LETTER MEEM * 062D ARABIC LETTER HAH * 0645 ARABIC LETTER MEEM * 062F ARABIC LETTER DAL * FDF5 ARABIC LIGATURE SALAM ISOLATED FORM * 0635 ARABIC LETTER SAD * 0644 ARABIC LETTER LAM * 0639 ARABIC LETTER AIN * 0645 ARABIC LETTER MEEM * FDF6 ARABIC LIGATURE RASOUL ISOLATED FORM * 0631 ARABIC LETTER REH * 0633 ARABIC LETTER SEEN * 0648 ARABIC LETTER WAW * 0644 ARABIC LETTER LAM * FDF7 ARABIC LIGATURE ALAYHE ISOLATED FORM * 0639 ARABIC LETTER AIN * 0644 ARABIC LETTER LAM * 064A ARABIC LETTER YEH * 0647 ARABIC LETTER HEH * FDF8 ARABIC LIGATURE WASALLAM ISOLATED FORM * 0648 ARABIC LETTER WAW * 0633 ARABIC LETTER SEEN * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * FDF9 ARABIC LIGATURE SALLA ISOLATED FORM * 0635 ARABIC LETTER SAD * 0644 ARABIC LETTER LAM * 0649 ARABIC LETTER ALEF MAKSURA * FDFA ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM * 0635 ARABIC LETTER SAD * 0644 ARABIC LETTER LAM * 0649 ARABIC LETTER ALEF MAKSURA * 0020 SPACE * 0627 ARABIC LETTER ALEF * 0644 ARABIC LETTER LAM * 0644 ARABIC LETTER LAM * 0647 ARABIC LETTER HEH * 0020 SPACE * 0639 ARABIC LETTER AIN * 0644 ARABIC LETTER LAM * 064A ARABIC LETTER YEH * 0647 ARABIC LETTER HEH * 0020 SPACE * 0648 ARABIC LETTER WAW * 0633 ARABIC LETTER SEEN * 0644 ARABIC LETTER LAM * 0645 ARABIC LETTER MEEM * FDFB ARABIC LIGATURE JALLAJALALOUHOU * 062C ARABIC LETTER JEEM * 0644 ARABIC LETTER LAM * 0020 SPACE * 062C ARABIC LETTER JEEM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * 0644 ARABIC LETTER LAM * 0647 ARABIC LETTER HEH * FDFC RIAL SIGN * 0631 ARABIC LETTER REH * 06CC ARABIC LETTER FARSI YEH * 0627 ARABIC LETTER ALEF * 0644 ARABIC LETTER LAM * FE00 VARIATION SELECTOR-1 * 0000 * FE01 VARIATION SELECTOR-2 * 0000 * FE02 VARIATION SELECTOR-3 * 0000 * FE03 VARIATION SELECTOR-4 * 0000 * FE04 VARIATION SELECTOR-5 * 0000 * FE05 VARIATION SELECTOR-6 * 0000 * FE06 VARIATION SELECTOR-7 * 0000 * FE07 VARIATION SELECTOR-8 * 0000 * FE08 VARIATION SELECTOR-9 * 0000 * FE09 VARIATION SELECTOR-10 * 0000 * FE0A VARIATION SELECTOR-11 * 0000 * FE0B VARIATION SELECTOR-12 * 0000 * FE0C VARIATION SELECTOR-13 * 0000 * FE0D VARIATION SELECTOR-14 * 0000 * FE0E VARIATION SELECTOR-15 * 0000 * FE0F VARIATION SELECTOR-16 * 0000 * FE10 PRESENTATION FORM FOR VERTICAL COMMA * 002C COMMA * FE11 PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC COMMA * 3001 IDEOGRAPHIC COMMA * FE12 PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC FULL STOP * 3002 IDEOGRAPHIC FULL STOP * FE13 PRESENTATION FORM FOR VERTICAL COLON * 003A COLON * FE14 PRESENTATION FORM FOR VERTICAL SEMICOLON * 003B SEMICOLON * FE15 PRESENTATION FORM FOR VERTICAL EXCLAMATION MARK * 0021 EXCLAMATION MARK * FE16 PRESENTATION FORM FOR VERTICAL QUESTION MARK * 003F QUESTION MARK * FE17 PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET * 3016 LEFT WHITE LENTICULAR BRACKET * FE18 PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET * 3017 RIGHT WHITE LENTICULAR BRACKET * FE19 PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS * 002E FULL STOP * 002E FULL STOP * 002E FULL STOP * FE20 COMBINING LIGATURE LEFT HALF * 0000 * FE21 COMBINING LIGATURE RIGHT HALF * 0000 * FE22 COMBINING DOUBLE TILDE LEFT HALF * 0000 * FE23 COMBINING DOUBLE TILDE RIGHT HALF * 0000 * FE24 COMBINING MACRON LEFT HALF * 0000 * FE25 COMBINING MACRON RIGHT HALF * 0000 * FE26 COMBINING CONJOINING MACRON * 0000 * FE27 COMBINING LIGATURE LEFT HALF BELOW * 0000 * FE28 COMBINING LIGATURE RIGHT HALF BELOW * 0000 * FE29 COMBINING TILDE LEFT HALF BELOW * 0000 * FE2A COMBINING TILDE RIGHT HALF BELOW * 0000 * FE2B COMBINING MACRON LEFT HALF BELOW * 0000 * FE2C COMBINING MACRON RIGHT HALF BELOW * 0000 * FE2D COMBINING CONJOINING MACRON BELOW * 0000 * FE2E COMBINING CYRILLIC TITLO LEFT HALF * 0000 * FE2F COMBINING CYRILLIC TITLO RIGHT HALF * 0000 * FE30 PRESENTATION FORM FOR VERTICAL TWO DOT LEADER * 002E FULL STOP * 002E FULL STOP * FE31 PRESENTATION FORM FOR VERTICAL EM DASH * 2014 EM DASH * FE32 PRESENTATION FORM FOR VERTICAL EN DASH * 2013 EN DASH * FE33 PRESENTATION FORM FOR VERTICAL LOW LINE * 005F LOW LINE * FE34 PRESENTATION FORM FOR VERTICAL WAVY LOW LINE * 005F LOW LINE * FE35 PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS * 0028 LEFT PARENTHESIS * FE36 PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS * 0029 RIGHT PARENTHESIS * FE37 PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET * 007B LEFT CURLY BRACKET * FE38 PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET * 007D RIGHT CURLY BRACKET * FE39 PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET * 3014 LEFT TORTOISE SHELL BRACKET * FE3A PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET * 3015 RIGHT TORTOISE SHELL BRACKET * FE3B PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET * 3010 LEFT BLACK LENTICULAR BRACKET * FE3C PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET * 3011 RIGHT BLACK LENTICULAR BRACKET * FE3D PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET * 300A LEFT DOUBLE ANGLE BRACKET * FE3E PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET * 300B RIGHT DOUBLE ANGLE BRACKET * FE3F PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET * 3008 LEFT ANGLE BRACKET * FE40 PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET * 3009 RIGHT ANGLE BRACKET * FE41 PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET * 300C LEFT CORNER BRACKET * FE42 PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET * 300D RIGHT CORNER BRACKET * FE43 PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET * 300E LEFT WHITE CORNER BRACKET * FE44 PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET * 300F RIGHT WHITE CORNER BRACKET * FE47 PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET * 005B LEFT SQUARE BRACKET * FE48 PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET * 005D RIGHT SQUARE BRACKET * FE49 DASHED OVERLINE * 0020 SPACE * FE4A CENTRELINE OVERLINE * 0020 SPACE * FE4B WAVY OVERLINE * 0020 SPACE * FE4C DOUBLE WAVY OVERLINE * 0020 SPACE * FE4D DASHED LOW LINE * 005F LOW LINE * FE4E CENTRELINE LOW LINE * 005F LOW LINE * FE4F WAVY LOW LINE * 005F LOW LINE * FE50 SMALL COMMA * 002C COMMA * FE51 SMALL IDEOGRAPHIC COMMA * 3001 IDEOGRAPHIC COMMA * FE52 SMALL FULL STOP * 002E FULL STOP * FE54 SMALL SEMICOLON * 003B SEMICOLON * FE55 SMALL COLON * 003A COLON * FE56 SMALL QUESTION MARK * 003F QUESTION MARK * FE57 SMALL EXCLAMATION MARK * 0021 EXCLAMATION MARK * FE58 SMALL EM DASH * 2014 EM DASH * FE59 SMALL LEFT PARENTHESIS * 0028 LEFT PARENTHESIS * FE5A SMALL RIGHT PARENTHESIS * 0029 RIGHT PARENTHESIS * FE5B SMALL LEFT CURLY BRACKET * 007B LEFT CURLY BRACKET * FE5C SMALL RIGHT CURLY BRACKET * 007D RIGHT CURLY BRACKET * FE5D SMALL LEFT TORTOISE SHELL BRACKET * 3014 LEFT TORTOISE SHELL BRACKET * FE5E SMALL RIGHT TORTOISE SHELL BRACKET * 3015 RIGHT TORTOISE SHELL BRACKET * FE5F SMALL NUMBER SIGN * 0023 NUMBER SIGN * FE60 SMALL AMPERSAND * 0026 AMPERSAND * FE61 SMALL ASTERISK * 002A ASTERISK * FE62 SMALL PLUS SIGN * 002B PLUS SIGN * FE63 SMALL HYPHEN-MINUS * 002D HYPHEN-MINUS * FE64 SMALL LESS-THAN SIGN * 003C LESS-THAN SIGN * FE65 SMALL GREATER-THAN SIGN * 003E GREATER-THAN SIGN * FE66 SMALL EQUALS SIGN * 003D EQUALS SIGN * FE68 SMALL REVERSE SOLIDUS * 005C REVERSE SOLIDUS * FE69 SMALL DOLLAR SIGN * 0024 DOLLAR SIGN * FE6A SMALL PERCENT SIGN * 0025 PERCENT SIGN * FE6B SMALL COMMERCIAL AT * 0040 COMMERCIAL AT * FE70 ARABIC FATHATAN ISOLATED FORM * 0020 SPACE * FE71 ARABIC TATWEEL WITH FATHATAN ABOVE * 0640 ARABIC TATWEEL * FE72 ARABIC DAMMATAN ISOLATED FORM * 0020 SPACE * FE74 ARABIC KASRATAN ISOLATED FORM * 0020 SPACE * FE76 ARABIC FATHA ISOLATED FORM * 0020 SPACE * FE77 ARABIC FATHA MEDIAL FORM * 0640 ARABIC TATWEEL * FE78 ARABIC DAMMA ISOLATED FORM * 0020 SPACE * FE79 ARABIC DAMMA MEDIAL FORM * 0640 ARABIC TATWEEL * FE7A ARABIC KASRA ISOLATED FORM * 0020 SPACE * FE7B ARABIC KASRA MEDIAL FORM * 0640 ARABIC TATWEEL * FE7C ARABIC SHADDA ISOLATED FORM * 0020 SPACE * FE7D ARABIC SHADDA MEDIAL FORM * 0640 ARABIC TATWEEL * FE7E ARABIC SUKUN ISOLATED FORM * 0020 SPACE * FE7F ARABIC SUKUN MEDIAL FORM * 0640 ARABIC TATWEEL * FE80 ARABIC LETTER HAMZA ISOLATED FORM * 0621 ARABIC LETTER HAMZA * FE81 ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM * 0627 ARABIC LETTER ALEF * FE82 ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM * 0627 ARABIC LETTER ALEF * FE83 ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM * 0627 ARABIC LETTER ALEF * FE84 ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM * 0627 ARABIC LETTER ALEF * FE85 ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM * 0648 ARABIC LETTER WAW * FE86 ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM * 0648 ARABIC LETTER WAW * FE87 ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM * 0627 ARABIC LETTER ALEF * FE88 ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM * 0627 ARABIC LETTER ALEF * FE89 ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM * 064A ARABIC LETTER YEH * FE8A ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM * 064A ARABIC LETTER YEH * FE8B ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM * 064A ARABIC LETTER YEH * FE8C ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM * 064A ARABIC LETTER YEH * FE8D ARABIC LETTER ALEF ISOLATED FORM * 0627 ARABIC LETTER ALEF * FE8E ARABIC LETTER ALEF FINAL FORM * 0627 ARABIC LETTER ALEF * FE8F ARABIC LETTER BEH ISOLATED FORM * 0628 ARABIC LETTER BEH * FE90 ARABIC LETTER BEH FINAL FORM * 0628 ARABIC LETTER BEH * FE91 ARABIC LETTER BEH INITIAL FORM * 0628 ARABIC LETTER BEH * FE92 ARABIC LETTER BEH MEDIAL FORM * 0628 ARABIC LETTER BEH * FE93 ARABIC LETTER TEH MARBUTA ISOLATED FORM * 0629 ARABIC LETTER TEH MARBUTA * FE94 ARABIC LETTER TEH MARBUTA FINAL FORM * 0629 ARABIC LETTER TEH MARBUTA * FE95 ARABIC LETTER TEH ISOLATED FORM * 062A ARABIC LETTER TEH * FE96 ARABIC LETTER TEH FINAL FORM * 062A ARABIC LETTER TEH * FE97 ARABIC LETTER TEH INITIAL FORM * 062A ARABIC LETTER TEH * FE98 ARABIC LETTER TEH MEDIAL FORM * 062A ARABIC LETTER TEH * FE99 ARABIC LETTER THEH ISOLATED FORM * 062B ARABIC LETTER THEH * FE9A ARABIC LETTER THEH FINAL FORM * 062B ARABIC LETTER THEH * FE9B ARABIC LETTER THEH INITIAL FORM * 062B ARABIC LETTER THEH * FE9C ARABIC LETTER THEH MEDIAL FORM * 062B ARABIC LETTER THEH * FE9D ARABIC LETTER JEEM ISOLATED FORM * 062C ARABIC LETTER JEEM * FE9E ARABIC LETTER JEEM FINAL FORM * 062C ARABIC LETTER JEEM * FE9F ARABIC LETTER JEEM INITIAL FORM * 062C ARABIC LETTER JEEM * FEA0 ARABIC LETTER JEEM MEDIAL FORM * 062C ARABIC LETTER JEEM * FEA1 ARABIC LETTER HAH ISOLATED FORM * 062D ARABIC LETTER HAH * FEA2 ARABIC LETTER HAH FINAL FORM * 062D ARABIC LETTER HAH * FEA3 ARABIC LETTER HAH INITIAL FORM * 062D ARABIC LETTER HAH * FEA4 ARABIC LETTER HAH MEDIAL FORM * 062D ARABIC LETTER HAH * FEA5 ARABIC LETTER KHAH ISOLATED FORM * 062E ARABIC LETTER KHAH * FEA6 ARABIC LETTER KHAH FINAL FORM * 062E ARABIC LETTER KHAH * FEA7 ARABIC LETTER KHAH INITIAL FORM * 062E ARABIC LETTER KHAH * FEA8 ARABIC LETTER KHAH MEDIAL FORM * 062E ARABIC LETTER KHAH * FEA9 ARABIC LETTER DAL ISOLATED FORM * 062F ARABIC LETTER DAL * FEAA ARABIC LETTER DAL FINAL FORM * 062F ARABIC LETTER DAL * FEAB ARABIC LETTER THAL ISOLATED FORM * 0630 ARABIC LETTER THAL * FEAC ARABIC LETTER THAL FINAL FORM * 0630 ARABIC LETTER THAL * FEAD ARABIC LETTER REH ISOLATED FORM * 0631 ARABIC LETTER REH * FEAE ARABIC LETTER REH FINAL FORM * 0631 ARABIC LETTER REH * FEAF ARABIC LETTER ZAIN ISOLATED FORM * 0632 ARABIC LETTER ZAIN * FEB0 ARABIC LETTER ZAIN FINAL FORM * 0632 ARABIC LETTER ZAIN * FEB1 ARABIC LETTER SEEN ISOLATED FORM * 0633 ARABIC LETTER SEEN * FEB2 ARABIC LETTER SEEN FINAL FORM * 0633 ARABIC LETTER SEEN * FEB3 ARABIC LETTER SEEN INITIAL FORM * 0633 ARABIC LETTER SEEN * FEB4 ARABIC LETTER SEEN MEDIAL FORM * 0633 ARABIC LETTER SEEN * FEB5 ARABIC LETTER SHEEN ISOLATED FORM * 0634 ARABIC LETTER SHEEN * FEB6 ARABIC LETTER SHEEN FINAL FORM * 0634 ARABIC LETTER SHEEN * FEB7 ARABIC LETTER SHEEN INITIAL FORM * 0634 ARABIC LETTER SHEEN * FEB8 ARABIC LETTER SHEEN MEDIAL FORM * 0634 ARABIC LETTER SHEEN * FEB9 ARABIC LETTER SAD ISOLATED FORM * 0635 ARABIC LETTER SAD * FEBA ARABIC LETTER SAD FINAL FORM * 0635 ARABIC LETTER SAD * FEBB ARABIC LETTER SAD INITIAL FORM * 0635 ARABIC LETTER SAD * FEBC ARABIC LETTER SAD MEDIAL FORM * 0635 ARABIC LETTER SAD * FEBD ARABIC LETTER DAD ISOLATED FORM * 0636 ARABIC LETTER DAD * FEBE ARABIC LETTER DAD FINAL FORM * 0636 ARABIC LETTER DAD * FEBF ARABIC LETTER DAD INITIAL FORM * 0636 ARABIC LETTER DAD * FEC0 ARABIC LETTER DAD MEDIAL FORM * 0636 ARABIC LETTER DAD * FEC1 ARABIC LETTER TAH ISOLATED FORM * 0637 ARABIC LETTER TAH * FEC2 ARABIC LETTER TAH FINAL FORM * 0637 ARABIC LETTER TAH * FEC3 ARABIC LETTER TAH INITIAL FORM * 0637 ARABIC LETTER TAH * FEC4 ARABIC LETTER TAH MEDIAL FORM * 0637 ARABIC LETTER TAH * FEC5 ARABIC LETTER ZAH ISOLATED FORM * 0638 ARABIC LETTER ZAH * FEC6 ARABIC LETTER ZAH FINAL FORM * 0638 ARABIC LETTER ZAH * FEC7 ARABIC LETTER ZAH INITIAL FORM * 0638 ARABIC LETTER ZAH * FEC8 ARABIC LETTER ZAH MEDIAL FORM * 0638 ARABIC LETTER ZAH * FEC9 ARABIC LETTER AIN ISOLATED FORM * 0639 ARABIC LETTER AIN * FECA ARABIC LETTER AIN FINAL FORM * 0639 ARABIC LETTER AIN * FECB ARABIC LETTER AIN INITIAL FORM * 0639 ARABIC LETTER AIN * FECC ARABIC LETTER AIN MEDIAL FORM * 0639 ARABIC LETTER AIN * FECD ARABIC LETTER GHAIN ISOLATED FORM * 063A ARABIC LETTER GHAIN * FECE ARABIC LETTER GHAIN FINAL FORM * 063A ARABIC LETTER GHAIN * FECF ARABIC LETTER GHAIN INITIAL FORM * 063A ARABIC LETTER GHAIN * FED0 ARABIC LETTER GHAIN MEDIAL FORM * 063A ARABIC LETTER GHAIN * FED1 ARABIC LETTER FEH ISOLATED FORM * 0641 ARABIC LETTER FEH * FED2 ARABIC LETTER FEH FINAL FORM * 0641 ARABIC LETTER FEH * FED3 ARABIC LETTER FEH INITIAL FORM * 0641 ARABIC LETTER FEH * FED4 ARABIC LETTER FEH MEDIAL FORM * 0641 ARABIC LETTER FEH * FED5 ARABIC LETTER QAF ISOLATED FORM * 0642 ARABIC LETTER QAF * FED6 ARABIC LETTER QAF FINAL FORM * 0642 ARABIC LETTER QAF * FED7 ARABIC LETTER QAF INITIAL FORM * 0642 ARABIC LETTER QAF * FED8 ARABIC LETTER QAF MEDIAL FORM * 0642 ARABIC LETTER QAF * FED9 ARABIC LETTER KAF ISOLATED FORM * 0643 ARABIC LETTER KAF * FEDA ARABIC LETTER KAF FINAL FORM * 0643 ARABIC LETTER KAF * FEDB ARABIC LETTER KAF INITIAL FORM * 0643 ARABIC LETTER KAF * FEDC ARABIC LETTER KAF MEDIAL FORM * 0643 ARABIC LETTER KAF * FEDD ARABIC LETTER LAM ISOLATED FORM * 0644 ARABIC LETTER LAM * FEDE ARABIC LETTER LAM FINAL FORM * 0644 ARABIC LETTER LAM * FEDF ARABIC LETTER LAM INITIAL FORM * 0644 ARABIC LETTER LAM * FEE0 ARABIC LETTER LAM MEDIAL FORM * 0644 ARABIC LETTER LAM * FEE1 ARABIC LETTER MEEM ISOLATED FORM * 0645 ARABIC LETTER MEEM * FEE2 ARABIC LETTER MEEM FINAL FORM * 0645 ARABIC LETTER MEEM * FEE3 ARABIC LETTER MEEM INITIAL FORM * 0645 ARABIC LETTER MEEM * FEE4 ARABIC LETTER MEEM MEDIAL FORM * 0645 ARABIC LETTER MEEM * FEE5 ARABIC LETTER NOON ISOLATED FORM * 0646 ARABIC LETTER NOON * FEE6 ARABIC LETTER NOON FINAL FORM * 0646 ARABIC LETTER NOON * FEE7 ARABIC LETTER NOON INITIAL FORM * 0646 ARABIC LETTER NOON * FEE8 ARABIC LETTER NOON MEDIAL FORM * 0646 ARABIC LETTER NOON * FEE9 ARABIC LETTER HEH ISOLATED FORM * 0647 ARABIC LETTER HEH * FEEA ARABIC LETTER HEH FINAL FORM * 0647 ARABIC LETTER HEH * FEEB ARABIC LETTER HEH INITIAL FORM * 0647 ARABIC LETTER HEH * FEEC ARABIC LETTER HEH MEDIAL FORM * 0647 ARABIC LETTER HEH * FEED ARABIC LETTER WAW ISOLATED FORM * 0648 ARABIC LETTER WAW * FEEE ARABIC LETTER WAW FINAL FORM * 0648 ARABIC LETTER WAW * FEEF ARABIC LETTER ALEF MAKSURA ISOLATED FORM * 0649 ARABIC LETTER ALEF MAKSURA * FEF0 ARABIC LETTER ALEF MAKSURA FINAL FORM * 0649 ARABIC LETTER ALEF MAKSURA * FEF1 ARABIC LETTER YEH ISOLATED FORM * 064A ARABIC LETTER YEH * FEF2 ARABIC LETTER YEH FINAL FORM * 064A ARABIC LETTER YEH * FEF3 ARABIC LETTER YEH INITIAL FORM * 064A ARABIC LETTER YEH * FEF4 ARABIC LETTER YEH MEDIAL FORM * 064A ARABIC LETTER YEH * FEF5 ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * FEF6 ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * FEF7 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * FEF8 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * FEF9 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * FEFA ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * FEFB ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * FEFC ARABIC LIGATURE LAM WITH ALEF FINAL FORM * 0644 ARABIC LETTER LAM * 0627 ARABIC LETTER ALEF * FF01 FULLWIDTH EXCLAMATION MARK * 0021 EXCLAMATION MARK * FF02 FULLWIDTH QUOTATION MARK * 0022 QUOTATION MARK * FF03 FULLWIDTH NUMBER SIGN * 0023 NUMBER SIGN * FF04 FULLWIDTH DOLLAR SIGN * 0024 DOLLAR SIGN * FF05 FULLWIDTH PERCENT SIGN * 0025 PERCENT SIGN * FF06 FULLWIDTH AMPERSAND * 0026 AMPERSAND * FF07 FULLWIDTH APOSTROPHE * 0027 APOSTROPHE * FF08 FULLWIDTH LEFT PARENTHESIS * 0028 LEFT PARENTHESIS * FF09 FULLWIDTH RIGHT PARENTHESIS * 0029 RIGHT PARENTHESIS * FF0A FULLWIDTH ASTERISK * 002A ASTERISK * FF0B FULLWIDTH PLUS SIGN * 002B PLUS SIGN * FF0C FULLWIDTH COMMA * 002C COMMA * FF0D FULLWIDTH HYPHEN-MINUS * 002D HYPHEN-MINUS * FF0E FULLWIDTH FULL STOP * 002E FULL STOP * FF0F FULLWIDTH SOLIDUS * 002F SOLIDUS * FF10 FULLWIDTH DIGIT ZERO * 0030 DIGIT ZERO * FF11 FULLWIDTH DIGIT ONE * 0031 DIGIT ONE * FF12 FULLWIDTH DIGIT TWO * 0032 DIGIT TWO * FF13 FULLWIDTH DIGIT THREE * 0033 DIGIT THREE * FF14 FULLWIDTH DIGIT FOUR * 0034 DIGIT FOUR * FF15 FULLWIDTH DIGIT FIVE * 0035 DIGIT FIVE * FF16 FULLWIDTH DIGIT SIX * 0036 DIGIT SIX * FF17 FULLWIDTH DIGIT SEVEN * 0037 DIGIT SEVEN * FF18 FULLWIDTH DIGIT EIGHT * 0038 DIGIT EIGHT * FF19 FULLWIDTH DIGIT NINE * 0039 DIGIT NINE * FF1A FULLWIDTH COLON * 003A COLON * FF1B FULLWIDTH SEMICOLON * 003B SEMICOLON * FF1C FULLWIDTH LESS-THAN SIGN * 003C LESS-THAN SIGN * FF1D FULLWIDTH EQUALS SIGN * 003D EQUALS SIGN * FF1E FULLWIDTH GREATER-THAN SIGN * 003E GREATER-THAN SIGN * FF1F FULLWIDTH QUESTION MARK * 003F QUESTION MARK * FF20 FULLWIDTH COMMERCIAL AT * 0040 COMMERCIAL AT * FF21 FULLWIDTH LATIN CAPITAL LETTER A * 0041 LATIN CAPITAL LETTER A * FF22 FULLWIDTH LATIN CAPITAL LETTER B * 0042 LATIN CAPITAL LETTER B * FF23 FULLWIDTH LATIN CAPITAL LETTER C * 0043 LATIN CAPITAL LETTER C * FF24 FULLWIDTH LATIN CAPITAL LETTER D * 0044 LATIN CAPITAL LETTER D * FF25 FULLWIDTH LATIN CAPITAL LETTER E * 0045 LATIN CAPITAL LETTER E * FF26 FULLWIDTH LATIN CAPITAL LETTER F * 0046 LATIN CAPITAL LETTER F * FF27 FULLWIDTH LATIN CAPITAL LETTER G * 0047 LATIN CAPITAL LETTER G * FF28 FULLWIDTH LATIN CAPITAL LETTER H * 0048 LATIN CAPITAL LETTER H * FF29 FULLWIDTH LATIN CAPITAL LETTER I * 0049 LATIN CAPITAL LETTER I * FF2A FULLWIDTH LATIN CAPITAL LETTER J * 004A LATIN CAPITAL LETTER J * FF2B FULLWIDTH LATIN CAPITAL LETTER K * 004B LATIN CAPITAL LETTER K * FF2C FULLWIDTH LATIN CAPITAL LETTER L * 004C LATIN CAPITAL LETTER L * FF2D FULLWIDTH LATIN CAPITAL LETTER M * 004D LATIN CAPITAL LETTER M * FF2E FULLWIDTH LATIN CAPITAL LETTER N * 004E LATIN CAPITAL LETTER N * FF2F FULLWIDTH LATIN CAPITAL LETTER O * 004F LATIN CAPITAL LETTER O * FF30 FULLWIDTH LATIN CAPITAL LETTER P * 0050 LATIN CAPITAL LETTER P * FF31 FULLWIDTH LATIN CAPITAL LETTER Q * 0051 LATIN CAPITAL LETTER Q * FF32 FULLWIDTH LATIN CAPITAL LETTER R * 0052 LATIN CAPITAL LETTER R * FF33 FULLWIDTH LATIN CAPITAL LETTER S * 0053 LATIN CAPITAL LETTER S * FF34 FULLWIDTH LATIN CAPITAL LETTER T * 0054 LATIN CAPITAL LETTER T * FF35 FULLWIDTH LATIN CAPITAL LETTER U * 0055 LATIN CAPITAL LETTER U * FF36 FULLWIDTH LATIN CAPITAL LETTER V * 0056 LATIN CAPITAL LETTER V * FF37 FULLWIDTH LATIN CAPITAL LETTER W * 0057 LATIN CAPITAL LETTER W * FF38 FULLWIDTH LATIN CAPITAL LETTER X * 0058 LATIN CAPITAL LETTER X * FF39 FULLWIDTH LATIN CAPITAL LETTER Y * 0059 LATIN CAPITAL LETTER Y * FF3A FULLWIDTH LATIN CAPITAL LETTER Z * 005A LATIN CAPITAL LETTER Z * FF3B FULLWIDTH LEFT SQUARE BRACKET * 005B LEFT SQUARE BRACKET * FF3C FULLWIDTH REVERSE SOLIDUS * 005C REVERSE SOLIDUS * FF3D FULLWIDTH RIGHT SQUARE BRACKET * 005D RIGHT SQUARE BRACKET * FF3E FULLWIDTH CIRCUMFLEX ACCENT * 005E CIRCUMFLEX ACCENT * FF3F FULLWIDTH LOW LINE * 005F LOW LINE * FF40 FULLWIDTH GRAVE ACCENT * 0060 GRAVE ACCENT * FF41 FULLWIDTH LATIN SMALL LETTER A * 0061 LATIN SMALL LETTER A * FF42 FULLWIDTH LATIN SMALL LETTER B * 0062 LATIN SMALL LETTER B * FF43 FULLWIDTH LATIN SMALL LETTER C * 0063 LATIN SMALL LETTER C * FF44 FULLWIDTH LATIN SMALL LETTER D * 0064 LATIN SMALL LETTER D * FF45 FULLWIDTH LATIN SMALL LETTER E * 0065 LATIN SMALL LETTER E * FF46 FULLWIDTH LATIN SMALL LETTER F * 0066 LATIN SMALL LETTER F * FF47 FULLWIDTH LATIN SMALL LETTER G * 0067 LATIN SMALL LETTER G * FF48 FULLWIDTH LATIN SMALL LETTER H * 0068 LATIN SMALL LETTER H * FF49 FULLWIDTH LATIN SMALL LETTER I * 0069 LATIN SMALL LETTER I * FF4A FULLWIDTH LATIN SMALL LETTER J * 006A LATIN SMALL LETTER J * FF4B FULLWIDTH LATIN SMALL LETTER K * 006B LATIN SMALL LETTER K * FF4C FULLWIDTH LATIN SMALL LETTER L * 006C LATIN SMALL LETTER L * FF4D FULLWIDTH LATIN SMALL LETTER M * 006D LATIN SMALL LETTER M * FF4E FULLWIDTH LATIN SMALL LETTER N * 006E LATIN SMALL LETTER N * FF4F FULLWIDTH LATIN SMALL LETTER O * 006F LATIN SMALL LETTER O * FF50 FULLWIDTH LATIN SMALL LETTER P * 0070 LATIN SMALL LETTER P * FF51 FULLWIDTH LATIN SMALL LETTER Q * 0071 LATIN SMALL LETTER Q * FF52 FULLWIDTH LATIN SMALL LETTER R * 0072 LATIN SMALL LETTER R * FF53 FULLWIDTH LATIN SMALL LETTER S * 0073 LATIN SMALL LETTER S * FF54 FULLWIDTH LATIN SMALL LETTER T * 0074 LATIN SMALL LETTER T * FF55 FULLWIDTH LATIN SMALL LETTER U * 0075 LATIN SMALL LETTER U * FF56 FULLWIDTH LATIN SMALL LETTER V * 0076 LATIN SMALL LETTER V * FF57 FULLWIDTH LATIN SMALL LETTER W * 0077 LATIN SMALL LETTER W * FF58 FULLWIDTH LATIN SMALL LETTER X * 0078 LATIN SMALL LETTER X * FF59 FULLWIDTH LATIN SMALL LETTER Y * 0079 LATIN SMALL LETTER Y * FF5A FULLWIDTH LATIN SMALL LETTER Z * 007A LATIN SMALL LETTER Z * FF5B FULLWIDTH LEFT CURLY BRACKET * 007B LEFT CURLY BRACKET * FF5C FULLWIDTH VERTICAL LINE * 007C VERTICAL LINE * FF5D FULLWIDTH RIGHT CURLY BRACKET * 007D RIGHT CURLY BRACKET * FF5E FULLWIDTH TILDE * 007E TILDE * FF5F FULLWIDTH LEFT WHITE PARENTHESIS * 2985 LEFT WHITE PARENTHESIS * FF60 FULLWIDTH RIGHT WHITE PARENTHESIS * 2986 RIGHT WHITE PARENTHESIS * FF61 HALFWIDTH IDEOGRAPHIC FULL STOP * 3002 IDEOGRAPHIC FULL STOP * FF62 HALFWIDTH LEFT CORNER BRACKET * 300C LEFT CORNER BRACKET * FF63 HALFWIDTH RIGHT CORNER BRACKET * 300D RIGHT CORNER BRACKET * FF64 HALFWIDTH IDEOGRAPHIC COMMA * 3001 IDEOGRAPHIC COMMA * FFA0 HALFWIDTH HANGUL FILLER * 1160 HANGUL JUNGSEONG FILLER * FFA1 HALFWIDTH HANGUL LETTER KIYEOK * 1100 HANGUL CHOSEONG KIYEOK * FFA2 HALFWIDTH HANGUL LETTER SSANGKIYEOK * 1101 HANGUL CHOSEONG SSANGKIYEOK * FFA3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS * 11AA HANGUL JONGSEONG KIYEOK-SIOS * FFA4 HALFWIDTH HANGUL LETTER NIEUN * 1102 HANGUL CHOSEONG NIEUN * FFA5 HALFWIDTH HANGUL LETTER NIEUN-CIEUC * 11AC HANGUL JONGSEONG NIEUN-CIEUC * FFA6 HALFWIDTH HANGUL LETTER NIEUN-HIEUH * 11AD HANGUL JONGSEONG NIEUN-HIEUH * FFA7 HALFWIDTH HANGUL LETTER TIKEUT * 1103 HANGUL CHOSEONG TIKEUT * FFA8 HALFWIDTH HANGUL LETTER SSANGTIKEUT * 1104 HANGUL CHOSEONG SSANGTIKEUT * FFA9 HALFWIDTH HANGUL LETTER RIEUL * 1105 HANGUL CHOSEONG RIEUL * FFAA HALFWIDTH HANGUL LETTER RIEUL-KIYEOK * 11B0 HANGUL JONGSEONG RIEUL-KIYEOK * FFAB HALFWIDTH HANGUL LETTER RIEUL-MIEUM * 11B1 HANGUL JONGSEONG RIEUL-MIEUM * FFAC HALFWIDTH HANGUL LETTER RIEUL-PIEUP * 11B2 HANGUL JONGSEONG RIEUL-PIEUP * FFAD HALFWIDTH HANGUL LETTER RIEUL-SIOS * 11B3 HANGUL JONGSEONG RIEUL-SIOS * FFAE HALFWIDTH HANGUL LETTER RIEUL-THIEUTH * 11B4 HANGUL JONGSEONG RIEUL-THIEUTH * FFAF HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH * 11B5 HANGUL JONGSEONG RIEUL-PHIEUPH * FFB0 HALFWIDTH HANGUL LETTER RIEUL-HIEUH * 111A HANGUL CHOSEONG RIEUL-HIEUH * FFB1 HALFWIDTH HANGUL LETTER MIEUM * 1106 HANGUL CHOSEONG MIEUM * FFB2 HALFWIDTH HANGUL LETTER PIEUP * 1107 HANGUL CHOSEONG PIEUP * FFB3 HALFWIDTH HANGUL LETTER SSANGPIEUP * 1108 HANGUL CHOSEONG SSANGPIEUP * FFB4 HALFWIDTH HANGUL LETTER PIEUP-SIOS * 1121 HANGUL CHOSEONG PIEUP-SIOS * FFB5 HALFWIDTH HANGUL LETTER SIOS * 1109 HANGUL CHOSEONG SIOS * FFB6 HALFWIDTH HANGUL LETTER SSANGSIOS * 110A HANGUL CHOSEONG SSANGSIOS * FFB7 HALFWIDTH HANGUL LETTER IEUNG * 110B HANGUL CHOSEONG IEUNG * FFB8 HALFWIDTH HANGUL LETTER CIEUC * 110C HANGUL CHOSEONG CIEUC * FFB9 HALFWIDTH HANGUL LETTER SSANGCIEUC * 110D HANGUL CHOSEONG SSANGCIEUC * FFBA HALFWIDTH HANGUL LETTER CHIEUCH * 110E HANGUL CHOSEONG CHIEUCH * FFBB HALFWIDTH HANGUL LETTER KHIEUKH * 110F HANGUL CHOSEONG KHIEUKH * FFBC HALFWIDTH HANGUL LETTER THIEUTH * 1110 HANGUL CHOSEONG THIEUTH * FFBD HALFWIDTH HANGUL LETTER PHIEUPH * 1111 HANGUL CHOSEONG PHIEUPH * FFBE HALFWIDTH HANGUL LETTER HIEUH * 1112 HANGUL CHOSEONG HIEUH * FFC2 HALFWIDTH HANGUL LETTER A * 1161 HANGUL JUNGSEONG A * FFC3 HALFWIDTH HANGUL LETTER AE * 1162 HANGUL JUNGSEONG AE * FFC4 HALFWIDTH HANGUL LETTER YA * 1163 HANGUL JUNGSEONG YA * FFC5 HALFWIDTH HANGUL LETTER YAE * 1164 HANGUL JUNGSEONG YAE * FFC6 HALFWIDTH HANGUL LETTER EO * 1165 HANGUL JUNGSEONG EO * FFC7 HALFWIDTH HANGUL LETTER E * 1166 HANGUL JUNGSEONG E * FFCA HALFWIDTH HANGUL LETTER YEO * 1167 HANGUL JUNGSEONG YEO * FFCB HALFWIDTH HANGUL LETTER YE * 1168 HANGUL JUNGSEONG YE * FFCC HALFWIDTH HANGUL LETTER O * 1169 HANGUL JUNGSEONG O * FFCD HALFWIDTH HANGUL LETTER WA * 116A HANGUL JUNGSEONG WA * FFCE HALFWIDTH HANGUL LETTER WAE * 116B HANGUL JUNGSEONG WAE * FFCF HALFWIDTH HANGUL LETTER OE * 116C HANGUL JUNGSEONG OE * FFD2 HALFWIDTH HANGUL LETTER YO * 116D HANGUL JUNGSEONG YO * FFD3 HALFWIDTH HANGUL LETTER U * 116E HANGUL JUNGSEONG U * FFD4 HALFWIDTH HANGUL LETTER WEO * 116F HANGUL JUNGSEONG WEO * FFD5 HALFWIDTH HANGUL LETTER WE * 1170 HANGUL JUNGSEONG WE * FFD6 HALFWIDTH HANGUL LETTER WI * 1171 HANGUL JUNGSEONG WI * FFD7 HALFWIDTH HANGUL LETTER YU * 1172 HANGUL JUNGSEONG YU * FFDA HALFWIDTH HANGUL LETTER EU * 1173 HANGUL JUNGSEONG EU * FFDB HALFWIDTH HANGUL LETTER YI * 1174 HANGUL JUNGSEONG YI * FFDC HALFWIDTH HANGUL LETTER I * 1175 HANGUL JUNGSEONG I * FFE0 FULLWIDTH CENT SIGN * 00A2 CENT SIGN * FFE1 FULLWIDTH POUND SIGN * 00A3 POUND SIGN * FFE2 FULLWIDTH NOT SIGN * 00AC NOT SIGN * FFE3 FULLWIDTH MACRON * 0020 SPACE * FFE4 FULLWIDTH BROKEN BAR * 00A6 BROKEN BAR * FFE5 FULLWIDTH YEN SIGN * 00A5 YEN SIGN * FFE6 FULLWIDTH WON SIGN * 20A9 WON SIGN * FFE8 HALFWIDTH FORMS LIGHT VERTICAL * 2502 BOX DRAWINGS LIGHT VERTICAL * FFE9 HALFWIDTH LEFTWARDS ARROW * 2190 LEFTWARDS ARROW * FFEA HALFWIDTH UPWARDS ARROW * 2191 UPWARDS ARROW * FFEB HALFWIDTH RIGHTWARDS ARROW * 2192 RIGHTWARDS ARROW * FFEC HALFWIDTH DOWNWARDS ARROW * 2193 DOWNWARDS ARROW * FFED HALFWIDTH BLACK SQUARE * 25A0 BLACK SQUARE * FFEE HALFWIDTH WHITE CIRCLE * 25CB WHITE CIRCLE */ unsigned short unac_indexes[UNAC_INDEXES_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 60, 0, 0, 0, 61, 62, 0, 0, 0, 63, 63, 63, 63, 63, 63, 63, 63, 64, 63, 63, 63, 63, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 0, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 0, 88, 0, 0, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 0, 0, 0, 0, 0, 122, 0, 123, 63, 63, 63, 63, 124, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 126, 127, 0, 0, 0, 0, 128, 63, 63, 0, 0, 129, 130, 0, 0, 0, 0, 0, 0, 0, 0, 131, 0, 132, 133, 133, 134, 0, 0, 0, 0, 135, 0, 0, 0, 63, 63, 63, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 63, 137, 0, 0, 0, 0, 0, 0, 128, 138, 139, 0, 0, 136, 140, 141, 142, 0, 0, 0, 0, 0, 143, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 144, 63, 63, 140, 63, 63, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 0, 0, 0, 0, 0, 145, 146, 147, 148, 135, 149, 0, 0, 150, 0, 143, 0, 0, 0, 0, 0, 0, 146, 124, 151, 0, 0, 152, 0, 0, 144, 143, 0, 0, 0, 0, 0, 0, 146, 133, 148, 153, 154, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 0, 0, 0, 0, 0, 0, 146, 156, 134, 157, 0, 152, 0, 0, 0, 143, 0, 0, 0, 0, 0, 0, 146, 156, 134, 157, 0, 152, 0, 0, 0, 138, 0, 0, 0, 0, 0, 0, 158, 156, 134, 159, 0, 152, 0, 0, 0, 143, 0, 0, 0, 0, 0, 0, 0, 0, 160, 161, 63, 0, 0, 152, 0, 0, 0, 0, 0, 0, 0, 162, 126, 159, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 155, 0, 165, 0, 166, 0, 0, 0, 0, 0, 167, 0, 168, 0, 0, 169, 170, 171, 172, 173, 174, 0, 175, 123, 63, 156, 153, 63, 123, 63, 63, 63, 155, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 128, 63, 163, 0, 0, 136, 178, 179, 165, 180, 0, 144, 124, 0, 181, 182, 183, 184, 185, 186, 187, 0, 0, 0, 0, 0, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 0, 0, 0, 190, 0, 0, 0, 152, 0, 0, 0, 152, 0, 0, 0, 0, 0, 0, 0, 191, 63, 63, 63, 138, 139, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 0, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 138, 63, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 138, 0, 0, 0, 0, 0, 0, 153, 163, 63, 63, 63, 133, 0, 0, 0, 0, 0, 0, 63, 63, 63, 163, 0, 0, 0, 0, 0, 0, 193, 194, 195, 0, 0, 0, 191, 63, 155, 0, 0, 0, 0, 128, 138, 0, 126, 0, 0, 0, 123, 165, 0, 0, 0, 0, 0, 0, 136, 63, 138, 0, 0, 0, 0, 0, 191, 63, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 197, 198, 199, 200, 201, 202, 203, 0, 0, 204, 63, 63, 205, 206, 168, 0, 0, 0, 0, 0, 207, 208, 209, 210, 211, 212, 213, 214, 215, 0, 216, 0, 0, 0, 217, 218, 219, 220, 221, 63, 63, 63, 63, 63, 63, 63, 63, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 0, 289, 290, 291, 292, 293, 294, 295, 290, 0, 0, 296, 297, 298, 299, 300, 301, 0, 302, 0, 0, 0, 0, 63, 63, 63, 63, 137, 0, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 0, 321, 0, 322, 0, 0, 0, 323, 0, 0, 0, 0, 0, 0, 324, 325, 0, 0, 326, 327, 328, 0, 329, 330, 0, 0, 331, 332, 333, 334, 335, 336, 0, 0, 0, 337, 0, 0, 0, 0, 0, 0, 338, 339, 0, 0, 0, 0, 0, 0, 0, 340, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 359, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 361, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 362, 363, 364, 365, 366, 367, 0, 0, 0, 0, 0, 0, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 387, 0, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 63, 63, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 388, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 389, 0, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 144, 417, 418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 433, 434, 435, 436, 437, 438, 439, 440, 441, 0, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 496, 497, 498, 499, 500, 501, 204, 165, 502, 503, 504, 505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 0, 0, 530, 531, 532, 533, 0, 0, 128, 534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 191, 63, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 165, 0, 0, 159, 63, 138, 0, 0, 0, 0, 0, 138, 0, 0, 0, 0, 0, 128, 63, 137, 0, 0, 0, 139, 0, 0, 0, 0, 0, 0, 0, 0, 123, 163, 0, 533, 535, 0, 0, 0, 0, 0, 536, 0, 0, 0, 0, 0, 0, 179, 537, 135, 0, 0, 0, 0, 128, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 538, 0, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 0, 0, 0, 0, 128, 550, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 0, 0, 0, 0, 611, 0, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 0, 0, 0, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 0, 0, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 0, 0, 0, 0, 0, 694, 695, 63, 63, 696, 697, 63, 63, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 298, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 0, 0, 0, 0, 0, 0, 0, 736, 420, 421, 737, 738, 739, 740, 741, 742, 743, 0, 0 }; unsigned char unac_positions[UNAC_BLOCK_COUNT][3*UNAC_BLOCK_SIZE + 1] = { /* 0 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 1 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 2 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 3 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 4 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 5 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 6 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 7 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 8 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 18, 19, 22, 25, 26, 29, 32, 33, 34, 35, 36 }, /* 9 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 10 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 11 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 12 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26 }, /* 13 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 14 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 15 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 16 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 17 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 18 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 19 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 20 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 21 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 22 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 23 */ { 0, 1, 2, 4, 5, 6, 7, 9, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 }, /* 24 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26 }, /* 25 */ { 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 26 */ { 0, 1, 2, 3, 5, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }, /* 27 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 28 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 29 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 30 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 31 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 32 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 33 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 34 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 35 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 36 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 37 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 38 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 39 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 40 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 41 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 17, 19, 21, 22, 24, 26, 27, 29, 31, 32 }, /* 42 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 }, /* 43 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 44 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 45 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 46 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 47 */ { 0, 1, 2, 4, 6, 8, 9, 11, 13, 14, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, /* 48 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 49 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 50 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 51 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 52 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 53 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 54 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 55 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 56 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 57 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 58 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 59 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 60 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 61 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 62 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 63 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 64 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 65 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 66 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 67 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 68 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 69 */ { 0, 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 70 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 71 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 72 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 73 */ { 0, 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 74 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 75 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 76 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 77 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 78 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 79 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 80 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 81 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 82 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 83 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 84 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 85 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 86 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 87 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 88 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 89 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 90 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 91 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 92 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 93 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 94 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 95 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 96 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 97 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 98 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 99 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 100 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 101 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 102 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 103 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 104 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 105 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 106 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 107 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 108 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 109 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 110 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 111 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 112 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 113 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 114 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 115 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 116 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 117 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 118 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 119 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 120 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 121 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 122 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 25, 27 }, /* 123 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 124 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 125 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 126 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 127 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 128 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 129 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30 }, /* 130 */ { 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 131 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 132 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 133 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 134 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 135 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 136 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 137 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 138 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 139 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 140 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 141 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 142 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 143 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 144 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 145 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 146 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 147 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 148 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 149 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 150 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 151 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 152 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 153 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 154 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 155 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 156 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 157 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 158 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 159 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 160 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 161 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 162 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 163 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 164 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 165 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 166 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 17, 19, 21, 22, 23, 24, 25, 26, 27, 28 }, /* 167 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 168 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 169 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 170 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 171 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 172 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 173 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 174 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 175 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 176 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 177 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 178 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 179 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 180 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 181 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 182 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 183 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 184 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 185 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 186 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 187 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 188 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 189 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 190 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 191 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 192 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 193 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 194 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 195 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 196 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 197 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 198 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 199 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 200 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 201 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 202 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 203 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 204 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 205 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 206 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 207 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 208 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 209 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 210 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 211 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 212 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 213 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 214 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 215 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 216 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 217 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 218 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 219 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 220 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 221 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 222 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 223 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 224 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 225 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 226 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 227 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 228 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 229 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 230 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 231 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 232 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 233 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 234 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 235 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 236 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 237 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 238 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 239 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 240 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 26 }, /* 241 */ { 0, 1, 2, 4, 5, 6, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 28, 29, 30, 31 }, /* 242 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 243 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 244 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 245 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 246 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 247 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 248 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 249 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 250 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 251 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 252 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 253 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 254 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 255 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 256 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 257 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 258 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 259 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 260 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 261 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 262 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 263 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 264 */ { 0, 1, 2, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31 }, /* 265 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 266 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 267 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 268 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 269 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 270 */ { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, 26, 28, 29, 30, 32 }, /* 271 */ { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, 26, 28, 29, 30, 32 }, /* 272 */ { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, 26, 28, 29, 30, 32 }, /* 273 */ { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, 26, 28, 29, 30, 32 }, /* 274 */ { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, 26, 28, 29, 30, 32 }, /* 275 */ { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, 26, 28, 29, 30, 32 }, /* 276 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20, 21, 22, 23, 25, 26, 27, 30 }, /* 277 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }, /* 278 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20, 21, 22, 23, 25, 26, 27, 30 }, /* 279 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }, /* 280 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 31 }, /* 281 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 282 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 16, 17, 18, 20, 21, 22, 23, 24, 25, 27, 28, 29, 32 }, /* 283 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 284 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20, 21, 22, 23, 25, 26, 27, 30 }, /* 285 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }, /* 286 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 287 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 288 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 289 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 19, 20, 23, 26, 27, 28, 29, 30 }, /* 290 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 291 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 17, 20, 21, 22, 23, 24, 26, 28, 29, 32, 35, 36 }, /* 292 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 293 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26 }, /* 294 */ { 0, 2, 4, 5, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 }, /* 295 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 25, 29, 30 }, /* 296 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 297 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 298 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 299 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 300 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 301 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 302 */ { 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 303 */ { 0, 3, 6, 7, 10, 13, 14, 15, 16, 17, 19, 21, 22, 23, 24, 25, 28, 31, 32, 35, 38, 39, 40, 41, 42 }, /* 304 */ { 0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 305 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 23, 24, 25, 26 }, /* 306 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 307 */ { 0, 2, 4, 5, 8, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }, /* 308 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 309 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 310 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 }, /* 311 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 312 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 313 */ { 0, 3, 6, 7, 10, 13, 14, 18, 22, 23, 26, 29, 30, 33, 36, 37, 40, 43, 44, 47, 50, 51, 54, 57, 58 }, /* 314 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 51, 53, 54 }, /* 315 */ { 0, 1, 2, 3, 5, 7, 8, 11, 14, 15, 17, 19, 20, 21, 22, 23, 25, 27, 28, 31, 34, 35, 39, 43, 44 }, /* 316 */ { 0, 2, 4, 5, 6, 7, 8, 10, 12, 13, 16, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }, /* 317 */ { 0, 1, 2, 3, 5, 7, 8, 11, 14, 15, 17, 19, 20, 21, 22, 23, 25, 27, 28, 31, 34, 35, 39, 43, 44 }, /* 318 */ { 0, 2, 4, 5, 6, 7, 8, 10, 12, 13, 16, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }, /* 319 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 320 */ { 0, 1, 2, 3, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 }, /* 321 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 322 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 323 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 324 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 325 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 326 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 327 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 17, 20, 23, 24, 25, 26, 27, 29, 31, 32 }, /* 328 */ { 0, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 }, /* 329 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 330 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 331 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 332 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 333 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 334 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 335 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 336 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 337 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 338 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 339 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 340 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 341 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 342 */ { 0, 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 17, 18, 20, 22, 23, 25, 27, 28, 30, 32, 33, 35, 37, 38 }, /* 343 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 23, 26, 27, 30, 33, 34, 37, 40, 41, 44, 47, 48 }, /* 344 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 39, 43, 44, 48, 52, 53, 57, 61, 62 }, /* 345 */ { 0, 4, 8, 9, 13, 17, 18, 22, 26, 27, 31, 35, 36, 40, 44, 45, 49, 53, 54, 58, 62, 63, 67, 71, 72 }, /* 346 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 347 */ { 0, 2, 4, 5, 8, 11, 12, 15, 18, 19, 22, 25, 26, 29, 32, 33, 36, 39, 40, 43, 46, 47, 50, 53, 54 }, /* 348 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 349 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 350 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 351 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 43, 44, 45, 46, 47, 48 }, /* 352 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 353 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 354 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 355 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 356 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 357 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 358 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 359 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }, /* 360 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 18, 19, 21, 23, 24, 27, 30, 31, 32, 33, 34 }, /* 361 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 362 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 363 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 364 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 365 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 366 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 367 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 368 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 369 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 370 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 371 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 372 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 373 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 374 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 375 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 376 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 377 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 378 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 379 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 380 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 381 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 382 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 383 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 384 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 385 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 386 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 387 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 388 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 389 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 390 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 391 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 392 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 393 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 394 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 395 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 396 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 397 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 398 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 399 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 400 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 401 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 402 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 403 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 404 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 405 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 406 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 407 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 408 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 409 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 410 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 411 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 412 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 413 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 414 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 415 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 416 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 417 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 418 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 419 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 420 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 421 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 422 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 423 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 424 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 425 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 426 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 427 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 428 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 429 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 430 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 431 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 432 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 433 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 434 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 46, 50, 51, 55, 59, 60 }, /* 435 */ { 0, 4, 8, 9, 13, 17, 18, 22, 26, 27, 31, 35, 36, 40, 44, 45, 49, 53, 54, 58, 62, 63, 67, 71, 72 }, /* 436 */ { 0, 4, 8, 9, 13, 17, 18, 22, 26, 27, 31, 35, 36, 40, 44, 45, 52, 59, 60, 66, 72, 73, 74, 75, 76 }, /* 437 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 438 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 439 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 440 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 441 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 }, /* 442 */ { 0, 3, 6, 7, 9, 11, 12, 14, 16, 17, 19, 21, 22, 24, 26, 27, 29, 31, 32, 34, 36, 37, 39, 41, 42 }, /* 443 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 444 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 445 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 23, 25, 27, 28 }, /* 446 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 447 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 25, 30, 31, 35, 39, 40, 42, 44, 45, 46, 47, 48 }, /* 448 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 449 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 450 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 451 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 452 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 453 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 454 */ { 0, 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 17, 18, 20, 22, 23, 25, 27, 28, 30, 32, 33, 35, 37, 38 }, /* 455 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 456 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 457 */ { 0, 2, 4, 5, 8, 11, 12, 15, 18, 19, 22, 25, 26, 28, 30, 31, 34, 37, 38, 40, 42, 43, 46, 49, 50 }, /* 458 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 459 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 460 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 461 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 462 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 463 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26 }, /* 464 */ { 0, 4, 8, 9, 13, 17, 18, 22, 26, 27, 30, 33, 34, 38, 42, 43, 46, 49, 50, 53, 56, 57, 62, 67, 68 }, /* 465 */ { 0, 4, 8, 9, 12, 15, 16, 19, 22, 23, 26, 29, 30, 34, 38, 39, 43, 47, 48, 51, 54, 55, 58, 61, 62 }, /* 466 */ { 0, 2, 4, 5, 8, 11, 12, 16, 20, 21, 25, 29, 30, 32, 34, 35, 40, 45, 46, 52, 58, 59, 64, 69, 70 }, /* 467 */ { 0, 3, 6, 7, 12, 17, 18, 23, 28, 29, 33, 37, 38, 41, 44, 45, 48, 51, 52, 55, 58, 59, 63, 67, 68 }, /* 468 */ { 0, 5, 10, 11, 15, 19, 20, 23, 26, 27, 30, 33, 34, 37, 40, 41, 43, 45, 46, 48, 50, 51, 53, 55, 56 }, /* 469 */ { 0, 2, 4, 5, 8, 11, 12, 15, 18, 19, 24, 29, 30, 33, 36, 37, 41, 45, 46, 51, 56, 57, 60, 63, 64 }, /* 470 */ { 0, 2, 4, 5, 7, 9, 10, 15, 20, 21, 25, 29, 30, 35, 40, 41, 44, 47, 48, 53, 58, 59, 61, 63, 64 }, /* 471 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 39, 43, 44, 47, 50, 51, 53, 55, 56 }, /* 472 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 25, 29, 30, 33, 36, 37, 40, 43, 44, 47, 50, 51, 56, 61, 62 }, /* 473 */ { 0, 4, 8, 9, 11, 13, 14, 19, 24, 25, 27, 29, 30, 34, 38, 39, 43, 47, 48, 51, 54, 55, 58, 61, 62 }, /* 474 */ { 0, 3, 6, 7, 11, 15, 16, 18, 20, 21, 24, 27, 28, 32, 36, 37, 39, 41, 42, 47, 52, 53, 56, 59, 60 }, /* 475 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 476 */ { 0, 2, 4, 5, 7, 9, 10, 13, 16, 17, 20, 23, 24, 27, 30, 31, 34, 37, 38, 41, 44, 45, 48, 51, 52 }, /* 477 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 478 */ { 0, 3, 6, 7, 10, 13, 14, 16, 18, 19, 21, 23, 24, 27, 30, 31, 33, 35, 36, 38, 40, 41, 43, 45, 46 }, /* 479 */ { 0, 3, 6, 7, 10, 13, 14, 16, 18, 19, 21, 23, 24, 26, 28, 29, 31, 33, 34, 36, 38, 39, 43, 47, 48 }, /* 480 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 481 */ { 0, 3, 6, 7, 11, 15, 16, 18, 20, 21, 23, 25, 26, 28, 30, 31, 33, 35, 36, 38, 40, 41, 43, 45, 46 }, /* 482 */ { 0, 2, 4, 5, 8, 11, 12, 15, 18, 19, 22, 25, 26, 29, 32, 33, 35, 37, 38, 40, 42, 43, 45, 47, 48 }, /* 483 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 38, 41, 42 }, /* 484 */ { 0, 3, 6, 7, 9, 11, 12, 15, 18, 19, 22, 25, 26, 29, 32, 33, 35, 37, 38, 41, 44, 45, 48, 51, 52 }, /* 485 */ { 0, 4, 8, 9, 11, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 47, 52, 53, 59, 65, 66 }, /* 486 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 487 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 488 */ { 0, 2, 4, 5, 7, 9, 10, 14, 18, 19, 21, 23, 24, 26, 28, 29, 31, 33, 34, 38, 42, 43, 46, 49, 50 }, /* 489 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 490 */ { 0, 2, 4, 5, 7, 9, 10, 13, 16, 17, 19, 21, 22, 24, 26, 27, 30, 33, 34, 37, 40, 41, 43, 45, 46 }, /* 491 */ { 0, 4, 8, 9, 12, 15, 16, 18, 20, 21, 23, 25, 26, 28, 30, 31, 33, 35, 36, 39, 42, 43, 46, 49, 50 }, /* 492 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 493 */ { 0, 2, 4, 5, 8, 11, 12, 15, 18, 19, 22, 25, 26, 29, 32, 33, 36, 39, 40, 43, 46, 47, 50, 53, 54 }, /* 494 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 495 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 496 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 497 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 498 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 499 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 500 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 501 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 502 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 503 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 504 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 505 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 506 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 507 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 508 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 509 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 510 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 511 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 512 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 513 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 514 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 515 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 516 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 517 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 518 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 519 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 520 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 521 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 522 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 523 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 524 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 525 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 526 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 527 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 528 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 529 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 530 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 531 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 532 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 533 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 534 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 535 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 536 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 537 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 538 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 539 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 540 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 541 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 542 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 543 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 544 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 545 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 546 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 547 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 548 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 549 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 550 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 551 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 552 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 553 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 554 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 555 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 556 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 557 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 558 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 559 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 560 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 561 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 562 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 563 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 564 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 565 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 566 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 567 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 568 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 569 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 570 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 571 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 572 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 573 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 574 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 575 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 576 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 577 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 578 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 579 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 580 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 581 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 582 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 583 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 584 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 585 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 586 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 587 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 588 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 589 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 590 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 591 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 592 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 593 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 594 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 595 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 596 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 597 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 598 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 599 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 600 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 601 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 602 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 603 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 604 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 605 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 606 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 607 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 608 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 609 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 610 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 611 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 21, 24, 27, 30, 33, 36, 38, 40, 42, 44, 46, 48, 49, 50, 51 }, /* 612 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39 }, /* 613 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 614 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 615 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 616 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 617 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 618 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 619 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26 }, /* 620 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 621 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 622 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 623 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 624 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 625 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 626 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 627 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 628 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 629 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 630 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 631 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 632 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 633 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 634 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 635 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 636 */ { 0, 1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 15, 16, 18, 20, 21, 23, 25, 26, 28, 30, 31, 33, 35, 36 }, /* 637 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 638 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }, /* 639 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 640 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 641 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 642 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 643 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 644 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 645 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 646 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 647 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 648 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 649 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 650 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }, /* 651 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 17, 19, 21, 22, 24, 26, 27, 29, 31, 32 }, /* 652 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 653 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 654 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 655 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 656 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 657 */ { 0, 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 17, 18, 20, 22, 23, 25, 27, 28, 30, 32, 33, 35, 37, 38 }, /* 658 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 659 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 660 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 661 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 662 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 663 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 664 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 665 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 666 */ { 0, 2, 4, 5, 6, 7, 8, 10, 12, 13, 15, 17, 18, 20, 22, 23, 25, 27, 28, 30, 32, 33, 35, 37, 38 }, /* 667 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 668 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 669 */ { 0, 2, 4, 5, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 24, 26, 28, 29, 31, 33, 34 }, /* 670 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 671 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 672 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 673 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 674 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 675 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 676 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 677 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40 }, /* 678 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }, /* 679 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 680 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 681 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 682 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 683 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 684 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 685 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 686 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 687 */ { 0, 1, 2, 3, 4, 5, 6, 9, 12, 13, 16, 19, 20, 23, 26, 27, 30, 33, 34, 37, 40, 41, 44, 47, 48 }, /* 688 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 689 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 690 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 691 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 692 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 693 */ { 0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 27, 28, 31, 34, 35, 38, 41, 42, 45, 48, 49, 52, 55, 56 }, /* 694 */ { 0, 3, 6, 7, 10, 13, 14, 18, 22, 23, 27, 31, 32, 36, 40, 41, 45, 49, 50, 54, 58, 59, 63, 67, 68 }, /* 695 */ { 0, 4, 8, 9, 12, 15, 16, 34, 52, 53, 61, 69, 70, 74, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88 }, /* 696 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 697 */ { 0, 1, 2, 3, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 }, /* 698 */ { 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, /* 699 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 700 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 701 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 702 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 703 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 704 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 705 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 706 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 707 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 708 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 709 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 710 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 711 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 712 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 713 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 714 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 715 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 716 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 717 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 718 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 719 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 720 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 721 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 722 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30 }, /* 723 */ { 0, 2, 4, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 }, /* 724 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 725 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 726 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 727 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 728 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 729 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 730 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 731 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 732 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 733 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 734 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 735 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 736 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 737 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 738 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 739 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 740 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 741 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 742 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }, /* 743 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 } }; unsigned short unac_data0[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data1[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0x0062, 0x0062, 0xFFFF, 0x0063, 0x0063, 0xFFFF, 0x0064, 0x0064, 0xFFFF, 0x0065, 0x0065, 0xFFFF, 0x0066, 0x0066, 0xFFFF, 0x0067, 0x0067 }; unsigned short unac_data2[] = { 0xFFFF, 0x0068, 0x0068, 0xFFFF, 0x0069, 0x0069, 0xFFFF, 0x006A, 0x006A, 0xFFFF, 0x006B, 0x006B, 0xFFFF, 0x006C, 0x006C, 0xFFFF, 0x006D, 0x006D, 0xFFFF, 0x006E, 0x006E, 0xFFFF, 0x006F, 0x006F }; unsigned short unac_data3[] = { 0xFFFF, 0x0070, 0x0070, 0xFFFF, 0x0071, 0x0071, 0xFFFF, 0x0072, 0x0072, 0xFFFF, 0x0073, 0x0073, 0xFFFF, 0x0074, 0x0074, 0xFFFF, 0x0075, 0x0075, 0xFFFF, 0x0076, 0x0076, 0xFFFF, 0x0077, 0x0077 }; unsigned short unac_data4[] = { 0xFFFF, 0x0078, 0x0078, 0xFFFF, 0x0079, 0x0079, 0xFFFF, 0x007A, 0x007A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data5[] = { 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data6[] = { 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF }; unsigned short unac_data7[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0032, 0x0032, 0xFFFF, 0x0033, 0x0033, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x03BC, 0x03BC, 0x03BC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data8[] = { 0x0020, 0x0020, 0xFFFF, 0x0031, 0x0031, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0031, 0x2044, 0x0034, 0x0031, 0x2044, 0x0034, 0xFFFF, 0x0031, 0x2044, 0x0032, 0x0031, 0x2044, 0x0032, 0xFFFF, 0x0033, 0x2044, 0x0034, 0x0033, 0x2044, 0x0034, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data9[] = { 0x0041, 0x0061, 0x00E0, 0x0041, 0x0061, 0x00E1, 0x0041, 0x0061, 0x00E2, 0x0041, 0x0061, 0x00E3, 0x0041, 0x0061, 0x00E4, 0x0041, 0x0061, 0x00E5, 0xFFFF, 0x00E6, 0x00E6, 0x0043, 0x0063, 0x00E7 }; unsigned short unac_data10[] = { 0x0045, 0x0065, 0x00E8, 0x0045, 0x0065, 0x00E9, 0x0045, 0x0065, 0x00EA, 0x0045, 0x0065, 0x00EB, 0x0049, 0x0069, 0x00EC, 0x0049, 0x0069, 0x00ED, 0x0049, 0x0069, 0x00EE, 0x0049, 0x0069, 0x00EF }; unsigned short unac_data11[] = { 0xFFFF, 0x00F0, 0x00F0, 0x004E, 0x006E, 0x00F1, 0x004F, 0x006F, 0x00F2, 0x004F, 0x006F, 0x00F3, 0x004F, 0x006F, 0x00F4, 0x004F, 0x006F, 0x00F5, 0x004F, 0x006F, 0x00F6, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data12[] = { 0xFFFF, 0x00F8, 0x00F8, 0x0055, 0x0075, 0x00F9, 0x0055, 0x0075, 0x00FA, 0x0055, 0x0075, 0x00FB, 0x0055, 0x0075, 0x00FC, 0x0059, 0x0079, 0x00FD, 0xFFFF, 0x00FE, 0x00FE, 0xFFFF, 0x0073, 0x0073, 0x0073, 0x0073 }; unsigned short unac_data13[] = { 0x0061, 0x0061, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0063, 0x0063, 0xFFFF }; unsigned short unac_data14[] = { 0x0065, 0x0065, 0xFFFF, 0x0065, 0x0065, 0xFFFF, 0x0065, 0x0065, 0xFFFF, 0x0065, 0x0065, 0xFFFF, 0x0069, 0x0069, 0xFFFF, 0x0069, 0x0069, 0xFFFF, 0x0069, 0x0069, 0xFFFF, 0x0069, 0x0069, 0xFFFF }; unsigned short unac_data15[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x006E, 0x006E, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data16[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0075, 0x0075, 0xFFFF, 0x0075, 0x0075, 0xFFFF, 0x0075, 0x0075, 0xFFFF, 0x0075, 0x0075, 0xFFFF, 0x0079, 0x0079, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0079, 0x0079, 0xFFFF }; unsigned short unac_data17[] = { 0x0041, 0x0061, 0x0101, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x0103, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x0105, 0x0061, 0x0061, 0xFFFF, 0x0043, 0x0063, 0x0107, 0x0063, 0x0063, 0xFFFF }; unsigned short unac_data18[] = { 0x0043, 0x0063, 0x0109, 0x0063, 0x0063, 0xFFFF, 0x0043, 0x0063, 0x010B, 0x0063, 0x0063, 0xFFFF, 0x0043, 0x0063, 0x010D, 0x0063, 0x0063, 0xFFFF, 0x0044, 0x0064, 0x010F, 0x0064, 0x0064, 0xFFFF }; unsigned short unac_data19[] = { 0xFFFF, 0x0111, 0x0111, 0xFFFF, 0xFFFF, 0xFFFF, 0x0045, 0x0065, 0x0113, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x0115, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x0117, 0x0065, 0x0065, 0xFFFF }; unsigned short unac_data20[] = { 0x0045, 0x0065, 0x0119, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x011B, 0x0065, 0x0065, 0xFFFF, 0x0047, 0x0067, 0x011D, 0x0067, 0x0067, 0xFFFF, 0x0047, 0x0067, 0x011F, 0x0067, 0x0067, 0xFFFF }; unsigned short unac_data21[] = { 0x0047, 0x0067, 0x0121, 0x0067, 0x0067, 0xFFFF, 0x0047, 0x0067, 0x0123, 0x0067, 0x0067, 0xFFFF, 0x0048, 0x0068, 0x0125, 0x0068, 0x0068, 0xFFFF, 0xFFFF, 0x0127, 0x0127, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data22[] = { 0x0049, 0x0069, 0x0129, 0x0069, 0x0069, 0xFFFF, 0x0049, 0x0069, 0x012B, 0x0069, 0x0069, 0xFFFF, 0x0049, 0x0069, 0x012D, 0x0069, 0x0069, 0xFFFF, 0x0049, 0x0069, 0x012F, 0x0069, 0x0069, 0xFFFF }; unsigned short unac_data23[] = { 0x0049, 0x0069, 0x0069, 0x0307, 0xFFFF, 0xFFFF, 0xFFFF, 0x0049, 0x004A, 0x0069, 0x006A, 0x0133, 0x0069, 0x006A, 0x0069, 0x006A, 0xFFFF, 0x004A, 0x006A, 0x0135, 0x006A, 0x006A, 0xFFFF, 0x004B, 0x006B, 0x0137, 0x006B, 0x006B, 0xFFFF }; unsigned short unac_data24[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x004C, 0x006C, 0x013A, 0x006C, 0x006C, 0xFFFF, 0x004C, 0x006C, 0x013C, 0x006C, 0x006C, 0xFFFF, 0x004C, 0x006C, 0x013E, 0x006C, 0x006C, 0xFFFF, 0x004C, 0x00B7, 0x006C, 0x00B7, 0x0140 }; unsigned short unac_data25[] = { 0x006C, 0x00B7, 0x006C, 0x00B7, 0xFFFF, 0xFFFF, 0x0142, 0x0142, 0xFFFF, 0xFFFF, 0xFFFF, 0x004E, 0x006E, 0x0144, 0x006E, 0x006E, 0xFFFF, 0x004E, 0x006E, 0x0146, 0x006E, 0x006E, 0xFFFF, 0x004E, 0x006E, 0x0148 }; unsigned short unac_data26[] = { 0x006E, 0x006E, 0xFFFF, 0x02BC, 0x006E, 0x02BC, 0x006E, 0x02BC, 0x006E, 0xFFFF, 0x014B, 0x014B, 0xFFFF, 0xFFFF, 0xFFFF, 0x004F, 0x006F, 0x014D, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x014F, 0x006F, 0x006F, 0xFFFF }; unsigned short unac_data27[] = { 0x004F, 0x006F, 0x0151, 0x006F, 0x006F, 0xFFFF, 0xFFFF, 0x0153, 0x0153, 0xFFFF, 0xFFFF, 0xFFFF, 0x0052, 0x0072, 0x0155, 0x0072, 0x0072, 0xFFFF, 0x0052, 0x0072, 0x0157, 0x0072, 0x0072, 0xFFFF }; unsigned short unac_data28[] = { 0x0052, 0x0072, 0x0159, 0x0072, 0x0072, 0xFFFF, 0x0053, 0x0073, 0x015B, 0x0073, 0x0073, 0xFFFF, 0x0053, 0x0073, 0x015D, 0x0073, 0x0073, 0xFFFF, 0x0053, 0x0073, 0x015F, 0x0073, 0x0073, 0xFFFF }; unsigned short unac_data29[] = { 0x0053, 0x0073, 0x0161, 0x0073, 0x0073, 0xFFFF, 0x0054, 0x0074, 0x0163, 0x0074, 0x0074, 0xFFFF, 0x0054, 0x0074, 0x0165, 0x0074, 0x0074, 0xFFFF, 0xFFFF, 0x0167, 0x0167, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data30[] = { 0x0055, 0x0075, 0x0169, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x016B, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x016D, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x016F, 0x0075, 0x0075, 0xFFFF }; unsigned short unac_data31[] = { 0x0055, 0x0075, 0x0171, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x0173, 0x0075, 0x0075, 0xFFFF, 0x0057, 0x0077, 0x0175, 0x0077, 0x0077, 0xFFFF, 0x0059, 0x0079, 0x0177, 0x0079, 0x0079, 0xFFFF }; unsigned short unac_data32[] = { 0x0059, 0x0079, 0x00FF, 0x005A, 0x007A, 0x017A, 0x007A, 0x007A, 0xFFFF, 0x005A, 0x007A, 0x017C, 0x007A, 0x007A, 0xFFFF, 0x005A, 0x007A, 0x017E, 0x007A, 0x007A, 0xFFFF, 0x0073, 0x0073, 0x0073 }; unsigned short unac_data33[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0253, 0x0253, 0xFFFF, 0x0183, 0x0183, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0185, 0x0185, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0254, 0x0254, 0xFFFF, 0x0188, 0x0188 }; unsigned short unac_data34[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0256, 0x0256, 0xFFFF, 0x0257, 0x0257, 0xFFFF, 0x018C, 0x018C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x01DD, 0x01DD, 0xFFFF, 0x0259, 0x0259 }; unsigned short unac_data35[] = { 0xFFFF, 0x025B, 0x025B, 0xFFFF, 0x0192, 0x0192, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0260, 0x0260, 0xFFFF, 0x0263, 0x0263, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0269, 0x0269, 0xFFFF, 0x0268, 0x0268 }; unsigned short unac_data36[] = { 0xFFFF, 0x0199, 0x0199, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x026F, 0x026F, 0xFFFF, 0x0272, 0x0272, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0275, 0x0275 }; unsigned short unac_data37[] = { 0x004F, 0x006F, 0x01A1, 0x006F, 0x006F, 0xFFFF, 0xFFFF, 0x01A3, 0x01A3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x01A5, 0x01A5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0280, 0x0280, 0xFFFF, 0x01A8, 0x01A8 }; unsigned short unac_data38[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0283, 0x0283, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x01AD, 0x01AD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0288, 0x0288, 0x0055, 0x0075, 0x01B0 }; unsigned short unac_data39[] = { 0x0075, 0x0075, 0xFFFF, 0xFFFF, 0x028A, 0x028A, 0xFFFF, 0x028B, 0x028B, 0xFFFF, 0x01B4, 0x01B4, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x01B6, 0x01B6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0292, 0x0292 }; unsigned short unac_data40[] = { 0xFFFF, 0x01B9, 0x01B9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x01BD, 0x01BD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data41[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0044, 0x005A, 0x0064, 0x007A, 0x01C6, 0x0044, 0x007A, 0x0064, 0x007A, 0x01C6, 0x0064, 0x007A, 0x0064, 0x007A, 0xFFFF, 0x004C, 0x004A, 0x006C, 0x006A, 0x01C9 }; unsigned short unac_data42[] = { 0x004C, 0x006A, 0x006C, 0x006A, 0x01C9, 0x006C, 0x006A, 0x006C, 0x006A, 0xFFFF, 0x004E, 0x004A, 0x006E, 0x006A, 0x01CC, 0x004E, 0x006A, 0x006E, 0x006A, 0x01CC, 0x006E, 0x006A, 0x006E, 0x006A, 0xFFFF, 0x0041, 0x0061, 0x01CE, 0x0061, 0x0061, 0xFFFF, 0x0049, 0x0069, 0x01D0 }; unsigned short unac_data43[] = { 0x0069, 0x0069, 0xFFFF, 0x004F, 0x006F, 0x01D2, 0x006F, 0x006F, 0xFFFF, 0x0055, 0x0075, 0x01D4, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x01D6, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x01D8 }; unsigned short unac_data44[] = { 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x01DA, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x01DC, 0x0075, 0x0075, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0041, 0x0061, 0x01DF, 0x0061, 0x0061, 0xFFFF }; unsigned short unac_data45[] = { 0x0041, 0x0061, 0x01E1, 0x0061, 0x0061, 0xFFFF, 0x00C6, 0x00E6, 0x01E3, 0x00E6, 0x00E6, 0xFFFF, 0xFFFF, 0x01E5, 0x01E5, 0xFFFF, 0xFFFF, 0xFFFF, 0x0047, 0x0067, 0x01E7, 0x0067, 0x0067, 0xFFFF }; unsigned short unac_data46[] = { 0x004B, 0x006B, 0x01E9, 0x006B, 0x006B, 0xFFFF, 0x004F, 0x006F, 0x01EB, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x01ED, 0x006F, 0x006F, 0xFFFF, 0x01B7, 0x0292, 0x01EF, 0x0292, 0x0292, 0xFFFF }; unsigned short unac_data47[] = { 0x006A, 0x006A, 0x006A, 0x030C, 0x0044, 0x005A, 0x0064, 0x007A, 0x01F3, 0x0044, 0x007A, 0x0064, 0x007A, 0x01F3, 0x0064, 0x007A, 0x0064, 0x007A, 0xFFFF, 0x0047, 0x0067, 0x01F5, 0x0067, 0x0067, 0xFFFF, 0xFFFF, 0x0195, 0x0195, 0xFFFF, 0x01BF, 0x01BF }; unsigned short unac_data48[] = { 0x004E, 0x006E, 0x01F9, 0x006E, 0x006E, 0xFFFF, 0x0041, 0x0061, 0x01FB, 0x0061, 0x0061, 0xFFFF, 0x00C6, 0x00E6, 0x01FD, 0x00E6, 0x00E6, 0xFFFF, 0x00D8, 0x00F8, 0x01FF, 0x00F8, 0x00F8, 0xFFFF }; unsigned short unac_data49[] = { 0x0041, 0x0061, 0x0201, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x0203, 0x0061, 0x0061, 0xFFFF, 0x0045, 0x0065, 0x0205, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x0207, 0x0065, 0x0065, 0xFFFF }; unsigned short unac_data50[] = { 0x0049, 0x0069, 0x0209, 0x0069, 0x0069, 0xFFFF, 0x0049, 0x0069, 0x020B, 0x0069, 0x0069, 0xFFFF, 0x004F, 0x006F, 0x020D, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x020F, 0x006F, 0x006F, 0xFFFF }; unsigned short unac_data51[] = { 0x0052, 0x0072, 0x0211, 0x0072, 0x0072, 0xFFFF, 0x0052, 0x0072, 0x0213, 0x0072, 0x0072, 0xFFFF, 0x0055, 0x0075, 0x0215, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x0217, 0x0075, 0x0075, 0xFFFF }; unsigned short unac_data52[] = { 0x0053, 0x0073, 0x0219, 0x0073, 0x0073, 0xFFFF, 0x0054, 0x0074, 0x021B, 0x0074, 0x0074, 0xFFFF, 0xFFFF, 0x021D, 0x021D, 0xFFFF, 0xFFFF, 0xFFFF, 0x0048, 0x0068, 0x021F, 0x0068, 0x0068, 0xFFFF }; unsigned short unac_data53[] = { 0xFFFF, 0x019E, 0x019E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0223, 0x0223, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0225, 0x0225, 0xFFFF, 0xFFFF, 0xFFFF, 0x0041, 0x0061, 0x0227, 0x0061, 0x0061, 0xFFFF }; unsigned short unac_data54[] = { 0x0045, 0x0065, 0x0229, 0x0065, 0x0065, 0xFFFF, 0x004F, 0x006F, 0x022B, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x022D, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x022F, 0x006F, 0x006F, 0xFFFF }; unsigned short unac_data55[] = { 0x004F, 0x006F, 0x0231, 0x006F, 0x006F, 0xFFFF, 0x0059, 0x0079, 0x0233, 0x0079, 0x0079, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data56[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C65, 0x2C65, 0xFFFF, 0x023C, 0x023C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x019A, 0x019A, 0xFFFF, 0x2C66, 0x2C66, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data57[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0242, 0x0242, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0180, 0x0180, 0xFFFF, 0x0289, 0x0289, 0xFFFF, 0x028C, 0x028C, 0xFFFF, 0x0247, 0x0247, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data58[] = { 0xFFFF, 0x0249, 0x0249, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x024B, 0x024B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x024D, 0x024D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x024F, 0x024F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data59[] = { 0x0068, 0x0068, 0xFFFF, 0x0266, 0x0266, 0xFFFF, 0x006A, 0x006A, 0xFFFF, 0x0072, 0x0072, 0xFFFF, 0x0279, 0x0279, 0xFFFF, 0x027B, 0x027B, 0xFFFF, 0x0281, 0x0281, 0xFFFF, 0x0077, 0x0077, 0xFFFF }; unsigned short unac_data60[] = { 0x0079, 0x0079, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data61[] = { 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data62[] = { 0x0263, 0x0263, 0xFFFF, 0x006C, 0x006C, 0xFFFF, 0x0073, 0x0073, 0xFFFF, 0x0078, 0x0078, 0xFFFF, 0x0295, 0x0295, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data63[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data64[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0x03B9, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data65[] = { 0xFFFF, 0x0371, 0x0371, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0373, 0x0373, 0xFFFF, 0xFFFF, 0xFFFF, 0x02B9, 0x02B9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0377, 0x0377, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data66[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x003B, 0x003B, 0xFFFF, 0xFFFF, 0x03F3, 0x03F3 }; unsigned short unac_data67[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0391, 0x03B1, 0x03AC, 0x00B7, 0x00B7, 0xFFFF }; unsigned short unac_data68[] = { 0x0395, 0x03B5, 0x03AD, 0x0397, 0x03B7, 0x03AE, 0x0399, 0x03B9, 0x03AF, 0xFFFF, 0xFFFF, 0xFFFF, 0x039F, 0x03BF, 0x03CC, 0xFFFF, 0xFFFF, 0xFFFF, 0x03A5, 0x03C5, 0x03CD, 0x03A9, 0x03C9, 0x03CE }; unsigned short unac_data69[] = { 0x03B9, 0x03B9, 0x03B9, 0x0308, 0x0301, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B2, 0x03B2, 0xFFFF, 0x03B3, 0x03B3, 0xFFFF, 0x03B4, 0x03B4, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0x03B6, 0x03B6, 0xFFFF, 0x03B7, 0x03B7 }; unsigned short unac_data70[] = { 0xFFFF, 0x03B8, 0x03B8, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03BA, 0x03BA, 0xFFFF, 0x03BB, 0x03BB, 0xFFFF, 0x03BC, 0x03BC, 0xFFFF, 0x03BD, 0x03BD, 0xFFFF, 0x03BE, 0x03BE, 0xFFFF, 0x03BF, 0x03BF }; unsigned short unac_data71[] = { 0xFFFF, 0x03C0, 0x03C0, 0xFFFF, 0x03C1, 0x03C1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03C3, 0x03C3, 0xFFFF, 0x03C4, 0x03C4, 0xFFFF, 0x03C5, 0x03C5, 0xFFFF, 0x03C6, 0x03C6, 0xFFFF, 0x03C7, 0x03C7 }; unsigned short unac_data72[] = { 0xFFFF, 0x03C8, 0x03C8, 0xFFFF, 0x03C9, 0x03C9, 0x0399, 0x03B9, 0x03CA, 0x03A5, 0x03C5, 0x03CB, 0x03B1, 0x03B1, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF }; unsigned short unac_data73[] = { 0x03C5, 0x03C5, 0x03C5, 0x0308, 0x0301, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data74[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03C3, 0x03C3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data75[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03C5, 0x03C5, 0xFFFF, 0x03BF, 0x03BF, 0xFFFF, 0x03C5, 0x03C5, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0xFFFF, 0x03D7, 0x03D7 }; unsigned short unac_data76[] = { 0x03B2, 0x03B2, 0x03B2, 0x03B8, 0x03B8, 0x03B8, 0x03A5, 0x03C5, 0xFFFF, 0x03A5, 0x03C5, 0xFFFF, 0x03A5, 0x03C5, 0xFFFF, 0x03C6, 0x03C6, 0x03C6, 0x03C0, 0x03C0, 0x03C0, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data77[] = { 0xFFFF, 0x03D9, 0x03D9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03DB, 0x03DB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03DD, 0x03DD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03DF, 0x03DF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data78[] = { 0xFFFF, 0x03E1, 0x03E1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03E3, 0x03E3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03E5, 0x03E5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03E7, 0x03E7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data79[] = { 0xFFFF, 0x03E9, 0x03E9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03EB, 0x03EB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03ED, 0x03ED, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03EF, 0x03EF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data80[] = { 0x03BA, 0x03BA, 0x03BA, 0x03C1, 0x03C1, 0x03C1, 0x03C2, 0x03C3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0398, 0x03B8, 0x03B8, 0x03B5, 0x03B5, 0x03B5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03F8, 0x03F8 }; unsigned short unac_data81[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x03A3, 0x03C3, 0x03F2, 0xFFFF, 0x03FB, 0x03FB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x037B, 0x037B, 0xFFFF, 0x037C, 0x037C, 0xFFFF, 0x037D, 0x037D }; unsigned short unac_data82[] = { 0x0415, 0x0435, 0x0450, 0x0415, 0x0435, 0x0451, 0xFFFF, 0x0452, 0x0452, 0x0413, 0x0433, 0x0453, 0xFFFF, 0x0454, 0x0454, 0xFFFF, 0x0455, 0x0455, 0xFFFF, 0x0456, 0x0456, 0x0406, 0x0456, 0x0457 }; unsigned short unac_data83[] = { 0xFFFF, 0x0458, 0x0458, 0xFFFF, 0x0459, 0x0459, 0xFFFF, 0x045A, 0x045A, 0xFFFF, 0x045B, 0x045B, 0x041A, 0x043A, 0x045C, 0x0418, 0x0438, 0x045D, 0x0423, 0x0443, 0x045E, 0xFFFF, 0x045F, 0x045F }; unsigned short unac_data84[] = { 0xFFFF, 0x0430, 0x0430, 0xFFFF, 0x0431, 0x0431, 0xFFFF, 0x0432, 0x0432, 0xFFFF, 0x0433, 0x0433, 0xFFFF, 0x0434, 0x0434, 0xFFFF, 0x0435, 0x0435, 0xFFFF, 0x0436, 0x0436, 0xFFFF, 0x0437, 0x0437 }; unsigned short unac_data85[] = { 0xFFFF, 0x0438, 0x0438, 0x0418, 0x0438, 0x0439, 0xFFFF, 0x043A, 0x043A, 0xFFFF, 0x043B, 0x043B, 0xFFFF, 0x043C, 0x043C, 0xFFFF, 0x043D, 0x043D, 0xFFFF, 0x043E, 0x043E, 0xFFFF, 0x043F, 0x043F }; unsigned short unac_data86[] = { 0xFFFF, 0x0440, 0x0440, 0xFFFF, 0x0441, 0x0441, 0xFFFF, 0x0442, 0x0442, 0xFFFF, 0x0443, 0x0443, 0xFFFF, 0x0444, 0x0444, 0xFFFF, 0x0445, 0x0445, 0xFFFF, 0x0446, 0x0446, 0xFFFF, 0x0447, 0x0447 }; unsigned short unac_data87[] = { 0xFFFF, 0x0448, 0x0448, 0xFFFF, 0x0449, 0x0449, 0xFFFF, 0x044A, 0x044A, 0xFFFF, 0x044B, 0x044B, 0xFFFF, 0x044C, 0x044C, 0xFFFF, 0x044D, 0x044D, 0xFFFF, 0x044E, 0x044E, 0xFFFF, 0x044F, 0x044F }; unsigned short unac_data88[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0438, 0x0438, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data89[] = { 0x0435, 0x0435, 0xFFFF, 0x0435, 0x0435, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0433, 0x0433, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0456, 0x0456, 0xFFFF }; unsigned short unac_data90[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x043A, 0x043A, 0xFFFF, 0x0438, 0x0438, 0xFFFF, 0x0443, 0x0443, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data91[] = { 0xFFFF, 0x0461, 0x0461, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0463, 0x0463, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0465, 0x0465, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0467, 0x0467, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data92[] = { 0xFFFF, 0x0469, 0x0469, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x046B, 0x046B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x046D, 0x046D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x046F, 0x046F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data93[] = { 0xFFFF, 0x0471, 0x0471, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0473, 0x0473, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0475, 0x0475, 0xFFFF, 0xFFFF, 0xFFFF, 0x0474, 0x0475, 0x0477, 0x0475, 0x0475, 0xFFFF }; unsigned short unac_data94[] = { 0xFFFF, 0x0479, 0x0479, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x047B, 0x047B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x047D, 0x047D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x047F, 0x047F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data95[] = { 0xFFFF, 0x0481, 0x0481, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data96[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0x048B, 0x048B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x048D, 0x048D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x048F, 0x048F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data97[] = { 0xFFFF, 0x0491, 0x0491, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0493, 0x0493, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0495, 0x0495, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0497, 0x0497, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data98[] = { 0xFFFF, 0x0499, 0x0499, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x049B, 0x049B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x049D, 0x049D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x049F, 0x049F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data99[] = { 0xFFFF, 0x04A1, 0x04A1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04A3, 0x04A3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04A5, 0x04A5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04A7, 0x04A7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data100[] = { 0xFFFF, 0x04A9, 0x04A9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04AB, 0x04AB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04AD, 0x04AD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04AF, 0x04AF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data101[] = { 0xFFFF, 0x04B1, 0x04B1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04B3, 0x04B3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04B5, 0x04B5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04B7, 0x04B7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data102[] = { 0xFFFF, 0x04B9, 0x04B9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04BB, 0x04BB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04BD, 0x04BD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04BF, 0x04BF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data103[] = { 0xFFFF, 0x04CF, 0x04CF, 0x0416, 0x0436, 0x04C2, 0x0436, 0x0436, 0xFFFF, 0xFFFF, 0x04C4, 0x04C4, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04C6, 0x04C6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04C8, 0x04C8 }; unsigned short unac_data104[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04CA, 0x04CA, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04CC, 0x04CC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04CE, 0x04CE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data105[] = { 0x0410, 0x0430, 0x04D1, 0x0430, 0x0430, 0xFFFF, 0x0410, 0x0430, 0x04D3, 0x0430, 0x0430, 0xFFFF, 0xFFFF, 0x04D5, 0x04D5, 0xFFFF, 0xFFFF, 0xFFFF, 0x0415, 0x0435, 0x04D7, 0x0435, 0x0435, 0xFFFF }; unsigned short unac_data106[] = { 0xFFFF, 0x04D9, 0x04D9, 0xFFFF, 0xFFFF, 0xFFFF, 0x04D8, 0x04D9, 0x04DB, 0x04D9, 0x04D9, 0xFFFF, 0x0416, 0x0436, 0x04DD, 0x0436, 0x0436, 0xFFFF, 0x0417, 0x0437, 0x04DF, 0x0437, 0x0437, 0xFFFF }; unsigned short unac_data107[] = { 0xFFFF, 0x04E1, 0x04E1, 0xFFFF, 0xFFFF, 0xFFFF, 0x0418, 0x0438, 0x04E3, 0x0438, 0x0438, 0xFFFF, 0x0418, 0x0438, 0x04E5, 0x0438, 0x0438, 0xFFFF, 0x041E, 0x043E, 0x04E7, 0x043E, 0x043E, 0xFFFF }; unsigned short unac_data108[] = { 0xFFFF, 0x04E9, 0x04E9, 0xFFFF, 0xFFFF, 0xFFFF, 0x04E8, 0x04E9, 0x04EB, 0x04E9, 0x04E9, 0xFFFF, 0x042D, 0x044D, 0x04ED, 0x044D, 0x044D, 0xFFFF, 0x0423, 0x0443, 0x04EF, 0x0443, 0x0443, 0xFFFF }; unsigned short unac_data109[] = { 0x0423, 0x0443, 0x04F1, 0x0443, 0x0443, 0xFFFF, 0x0423, 0x0443, 0x04F3, 0x0443, 0x0443, 0xFFFF, 0x0427, 0x0447, 0x04F5, 0x0447, 0x0447, 0xFFFF, 0xFFFF, 0x04F7, 0x04F7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data110[] = { 0x042B, 0x044B, 0x04F9, 0x044B, 0x044B, 0xFFFF, 0xFFFF, 0x04FB, 0x04FB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04FD, 0x04FD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x04FF, 0x04FF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data111[] = { 0xFFFF, 0x0501, 0x0501, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0503, 0x0503, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0505, 0x0505, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0507, 0x0507, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data112[] = { 0xFFFF, 0x0509, 0x0509, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x050B, 0x050B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x050D, 0x050D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x050F, 0x050F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data113[] = { 0xFFFF, 0x0511, 0x0511, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0513, 0x0513, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0515, 0x0515, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0517, 0x0517, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data114[] = { 0xFFFF, 0x0519, 0x0519, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x051B, 0x051B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x051D, 0x051D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x051F, 0x051F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data115[] = { 0xFFFF, 0x0521, 0x0521, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0523, 0x0523, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0525, 0x0525, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0527, 0x0527, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data116[] = { 0xFFFF, 0x0529, 0x0529, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x052B, 0x052B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x052D, 0x052D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x052F, 0x052F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data117[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0561, 0x0561, 0xFFFF, 0x0562, 0x0562, 0xFFFF, 0x0563, 0x0563, 0xFFFF, 0x0564, 0x0564, 0xFFFF, 0x0565, 0x0565, 0xFFFF, 0x0566, 0x0566, 0xFFFF, 0x0567, 0x0567 }; unsigned short unac_data118[] = { 0xFFFF, 0x0568, 0x0568, 0xFFFF, 0x0569, 0x0569, 0xFFFF, 0x056A, 0x056A, 0xFFFF, 0x056B, 0x056B, 0xFFFF, 0x056C, 0x056C, 0xFFFF, 0x056D, 0x056D, 0xFFFF, 0x056E, 0x056E, 0xFFFF, 0x056F, 0x056F }; unsigned short unac_data119[] = { 0xFFFF, 0x0570, 0x0570, 0xFFFF, 0x0571, 0x0571, 0xFFFF, 0x0572, 0x0572, 0xFFFF, 0x0573, 0x0573, 0xFFFF, 0x0574, 0x0574, 0xFFFF, 0x0575, 0x0575, 0xFFFF, 0x0576, 0x0576, 0xFFFF, 0x0577, 0x0577 }; unsigned short unac_data120[] = { 0xFFFF, 0x0578, 0x0578, 0xFFFF, 0x0579, 0x0579, 0xFFFF, 0x057A, 0x057A, 0xFFFF, 0x057B, 0x057B, 0xFFFF, 0x057C, 0x057C, 0xFFFF, 0x057D, 0x057D, 0xFFFF, 0x057E, 0x057E, 0xFFFF, 0x057F, 0x057F }; unsigned short unac_data121[] = { 0xFFFF, 0x0580, 0x0580, 0xFFFF, 0x0581, 0x0581, 0xFFFF, 0x0582, 0x0582, 0xFFFF, 0x0583, 0x0583, 0xFFFF, 0x0584, 0x0584, 0xFFFF, 0x0585, 0x0585, 0xFFFF, 0x0586, 0x0586, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data122[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0565, 0x0582, 0x0565, 0x0582, 0x0565, 0x0582 }; unsigned short unac_data123[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data124[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data125[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data126[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data127[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0648, 0x0648, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data128[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data129[] = { 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0627, 0x0674, 0x0627, 0x0674, 0xFFFF, 0x0648, 0x0674, 0x0648, 0x0674, 0xFFFF, 0x06C7, 0x0674, 0x06C7, 0x0674, 0xFFFF }; unsigned short unac_data130[] = { 0x064A, 0x0674, 0x064A, 0x0674, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data131[] = { 0x06D5, 0x06D5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x06C1, 0x06C1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data132[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x06D2, 0x06D2, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data133[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data134[] = { 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data135[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data136[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data137[] = { 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data138[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data139[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data140[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data141[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data142[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data143[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data144[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data145[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0A32, 0x0A32, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0A38, 0x0A38, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data146[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data147[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data148[] = { 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data149[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0A16, 0x0A16, 0xFFFF, 0x0A17, 0x0A17, 0xFFFF, 0x0A1C, 0x0A1C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0A2B, 0x0A2B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data150[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data151[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data152[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data153[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data154[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0B21, 0x0B21, 0xFFFF, 0x0B22, 0x0B22, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data155[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data156[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data157[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data158[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data159[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data160[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data161[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data162[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0E32, 0x0E32, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data163[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data164[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0EB2, 0x0EB2, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data165[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data166[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0EAB, 0x0E99, 0x0EAB, 0x0E99, 0xFFFF, 0x0EAB, 0x0EA1, 0x0EAB, 0x0EA1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data167[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F0B, 0x0F0B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data168[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data169[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data170[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data171[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F42, 0x0F42, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data172[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F4C, 0x0F4C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data173[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F51, 0x0F51, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F56, 0x0F56, 0xFFFF }; unsigned short unac_data174[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F5B, 0x0F5B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data175[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0F40, 0x0F40, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data176[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data177[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1025, 0x1025, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data178[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data179[] = { 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data180[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data181[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data182[] = { 0xFFFF, 0x2D00, 0x2D00, 0xFFFF, 0x2D01, 0x2D01, 0xFFFF, 0x2D02, 0x2D02, 0xFFFF, 0x2D03, 0x2D03, 0xFFFF, 0x2D04, 0x2D04, 0xFFFF, 0x2D05, 0x2D05, 0xFFFF, 0x2D06, 0x2D06, 0xFFFF, 0x2D07, 0x2D07 }; unsigned short unac_data183[] = { 0xFFFF, 0x2D08, 0x2D08, 0xFFFF, 0x2D09, 0x2D09, 0xFFFF, 0x2D0A, 0x2D0A, 0xFFFF, 0x2D0B, 0x2D0B, 0xFFFF, 0x2D0C, 0x2D0C, 0xFFFF, 0x2D0D, 0x2D0D, 0xFFFF, 0x2D0E, 0x2D0E, 0xFFFF, 0x2D0F, 0x2D0F }; unsigned short unac_data184[] = { 0xFFFF, 0x2D10, 0x2D10, 0xFFFF, 0x2D11, 0x2D11, 0xFFFF, 0x2D12, 0x2D12, 0xFFFF, 0x2D13, 0x2D13, 0xFFFF, 0x2D14, 0x2D14, 0xFFFF, 0x2D15, 0x2D15, 0xFFFF, 0x2D16, 0x2D16, 0xFFFF, 0x2D17, 0x2D17 }; unsigned short unac_data185[] = { 0xFFFF, 0x2D18, 0x2D18, 0xFFFF, 0x2D19, 0x2D19, 0xFFFF, 0x2D1A, 0x2D1A, 0xFFFF, 0x2D1B, 0x2D1B, 0xFFFF, 0x2D1C, 0x2D1C, 0xFFFF, 0x2D1D, 0x2D1D, 0xFFFF, 0x2D1E, 0x2D1E, 0xFFFF, 0x2D1F, 0x2D1F }; unsigned short unac_data186[] = { 0xFFFF, 0x2D20, 0x2D20, 0xFFFF, 0x2D21, 0x2D21, 0xFFFF, 0x2D22, 0x2D22, 0xFFFF, 0x2D23, 0x2D23, 0xFFFF, 0x2D24, 0x2D24, 0xFFFF, 0x2D25, 0x2D25, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2D27, 0x2D27 }; unsigned short unac_data187[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2D2D, 0x2D2D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data188[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x10DC, 0x10DC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data189[] = { 0xFFFF, 0x13F0, 0x13F0, 0xFFFF, 0x13F1, 0x13F1, 0xFFFF, 0x13F2, 0x13F2, 0xFFFF, 0x13F3, 0x13F3, 0xFFFF, 0x13F4, 0x13F4, 0xFFFF, 0x13F5, 0x13F5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data190[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data191[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data192[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data193[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1B05, 0x1B05, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data194[] = { 0x1B07, 0x1B07, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1B09, 0x1B09, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1B0B, 0x1B0B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1B0D, 0x1B0D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data195[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1B11, 0x1B11, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data196[] = { 0xFFFF, 0x0432, 0x0432, 0xFFFF, 0x0434, 0x0434, 0xFFFF, 0x043E, 0x043E, 0xFFFF, 0x0441, 0x0441, 0xFFFF, 0x0442, 0x0442, 0xFFFF, 0x0442, 0x0442, 0xFFFF, 0x044A, 0x044A, 0xFFFF, 0x0463, 0x0463 }; unsigned short unac_data197[] = { 0xFFFF, 0xA64B, 0xA64B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data198[] = { 0xFFFF, 0x10D0, 0x10D0, 0xFFFF, 0x10D1, 0x10D1, 0xFFFF, 0x10D2, 0x10D2, 0xFFFF, 0x10D3, 0x10D3, 0xFFFF, 0x10D4, 0x10D4, 0xFFFF, 0x10D5, 0x10D5, 0xFFFF, 0x10D6, 0x10D6, 0xFFFF, 0x10D7, 0x10D7 }; unsigned short unac_data199[] = { 0xFFFF, 0x10D8, 0x10D8, 0xFFFF, 0x10D9, 0x10D9, 0xFFFF, 0x10DA, 0x10DA, 0xFFFF, 0x10DB, 0x10DB, 0xFFFF, 0x10DC, 0x10DC, 0xFFFF, 0x10DD, 0x10DD, 0xFFFF, 0x10DE, 0x10DE, 0xFFFF, 0x10DF, 0x10DF }; unsigned short unac_data200[] = { 0xFFFF, 0x10E0, 0x10E0, 0xFFFF, 0x10E1, 0x10E1, 0xFFFF, 0x10E2, 0x10E2, 0xFFFF, 0x10E3, 0x10E3, 0xFFFF, 0x10E4, 0x10E4, 0xFFFF, 0x10E5, 0x10E5, 0xFFFF, 0x10E6, 0x10E6, 0xFFFF, 0x10E7, 0x10E7 }; unsigned short unac_data201[] = { 0xFFFF, 0x10E8, 0x10E8, 0xFFFF, 0x10E9, 0x10E9, 0xFFFF, 0x10EA, 0x10EA, 0xFFFF, 0x10EB, 0x10EB, 0xFFFF, 0x10EC, 0x10EC, 0xFFFF, 0x10ED, 0x10ED, 0xFFFF, 0x10EE, 0x10EE, 0xFFFF, 0x10EF, 0x10EF }; unsigned short unac_data202[] = { 0xFFFF, 0x10F0, 0x10F0, 0xFFFF, 0x10F1, 0x10F1, 0xFFFF, 0x10F2, 0x10F2, 0xFFFF, 0x10F3, 0x10F3, 0xFFFF, 0x10F4, 0x10F4, 0xFFFF, 0x10F5, 0x10F5, 0xFFFF, 0x10F6, 0x10F6, 0xFFFF, 0x10F7, 0x10F7 }; unsigned short unac_data203[] = { 0xFFFF, 0x10F8, 0x10F8, 0xFFFF, 0x10F9, 0x10F9, 0xFFFF, 0x10FA, 0x10FA, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x10FD, 0x10FD, 0xFFFF, 0x10FE, 0x10FE, 0xFFFF, 0x10FF, 0x10FF }; unsigned short unac_data204[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data205[] = { 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data206[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data207[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0041, 0x0061, 0xFFFF, 0x00C6, 0x00E6, 0xFFFF, 0x0042, 0x0062, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data208[] = { 0x0044, 0x0064, 0xFFFF, 0x0045, 0x0065, 0xFFFF, 0x018E, 0x01DD, 0xFFFF, 0x0047, 0x0067, 0xFFFF, 0x0048, 0x0068, 0xFFFF, 0x0049, 0x0069, 0xFFFF, 0x004A, 0x006A, 0xFFFF, 0x004B, 0x006B, 0xFFFF }; unsigned short unac_data209[] = { 0x004C, 0x006C, 0xFFFF, 0x004D, 0x006D, 0xFFFF, 0x004E, 0x006E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x004F, 0x006F, 0xFFFF, 0x0222, 0x0223, 0xFFFF, 0x0050, 0x0070, 0xFFFF, 0x0052, 0x0072, 0xFFFF }; unsigned short unac_data210[] = { 0x0054, 0x0074, 0xFFFF, 0x0055, 0x0075, 0xFFFF, 0x0057, 0x0077, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0x0250, 0x0250, 0xFFFF, 0x0251, 0x0251, 0xFFFF, 0x1D02, 0x1D02, 0xFFFF, 0x0062, 0x0062, 0xFFFF }; unsigned short unac_data211[] = { 0x0064, 0x0064, 0xFFFF, 0x0065, 0x0065, 0xFFFF, 0x0259, 0x0259, 0xFFFF, 0x025B, 0x025B, 0xFFFF, 0x025C, 0x025C, 0xFFFF, 0x0067, 0x0067, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x006B, 0x006B, 0xFFFF }; unsigned short unac_data212[] = { 0x006D, 0x006D, 0xFFFF, 0x014B, 0x014B, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0x0254, 0x0254, 0xFFFF, 0x1D16, 0x1D16, 0xFFFF, 0x1D17, 0x1D17, 0xFFFF, 0x0070, 0x0070, 0xFFFF, 0x0074, 0x0074, 0xFFFF }; unsigned short unac_data213[] = { 0x0075, 0x0075, 0xFFFF, 0x1D1D, 0x1D1D, 0xFFFF, 0x026F, 0x026F, 0xFFFF, 0x0076, 0x0076, 0xFFFF, 0x1D25, 0x1D25, 0xFFFF, 0x03B2, 0x03B2, 0xFFFF, 0x03B3, 0x03B3, 0xFFFF, 0x03B4, 0x03B4, 0xFFFF }; unsigned short unac_data214[] = { 0x03C6, 0x03C6, 0xFFFF, 0x03C7, 0x03C7, 0xFFFF, 0x0069, 0x0069, 0xFFFF, 0x0072, 0x0072, 0xFFFF, 0x0075, 0x0075, 0xFFFF, 0x0076, 0x0076, 0xFFFF, 0x03B2, 0x03B2, 0xFFFF, 0x03B3, 0x03B3, 0xFFFF }; unsigned short unac_data215[] = { 0x03C1, 0x03C1, 0xFFFF, 0x03C6, 0x03C6, 0xFFFF, 0x03C7, 0x03C7, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data216[] = { 0x043D, 0x043D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data217[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0252, 0x0252, 0xFFFF, 0x0063, 0x0063, 0xFFFF, 0x0255, 0x0255, 0xFFFF, 0x00F0, 0x00F0, 0xFFFF, 0x025C, 0x025C, 0xFFFF }; unsigned short unac_data218[] = { 0x0066, 0x0066, 0xFFFF, 0x025F, 0x025F, 0xFFFF, 0x0261, 0x0261, 0xFFFF, 0x0265, 0x0265, 0xFFFF, 0x0268, 0x0268, 0xFFFF, 0x0269, 0x0269, 0xFFFF, 0x026A, 0x026A, 0xFFFF, 0x1D7B, 0x1D7B, 0xFFFF }; unsigned short unac_data219[] = { 0x029D, 0x029D, 0xFFFF, 0x026D, 0x026D, 0xFFFF, 0x1D85, 0x1D85, 0xFFFF, 0x029F, 0x029F, 0xFFFF, 0x0271, 0x0271, 0xFFFF, 0x0270, 0x0270, 0xFFFF, 0x0272, 0x0272, 0xFFFF, 0x0273, 0x0273, 0xFFFF }; unsigned short unac_data220[] = { 0x0274, 0x0274, 0xFFFF, 0x0275, 0x0275, 0xFFFF, 0x0278, 0x0278, 0xFFFF, 0x0282, 0x0282, 0xFFFF, 0x0283, 0x0283, 0xFFFF, 0x01AB, 0x01AB, 0xFFFF, 0x0289, 0x0289, 0xFFFF, 0x028A, 0x028A, 0xFFFF }; unsigned short unac_data221[] = { 0x1D1C, 0x1D1C, 0xFFFF, 0x028B, 0x028B, 0xFFFF, 0x028C, 0x028C, 0xFFFF, 0x007A, 0x007A, 0xFFFF, 0x0290, 0x0290, 0xFFFF, 0x0291, 0x0291, 0xFFFF, 0x0292, 0x0292, 0xFFFF, 0x03B8, 0x03B8, 0xFFFF }; unsigned short unac_data222[] = { 0x0041, 0x0061, 0x1E01, 0x0061, 0x0061, 0xFFFF, 0x0042, 0x0062, 0x1E03, 0x0062, 0x0062, 0xFFFF, 0x0042, 0x0062, 0x1E05, 0x0062, 0x0062, 0xFFFF, 0x0042, 0x0062, 0x1E07, 0x0062, 0x0062, 0xFFFF }; unsigned short unac_data223[] = { 0x0043, 0x0063, 0x1E09, 0x0063, 0x0063, 0xFFFF, 0x0044, 0x0064, 0x1E0B, 0x0064, 0x0064, 0xFFFF, 0x0044, 0x0064, 0x1E0D, 0x0064, 0x0064, 0xFFFF, 0x0044, 0x0064, 0x1E0F, 0x0064, 0x0064, 0xFFFF }; unsigned short unac_data224[] = { 0x0044, 0x0064, 0x1E11, 0x0064, 0x0064, 0xFFFF, 0x0044, 0x0064, 0x1E13, 0x0064, 0x0064, 0xFFFF, 0x0045, 0x0065, 0x1E15, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1E17, 0x0065, 0x0065, 0xFFFF }; unsigned short unac_data225[] = { 0x0045, 0x0065, 0x1E19, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1E1B, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1E1D, 0x0065, 0x0065, 0xFFFF, 0x0046, 0x0066, 0x1E1F, 0x0066, 0x0066, 0xFFFF }; unsigned short unac_data226[] = { 0x0047, 0x0067, 0x1E21, 0x0067, 0x0067, 0xFFFF, 0x0048, 0x0068, 0x1E23, 0x0068, 0x0068, 0xFFFF, 0x0048, 0x0068, 0x1E25, 0x0068, 0x0068, 0xFFFF, 0x0048, 0x0068, 0x1E27, 0x0068, 0x0068, 0xFFFF }; unsigned short unac_data227[] = { 0x0048, 0x0068, 0x1E29, 0x0068, 0x0068, 0xFFFF, 0x0048, 0x0068, 0x1E2B, 0x0068, 0x0068, 0xFFFF, 0x0049, 0x0069, 0x1E2D, 0x0069, 0x0069, 0xFFFF, 0x0049, 0x0069, 0x1E2F, 0x0069, 0x0069, 0xFFFF }; unsigned short unac_data228[] = { 0x004B, 0x006B, 0x1E31, 0x006B, 0x006B, 0xFFFF, 0x004B, 0x006B, 0x1E33, 0x006B, 0x006B, 0xFFFF, 0x004B, 0x006B, 0x1E35, 0x006B, 0x006B, 0xFFFF, 0x004C, 0x006C, 0x1E37, 0x006C, 0x006C, 0xFFFF }; unsigned short unac_data229[] = { 0x004C, 0x006C, 0x1E39, 0x006C, 0x006C, 0xFFFF, 0x004C, 0x006C, 0x1E3B, 0x006C, 0x006C, 0xFFFF, 0x004C, 0x006C, 0x1E3D, 0x006C, 0x006C, 0xFFFF, 0x004D, 0x006D, 0x1E3F, 0x006D, 0x006D, 0xFFFF }; unsigned short unac_data230[] = { 0x004D, 0x006D, 0x1E41, 0x006D, 0x006D, 0xFFFF, 0x004D, 0x006D, 0x1E43, 0x006D, 0x006D, 0xFFFF, 0x004E, 0x006E, 0x1E45, 0x006E, 0x006E, 0xFFFF, 0x004E, 0x006E, 0x1E47, 0x006E, 0x006E, 0xFFFF }; unsigned short unac_data231[] = { 0x004E, 0x006E, 0x1E49, 0x006E, 0x006E, 0xFFFF, 0x004E, 0x006E, 0x1E4B, 0x006E, 0x006E, 0xFFFF, 0x004F, 0x006F, 0x1E4D, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1E4F, 0x006F, 0x006F, 0xFFFF }; unsigned short unac_data232[] = { 0x004F, 0x006F, 0x1E51, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1E53, 0x006F, 0x006F, 0xFFFF, 0x0050, 0x0070, 0x1E55, 0x0070, 0x0070, 0xFFFF, 0x0050, 0x0070, 0x1E57, 0x0070, 0x0070, 0xFFFF }; unsigned short unac_data233[] = { 0x0052, 0x0072, 0x1E59, 0x0072, 0x0072, 0xFFFF, 0x0052, 0x0072, 0x1E5B, 0x0072, 0x0072, 0xFFFF, 0x0052, 0x0072, 0x1E5D, 0x0072, 0x0072, 0xFFFF, 0x0052, 0x0072, 0x1E5F, 0x0072, 0x0072, 0xFFFF }; unsigned short unac_data234[] = { 0x0053, 0x0073, 0x1E61, 0x0073, 0x0073, 0xFFFF, 0x0053, 0x0073, 0x1E63, 0x0073, 0x0073, 0xFFFF, 0x0053, 0x0073, 0x1E65, 0x0073, 0x0073, 0xFFFF, 0x0053, 0x0073, 0x1E67, 0x0073, 0x0073, 0xFFFF }; unsigned short unac_data235[] = { 0x0053, 0x0073, 0x1E69, 0x0073, 0x0073, 0xFFFF, 0x0054, 0x0074, 0x1E6B, 0x0074, 0x0074, 0xFFFF, 0x0054, 0x0074, 0x1E6D, 0x0074, 0x0074, 0xFFFF, 0x0054, 0x0074, 0x1E6F, 0x0074, 0x0074, 0xFFFF }; unsigned short unac_data236[] = { 0x0054, 0x0074, 0x1E71, 0x0074, 0x0074, 0xFFFF, 0x0055, 0x0075, 0x1E73, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x1E75, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x1E77, 0x0075, 0x0075, 0xFFFF }; unsigned short unac_data237[] = { 0x0055, 0x0075, 0x1E79, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x1E7B, 0x0075, 0x0075, 0xFFFF, 0x0056, 0x0076, 0x1E7D, 0x0076, 0x0076, 0xFFFF, 0x0056, 0x0076, 0x1E7F, 0x0076, 0x0076, 0xFFFF }; unsigned short unac_data238[] = { 0x0057, 0x0077, 0x1E81, 0x0077, 0x0077, 0xFFFF, 0x0057, 0x0077, 0x1E83, 0x0077, 0x0077, 0xFFFF, 0x0057, 0x0077, 0x1E85, 0x0077, 0x0077, 0xFFFF, 0x0057, 0x0077, 0x1E87, 0x0077, 0x0077, 0xFFFF }; unsigned short unac_data239[] = { 0x0057, 0x0077, 0x1E89, 0x0077, 0x0077, 0xFFFF, 0x0058, 0x0078, 0x1E8B, 0x0078, 0x0078, 0xFFFF, 0x0058, 0x0078, 0x1E8D, 0x0078, 0x0078, 0xFFFF, 0x0059, 0x0079, 0x1E8F, 0x0079, 0x0079, 0xFFFF }; unsigned short unac_data240[] = { 0x005A, 0x007A, 0x1E91, 0x007A, 0x007A, 0xFFFF, 0x005A, 0x007A, 0x1E93, 0x007A, 0x007A, 0xFFFF, 0x005A, 0x007A, 0x1E95, 0x007A, 0x007A, 0xFFFF, 0x0068, 0x0068, 0x0068, 0x0331, 0x0074, 0x0074, 0x0074, 0x0308 }; unsigned short unac_data241[] = { 0x0077, 0x0077, 0x0077, 0x030A, 0x0079, 0x0079, 0x0079, 0x030A, 0x0061, 0x02BE, 0x0061, 0x02BE, 0x0061, 0x02BE, 0x0073, 0x0073, 0x1E61, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0073, 0x0073, 0x0073, 0x0073, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data242[] = { 0x0041, 0x0061, 0x1EA1, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EA3, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EA5, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EA7, 0x0061, 0x0061, 0xFFFF }; unsigned short unac_data243[] = { 0x0041, 0x0061, 0x1EA9, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EAB, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EAD, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EAF, 0x0061, 0x0061, 0xFFFF }; unsigned short unac_data244[] = { 0x0041, 0x0061, 0x1EB1, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EB3, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EB5, 0x0061, 0x0061, 0xFFFF, 0x0041, 0x0061, 0x1EB7, 0x0061, 0x0061, 0xFFFF }; unsigned short unac_data245[] = { 0x0045, 0x0065, 0x1EB9, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1EBB, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1EBD, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1EBF, 0x0065, 0x0065, 0xFFFF }; unsigned short unac_data246[] = { 0x0045, 0x0065, 0x1EC1, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1EC3, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1EC5, 0x0065, 0x0065, 0xFFFF, 0x0045, 0x0065, 0x1EC7, 0x0065, 0x0065, 0xFFFF }; unsigned short unac_data247[] = { 0x0049, 0x0069, 0x1EC9, 0x0069, 0x0069, 0xFFFF, 0x0049, 0x0069, 0x1ECB, 0x0069, 0x0069, 0xFFFF, 0x004F, 0x006F, 0x1ECD, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1ECF, 0x006F, 0x006F, 0xFFFF }; unsigned short unac_data248[] = { 0x004F, 0x006F, 0x1ED1, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1ED3, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1ED5, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1ED7, 0x006F, 0x006F, 0xFFFF }; unsigned short unac_data249[] = { 0x004F, 0x006F, 0x1ED9, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1EDB, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1EDD, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1EDF, 0x006F, 0x006F, 0xFFFF }; unsigned short unac_data250[] = { 0x004F, 0x006F, 0x1EE1, 0x006F, 0x006F, 0xFFFF, 0x004F, 0x006F, 0x1EE3, 0x006F, 0x006F, 0xFFFF, 0x0055, 0x0075, 0x1EE5, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x1EE7, 0x0075, 0x0075, 0xFFFF }; unsigned short unac_data251[] = { 0x0055, 0x0075, 0x1EE9, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x1EEB, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x1EED, 0x0075, 0x0075, 0xFFFF, 0x0055, 0x0075, 0x1EEF, 0x0075, 0x0075, 0xFFFF }; unsigned short unac_data252[] = { 0x0055, 0x0075, 0x1EF1, 0x0075, 0x0075, 0xFFFF, 0x0059, 0x0079, 0x1EF3, 0x0079, 0x0079, 0xFFFF, 0x0059, 0x0079, 0x1EF5, 0x0079, 0x0079, 0xFFFF, 0x0059, 0x0079, 0x1EF7, 0x0079, 0x0079, 0xFFFF }; unsigned short unac_data253[] = { 0x0059, 0x0079, 0x1EF9, 0x0079, 0x0079, 0xFFFF, 0xFFFF, 0x1EFB, 0x1EFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1EFD, 0x1EFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1EFF, 0x1EFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data254[] = { 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF }; unsigned short unac_data255[] = { 0x0391, 0x03B1, 0x1F00, 0x0391, 0x03B1, 0x1F01, 0x0391, 0x03B1, 0x1F02, 0x0391, 0x03B1, 0x1F03, 0x0391, 0x03B1, 0x1F04, 0x0391, 0x03B1, 0x1F05, 0x0391, 0x03B1, 0x1F06, 0x0391, 0x03B1, 0x1F07 }; unsigned short unac_data256[] = { 0x03B5, 0x03B5, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data257[] = { 0x0395, 0x03B5, 0x1F10, 0x0395, 0x03B5, 0x1F11, 0x0395, 0x03B5, 0x1F12, 0x0395, 0x03B5, 0x1F13, 0x0395, 0x03B5, 0x1F14, 0x0395, 0x03B5, 0x1F15, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data258[] = { 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF }; unsigned short unac_data259[] = { 0x0397, 0x03B7, 0x1F20, 0x0397, 0x03B7, 0x1F21, 0x0397, 0x03B7, 0x1F22, 0x0397, 0x03B7, 0x1F23, 0x0397, 0x03B7, 0x1F24, 0x0397, 0x03B7, 0x1F25, 0x0397, 0x03B7, 0x1F26, 0x0397, 0x03B7, 0x1F27 }; unsigned short unac_data260[] = { 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF }; unsigned short unac_data261[] = { 0x0399, 0x03B9, 0x1F30, 0x0399, 0x03B9, 0x1F31, 0x0399, 0x03B9, 0x1F32, 0x0399, 0x03B9, 0x1F33, 0x0399, 0x03B9, 0x1F34, 0x0399, 0x03B9, 0x1F35, 0x0399, 0x03B9, 0x1F36, 0x0399, 0x03B9, 0x1F37 }; unsigned short unac_data262[] = { 0x03BF, 0x03BF, 0xFFFF, 0x03BF, 0x03BF, 0xFFFF, 0x03BF, 0x03BF, 0xFFFF, 0x03BF, 0x03BF, 0xFFFF, 0x03BF, 0x03BF, 0xFFFF, 0x03BF, 0x03BF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data263[] = { 0x039F, 0x03BF, 0x1F40, 0x039F, 0x03BF, 0x1F41, 0x039F, 0x03BF, 0x1F42, 0x039F, 0x03BF, 0x1F43, 0x039F, 0x03BF, 0x1F44, 0x039F, 0x03BF, 0x1F45, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data264[] = { 0x03C5, 0x03C5, 0x03C5, 0x0313, 0x03C5, 0x03C5, 0xFFFF, 0x03C5, 0x03C5, 0x03C5, 0x0313, 0x0300, 0x03C5, 0x03C5, 0xFFFF, 0x03C5, 0x03C5, 0x03C5, 0x0313, 0x0301, 0x03C5, 0x03C5, 0xFFFF, 0x03C5, 0x03C5, 0x03C5, 0x0313, 0x0342, 0x03C5, 0x03C5, 0xFFFF }; unsigned short unac_data265[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x03A5, 0x03C5, 0x1F51, 0xFFFF, 0xFFFF, 0xFFFF, 0x03A5, 0x03C5, 0x1F53, 0xFFFF, 0xFFFF, 0xFFFF, 0x03A5, 0x03C5, 0x1F55, 0xFFFF, 0xFFFF, 0xFFFF, 0x03A5, 0x03C5, 0x1F57 }; unsigned short unac_data266[] = { 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF }; unsigned short unac_data267[] = { 0x03A9, 0x03C9, 0x1F60, 0x03A9, 0x03C9, 0x1F61, 0x03A9, 0x03C9, 0x1F62, 0x03A9, 0x03C9, 0x1F63, 0x03A9, 0x03C9, 0x1F64, 0x03A9, 0x03C9, 0x1F65, 0x03A9, 0x03C9, 0x1F66, 0x03A9, 0x03C9, 0x1F67 }; unsigned short unac_data268[] = { 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0x03B5, 0x03B5, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF }; unsigned short unac_data269[] = { 0x03BF, 0x03BF, 0xFFFF, 0x03BF, 0x03BF, 0xFFFF, 0x03C5, 0x03C5, 0xFFFF, 0x03C5, 0x03C5, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data270[] = { 0x03B1, 0x03B1, 0x1F00, 0x03B9, 0x03B1, 0x03B1, 0x1F01, 0x03B9, 0x03B1, 0x03B1, 0x1F02, 0x03B9, 0x03B1, 0x03B1, 0x1F03, 0x03B9, 0x03B1, 0x03B1, 0x1F04, 0x03B9, 0x03B1, 0x03B1, 0x1F05, 0x03B9, 0x03B1, 0x03B1, 0x1F06, 0x03B9, 0x03B1, 0x03B1, 0x1F07, 0x03B9 }; unsigned short unac_data271[] = { 0x0391, 0x03B1, 0x1F00, 0x03B9, 0x0391, 0x03B1, 0x1F01, 0x03B9, 0x0391, 0x03B1, 0x1F02, 0x03B9, 0x0391, 0x03B1, 0x1F03, 0x03B9, 0x0391, 0x03B1, 0x1F04, 0x03B9, 0x0391, 0x03B1, 0x1F05, 0x03B9, 0x0391, 0x03B1, 0x1F06, 0x03B9, 0x0391, 0x03B1, 0x1F07, 0x03B9 }; unsigned short unac_data272[] = { 0x03B7, 0x03B7, 0x1F20, 0x03B9, 0x03B7, 0x03B7, 0x1F21, 0x03B9, 0x03B7, 0x03B7, 0x1F22, 0x03B9, 0x03B7, 0x03B7, 0x1F23, 0x03B9, 0x03B7, 0x03B7, 0x1F24, 0x03B9, 0x03B7, 0x03B7, 0x1F25, 0x03B9, 0x03B7, 0x03B7, 0x1F26, 0x03B9, 0x03B7, 0x03B7, 0x1F27, 0x03B9 }; unsigned short unac_data273[] = { 0x0397, 0x03B7, 0x1F20, 0x03B9, 0x0397, 0x03B7, 0x1F21, 0x03B9, 0x0397, 0x03B7, 0x1F22, 0x03B9, 0x0397, 0x03B7, 0x1F23, 0x03B9, 0x0397, 0x03B7, 0x1F24, 0x03B9, 0x0397, 0x03B7, 0x1F25, 0x03B9, 0x0397, 0x03B7, 0x1F26, 0x03B9, 0x0397, 0x03B7, 0x1F27, 0x03B9 }; unsigned short unac_data274[] = { 0x03C9, 0x03C9, 0x1F60, 0x03B9, 0x03C9, 0x03C9, 0x1F61, 0x03B9, 0x03C9, 0x03C9, 0x1F62, 0x03B9, 0x03C9, 0x03C9, 0x1F63, 0x03B9, 0x03C9, 0x03C9, 0x1F64, 0x03B9, 0x03C9, 0x03C9, 0x1F65, 0x03B9, 0x03C9, 0x03C9, 0x1F66, 0x03B9, 0x03C9, 0x03C9, 0x1F67, 0x03B9 }; unsigned short unac_data275[] = { 0x03A9, 0x03C9, 0x1F60, 0x03B9, 0x03A9, 0x03C9, 0x1F61, 0x03B9, 0x03A9, 0x03C9, 0x1F62, 0x03B9, 0x03A9, 0x03C9, 0x1F63, 0x03B9, 0x03A9, 0x03C9, 0x1F64, 0x03B9, 0x03A9, 0x03C9, 0x1F65, 0x03B9, 0x03A9, 0x03C9, 0x1F66, 0x03B9, 0x03A9, 0x03C9, 0x1F67, 0x03B9 }; unsigned short unac_data276[] = { 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0x1F70, 0x03B9, 0x03B1, 0x03B1, 0x03B1, 0x03B9, 0x03B1, 0x03B1, 0x03AC, 0x03B9, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B1, 0x03B1, 0x03B1, 0x0342, 0x03B1, 0x03B1, 0x03B1, 0x0342, 0x03B9 }; unsigned short unac_data277[] = { 0x0391, 0x03B1, 0x1FB0, 0x0391, 0x03B1, 0x1FB1, 0x0391, 0x03B1, 0x1F70, 0x0391, 0x03B1, 0x1F71, 0x0391, 0x03B1, 0x03B1, 0x03B9, 0x0020, 0x0020, 0xFFFF, 0x03B9, 0x03B9, 0x03B9, 0x0020, 0x0020, 0xFFFF }; unsigned short unac_data278[] = { 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x03B7, 0x03B7, 0x1F74, 0x03B9, 0x03B7, 0x03B7, 0x03B7, 0x03B9, 0x03B7, 0x03B7, 0x03AE, 0x03B9, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B7, 0x03B7, 0x03B7, 0x0342, 0x03B7, 0x03B7, 0x03B7, 0x0342, 0x03B9 }; unsigned short unac_data279[] = { 0x0395, 0x03B5, 0x1F72, 0x0395, 0x03B5, 0x1F73, 0x0397, 0x03B7, 0x1F74, 0x0397, 0x03B7, 0x1F75, 0x0397, 0x03B7, 0x03B7, 0x03B9, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF }; unsigned short unac_data280[] = { 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0xFFFF, 0x03B9, 0x03B9, 0x03B9, 0x0308, 0x0300, 0x03B9, 0x03B9, 0x03B9, 0x0308, 0x0301, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B9, 0x03B9, 0x03B9, 0x0342, 0x03B9, 0x03B9, 0x03B9, 0x0308, 0x0342 }; unsigned short unac_data281[] = { 0x0399, 0x03B9, 0x1FD0, 0x0399, 0x03B9, 0x1FD1, 0x0399, 0x03B9, 0x1F76, 0x0399, 0x03B9, 0x1F77, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF }; unsigned short unac_data282[] = { 0x03C5, 0x03C5, 0xFFFF, 0x03C5, 0x03C5, 0xFFFF, 0x03C5, 0x03C5, 0x03C5, 0x0308, 0x0300, 0x03C5, 0x03C5, 0x03C5, 0x0308, 0x0301, 0x03C1, 0x03C1, 0x03C1, 0x0313, 0x03C1, 0x03C1, 0xFFFF, 0x03C5, 0x03C5, 0x03C5, 0x0342, 0x03C5, 0x03C5, 0x03C5, 0x0308, 0x0342 }; unsigned short unac_data283[] = { 0x03A5, 0x03C5, 0x1FE0, 0x03A5, 0x03C5, 0x1FE1, 0x03A5, 0x03C5, 0x1F7A, 0x03A5, 0x03C5, 0x1F7B, 0x03A1, 0x03C1, 0x1FE5, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0060, 0x0060, 0xFFFF }; unsigned short unac_data284[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03C9, 0x03C9, 0x1F7C, 0x03B9, 0x03C9, 0x03C9, 0x03C9, 0x03B9, 0x03C9, 0x03C9, 0x03CE, 0x03B9, 0xFFFF, 0xFFFF, 0xFFFF, 0x03C9, 0x03C9, 0x03C9, 0x0342, 0x03C9, 0x03C9, 0x03C9, 0x0342, 0x03B9 }; unsigned short unac_data285[] = { 0x039F, 0x03BF, 0x1F78, 0x039F, 0x03BF, 0x1F79, 0x03A9, 0x03C9, 0x1F7C, 0x03A9, 0x03C9, 0x1F7D, 0x03A9, 0x03C9, 0x03C9, 0x03B9, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data286[] = { 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF }; unsigned short unac_data287[] = { 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data288[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x2010, 0x2010, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF }; unsigned short unac_data289[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x002E, 0x002E, 0xFFFF, 0x002E, 0x002E, 0x002E, 0x002E, 0xFFFF, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data290[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF }; unsigned short unac_data291[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2032, 0x2032, 0x2032, 0x2032, 0xFFFF, 0x2032, 0x2032, 0x2032, 0x2032, 0x2032, 0x2032, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2035, 0x2035, 0x2035, 0x2035, 0xFFFF, 0x2035, 0x2035, 0x2035, 0x2035, 0x2035, 0x2035, 0xFFFF }; unsigned short unac_data292[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0021, 0x0021, 0x0021, 0x0021, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data293[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x003F, 0x003F, 0x003F, 0x003F, 0xFFFF }; unsigned short unac_data294[] = { 0x003F, 0x0021, 0x003F, 0x0021, 0xFFFF, 0x0021, 0x003F, 0x0021, 0x003F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data295[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2032, 0x2032, 0x2032, 0x2032, 0x2032, 0x2032, 0x2032, 0x2032, 0xFFFF }; unsigned short unac_data296[] = { 0x0030, 0x0030, 0xFFFF, 0x0069, 0x0069, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0034, 0x0034, 0xFFFF, 0x0035, 0x0035, 0xFFFF, 0x0036, 0x0036, 0xFFFF, 0x0037, 0x0037, 0xFFFF }; unsigned short unac_data297[] = { 0x0038, 0x0038, 0xFFFF, 0x0039, 0x0039, 0xFFFF, 0x002B, 0x002B, 0xFFFF, 0x2212, 0x2212, 0xFFFF, 0x003D, 0x003D, 0xFFFF, 0x0028, 0x0028, 0xFFFF, 0x0029, 0x0029, 0xFFFF, 0x006E, 0x006E, 0xFFFF }; unsigned short unac_data298[] = { 0x0030, 0x0030, 0xFFFF, 0x0031, 0x0031, 0xFFFF, 0x0032, 0x0032, 0xFFFF, 0x0033, 0x0033, 0xFFFF, 0x0034, 0x0034, 0xFFFF, 0x0035, 0x0035, 0xFFFF, 0x0036, 0x0036, 0xFFFF, 0x0037, 0x0037, 0xFFFF }; unsigned short unac_data299[] = { 0x0038, 0x0038, 0xFFFF, 0x0039, 0x0039, 0xFFFF, 0x002B, 0x002B, 0xFFFF, 0x2212, 0x2212, 0xFFFF, 0x003D, 0x003D, 0xFFFF, 0x0028, 0x0028, 0xFFFF, 0x0029, 0x0029, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data300[] = { 0x0061, 0x0061, 0xFFFF, 0x0065, 0x0065, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0x0078, 0x0078, 0xFFFF, 0x0259, 0x0259, 0xFFFF, 0x0068, 0x0068, 0xFFFF, 0x006B, 0x006B, 0xFFFF, 0x006C, 0x006C, 0xFFFF }; unsigned short unac_data301[] = { 0x006D, 0x006D, 0xFFFF, 0x006E, 0x006E, 0xFFFF, 0x0070, 0x0070, 0xFFFF, 0x0073, 0x0073, 0xFFFF, 0x0074, 0x0074, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data302[] = { 0x0052, 0x0073, 0x0072, 0x0073, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data303[] = { 0x0061, 0x002F, 0x0063, 0x0061, 0x002F, 0x0063, 0xFFFF, 0x0061, 0x002F, 0x0073, 0x0061, 0x002F, 0x0073, 0xFFFF, 0x0043, 0x0063, 0xFFFF, 0x00B0, 0x0043, 0x00B0, 0x0063, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0063, 0x002F, 0x006F, 0x0063, 0x002F, 0x006F, 0xFFFF, 0x0063, 0x002F, 0x0075, 0x0063, 0x002F, 0x0075, 0xFFFF, 0x0190, 0x025B, 0xFFFF }; unsigned short unac_data304[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x00B0, 0x0046, 0x00B0, 0x0066, 0xFFFF, 0x0067, 0x0067, 0xFFFF, 0x0048, 0x0068, 0xFFFF, 0x0048, 0x0068, 0xFFFF, 0x0048, 0x0068, 0xFFFF, 0x0068, 0x0068, 0xFFFF, 0x0127, 0x0127, 0xFFFF }; unsigned short unac_data305[] = { 0x0049, 0x0069, 0xFFFF, 0x0049, 0x0069, 0xFFFF, 0x004C, 0x006C, 0xFFFF, 0x006C, 0x006C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x004E, 0x006E, 0xFFFF, 0x004E, 0x006F, 0x006E, 0x006F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data306[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0050, 0x0070, 0xFFFF, 0x0051, 0x0071, 0xFFFF, 0x0052, 0x0072, 0xFFFF, 0x0052, 0x0072, 0xFFFF, 0x0052, 0x0072, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data307[] = { 0x0053, 0x004D, 0x0073, 0x006D, 0xFFFF, 0x0054, 0x0045, 0x004C, 0x0074, 0x0065, 0x006C, 0xFFFF, 0x0054, 0x004D, 0x0074, 0x006D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x005A, 0x007A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03A9, 0x03C9, 0x03C9, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data308[] = { 0x005A, 0x007A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x004B, 0x006B, 0x006B, 0x0041, 0x0061, 0x00E5, 0x0042, 0x0062, 0xFFFF, 0x0043, 0x0063, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0065, 0x0065, 0xFFFF }; unsigned short unac_data309[] = { 0x0045, 0x0065, 0xFFFF, 0x0046, 0x0066, 0xFFFF, 0xFFFF, 0x214E, 0x214E, 0x004D, 0x006D, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0x05D0, 0x05D0, 0xFFFF, 0x05D1, 0x05D1, 0xFFFF, 0x05D2, 0x05D2, 0xFFFF }; unsigned short unac_data310[] = { 0x05D3, 0x05D3, 0xFFFF, 0x0069, 0x0069, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0046, 0x0041, 0x0058, 0x0066, 0x0061, 0x0078, 0xFFFF, 0x03C0, 0x03C0, 0xFFFF, 0x03B3, 0x03B3, 0xFFFF, 0x0393, 0x03B3, 0xFFFF, 0x03A0, 0x03C0, 0xFFFF }; unsigned short unac_data311[] = { 0x2211, 0x2211, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0044, 0x0064, 0xFFFF, 0x0064, 0x0064, 0xFFFF, 0x0065, 0x0065, 0xFFFF }; unsigned short unac_data312[] = { 0x0069, 0x0069, 0xFFFF, 0x006A, 0x006A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data313[] = { 0x0031, 0x2044, 0x0037, 0x0031, 0x2044, 0x0037, 0xFFFF, 0x0031, 0x2044, 0x0039, 0x0031, 0x2044, 0x0039, 0xFFFF, 0x0031, 0x2044, 0x0031, 0x0030, 0x0031, 0x2044, 0x0031, 0x0030, 0xFFFF, 0x0031, 0x2044, 0x0033, 0x0031, 0x2044, 0x0033, 0xFFFF, 0x0032, 0x2044, 0x0033, 0x0032, 0x2044, 0x0033, 0xFFFF, 0x0031, 0x2044, 0x0035, 0x0031, 0x2044, 0x0035, 0xFFFF, 0x0032, 0x2044, 0x0035, 0x0032, 0x2044, 0x0035, 0xFFFF, 0x0033, 0x2044, 0x0035, 0x0033, 0x2044, 0x0035, 0xFFFF }; unsigned short unac_data314[] = { 0x0034, 0x2044, 0x0035, 0x0034, 0x2044, 0x0035, 0xFFFF, 0x0031, 0x2044, 0x0036, 0x0031, 0x2044, 0x0036, 0xFFFF, 0x0035, 0x2044, 0x0036, 0x0035, 0x2044, 0x0036, 0xFFFF, 0x0031, 0x2044, 0x0038, 0x0031, 0x2044, 0x0038, 0xFFFF, 0x0033, 0x2044, 0x0038, 0x0033, 0x2044, 0x0038, 0xFFFF, 0x0035, 0x2044, 0x0038, 0x0035, 0x2044, 0x0038, 0xFFFF, 0x0037, 0x2044, 0x0038, 0x0037, 0x2044, 0x0038, 0xFFFF, 0x0031, 0x2044, 0x0031, 0x2044, 0xFFFF }; unsigned short unac_data315[] = { 0x0049, 0x0069, 0x2170, 0x0049, 0x0049, 0x0069, 0x0069, 0x2171, 0x0049, 0x0049, 0x0049, 0x0069, 0x0069, 0x0069, 0x2172, 0x0049, 0x0056, 0x0069, 0x0076, 0x2173, 0x0056, 0x0076, 0x2174, 0x0056, 0x0049, 0x0076, 0x0069, 0x2175, 0x0056, 0x0049, 0x0049, 0x0076, 0x0069, 0x0069, 0x2176, 0x0056, 0x0049, 0x0049, 0x0049, 0x0076, 0x0069, 0x0069, 0x0069, 0x2177 }; unsigned short unac_data316[] = { 0x0049, 0x0058, 0x0069, 0x0078, 0x2178, 0x0058, 0x0078, 0x2179, 0x0058, 0x0049, 0x0078, 0x0069, 0x217A, 0x0058, 0x0049, 0x0049, 0x0078, 0x0069, 0x0069, 0x217B, 0x004C, 0x006C, 0x217C, 0x0043, 0x0063, 0x217D, 0x0044, 0x0064, 0x217E, 0x004D, 0x006D, 0x217F }; unsigned short unac_data317[] = { 0x0069, 0x0069, 0xFFFF, 0x0069, 0x0069, 0x0069, 0x0069, 0xFFFF, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0xFFFF, 0x0069, 0x0076, 0x0069, 0x0076, 0xFFFF, 0x0076, 0x0076, 0xFFFF, 0x0076, 0x0069, 0x0076, 0x0069, 0xFFFF, 0x0076, 0x0069, 0x0069, 0x0076, 0x0069, 0x0069, 0xFFFF, 0x0076, 0x0069, 0x0069, 0x0069, 0x0076, 0x0069, 0x0069, 0x0069, 0xFFFF }; unsigned short unac_data318[] = { 0x0069, 0x0078, 0x0069, 0x0078, 0xFFFF, 0x0078, 0x0078, 0xFFFF, 0x0078, 0x0069, 0x0078, 0x0069, 0xFFFF, 0x0078, 0x0069, 0x0069, 0x0078, 0x0069, 0x0069, 0xFFFF, 0x006C, 0x006C, 0xFFFF, 0x0063, 0x0063, 0xFFFF, 0x0064, 0x0064, 0xFFFF, 0x006D, 0x006D, 0xFFFF }; unsigned short unac_data319[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2184, 0x2184, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data320[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0030, 0x2044, 0x0033, 0x0030, 0x2044, 0x0033, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data321[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2190, 0x2190, 0xFFFF, 0x2192, 0x2192, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data322[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2194, 0x2194, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data323[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x21D0, 0x21D0, 0xFFFF, 0x21D4, 0x21D4, 0xFFFF, 0x21D2, 0x21D2, 0xFFFF }; unsigned short unac_data324[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2203, 0x2203, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data325[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x2208, 0x2208, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x220B, 0x220B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data326[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2223, 0x2223, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2225, 0x2225, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data327[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x222B, 0x222B, 0x222B, 0x222B, 0xFFFF, 0x222B, 0x222B, 0x222B, 0x222B, 0x222B, 0x222B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x222E, 0x222E, 0x222E, 0x222E, 0xFFFF }; unsigned short unac_data328[] = { 0x222E, 0x222E, 0x222E, 0x222E, 0x222E, 0x222E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data329[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x223C, 0x223C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2243, 0x2243, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2245, 0x2245, 0xFFFF }; unsigned short unac_data330[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x2248, 0x2248, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data331[] = { 0x003D, 0x003D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2261, 0x2261, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data332[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x224D, 0x224D, 0xFFFF, 0x003C, 0x003C, 0xFFFF, 0x003E, 0x003E, 0xFFFF }; unsigned short unac_data333[] = { 0x2264, 0x2264, 0xFFFF, 0x2265, 0x2265, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2272, 0x2272, 0xFFFF, 0x2273, 0x2273, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data334[] = { 0x2276, 0x2276, 0xFFFF, 0x2277, 0x2277, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data335[] = { 0x227A, 0x227A, 0xFFFF, 0x227B, 0x227B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2282, 0x2282, 0xFFFF, 0x2283, 0x2283, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data336[] = { 0x2286, 0x2286, 0xFFFF, 0x2287, 0x2287, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data337[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x22A2, 0x22A2, 0xFFFF, 0x22A8, 0x22A8, 0xFFFF, 0x22A9, 0x22A9, 0xFFFF, 0x22AB, 0x22AB, 0xFFFF }; unsigned short unac_data338[] = { 0x227C, 0x227C, 0xFFFF, 0x227D, 0x227D, 0xFFFF, 0x2291, 0x2291, 0xFFFF, 0x2292, 0x2292, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data339[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x22B2, 0x22B2, 0xFFFF, 0x22B3, 0x22B3, 0xFFFF, 0x22B4, 0x22B4, 0xFFFF, 0x22B5, 0x22B5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data340[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x3008, 0x3008, 0xFFFF, 0x3009, 0x3009, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data341[] = { 0x0031, 0x0031, 0xFFFF, 0x0032, 0x0032, 0xFFFF, 0x0033, 0x0033, 0xFFFF, 0x0034, 0x0034, 0xFFFF, 0x0035, 0x0035, 0xFFFF, 0x0036, 0x0036, 0xFFFF, 0x0037, 0x0037, 0xFFFF, 0x0038, 0x0038, 0xFFFF }; unsigned short unac_data342[] = { 0x0039, 0x0039, 0xFFFF, 0x0031, 0x0030, 0x0031, 0x0030, 0xFFFF, 0x0031, 0x0031, 0x0031, 0x0031, 0xFFFF, 0x0031, 0x0032, 0x0031, 0x0032, 0xFFFF, 0x0031, 0x0033, 0x0031, 0x0033, 0xFFFF, 0x0031, 0x0034, 0x0031, 0x0034, 0xFFFF, 0x0031, 0x0035, 0x0031, 0x0035, 0xFFFF, 0x0031, 0x0036, 0x0031, 0x0036, 0xFFFF }; unsigned short unac_data343[] = { 0x0031, 0x0037, 0x0031, 0x0037, 0xFFFF, 0x0031, 0x0038, 0x0031, 0x0038, 0xFFFF, 0x0031, 0x0039, 0x0031, 0x0039, 0xFFFF, 0x0032, 0x0030, 0x0032, 0x0030, 0xFFFF, 0x0028, 0x0031, 0x0029, 0x0028, 0x0031, 0x0029, 0xFFFF, 0x0028, 0x0032, 0x0029, 0x0028, 0x0032, 0x0029, 0xFFFF, 0x0028, 0x0033, 0x0029, 0x0028, 0x0033, 0x0029, 0xFFFF, 0x0028, 0x0034, 0x0029, 0x0028, 0x0034, 0x0029, 0xFFFF }; unsigned short unac_data344[] = { 0x0028, 0x0035, 0x0029, 0x0028, 0x0035, 0x0029, 0xFFFF, 0x0028, 0x0036, 0x0029, 0x0028, 0x0036, 0x0029, 0xFFFF, 0x0028, 0x0037, 0x0029, 0x0028, 0x0037, 0x0029, 0xFFFF, 0x0028, 0x0038, 0x0029, 0x0028, 0x0038, 0x0029, 0xFFFF, 0x0028, 0x0039, 0x0029, 0x0028, 0x0039, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0030, 0x0029, 0x0028, 0x0031, 0x0030, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0031, 0x0029, 0x0028, 0x0031, 0x0031, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0032, 0x0029, 0x0028, 0x0031, 0x0032, 0x0029, 0xFFFF }; unsigned short unac_data345[] = { 0x0028, 0x0031, 0x0033, 0x0029, 0x0028, 0x0031, 0x0033, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0034, 0x0029, 0x0028, 0x0031, 0x0034, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0035, 0x0029, 0x0028, 0x0031, 0x0035, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0036, 0x0029, 0x0028, 0x0031, 0x0036, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0037, 0x0029, 0x0028, 0x0031, 0x0037, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0038, 0x0029, 0x0028, 0x0031, 0x0038, 0x0029, 0xFFFF, 0x0028, 0x0031, 0x0039, 0x0029, 0x0028, 0x0031, 0x0039, 0x0029, 0xFFFF, 0x0028, 0x0032, 0x0030, 0x0029, 0x0028, 0x0032, 0x0030, 0x0029, 0xFFFF }; unsigned short unac_data346[] = { 0x0031, 0x002E, 0x0031, 0x002E, 0xFFFF, 0x0032, 0x002E, 0x0032, 0x002E, 0xFFFF, 0x0033, 0x002E, 0x0033, 0x002E, 0xFFFF, 0x0034, 0x002E, 0x0034, 0x002E, 0xFFFF, 0x0035, 0x002E, 0x0035, 0x002E, 0xFFFF, 0x0036, 0x002E, 0x0036, 0x002E, 0xFFFF, 0x0037, 0x002E, 0x0037, 0x002E, 0xFFFF, 0x0038, 0x002E, 0x0038, 0x002E, 0xFFFF }; unsigned short unac_data347[] = { 0x0039, 0x002E, 0x0039, 0x002E, 0xFFFF, 0x0031, 0x0030, 0x002E, 0x0031, 0x0030, 0x002E, 0xFFFF, 0x0031, 0x0031, 0x002E, 0x0031, 0x0031, 0x002E, 0xFFFF, 0x0031, 0x0032, 0x002E, 0x0031, 0x0032, 0x002E, 0xFFFF, 0x0031, 0x0033, 0x002E, 0x0031, 0x0033, 0x002E, 0xFFFF, 0x0031, 0x0034, 0x002E, 0x0031, 0x0034, 0x002E, 0xFFFF, 0x0031, 0x0035, 0x002E, 0x0031, 0x0035, 0x002E, 0xFFFF, 0x0031, 0x0036, 0x002E, 0x0031, 0x0036, 0x002E, 0xFFFF }; unsigned short unac_data348[] = { 0x0031, 0x0037, 0x002E, 0x0031, 0x0037, 0x002E, 0xFFFF, 0x0031, 0x0038, 0x002E, 0x0031, 0x0038, 0x002E, 0xFFFF, 0x0031, 0x0039, 0x002E, 0x0031, 0x0039, 0x002E, 0xFFFF, 0x0032, 0x0030, 0x002E, 0x0032, 0x0030, 0x002E, 0xFFFF, 0x0028, 0x0061, 0x0029, 0x0028, 0x0061, 0x0029, 0xFFFF, 0x0028, 0x0062, 0x0029, 0x0028, 0x0062, 0x0029, 0xFFFF, 0x0028, 0x0063, 0x0029, 0x0028, 0x0063, 0x0029, 0xFFFF, 0x0028, 0x0064, 0x0029, 0x0028, 0x0064, 0x0029, 0xFFFF }; unsigned short unac_data349[] = { 0x0028, 0x0065, 0x0029, 0x0028, 0x0065, 0x0029, 0xFFFF, 0x0028, 0x0066, 0x0029, 0x0028, 0x0066, 0x0029, 0xFFFF, 0x0028, 0x0067, 0x0029, 0x0028, 0x0067, 0x0029, 0xFFFF, 0x0028, 0x0068, 0x0029, 0x0028, 0x0068, 0x0029, 0xFFFF, 0x0028, 0x0069, 0x0029, 0x0028, 0x0069, 0x0029, 0xFFFF, 0x0028, 0x006A, 0x0029, 0x0028, 0x006A, 0x0029, 0xFFFF, 0x0028, 0x006B, 0x0029, 0x0028, 0x006B, 0x0029, 0xFFFF, 0x0028, 0x006C, 0x0029, 0x0028, 0x006C, 0x0029, 0xFFFF }; unsigned short unac_data350[] = { 0x0028, 0x006D, 0x0029, 0x0028, 0x006D, 0x0029, 0xFFFF, 0x0028, 0x006E, 0x0029, 0x0028, 0x006E, 0x0029, 0xFFFF, 0x0028, 0x006F, 0x0029, 0x0028, 0x006F, 0x0029, 0xFFFF, 0x0028, 0x0070, 0x0029, 0x0028, 0x0070, 0x0029, 0xFFFF, 0x0028, 0x0071, 0x0029, 0x0028, 0x0071, 0x0029, 0xFFFF, 0x0028, 0x0072, 0x0029, 0x0028, 0x0072, 0x0029, 0xFFFF, 0x0028, 0x0073, 0x0029, 0x0028, 0x0073, 0x0029, 0xFFFF, 0x0028, 0x0074, 0x0029, 0x0028, 0x0074, 0x0029, 0xFFFF }; unsigned short unac_data351[] = { 0x0028, 0x0075, 0x0029, 0x0028, 0x0075, 0x0029, 0xFFFF, 0x0028, 0x0076, 0x0029, 0x0028, 0x0076, 0x0029, 0xFFFF, 0x0028, 0x0077, 0x0029, 0x0028, 0x0077, 0x0029, 0xFFFF, 0x0028, 0x0078, 0x0029, 0x0028, 0x0078, 0x0029, 0xFFFF, 0x0028, 0x0079, 0x0029, 0x0028, 0x0079, 0x0029, 0xFFFF, 0x0028, 0x007A, 0x0029, 0x0028, 0x007A, 0x0029, 0xFFFF, 0x0041, 0x0061, 0x24D0, 0x0042, 0x0062, 0x24D1 }; unsigned short unac_data352[] = { 0x0043, 0x0063, 0x24D2, 0x0044, 0x0064, 0x24D3, 0x0045, 0x0065, 0x24D4, 0x0046, 0x0066, 0x24D5, 0x0047, 0x0067, 0x24D6, 0x0048, 0x0068, 0x24D7, 0x0049, 0x0069, 0x24D8, 0x004A, 0x006A, 0x24D9 }; unsigned short unac_data353[] = { 0x004B, 0x006B, 0x24DA, 0x004C, 0x006C, 0x24DB, 0x004D, 0x006D, 0x24DC, 0x004E, 0x006E, 0x24DD, 0x004F, 0x006F, 0x24DE, 0x0050, 0x0070, 0x24DF, 0x0051, 0x0071, 0x24E0, 0x0052, 0x0072, 0x24E1 }; unsigned short unac_data354[] = { 0x0053, 0x0073, 0x24E2, 0x0054, 0x0074, 0x24E3, 0x0055, 0x0075, 0x24E4, 0x0056, 0x0076, 0x24E5, 0x0057, 0x0077, 0x24E6, 0x0058, 0x0078, 0x24E7, 0x0059, 0x0079, 0x24E8, 0x005A, 0x007A, 0x24E9 }; unsigned short unac_data355[] = { 0x0061, 0x0061, 0xFFFF, 0x0062, 0x0062, 0xFFFF, 0x0063, 0x0063, 0xFFFF, 0x0064, 0x0064, 0xFFFF, 0x0065, 0x0065, 0xFFFF, 0x0066, 0x0066, 0xFFFF, 0x0067, 0x0067, 0xFFFF, 0x0068, 0x0068, 0xFFFF }; unsigned short unac_data356[] = { 0x0069, 0x0069, 0xFFFF, 0x006A, 0x006A, 0xFFFF, 0x006B, 0x006B, 0xFFFF, 0x006C, 0x006C, 0xFFFF, 0x006D, 0x006D, 0xFFFF, 0x006E, 0x006E, 0xFFFF, 0x006F, 0x006F, 0xFFFF, 0x0070, 0x0070, 0xFFFF }; unsigned short unac_data357[] = { 0x0071, 0x0071, 0xFFFF, 0x0072, 0x0072, 0xFFFF, 0x0073, 0x0073, 0xFFFF, 0x0074, 0x0074, 0xFFFF, 0x0075, 0x0075, 0xFFFF, 0x0076, 0x0076, 0xFFFF, 0x0077, 0x0077, 0xFFFF, 0x0078, 0x0078, 0xFFFF }; unsigned short unac_data358[] = { 0x0079, 0x0079, 0xFFFF, 0x007A, 0x007A, 0xFFFF, 0x0030, 0x0030, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data359[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x222B, 0x222B, 0x222B, 0x222B, 0x222B, 0x222B, 0x222B, 0x222B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data360[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x003A, 0x003A, 0x003D, 0x003A, 0x003A, 0x003D, 0xFFFF, 0x003D, 0x003D, 0x003D, 0x003D, 0xFFFF, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data361[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2ADD, 0x2ADD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data362[] = { 0xFFFF, 0x2C30, 0x2C30, 0xFFFF, 0x2C31, 0x2C31, 0xFFFF, 0x2C32, 0x2C32, 0xFFFF, 0x2C33, 0x2C33, 0xFFFF, 0x2C34, 0x2C34, 0xFFFF, 0x2C35, 0x2C35, 0xFFFF, 0x2C36, 0x2C36, 0xFFFF, 0x2C37, 0x2C37 }; unsigned short unac_data363[] = { 0xFFFF, 0x2C38, 0x2C38, 0xFFFF, 0x2C39, 0x2C39, 0xFFFF, 0x2C3A, 0x2C3A, 0xFFFF, 0x2C3B, 0x2C3B, 0xFFFF, 0x2C3C, 0x2C3C, 0xFFFF, 0x2C3D, 0x2C3D, 0xFFFF, 0x2C3E, 0x2C3E, 0xFFFF, 0x2C3F, 0x2C3F }; unsigned short unac_data364[] = { 0xFFFF, 0x2C40, 0x2C40, 0xFFFF, 0x2C41, 0x2C41, 0xFFFF, 0x2C42, 0x2C42, 0xFFFF, 0x2C43, 0x2C43, 0xFFFF, 0x2C44, 0x2C44, 0xFFFF, 0x2C45, 0x2C45, 0xFFFF, 0x2C46, 0x2C46, 0xFFFF, 0x2C47, 0x2C47 }; unsigned short unac_data365[] = { 0xFFFF, 0x2C48, 0x2C48, 0xFFFF, 0x2C49, 0x2C49, 0xFFFF, 0x2C4A, 0x2C4A, 0xFFFF, 0x2C4B, 0x2C4B, 0xFFFF, 0x2C4C, 0x2C4C, 0xFFFF, 0x2C4D, 0x2C4D, 0xFFFF, 0x2C4E, 0x2C4E, 0xFFFF, 0x2C4F, 0x2C4F }; unsigned short unac_data366[] = { 0xFFFF, 0x2C50, 0x2C50, 0xFFFF, 0x2C51, 0x2C51, 0xFFFF, 0x2C52, 0x2C52, 0xFFFF, 0x2C53, 0x2C53, 0xFFFF, 0x2C54, 0x2C54, 0xFFFF, 0x2C55, 0x2C55, 0xFFFF, 0x2C56, 0x2C56, 0xFFFF, 0x2C57, 0x2C57 }; unsigned short unac_data367[] = { 0xFFFF, 0x2C58, 0x2C58, 0xFFFF, 0x2C59, 0x2C59, 0xFFFF, 0x2C5A, 0x2C5A, 0xFFFF, 0x2C5B, 0x2C5B, 0xFFFF, 0x2C5C, 0x2C5C, 0xFFFF, 0x2C5D, 0x2C5D, 0xFFFF, 0x2C5E, 0x2C5E, 0xFFFF, 0x2C5F, 0x2C5F }; unsigned short unac_data368[] = { 0xFFFF, 0x2C61, 0x2C61, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x026B, 0x026B, 0xFFFF, 0x1D7D, 0x1D7D, 0xFFFF, 0x027D, 0x027D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C68, 0x2C68 }; unsigned short unac_data369[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C6A, 0x2C6A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C6C, 0x2C6C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0251, 0x0251, 0xFFFF, 0x0271, 0x0271, 0xFFFF, 0x0250, 0x0250 }; unsigned short unac_data370[] = { 0xFFFF, 0x0252, 0x0252, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C73, 0x2C73, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C76, 0x2C76, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data371[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x006A, 0x006A, 0xFFFF, 0x0056, 0x0076, 0xFFFF, 0xFFFF, 0x023F, 0x023F, 0xFFFF, 0x0240, 0x0240 }; unsigned short unac_data372[] = { 0xFFFF, 0x2C81, 0x2C81, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C83, 0x2C83, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C85, 0x2C85, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C87, 0x2C87, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data373[] = { 0xFFFF, 0x2C89, 0x2C89, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C8B, 0x2C8B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C8D, 0x2C8D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C8F, 0x2C8F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data374[] = { 0xFFFF, 0x2C91, 0x2C91, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C93, 0x2C93, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C95, 0x2C95, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C97, 0x2C97, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data375[] = { 0xFFFF, 0x2C99, 0x2C99, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C9B, 0x2C9B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C9D, 0x2C9D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2C9F, 0x2C9F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data376[] = { 0xFFFF, 0x2CA1, 0x2CA1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CA3, 0x2CA3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CA5, 0x2CA5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CA7, 0x2CA7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data377[] = { 0xFFFF, 0x2CA9, 0x2CA9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CAB, 0x2CAB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CAD, 0x2CAD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CAF, 0x2CAF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data378[] = { 0xFFFF, 0x2CB1, 0x2CB1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CB3, 0x2CB3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CB5, 0x2CB5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CB7, 0x2CB7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data379[] = { 0xFFFF, 0x2CB9, 0x2CB9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CBB, 0x2CBB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CBD, 0x2CBD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CBF, 0x2CBF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data380[] = { 0xFFFF, 0x2CC1, 0x2CC1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CC3, 0x2CC3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CC5, 0x2CC5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CC7, 0x2CC7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data381[] = { 0xFFFF, 0x2CC9, 0x2CC9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CCB, 0x2CCB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CCD, 0x2CCD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CCF, 0x2CCF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data382[] = { 0xFFFF, 0x2CD1, 0x2CD1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CD3, 0x2CD3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CD5, 0x2CD5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CD7, 0x2CD7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data383[] = { 0xFFFF, 0x2CD9, 0x2CD9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CDB, 0x2CDB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CDD, 0x2CDD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CDF, 0x2CDF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data384[] = { 0xFFFF, 0x2CE1, 0x2CE1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CE3, 0x2CE3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data385[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CEC, 0x2CEC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2CEE, 0x2CEE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data386[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0x2CF3, 0x2CF3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data387[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2D61, 0x2D61, 0xFFFF }; unsigned short unac_data388[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6BCD, 0x6BCD, 0xFFFF }; unsigned short unac_data389[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x9F9F, 0x9F9F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data390[] = { 0x4E00, 0x4E00, 0xFFFF, 0x4E28, 0x4E28, 0xFFFF, 0x4E36, 0x4E36, 0xFFFF, 0x4E3F, 0x4E3F, 0xFFFF, 0x4E59, 0x4E59, 0xFFFF, 0x4E85, 0x4E85, 0xFFFF, 0x4E8C, 0x4E8C, 0xFFFF, 0x4EA0, 0x4EA0, 0xFFFF }; unsigned short unac_data391[] = { 0x4EBA, 0x4EBA, 0xFFFF, 0x513F, 0x513F, 0xFFFF, 0x5165, 0x5165, 0xFFFF, 0x516B, 0x516B, 0xFFFF, 0x5182, 0x5182, 0xFFFF, 0x5196, 0x5196, 0xFFFF, 0x51AB, 0x51AB, 0xFFFF, 0x51E0, 0x51E0, 0xFFFF }; unsigned short unac_data392[] = { 0x51F5, 0x51F5, 0xFFFF, 0x5200, 0x5200, 0xFFFF, 0x529B, 0x529B, 0xFFFF, 0x52F9, 0x52F9, 0xFFFF, 0x5315, 0x5315, 0xFFFF, 0x531A, 0x531A, 0xFFFF, 0x5338, 0x5338, 0xFFFF, 0x5341, 0x5341, 0xFFFF }; unsigned short unac_data393[] = { 0x535C, 0x535C, 0xFFFF, 0x5369, 0x5369, 0xFFFF, 0x5382, 0x5382, 0xFFFF, 0x53B6, 0x53B6, 0xFFFF, 0x53C8, 0x53C8, 0xFFFF, 0x53E3, 0x53E3, 0xFFFF, 0x56D7, 0x56D7, 0xFFFF, 0x571F, 0x571F, 0xFFFF }; unsigned short unac_data394[] = { 0x58EB, 0x58EB, 0xFFFF, 0x5902, 0x5902, 0xFFFF, 0x590A, 0x590A, 0xFFFF, 0x5915, 0x5915, 0xFFFF, 0x5927, 0x5927, 0xFFFF, 0x5973, 0x5973, 0xFFFF, 0x5B50, 0x5B50, 0xFFFF, 0x5B80, 0x5B80, 0xFFFF }; unsigned short unac_data395[] = { 0x5BF8, 0x5BF8, 0xFFFF, 0x5C0F, 0x5C0F, 0xFFFF, 0x5C22, 0x5C22, 0xFFFF, 0x5C38, 0x5C38, 0xFFFF, 0x5C6E, 0x5C6E, 0xFFFF, 0x5C71, 0x5C71, 0xFFFF, 0x5DDB, 0x5DDB, 0xFFFF, 0x5DE5, 0x5DE5, 0xFFFF }; unsigned short unac_data396[] = { 0x5DF1, 0x5DF1, 0xFFFF, 0x5DFE, 0x5DFE, 0xFFFF, 0x5E72, 0x5E72, 0xFFFF, 0x5E7A, 0x5E7A, 0xFFFF, 0x5E7F, 0x5E7F, 0xFFFF, 0x5EF4, 0x5EF4, 0xFFFF, 0x5EFE, 0x5EFE, 0xFFFF, 0x5F0B, 0x5F0B, 0xFFFF }; unsigned short unac_data397[] = { 0x5F13, 0x5F13, 0xFFFF, 0x5F50, 0x5F50, 0xFFFF, 0x5F61, 0x5F61, 0xFFFF, 0x5F73, 0x5F73, 0xFFFF, 0x5FC3, 0x5FC3, 0xFFFF, 0x6208, 0x6208, 0xFFFF, 0x6236, 0x6236, 0xFFFF, 0x624B, 0x624B, 0xFFFF }; unsigned short unac_data398[] = { 0x652F, 0x652F, 0xFFFF, 0x6534, 0x6534, 0xFFFF, 0x6587, 0x6587, 0xFFFF, 0x6597, 0x6597, 0xFFFF, 0x65A4, 0x65A4, 0xFFFF, 0x65B9, 0x65B9, 0xFFFF, 0x65E0, 0x65E0, 0xFFFF, 0x65E5, 0x65E5, 0xFFFF }; unsigned short unac_data399[] = { 0x66F0, 0x66F0, 0xFFFF, 0x6708, 0x6708, 0xFFFF, 0x6728, 0x6728, 0xFFFF, 0x6B20, 0x6B20, 0xFFFF, 0x6B62, 0x6B62, 0xFFFF, 0x6B79, 0x6B79, 0xFFFF, 0x6BB3, 0x6BB3, 0xFFFF, 0x6BCB, 0x6BCB, 0xFFFF }; unsigned short unac_data400[] = { 0x6BD4, 0x6BD4, 0xFFFF, 0x6BDB, 0x6BDB, 0xFFFF, 0x6C0F, 0x6C0F, 0xFFFF, 0x6C14, 0x6C14, 0xFFFF, 0x6C34, 0x6C34, 0xFFFF, 0x706B, 0x706B, 0xFFFF, 0x722A, 0x722A, 0xFFFF, 0x7236, 0x7236, 0xFFFF }; unsigned short unac_data401[] = { 0x723B, 0x723B, 0xFFFF, 0x723F, 0x723F, 0xFFFF, 0x7247, 0x7247, 0xFFFF, 0x7259, 0x7259, 0xFFFF, 0x725B, 0x725B, 0xFFFF, 0x72AC, 0x72AC, 0xFFFF, 0x7384, 0x7384, 0xFFFF, 0x7389, 0x7389, 0xFFFF }; unsigned short unac_data402[] = { 0x74DC, 0x74DC, 0xFFFF, 0x74E6, 0x74E6, 0xFFFF, 0x7518, 0x7518, 0xFFFF, 0x751F, 0x751F, 0xFFFF, 0x7528, 0x7528, 0xFFFF, 0x7530, 0x7530, 0xFFFF, 0x758B, 0x758B, 0xFFFF, 0x7592, 0x7592, 0xFFFF }; unsigned short unac_data403[] = { 0x7676, 0x7676, 0xFFFF, 0x767D, 0x767D, 0xFFFF, 0x76AE, 0x76AE, 0xFFFF, 0x76BF, 0x76BF, 0xFFFF, 0x76EE, 0x76EE, 0xFFFF, 0x77DB, 0x77DB, 0xFFFF, 0x77E2, 0x77E2, 0xFFFF, 0x77F3, 0x77F3, 0xFFFF }; unsigned short unac_data404[] = { 0x793A, 0x793A, 0xFFFF, 0x79B8, 0x79B8, 0xFFFF, 0x79BE, 0x79BE, 0xFFFF, 0x7A74, 0x7A74, 0xFFFF, 0x7ACB, 0x7ACB, 0xFFFF, 0x7AF9, 0x7AF9, 0xFFFF, 0x7C73, 0x7C73, 0xFFFF, 0x7CF8, 0x7CF8, 0xFFFF }; unsigned short unac_data405[] = { 0x7F36, 0x7F36, 0xFFFF, 0x7F51, 0x7F51, 0xFFFF, 0x7F8A, 0x7F8A, 0xFFFF, 0x7FBD, 0x7FBD, 0xFFFF, 0x8001, 0x8001, 0xFFFF, 0x800C, 0x800C, 0xFFFF, 0x8012, 0x8012, 0xFFFF, 0x8033, 0x8033, 0xFFFF }; unsigned short unac_data406[] = { 0x807F, 0x807F, 0xFFFF, 0x8089, 0x8089, 0xFFFF, 0x81E3, 0x81E3, 0xFFFF, 0x81EA, 0x81EA, 0xFFFF, 0x81F3, 0x81F3, 0xFFFF, 0x81FC, 0x81FC, 0xFFFF, 0x820C, 0x820C, 0xFFFF, 0x821B, 0x821B, 0xFFFF }; unsigned short unac_data407[] = { 0x821F, 0x821F, 0xFFFF, 0x826E, 0x826E, 0xFFFF, 0x8272, 0x8272, 0xFFFF, 0x8278, 0x8278, 0xFFFF, 0x864D, 0x864D, 0xFFFF, 0x866B, 0x866B, 0xFFFF, 0x8840, 0x8840, 0xFFFF, 0x884C, 0x884C, 0xFFFF }; unsigned short unac_data408[] = { 0x8863, 0x8863, 0xFFFF, 0x897E, 0x897E, 0xFFFF, 0x898B, 0x898B, 0xFFFF, 0x89D2, 0x89D2, 0xFFFF, 0x8A00, 0x8A00, 0xFFFF, 0x8C37, 0x8C37, 0xFFFF, 0x8C46, 0x8C46, 0xFFFF, 0x8C55, 0x8C55, 0xFFFF }; unsigned short unac_data409[] = { 0x8C78, 0x8C78, 0xFFFF, 0x8C9D, 0x8C9D, 0xFFFF, 0x8D64, 0x8D64, 0xFFFF, 0x8D70, 0x8D70, 0xFFFF, 0x8DB3, 0x8DB3, 0xFFFF, 0x8EAB, 0x8EAB, 0xFFFF, 0x8ECA, 0x8ECA, 0xFFFF, 0x8F9B, 0x8F9B, 0xFFFF }; unsigned short unac_data410[] = { 0x8FB0, 0x8FB0, 0xFFFF, 0x8FB5, 0x8FB5, 0xFFFF, 0x9091, 0x9091, 0xFFFF, 0x9149, 0x9149, 0xFFFF, 0x91C6, 0x91C6, 0xFFFF, 0x91CC, 0x91CC, 0xFFFF, 0x91D1, 0x91D1, 0xFFFF, 0x9577, 0x9577, 0xFFFF }; unsigned short unac_data411[] = { 0x9580, 0x9580, 0xFFFF, 0x961C, 0x961C, 0xFFFF, 0x96B6, 0x96B6, 0xFFFF, 0x96B9, 0x96B9, 0xFFFF, 0x96E8, 0x96E8, 0xFFFF, 0x9751, 0x9751, 0xFFFF, 0x975E, 0x975E, 0xFFFF, 0x9762, 0x9762, 0xFFFF }; unsigned short unac_data412[] = { 0x9769, 0x9769, 0xFFFF, 0x97CB, 0x97CB, 0xFFFF, 0x97ED, 0x97ED, 0xFFFF, 0x97F3, 0x97F3, 0xFFFF, 0x9801, 0x9801, 0xFFFF, 0x98A8, 0x98A8, 0xFFFF, 0x98DB, 0x98DB, 0xFFFF, 0x98DF, 0x98DF, 0xFFFF }; unsigned short unac_data413[] = { 0x9996, 0x9996, 0xFFFF, 0x9999, 0x9999, 0xFFFF, 0x99AC, 0x99AC, 0xFFFF, 0x9AA8, 0x9AA8, 0xFFFF, 0x9AD8, 0x9AD8, 0xFFFF, 0x9ADF, 0x9ADF, 0xFFFF, 0x9B25, 0x9B25, 0xFFFF, 0x9B2F, 0x9B2F, 0xFFFF }; unsigned short unac_data414[] = { 0x9B32, 0x9B32, 0xFFFF, 0x9B3C, 0x9B3C, 0xFFFF, 0x9B5A, 0x9B5A, 0xFFFF, 0x9CE5, 0x9CE5, 0xFFFF, 0x9E75, 0x9E75, 0xFFFF, 0x9E7F, 0x9E7F, 0xFFFF, 0x9EA5, 0x9EA5, 0xFFFF, 0x9EBB, 0x9EBB, 0xFFFF }; unsigned short unac_data415[] = { 0x9EC3, 0x9EC3, 0xFFFF, 0x9ECD, 0x9ECD, 0xFFFF, 0x9ED1, 0x9ED1, 0xFFFF, 0x9EF9, 0x9EF9, 0xFFFF, 0x9EFD, 0x9EFD, 0xFFFF, 0x9F0E, 0x9F0E, 0xFFFF, 0x9F13, 0x9F13, 0xFFFF, 0x9F20, 0x9F20, 0xFFFF }; unsigned short unac_data416[] = { 0x9F3B, 0x9F3B, 0xFFFF, 0x9F4A, 0x9F4A, 0xFFFF, 0x9F52, 0x9F52, 0xFFFF, 0x9F8D, 0x9F8D, 0xFFFF, 0x9F9C, 0x9F9C, 0xFFFF, 0x9FA0, 0x9FA0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data417[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3012, 0x3012, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data418[] = { 0x5341, 0x5341, 0xFFFF, 0x5344, 0x5344, 0xFFFF, 0x5345, 0x5345, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data419[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x1100, 0x1100, 0xFFFF, 0x1101, 0x1101, 0xFFFF, 0x11AA, 0x11AA, 0xFFFF, 0x1102, 0x1102, 0xFFFF, 0x11AC, 0x11AC, 0xFFFF, 0x11AD, 0x11AD, 0xFFFF, 0x1103, 0x1103, 0xFFFF }; unsigned short unac_data420[] = { 0x1104, 0x1104, 0xFFFF, 0x1105, 0x1105, 0xFFFF, 0x11B0, 0x11B0, 0xFFFF, 0x11B1, 0x11B1, 0xFFFF, 0x11B2, 0x11B2, 0xFFFF, 0x11B3, 0x11B3, 0xFFFF, 0x11B4, 0x11B4, 0xFFFF, 0x11B5, 0x11B5, 0xFFFF }; unsigned short unac_data421[] = { 0x111A, 0x111A, 0xFFFF, 0x1106, 0x1106, 0xFFFF, 0x1107, 0x1107, 0xFFFF, 0x1108, 0x1108, 0xFFFF, 0x1121, 0x1121, 0xFFFF, 0x1109, 0x1109, 0xFFFF, 0x110A, 0x110A, 0xFFFF, 0x110B, 0x110B, 0xFFFF }; unsigned short unac_data422[] = { 0x110C, 0x110C, 0xFFFF, 0x110D, 0x110D, 0xFFFF, 0x110E, 0x110E, 0xFFFF, 0x110F, 0x110F, 0xFFFF, 0x1110, 0x1110, 0xFFFF, 0x1111, 0x1111, 0xFFFF, 0x1112, 0x1112, 0xFFFF, 0x1161, 0x1161, 0xFFFF }; unsigned short unac_data423[] = { 0x1162, 0x1162, 0xFFFF, 0x1163, 0x1163, 0xFFFF, 0x1164, 0x1164, 0xFFFF, 0x1165, 0x1165, 0xFFFF, 0x1166, 0x1166, 0xFFFF, 0x1167, 0x1167, 0xFFFF, 0x1168, 0x1168, 0xFFFF, 0x1169, 0x1169, 0xFFFF }; unsigned short unac_data424[] = { 0x116A, 0x116A, 0xFFFF, 0x116B, 0x116B, 0xFFFF, 0x116C, 0x116C, 0xFFFF, 0x116D, 0x116D, 0xFFFF, 0x116E, 0x116E, 0xFFFF, 0x116F, 0x116F, 0xFFFF, 0x1170, 0x1170, 0xFFFF, 0x1171, 0x1171, 0xFFFF }; unsigned short unac_data425[] = { 0x1172, 0x1172, 0xFFFF, 0x1173, 0x1173, 0xFFFF, 0x1174, 0x1174, 0xFFFF, 0x1175, 0x1175, 0xFFFF, 0x1160, 0x1160, 0xFFFF, 0x1114, 0x1114, 0xFFFF, 0x1115, 0x1115, 0xFFFF, 0x11C7, 0x11C7, 0xFFFF }; unsigned short unac_data426[] = { 0x11C8, 0x11C8, 0xFFFF, 0x11CC, 0x11CC, 0xFFFF, 0x11CE, 0x11CE, 0xFFFF, 0x11D3, 0x11D3, 0xFFFF, 0x11D7, 0x11D7, 0xFFFF, 0x11D9, 0x11D9, 0xFFFF, 0x111C, 0x111C, 0xFFFF, 0x11DD, 0x11DD, 0xFFFF }; unsigned short unac_data427[] = { 0x11DF, 0x11DF, 0xFFFF, 0x111D, 0x111D, 0xFFFF, 0x111E, 0x111E, 0xFFFF, 0x1120, 0x1120, 0xFFFF, 0x1122, 0x1122, 0xFFFF, 0x1123, 0x1123, 0xFFFF, 0x1127, 0x1127, 0xFFFF, 0x1129, 0x1129, 0xFFFF }; unsigned short unac_data428[] = { 0x112B, 0x112B, 0xFFFF, 0x112C, 0x112C, 0xFFFF, 0x112D, 0x112D, 0xFFFF, 0x112E, 0x112E, 0xFFFF, 0x112F, 0x112F, 0xFFFF, 0x1132, 0x1132, 0xFFFF, 0x1136, 0x1136, 0xFFFF, 0x1140, 0x1140, 0xFFFF }; unsigned short unac_data429[] = { 0x1147, 0x1147, 0xFFFF, 0x114C, 0x114C, 0xFFFF, 0x11F1, 0x11F1, 0xFFFF, 0x11F2, 0x11F2, 0xFFFF, 0x1157, 0x1157, 0xFFFF, 0x1158, 0x1158, 0xFFFF, 0x1159, 0x1159, 0xFFFF, 0x1184, 0x1184, 0xFFFF }; unsigned short unac_data430[] = { 0x1185, 0x1185, 0xFFFF, 0x1188, 0x1188, 0xFFFF, 0x1191, 0x1191, 0xFFFF, 0x1192, 0x1192, 0xFFFF, 0x1194, 0x1194, 0xFFFF, 0x119E, 0x119E, 0xFFFF, 0x11A1, 0x11A1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data431[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x4E00, 0x4E00, 0xFFFF, 0x4E8C, 0x4E8C, 0xFFFF, 0x4E09, 0x4E09, 0xFFFF, 0x56DB, 0x56DB, 0xFFFF, 0x4E0A, 0x4E0A, 0xFFFF, 0x4E2D, 0x4E2D, 0xFFFF }; unsigned short unac_data432[] = { 0x4E0B, 0x4E0B, 0xFFFF, 0x7532, 0x7532, 0xFFFF, 0x4E59, 0x4E59, 0xFFFF, 0x4E19, 0x4E19, 0xFFFF, 0x4E01, 0x4E01, 0xFFFF, 0x5929, 0x5929, 0xFFFF, 0x5730, 0x5730, 0xFFFF, 0x4EBA, 0x4EBA, 0xFFFF }; unsigned short unac_data433[] = { 0x0028, 0x1100, 0x0029, 0x0028, 0x1100, 0x0029, 0xFFFF, 0x0028, 0x1102, 0x0029, 0x0028, 0x1102, 0x0029, 0xFFFF, 0x0028, 0x1103, 0x0029, 0x0028, 0x1103, 0x0029, 0xFFFF, 0x0028, 0x1105, 0x0029, 0x0028, 0x1105, 0x0029, 0xFFFF, 0x0028, 0x1106, 0x0029, 0x0028, 0x1106, 0x0029, 0xFFFF, 0x0028, 0x1107, 0x0029, 0x0028, 0x1107, 0x0029, 0xFFFF, 0x0028, 0x1109, 0x0029, 0x0028, 0x1109, 0x0029, 0xFFFF, 0x0028, 0x110B, 0x0029, 0x0028, 0x110B, 0x0029, 0xFFFF }; unsigned short unac_data434[] = { 0x0028, 0x110C, 0x0029, 0x0028, 0x110C, 0x0029, 0xFFFF, 0x0028, 0x110E, 0x0029, 0x0028, 0x110E, 0x0029, 0xFFFF, 0x0028, 0x110F, 0x0029, 0x0028, 0x110F, 0x0029, 0xFFFF, 0x0028, 0x1110, 0x0029, 0x0028, 0x1110, 0x0029, 0xFFFF, 0x0028, 0x1111, 0x0029, 0x0028, 0x1111, 0x0029, 0xFFFF, 0x0028, 0x1112, 0x0029, 0x0028, 0x1112, 0x0029, 0xFFFF, 0x0028, 0x1100, 0x1161, 0x0029, 0x0028, 0x1100, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x1102, 0x1161, 0x0029, 0x0028, 0x1102, 0x1161, 0x0029, 0xFFFF }; unsigned short unac_data435[] = { 0x0028, 0x1103, 0x1161, 0x0029, 0x0028, 0x1103, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x1105, 0x1161, 0x0029, 0x0028, 0x1105, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x1106, 0x1161, 0x0029, 0x0028, 0x1106, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x1107, 0x1161, 0x0029, 0x0028, 0x1107, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x1109, 0x1161, 0x0029, 0x0028, 0x1109, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x110B, 0x1161, 0x0029, 0x0028, 0x110B, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x110C, 0x1161, 0x0029, 0x0028, 0x110C, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x110E, 0x1161, 0x0029, 0x0028, 0x110E, 0x1161, 0x0029, 0xFFFF }; unsigned short unac_data436[] = { 0x0028, 0x110F, 0x1161, 0x0029, 0x0028, 0x110F, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x1110, 0x1161, 0x0029, 0x0028, 0x1110, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x1111, 0x1161, 0x0029, 0x0028, 0x1111, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x1112, 0x1161, 0x0029, 0x0028, 0x1112, 0x1161, 0x0029, 0xFFFF, 0x0028, 0x110C, 0x116E, 0x0029, 0x0028, 0x110C, 0x116E, 0x0029, 0xFFFF, 0x0028, 0x110B, 0x1169, 0x110C, 0x1165, 0x11AB, 0x0029, 0x0028, 0x110B, 0x1169, 0x110C, 0x1165, 0x11AB, 0x0029, 0xFFFF, 0x0028, 0x110B, 0x1169, 0x1112, 0x116E, 0x0029, 0x0028, 0x110B, 0x1169, 0x1112, 0x116E, 0x0029, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data437[] = { 0x0028, 0x4E00, 0x0029, 0x0028, 0x4E00, 0x0029, 0xFFFF, 0x0028, 0x4E8C, 0x0029, 0x0028, 0x4E8C, 0x0029, 0xFFFF, 0x0028, 0x4E09, 0x0029, 0x0028, 0x4E09, 0x0029, 0xFFFF, 0x0028, 0x56DB, 0x0029, 0x0028, 0x56DB, 0x0029, 0xFFFF, 0x0028, 0x4E94, 0x0029, 0x0028, 0x4E94, 0x0029, 0xFFFF, 0x0028, 0x516D, 0x0029, 0x0028, 0x516D, 0x0029, 0xFFFF, 0x0028, 0x4E03, 0x0029, 0x0028, 0x4E03, 0x0029, 0xFFFF, 0x0028, 0x516B, 0x0029, 0x0028, 0x516B, 0x0029, 0xFFFF }; unsigned short unac_data438[] = { 0x0028, 0x4E5D, 0x0029, 0x0028, 0x4E5D, 0x0029, 0xFFFF, 0x0028, 0x5341, 0x0029, 0x0028, 0x5341, 0x0029, 0xFFFF, 0x0028, 0x6708, 0x0029, 0x0028, 0x6708, 0x0029, 0xFFFF, 0x0028, 0x706B, 0x0029, 0x0028, 0x706B, 0x0029, 0xFFFF, 0x0028, 0x6C34, 0x0029, 0x0028, 0x6C34, 0x0029, 0xFFFF, 0x0028, 0x6728, 0x0029, 0x0028, 0x6728, 0x0029, 0xFFFF, 0x0028, 0x91D1, 0x0029, 0x0028, 0x91D1, 0x0029, 0xFFFF, 0x0028, 0x571F, 0x0029, 0x0028, 0x571F, 0x0029, 0xFFFF }; unsigned short unac_data439[] = { 0x0028, 0x65E5, 0x0029, 0x0028, 0x65E5, 0x0029, 0xFFFF, 0x0028, 0x682A, 0x0029, 0x0028, 0x682A, 0x0029, 0xFFFF, 0x0028, 0x6709, 0x0029, 0x0028, 0x6709, 0x0029, 0xFFFF, 0x0028, 0x793E, 0x0029, 0x0028, 0x793E, 0x0029, 0xFFFF, 0x0028, 0x540D, 0x0029, 0x0028, 0x540D, 0x0029, 0xFFFF, 0x0028, 0x7279, 0x0029, 0x0028, 0x7279, 0x0029, 0xFFFF, 0x0028, 0x8CA1, 0x0029, 0x0028, 0x8CA1, 0x0029, 0xFFFF, 0x0028, 0x795D, 0x0029, 0x0028, 0x795D, 0x0029, 0xFFFF }; unsigned short unac_data440[] = { 0x0028, 0x52B4, 0x0029, 0x0028, 0x52B4, 0x0029, 0xFFFF, 0x0028, 0x4EE3, 0x0029, 0x0028, 0x4EE3, 0x0029, 0xFFFF, 0x0028, 0x547C, 0x0029, 0x0028, 0x547C, 0x0029, 0xFFFF, 0x0028, 0x5B66, 0x0029, 0x0028, 0x5B66, 0x0029, 0xFFFF, 0x0028, 0x76E3, 0x0029, 0x0028, 0x76E3, 0x0029, 0xFFFF, 0x0028, 0x4F01, 0x0029, 0x0028, 0x4F01, 0x0029, 0xFFFF, 0x0028, 0x8CC7, 0x0029, 0x0028, 0x8CC7, 0x0029, 0xFFFF, 0x0028, 0x5354, 0x0029, 0x0028, 0x5354, 0x0029, 0xFFFF }; unsigned short unac_data441[] = { 0x0028, 0x796D, 0x0029, 0x0028, 0x796D, 0x0029, 0xFFFF, 0x0028, 0x4F11, 0x0029, 0x0028, 0x4F11, 0x0029, 0xFFFF, 0x0028, 0x81EA, 0x0029, 0x0028, 0x81EA, 0x0029, 0xFFFF, 0x0028, 0x81F3, 0x0029, 0x0028, 0x81F3, 0x0029, 0xFFFF, 0x554F, 0x554F, 0xFFFF, 0x5E7C, 0x5E7C, 0xFFFF, 0x6587, 0x6587, 0xFFFF, 0x7B8F, 0x7B8F, 0xFFFF }; unsigned short unac_data442[] = { 0x0050, 0x0054, 0x0045, 0x0070, 0x0074, 0x0065, 0xFFFF, 0x0032, 0x0031, 0x0032, 0x0031, 0xFFFF, 0x0032, 0x0032, 0x0032, 0x0032, 0xFFFF, 0x0032, 0x0033, 0x0032, 0x0033, 0xFFFF, 0x0032, 0x0034, 0x0032, 0x0034, 0xFFFF, 0x0032, 0x0035, 0x0032, 0x0035, 0xFFFF, 0x0032, 0x0036, 0x0032, 0x0036, 0xFFFF, 0x0032, 0x0037, 0x0032, 0x0037, 0xFFFF }; unsigned short unac_data443[] = { 0x0032, 0x0038, 0x0032, 0x0038, 0xFFFF, 0x0032, 0x0039, 0x0032, 0x0039, 0xFFFF, 0x0033, 0x0030, 0x0033, 0x0030, 0xFFFF, 0x0033, 0x0031, 0x0033, 0x0031, 0xFFFF, 0x0033, 0x0032, 0x0033, 0x0032, 0xFFFF, 0x0033, 0x0033, 0x0033, 0x0033, 0xFFFF, 0x0033, 0x0034, 0x0033, 0x0034, 0xFFFF, 0x0033, 0x0035, 0x0033, 0x0035, 0xFFFF }; unsigned short unac_data444[] = { 0x1100, 0x1100, 0xFFFF, 0x1102, 0x1102, 0xFFFF, 0x1103, 0x1103, 0xFFFF, 0x1105, 0x1105, 0xFFFF, 0x1106, 0x1106, 0xFFFF, 0x1107, 0x1107, 0xFFFF, 0x1109, 0x1109, 0xFFFF, 0x110B, 0x110B, 0xFFFF }; unsigned short unac_data445[] = { 0x110C, 0x110C, 0xFFFF, 0x110E, 0x110E, 0xFFFF, 0x110F, 0x110F, 0xFFFF, 0x1110, 0x1110, 0xFFFF, 0x1111, 0x1111, 0xFFFF, 0x1112, 0x1112, 0xFFFF, 0x1100, 0x1161, 0x1100, 0x1161, 0xFFFF, 0x1102, 0x1161, 0x1102, 0x1161, 0xFFFF }; unsigned short unac_data446[] = { 0x1103, 0x1161, 0x1103, 0x1161, 0xFFFF, 0x1105, 0x1161, 0x1105, 0x1161, 0xFFFF, 0x1106, 0x1161, 0x1106, 0x1161, 0xFFFF, 0x1107, 0x1161, 0x1107, 0x1161, 0xFFFF, 0x1109, 0x1161, 0x1109, 0x1161, 0xFFFF, 0x110B, 0x1161, 0x110B, 0x1161, 0xFFFF, 0x110C, 0x1161, 0x110C, 0x1161, 0xFFFF, 0x110E, 0x1161, 0x110E, 0x1161, 0xFFFF }; unsigned short unac_data447[] = { 0x110F, 0x1161, 0x110F, 0x1161, 0xFFFF, 0x1110, 0x1161, 0x1110, 0x1161, 0xFFFF, 0x1111, 0x1161, 0x1111, 0x1161, 0xFFFF, 0x1112, 0x1161, 0x1112, 0x1161, 0xFFFF, 0x110E, 0x1161, 0x11B7, 0x1100, 0x1169, 0x110E, 0x1161, 0x11B7, 0x1100, 0x1169, 0xFFFF, 0x110C, 0x116E, 0x110B, 0x1174, 0x110C, 0x116E, 0x110B, 0x1174, 0xFFFF, 0x110B, 0x116E, 0x110B, 0x116E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data448[] = { 0x4E00, 0x4E00, 0xFFFF, 0x4E8C, 0x4E8C, 0xFFFF, 0x4E09, 0x4E09, 0xFFFF, 0x56DB, 0x56DB, 0xFFFF, 0x4E94, 0x4E94, 0xFFFF, 0x516D, 0x516D, 0xFFFF, 0x4E03, 0x4E03, 0xFFFF, 0x516B, 0x516B, 0xFFFF }; unsigned short unac_data449[] = { 0x4E5D, 0x4E5D, 0xFFFF, 0x5341, 0x5341, 0xFFFF, 0x6708, 0x6708, 0xFFFF, 0x706B, 0x706B, 0xFFFF, 0x6C34, 0x6C34, 0xFFFF, 0x6728, 0x6728, 0xFFFF, 0x91D1, 0x91D1, 0xFFFF, 0x571F, 0x571F, 0xFFFF }; unsigned short unac_data450[] = { 0x65E5, 0x65E5, 0xFFFF, 0x682A, 0x682A, 0xFFFF, 0x6709, 0x6709, 0xFFFF, 0x793E, 0x793E, 0xFFFF, 0x540D, 0x540D, 0xFFFF, 0x7279, 0x7279, 0xFFFF, 0x8CA1, 0x8CA1, 0xFFFF, 0x795D, 0x795D, 0xFFFF }; unsigned short unac_data451[] = { 0x52B4, 0x52B4, 0xFFFF, 0x79D8, 0x79D8, 0xFFFF, 0x7537, 0x7537, 0xFFFF, 0x5973, 0x5973, 0xFFFF, 0x9069, 0x9069, 0xFFFF, 0x512A, 0x512A, 0xFFFF, 0x5370, 0x5370, 0xFFFF, 0x6CE8, 0x6CE8, 0xFFFF }; unsigned short unac_data452[] = { 0x9805, 0x9805, 0xFFFF, 0x4F11, 0x4F11, 0xFFFF, 0x5199, 0x5199, 0xFFFF, 0x6B63, 0x6B63, 0xFFFF, 0x4E0A, 0x4E0A, 0xFFFF, 0x4E2D, 0x4E2D, 0xFFFF, 0x4E0B, 0x4E0B, 0xFFFF, 0x5DE6, 0x5DE6, 0xFFFF }; unsigned short unac_data453[] = { 0x53F3, 0x53F3, 0xFFFF, 0x533B, 0x533B, 0xFFFF, 0x5B97, 0x5B97, 0xFFFF, 0x5B66, 0x5B66, 0xFFFF, 0x76E3, 0x76E3, 0xFFFF, 0x4F01, 0x4F01, 0xFFFF, 0x8CC7, 0x8CC7, 0xFFFF, 0x5354, 0x5354, 0xFFFF }; unsigned short unac_data454[] = { 0x591C, 0x591C, 0xFFFF, 0x0033, 0x0036, 0x0033, 0x0036, 0xFFFF, 0x0033, 0x0037, 0x0033, 0x0037, 0xFFFF, 0x0033, 0x0038, 0x0033, 0x0038, 0xFFFF, 0x0033, 0x0039, 0x0033, 0x0039, 0xFFFF, 0x0034, 0x0030, 0x0034, 0x0030, 0xFFFF, 0x0034, 0x0031, 0x0034, 0x0031, 0xFFFF, 0x0034, 0x0032, 0x0034, 0x0032, 0xFFFF }; unsigned short unac_data455[] = { 0x0034, 0x0033, 0x0034, 0x0033, 0xFFFF, 0x0034, 0x0034, 0x0034, 0x0034, 0xFFFF, 0x0034, 0x0035, 0x0034, 0x0035, 0xFFFF, 0x0034, 0x0036, 0x0034, 0x0036, 0xFFFF, 0x0034, 0x0037, 0x0034, 0x0037, 0xFFFF, 0x0034, 0x0038, 0x0034, 0x0038, 0xFFFF, 0x0034, 0x0039, 0x0034, 0x0039, 0xFFFF, 0x0035, 0x0030, 0x0035, 0x0030, 0xFFFF }; unsigned short unac_data456[] = { 0x0031, 0x6708, 0x0031, 0x6708, 0xFFFF, 0x0032, 0x6708, 0x0032, 0x6708, 0xFFFF, 0x0033, 0x6708, 0x0033, 0x6708, 0xFFFF, 0x0034, 0x6708, 0x0034, 0x6708, 0xFFFF, 0x0035, 0x6708, 0x0035, 0x6708, 0xFFFF, 0x0036, 0x6708, 0x0036, 0x6708, 0xFFFF, 0x0037, 0x6708, 0x0037, 0x6708, 0xFFFF, 0x0038, 0x6708, 0x0038, 0x6708, 0xFFFF }; unsigned short unac_data457[] = { 0x0039, 0x6708, 0x0039, 0x6708, 0xFFFF, 0x0031, 0x0030, 0x6708, 0x0031, 0x0030, 0x6708, 0xFFFF, 0x0031, 0x0031, 0x6708, 0x0031, 0x0031, 0x6708, 0xFFFF, 0x0031, 0x0032, 0x6708, 0x0031, 0x0032, 0x6708, 0xFFFF, 0x0048, 0x0067, 0x0068, 0x0067, 0xFFFF, 0x0065, 0x0072, 0x0067, 0x0065, 0x0072, 0x0067, 0xFFFF, 0x0065, 0x0056, 0x0065, 0x0076, 0xFFFF, 0x004C, 0x0054, 0x0044, 0x006C, 0x0074, 0x0064, 0xFFFF }; unsigned short unac_data458[] = { 0x30A2, 0x30A2, 0xFFFF, 0x30A4, 0x30A4, 0xFFFF, 0x30A6, 0x30A6, 0xFFFF, 0x30A8, 0x30A8, 0xFFFF, 0x30AA, 0x30AA, 0xFFFF, 0x30AB, 0x30AB, 0xFFFF, 0x30AD, 0x30AD, 0xFFFF, 0x30AF, 0x30AF, 0xFFFF }; unsigned short unac_data459[] = { 0x30B1, 0x30B1, 0xFFFF, 0x30B3, 0x30B3, 0xFFFF, 0x30B5, 0x30B5, 0xFFFF, 0x30B7, 0x30B7, 0xFFFF, 0x30B9, 0x30B9, 0xFFFF, 0x30BB, 0x30BB, 0xFFFF, 0x30BD, 0x30BD, 0xFFFF, 0x30BF, 0x30BF, 0xFFFF }; unsigned short unac_data460[] = { 0x30C1, 0x30C1, 0xFFFF, 0x30C4, 0x30C4, 0xFFFF, 0x30C6, 0x30C6, 0xFFFF, 0x30C8, 0x30C8, 0xFFFF, 0x30CA, 0x30CA, 0xFFFF, 0x30CB, 0x30CB, 0xFFFF, 0x30CC, 0x30CC, 0xFFFF, 0x30CD, 0x30CD, 0xFFFF }; unsigned short unac_data461[] = { 0x30CE, 0x30CE, 0xFFFF, 0x30CF, 0x30CF, 0xFFFF, 0x30D2, 0x30D2, 0xFFFF, 0x30D5, 0x30D5, 0xFFFF, 0x30D8, 0x30D8, 0xFFFF, 0x30DB, 0x30DB, 0xFFFF, 0x30DE, 0x30DE, 0xFFFF, 0x30DF, 0x30DF, 0xFFFF }; unsigned short unac_data462[] = { 0x30E0, 0x30E0, 0xFFFF, 0x30E1, 0x30E1, 0xFFFF, 0x30E2, 0x30E2, 0xFFFF, 0x30E4, 0x30E4, 0xFFFF, 0x30E6, 0x30E6, 0xFFFF, 0x30E8, 0x30E8, 0xFFFF, 0x30E9, 0x30E9, 0xFFFF, 0x30EA, 0x30EA, 0xFFFF }; unsigned short unac_data463[] = { 0x30EB, 0x30EB, 0xFFFF, 0x30EC, 0x30EC, 0xFFFF, 0x30ED, 0x30ED, 0xFFFF, 0x30EF, 0x30EF, 0xFFFF, 0x30F0, 0x30F0, 0xFFFF, 0x30F1, 0x30F1, 0xFFFF, 0x30F2, 0x30F2, 0xFFFF, 0x4EE4, 0x548C, 0x4EE4, 0x548C, 0xFFFF }; unsigned short unac_data464[] = { 0x30A2, 0x30D1, 0x30FC, 0x30C8, 0x30A2, 0x30D1, 0x30FC, 0x30C8, 0xFFFF, 0x30A2, 0x30EB, 0x30D5, 0x30A1, 0x30A2, 0x30EB, 0x30D5, 0x30A1, 0xFFFF, 0x30A2, 0x30F3, 0x30DA, 0x30A2, 0x30A2, 0x30F3, 0x30DA, 0x30A2, 0xFFFF, 0x30A2, 0x30FC, 0x30EB, 0x30A2, 0x30FC, 0x30EB, 0xFFFF, 0x30A4, 0x30CB, 0x30F3, 0x30B0, 0x30A4, 0x30CB, 0x30F3, 0x30B0, 0xFFFF, 0x30A4, 0x30F3, 0x30C1, 0x30A4, 0x30F3, 0x30C1, 0xFFFF, 0x30A6, 0x30A9, 0x30F3, 0x30A6, 0x30A9, 0x30F3, 0xFFFF, 0x30A8, 0x30B9, 0x30AF, 0x30FC, 0x30C9, 0x30A8, 0x30B9, 0x30AF, 0x30FC, 0x30C9, 0xFFFF }; unsigned short unac_data465[] = { 0x30A8, 0x30FC, 0x30AB, 0x30FC, 0x30A8, 0x30FC, 0x30AB, 0x30FC, 0xFFFF, 0x30AA, 0x30F3, 0x30B9, 0x30AA, 0x30F3, 0x30B9, 0xFFFF, 0x30AA, 0x30FC, 0x30E0, 0x30AA, 0x30FC, 0x30E0, 0xFFFF, 0x30AB, 0x30A4, 0x30EA, 0x30AB, 0x30A4, 0x30EA, 0xFFFF, 0x30AB, 0x30E9, 0x30C3, 0x30C8, 0x30AB, 0x30E9, 0x30C3, 0x30C8, 0xFFFF, 0x30AB, 0x30ED, 0x30EA, 0x30FC, 0x30AB, 0x30ED, 0x30EA, 0x30FC, 0xFFFF, 0x30AC, 0x30ED, 0x30F3, 0x30AC, 0x30ED, 0x30F3, 0xFFFF, 0x30AC, 0x30F3, 0x30DE, 0x30AC, 0x30F3, 0x30DE, 0xFFFF }; unsigned short unac_data466[] = { 0x30AE, 0x30AC, 0x30AE, 0x30AC, 0xFFFF, 0x30AE, 0x30CB, 0x30FC, 0x30AE, 0x30CB, 0x30FC, 0xFFFF, 0x30AD, 0x30E5, 0x30EA, 0x30FC, 0x30AD, 0x30E5, 0x30EA, 0x30FC, 0xFFFF, 0x30AE, 0x30EB, 0x30C0, 0x30FC, 0x30AE, 0x30EB, 0x30C0, 0x30FC, 0xFFFF, 0x30AD, 0x30ED, 0x30AD, 0x30ED, 0xFFFF, 0x30AD, 0x30ED, 0x30B0, 0x30E9, 0x30E0, 0x30AD, 0x30ED, 0x30B0, 0x30E9, 0x30E0, 0xFFFF, 0x30AD, 0x30ED, 0x30E1, 0x30FC, 0x30C8, 0x30EB, 0x30AD, 0x30ED, 0x30E1, 0x30FC, 0x30C8, 0x30EB, 0xFFFF, 0x30AD, 0x30ED, 0x30EF, 0x30C3, 0x30C8, 0x30AD, 0x30ED, 0x30EF, 0x30C3, 0x30C8, 0xFFFF }; unsigned short unac_data467[] = { 0x30B0, 0x30E9, 0x30E0, 0x30B0, 0x30E9, 0x30E0, 0xFFFF, 0x30B0, 0x30E9, 0x30E0, 0x30C8, 0x30F3, 0x30B0, 0x30E9, 0x30E0, 0x30C8, 0x30F3, 0xFFFF, 0x30AF, 0x30EB, 0x30BC, 0x30A4, 0x30ED, 0x30AF, 0x30EB, 0x30BC, 0x30A4, 0x30ED, 0xFFFF, 0x30AF, 0x30ED, 0x30FC, 0x30CD, 0x30AF, 0x30ED, 0x30FC, 0x30CD, 0xFFFF, 0x30B1, 0x30FC, 0x30B9, 0x30B1, 0x30FC, 0x30B9, 0xFFFF, 0x30B3, 0x30EB, 0x30CA, 0x30B3, 0x30EB, 0x30CA, 0xFFFF, 0x30B3, 0x30FC, 0x30DD, 0x30B3, 0x30FC, 0x30DD, 0xFFFF, 0x30B5, 0x30A4, 0x30AF, 0x30EB, 0x30B5, 0x30A4, 0x30AF, 0x30EB, 0xFFFF }; unsigned short unac_data468[] = { 0x30B5, 0x30F3, 0x30C1, 0x30FC, 0x30E0, 0x30B5, 0x30F3, 0x30C1, 0x30FC, 0x30E0, 0xFFFF, 0x30B7, 0x30EA, 0x30F3, 0x30B0, 0x30B7, 0x30EA, 0x30F3, 0x30B0, 0xFFFF, 0x30BB, 0x30F3, 0x30C1, 0x30BB, 0x30F3, 0x30C1, 0xFFFF, 0x30BB, 0x30F3, 0x30C8, 0x30BB, 0x30F3, 0x30C8, 0xFFFF, 0x30C0, 0x30FC, 0x30B9, 0x30C0, 0x30FC, 0x30B9, 0xFFFF, 0x30C7, 0x30B7, 0x30C7, 0x30B7, 0xFFFF, 0x30C9, 0x30EB, 0x30C9, 0x30EB, 0xFFFF, 0x30C8, 0x30F3, 0x30C8, 0x30F3, 0xFFFF }; unsigned short unac_data469[] = { 0x30CA, 0x30CE, 0x30CA, 0x30CE, 0xFFFF, 0x30CE, 0x30C3, 0x30C8, 0x30CE, 0x30C3, 0x30C8, 0xFFFF, 0x30CF, 0x30A4, 0x30C4, 0x30CF, 0x30A4, 0x30C4, 0xFFFF, 0x30D1, 0x30FC, 0x30BB, 0x30F3, 0x30C8, 0x30D1, 0x30FC, 0x30BB, 0x30F3, 0x30C8, 0xFFFF, 0x30D1, 0x30FC, 0x30C4, 0x30D1, 0x30FC, 0x30C4, 0xFFFF, 0x30D0, 0x30FC, 0x30EC, 0x30EB, 0x30D0, 0x30FC, 0x30EC, 0x30EB, 0xFFFF, 0x30D4, 0x30A2, 0x30B9, 0x30C8, 0x30EB, 0x30D4, 0x30A2, 0x30B9, 0x30C8, 0x30EB, 0xFFFF, 0x30D4, 0x30AF, 0x30EB, 0x30D4, 0x30AF, 0x30EB, 0xFFFF }; unsigned short unac_data470[] = { 0x30D4, 0x30B3, 0x30D4, 0x30B3, 0xFFFF, 0x30D3, 0x30EB, 0x30D3, 0x30EB, 0xFFFF, 0x30D5, 0x30A1, 0x30E9, 0x30C3, 0x30C9, 0x30D5, 0x30A1, 0x30E9, 0x30C3, 0x30C9, 0xFFFF, 0x30D5, 0x30A3, 0x30FC, 0x30C8, 0x30D5, 0x30A3, 0x30FC, 0x30C8, 0xFFFF, 0x30D6, 0x30C3, 0x30B7, 0x30A7, 0x30EB, 0x30D6, 0x30C3, 0x30B7, 0x30A7, 0x30EB, 0xFFFF, 0x30D5, 0x30E9, 0x30F3, 0x30D5, 0x30E9, 0x30F3, 0xFFFF, 0x30D8, 0x30AF, 0x30BF, 0x30FC, 0x30EB, 0x30D8, 0x30AF, 0x30BF, 0x30FC, 0x30EB, 0xFFFF, 0x30DA, 0x30BD, 0x30DA, 0x30BD, 0xFFFF }; unsigned short unac_data471[] = { 0x30DA, 0x30CB, 0x30D2, 0x30DA, 0x30CB, 0x30D2, 0xFFFF, 0x30D8, 0x30EB, 0x30C4, 0x30D8, 0x30EB, 0x30C4, 0xFFFF, 0x30DA, 0x30F3, 0x30B9, 0x30DA, 0x30F3, 0x30B9, 0xFFFF, 0x30DA, 0x30FC, 0x30B8, 0x30DA, 0x30FC, 0x30B8, 0xFFFF, 0x30D9, 0x30FC, 0x30BF, 0x30D9, 0x30FC, 0x30BF, 0xFFFF, 0x30DD, 0x30A4, 0x30F3, 0x30C8, 0x30DD, 0x30A4, 0x30F3, 0x30C8, 0xFFFF, 0x30DC, 0x30EB, 0x30C8, 0x30DC, 0x30EB, 0x30C8, 0xFFFF, 0x30DB, 0x30F3, 0x30DB, 0x30F3, 0xFFFF }; unsigned short unac_data472[] = { 0x30DD, 0x30F3, 0x30C9, 0x30DD, 0x30F3, 0x30C9, 0xFFFF, 0x30DB, 0x30FC, 0x30EB, 0x30DB, 0x30FC, 0x30EB, 0xFFFF, 0x30DB, 0x30FC, 0x30F3, 0x30DB, 0x30FC, 0x30F3, 0xFFFF, 0x30DE, 0x30A4, 0x30AF, 0x30ED, 0x30DE, 0x30A4, 0x30AF, 0x30ED, 0xFFFF, 0x30DE, 0x30A4, 0x30EB, 0x30DE, 0x30A4, 0x30EB, 0xFFFF, 0x30DE, 0x30C3, 0x30CF, 0x30DE, 0x30C3, 0x30CF, 0xFFFF, 0x30DE, 0x30EB, 0x30AF, 0x30DE, 0x30EB, 0x30AF, 0xFFFF, 0x30DE, 0x30F3, 0x30B7, 0x30E7, 0x30F3, 0x30DE, 0x30F3, 0x30B7, 0x30E7, 0x30F3, 0xFFFF }; unsigned short unac_data473[] = { 0x30DF, 0x30AF, 0x30ED, 0x30F3, 0x30DF, 0x30AF, 0x30ED, 0x30F3, 0xFFFF, 0x30DF, 0x30EA, 0x30DF, 0x30EA, 0xFFFF, 0x30DF, 0x30EA, 0x30D0, 0x30FC, 0x30EB, 0x30DF, 0x30EA, 0x30D0, 0x30FC, 0x30EB, 0xFFFF, 0x30E1, 0x30AC, 0x30E1, 0x30AC, 0xFFFF, 0x30E1, 0x30AC, 0x30C8, 0x30F3, 0x30E1, 0x30AC, 0x30C8, 0x30F3, 0xFFFF, 0x30E1, 0x30FC, 0x30C8, 0x30EB, 0x30E1, 0x30FC, 0x30C8, 0x30EB, 0xFFFF, 0x30E4, 0x30FC, 0x30C9, 0x30E4, 0x30FC, 0x30C9, 0xFFFF, 0x30E4, 0x30FC, 0x30EB, 0x30E4, 0x30FC, 0x30EB, 0xFFFF }; unsigned short unac_data474[] = { 0x30E6, 0x30A2, 0x30F3, 0x30E6, 0x30A2, 0x30F3, 0xFFFF, 0x30EA, 0x30C3, 0x30C8, 0x30EB, 0x30EA, 0x30C3, 0x30C8, 0x30EB, 0xFFFF, 0x30EA, 0x30E9, 0x30EA, 0x30E9, 0xFFFF, 0x30EB, 0x30D4, 0x30FC, 0x30EB, 0x30D4, 0x30FC, 0xFFFF, 0x30EB, 0x30FC, 0x30D6, 0x30EB, 0x30EB, 0x30FC, 0x30D6, 0x30EB, 0xFFFF, 0x30EC, 0x30E0, 0x30EC, 0x30E0, 0xFFFF, 0x30EC, 0x30F3, 0x30C8, 0x30B2, 0x30F3, 0x30EC, 0x30F3, 0x30C8, 0x30B2, 0x30F3, 0xFFFF, 0x30EF, 0x30C3, 0x30C8, 0x30EF, 0x30C3, 0x30C8, 0xFFFF }; unsigned short unac_data475[] = { 0x0030, 0x70B9, 0x0030, 0x70B9, 0xFFFF, 0x0031, 0x70B9, 0x0031, 0x70B9, 0xFFFF, 0x0032, 0x70B9, 0x0032, 0x70B9, 0xFFFF, 0x0033, 0x70B9, 0x0033, 0x70B9, 0xFFFF, 0x0034, 0x70B9, 0x0034, 0x70B9, 0xFFFF, 0x0035, 0x70B9, 0x0035, 0x70B9, 0xFFFF, 0x0036, 0x70B9, 0x0036, 0x70B9, 0xFFFF, 0x0037, 0x70B9, 0x0037, 0x70B9, 0xFFFF }; unsigned short unac_data476[] = { 0x0038, 0x70B9, 0x0038, 0x70B9, 0xFFFF, 0x0039, 0x70B9, 0x0039, 0x70B9, 0xFFFF, 0x0031, 0x0030, 0x70B9, 0x0031, 0x0030, 0x70B9, 0xFFFF, 0x0031, 0x0031, 0x70B9, 0x0031, 0x0031, 0x70B9, 0xFFFF, 0x0031, 0x0032, 0x70B9, 0x0031, 0x0032, 0x70B9, 0xFFFF, 0x0031, 0x0033, 0x70B9, 0x0031, 0x0033, 0x70B9, 0xFFFF, 0x0031, 0x0034, 0x70B9, 0x0031, 0x0034, 0x70B9, 0xFFFF, 0x0031, 0x0035, 0x70B9, 0x0031, 0x0035, 0x70B9, 0xFFFF }; unsigned short unac_data477[] = { 0x0031, 0x0036, 0x70B9, 0x0031, 0x0036, 0x70B9, 0xFFFF, 0x0031, 0x0037, 0x70B9, 0x0031, 0x0037, 0x70B9, 0xFFFF, 0x0031, 0x0038, 0x70B9, 0x0031, 0x0038, 0x70B9, 0xFFFF, 0x0031, 0x0039, 0x70B9, 0x0031, 0x0039, 0x70B9, 0xFFFF, 0x0032, 0x0030, 0x70B9, 0x0032, 0x0030, 0x70B9, 0xFFFF, 0x0032, 0x0031, 0x70B9, 0x0032, 0x0031, 0x70B9, 0xFFFF, 0x0032, 0x0032, 0x70B9, 0x0032, 0x0032, 0x70B9, 0xFFFF, 0x0032, 0x0033, 0x70B9, 0x0032, 0x0033, 0x70B9, 0xFFFF }; unsigned short unac_data478[] = { 0x0032, 0x0034, 0x70B9, 0x0032, 0x0034, 0x70B9, 0xFFFF, 0x0068, 0x0050, 0x0061, 0x0068, 0x0070, 0x0061, 0xFFFF, 0x0064, 0x0061, 0x0064, 0x0061, 0xFFFF, 0x0041, 0x0055, 0x0061, 0x0075, 0xFFFF, 0x0062, 0x0061, 0x0072, 0x0062, 0x0061, 0x0072, 0xFFFF, 0x006F, 0x0056, 0x006F, 0x0076, 0xFFFF, 0x0070, 0x0063, 0x0070, 0x0063, 0xFFFF, 0x0064, 0x006D, 0x0064, 0x006D, 0xFFFF }; unsigned short unac_data479[] = { 0x0064, 0x006D, 0x0032, 0x0064, 0x006D, 0x0032, 0xFFFF, 0x0064, 0x006D, 0x0033, 0x0064, 0x006D, 0x0033, 0xFFFF, 0x0049, 0x0055, 0x0069, 0x0075, 0xFFFF, 0x5E73, 0x6210, 0x5E73, 0x6210, 0xFFFF, 0x662D, 0x548C, 0x662D, 0x548C, 0xFFFF, 0x5927, 0x6B63, 0x5927, 0x6B63, 0xFFFF, 0x660E, 0x6CBB, 0x660E, 0x6CBB, 0xFFFF, 0x682A, 0x5F0F, 0x4F1A, 0x793E, 0x682A, 0x5F0F, 0x4F1A, 0x793E, 0xFFFF }; unsigned short unac_data480[] = { 0x0070, 0x0041, 0x0070, 0x0061, 0xFFFF, 0x006E, 0x0041, 0x006E, 0x0061, 0xFFFF, 0x03BC, 0x0041, 0x03BC, 0x0061, 0xFFFF, 0x006D, 0x0041, 0x006D, 0x0061, 0xFFFF, 0x006B, 0x0041, 0x006B, 0x0061, 0xFFFF, 0x004B, 0x0042, 0x006B, 0x0062, 0xFFFF, 0x004D, 0x0042, 0x006D, 0x0062, 0xFFFF, 0x0047, 0x0042, 0x0067, 0x0062, 0xFFFF }; unsigned short unac_data481[] = { 0x0063, 0x0061, 0x006C, 0x0063, 0x0061, 0x006C, 0xFFFF, 0x006B, 0x0063, 0x0061, 0x006C, 0x006B, 0x0063, 0x0061, 0x006C, 0xFFFF, 0x0070, 0x0046, 0x0070, 0x0066, 0xFFFF, 0x006E, 0x0046, 0x006E, 0x0066, 0xFFFF, 0x03BC, 0x0046, 0x03BC, 0x0066, 0xFFFF, 0x03BC, 0x0067, 0x03BC, 0x0067, 0xFFFF, 0x006D, 0x0067, 0x006D, 0x0067, 0xFFFF, 0x006B, 0x0067, 0x006B, 0x0067, 0xFFFF }; unsigned short unac_data482[] = { 0x0048, 0x007A, 0x0068, 0x007A, 0xFFFF, 0x006B, 0x0048, 0x007A, 0x006B, 0x0068, 0x007A, 0xFFFF, 0x004D, 0x0048, 0x007A, 0x006D, 0x0068, 0x007A, 0xFFFF, 0x0047, 0x0048, 0x007A, 0x0067, 0x0068, 0x007A, 0xFFFF, 0x0054, 0x0048, 0x007A, 0x0074, 0x0068, 0x007A, 0xFFFF, 0x03BC, 0x006C, 0x03BC, 0x006C, 0xFFFF, 0x006D, 0x006C, 0x006D, 0x006C, 0xFFFF, 0x0064, 0x006C, 0x0064, 0x006C, 0xFFFF }; unsigned short unac_data483[] = { 0x006B, 0x006C, 0x006B, 0x006C, 0xFFFF, 0x0066, 0x006D, 0x0066, 0x006D, 0xFFFF, 0x006E, 0x006D, 0x006E, 0x006D, 0xFFFF, 0x03BC, 0x006D, 0x03BC, 0x006D, 0xFFFF, 0x006D, 0x006D, 0x006D, 0x006D, 0xFFFF, 0x0063, 0x006D, 0x0063, 0x006D, 0xFFFF, 0x006B, 0x006D, 0x006B, 0x006D, 0xFFFF, 0x006D, 0x006D, 0x0032, 0x006D, 0x006D, 0x0032, 0xFFFF }; unsigned short unac_data484[] = { 0x0063, 0x006D, 0x0032, 0x0063, 0x006D, 0x0032, 0xFFFF, 0x006D, 0x0032, 0x006D, 0x0032, 0xFFFF, 0x006B, 0x006D, 0x0032, 0x006B, 0x006D, 0x0032, 0xFFFF, 0x006D, 0x006D, 0x0033, 0x006D, 0x006D, 0x0033, 0xFFFF, 0x0063, 0x006D, 0x0033, 0x0063, 0x006D, 0x0033, 0xFFFF, 0x006D, 0x0033, 0x006D, 0x0033, 0xFFFF, 0x006B, 0x006D, 0x0033, 0x006B, 0x006D, 0x0033, 0xFFFF, 0x006D, 0x2215, 0x0073, 0x006D, 0x2215, 0x0073, 0xFFFF }; unsigned short unac_data485[] = { 0x006D, 0x2215, 0x0073, 0x0032, 0x006D, 0x2215, 0x0073, 0x0032, 0xFFFF, 0x0050, 0x0061, 0x0070, 0x0061, 0xFFFF, 0x006B, 0x0050, 0x0061, 0x006B, 0x0070, 0x0061, 0xFFFF, 0x004D, 0x0050, 0x0061, 0x006D, 0x0070, 0x0061, 0xFFFF, 0x0047, 0x0050, 0x0061, 0x0067, 0x0070, 0x0061, 0xFFFF, 0x0072, 0x0061, 0x0064, 0x0072, 0x0061, 0x0064, 0xFFFF, 0x0072, 0x0061, 0x0064, 0x2215, 0x0073, 0x0072, 0x0061, 0x0064, 0x2215, 0x0073, 0xFFFF, 0x0072, 0x0061, 0x0064, 0x2215, 0x0073, 0x0032, 0x0072, 0x0061, 0x0064, 0x2215, 0x0073, 0x0032, 0xFFFF }; unsigned short unac_data486[] = { 0x0070, 0x0073, 0x0070, 0x0073, 0xFFFF, 0x006E, 0x0073, 0x006E, 0x0073, 0xFFFF, 0x03BC, 0x0073, 0x03BC, 0x0073, 0xFFFF, 0x006D, 0x0073, 0x006D, 0x0073, 0xFFFF, 0x0070, 0x0056, 0x0070, 0x0076, 0xFFFF, 0x006E, 0x0056, 0x006E, 0x0076, 0xFFFF, 0x03BC, 0x0056, 0x03BC, 0x0076, 0xFFFF, 0x006D, 0x0056, 0x006D, 0x0076, 0xFFFF }; unsigned short unac_data487[] = { 0x006B, 0x0056, 0x006B, 0x0076, 0xFFFF, 0x004D, 0x0056, 0x006D, 0x0076, 0xFFFF, 0x0070, 0x0057, 0x0070, 0x0077, 0xFFFF, 0x006E, 0x0057, 0x006E, 0x0077, 0xFFFF, 0x03BC, 0x0057, 0x03BC, 0x0077, 0xFFFF, 0x006D, 0x0057, 0x006D, 0x0077, 0xFFFF, 0x006B, 0x0057, 0x006B, 0x0077, 0xFFFF, 0x004D, 0x0057, 0x006D, 0x0077, 0xFFFF }; unsigned short unac_data488[] = { 0x006B, 0x03A9, 0x006B, 0x03C9, 0xFFFF, 0x004D, 0x03A9, 0x006D, 0x03C9, 0xFFFF, 0x0061, 0x002E, 0x006D, 0x002E, 0x0061, 0x002E, 0x006D, 0x002E, 0xFFFF, 0x0042, 0x0071, 0x0062, 0x0071, 0xFFFF, 0x0063, 0x0063, 0x0063, 0x0063, 0xFFFF, 0x0063, 0x0064, 0x0063, 0x0064, 0xFFFF, 0x0043, 0x2215, 0x006B, 0x0067, 0x0063, 0x2215, 0x006B, 0x0067, 0xFFFF, 0x0043, 0x006F, 0x002E, 0x0063, 0x006F, 0x002E, 0xFFFF }; unsigned short unac_data489[] = { 0x0064, 0x0042, 0x0064, 0x0062, 0xFFFF, 0x0047, 0x0079, 0x0067, 0x0079, 0xFFFF, 0x0068, 0x0061, 0x0068, 0x0061, 0xFFFF, 0x0048, 0x0050, 0x0068, 0x0070, 0xFFFF, 0x0069, 0x006E, 0x0069, 0x006E, 0xFFFF, 0x004B, 0x004B, 0x006B, 0x006B, 0xFFFF, 0x004B, 0x004D, 0x006B, 0x006D, 0xFFFF, 0x006B, 0x0074, 0x006B, 0x0074, 0xFFFF }; unsigned short unac_data490[] = { 0x006C, 0x006D, 0x006C, 0x006D, 0xFFFF, 0x006C, 0x006E, 0x006C, 0x006E, 0xFFFF, 0x006C, 0x006F, 0x0067, 0x006C, 0x006F, 0x0067, 0xFFFF, 0x006C, 0x0078, 0x006C, 0x0078, 0xFFFF, 0x006D, 0x0062, 0x006D, 0x0062, 0xFFFF, 0x006D, 0x0069, 0x006C, 0x006D, 0x0069, 0x006C, 0xFFFF, 0x006D, 0x006F, 0x006C, 0x006D, 0x006F, 0x006C, 0xFFFF, 0x0050, 0x0048, 0x0070, 0x0068, 0xFFFF }; unsigned short unac_data491[] = { 0x0070, 0x002E, 0x006D, 0x002E, 0x0070, 0x002E, 0x006D, 0x002E, 0xFFFF, 0x0050, 0x0050, 0x004D, 0x0070, 0x0070, 0x006D, 0xFFFF, 0x0050, 0x0052, 0x0070, 0x0072, 0xFFFF, 0x0073, 0x0072, 0x0073, 0x0072, 0xFFFF, 0x0053, 0x0076, 0x0073, 0x0076, 0xFFFF, 0x0057, 0x0062, 0x0077, 0x0062, 0xFFFF, 0x0056, 0x2215, 0x006D, 0x0076, 0x2215, 0x006D, 0xFFFF, 0x0041, 0x2215, 0x006D, 0x0061, 0x2215, 0x006D, 0xFFFF }; unsigned short unac_data492[] = { 0x0031, 0x65E5, 0x0031, 0x65E5, 0xFFFF, 0x0032, 0x65E5, 0x0032, 0x65E5, 0xFFFF, 0x0033, 0x65E5, 0x0033, 0x65E5, 0xFFFF, 0x0034, 0x65E5, 0x0034, 0x65E5, 0xFFFF, 0x0035, 0x65E5, 0x0035, 0x65E5, 0xFFFF, 0x0036, 0x65E5, 0x0036, 0x65E5, 0xFFFF, 0x0037, 0x65E5, 0x0037, 0x65E5, 0xFFFF, 0x0038, 0x65E5, 0x0038, 0x65E5, 0xFFFF }; unsigned short unac_data493[] = { 0x0039, 0x65E5, 0x0039, 0x65E5, 0xFFFF, 0x0031, 0x0030, 0x65E5, 0x0031, 0x0030, 0x65E5, 0xFFFF, 0x0031, 0x0031, 0x65E5, 0x0031, 0x0031, 0x65E5, 0xFFFF, 0x0031, 0x0032, 0x65E5, 0x0031, 0x0032, 0x65E5, 0xFFFF, 0x0031, 0x0033, 0x65E5, 0x0031, 0x0033, 0x65E5, 0xFFFF, 0x0031, 0x0034, 0x65E5, 0x0031, 0x0034, 0x65E5, 0xFFFF, 0x0031, 0x0035, 0x65E5, 0x0031, 0x0035, 0x65E5, 0xFFFF, 0x0031, 0x0036, 0x65E5, 0x0031, 0x0036, 0x65E5, 0xFFFF }; unsigned short unac_data494[] = { 0x0031, 0x0037, 0x65E5, 0x0031, 0x0037, 0x65E5, 0xFFFF, 0x0031, 0x0038, 0x65E5, 0x0031, 0x0038, 0x65E5, 0xFFFF, 0x0031, 0x0039, 0x65E5, 0x0031, 0x0039, 0x65E5, 0xFFFF, 0x0032, 0x0030, 0x65E5, 0x0032, 0x0030, 0x65E5, 0xFFFF, 0x0032, 0x0031, 0x65E5, 0x0032, 0x0031, 0x65E5, 0xFFFF, 0x0032, 0x0032, 0x65E5, 0x0032, 0x0032, 0x65E5, 0xFFFF, 0x0032, 0x0033, 0x65E5, 0x0032, 0x0033, 0x65E5, 0xFFFF, 0x0032, 0x0034, 0x65E5, 0x0032, 0x0034, 0x65E5, 0xFFFF }; unsigned short unac_data495[] = { 0x0032, 0x0035, 0x65E5, 0x0032, 0x0035, 0x65E5, 0xFFFF, 0x0032, 0x0036, 0x65E5, 0x0032, 0x0036, 0x65E5, 0xFFFF, 0x0032, 0x0037, 0x65E5, 0x0032, 0x0037, 0x65E5, 0xFFFF, 0x0032, 0x0038, 0x65E5, 0x0032, 0x0038, 0x65E5, 0xFFFF, 0x0032, 0x0039, 0x65E5, 0x0032, 0x0039, 0x65E5, 0xFFFF, 0x0033, 0x0030, 0x65E5, 0x0033, 0x0030, 0x65E5, 0xFFFF, 0x0033, 0x0031, 0x65E5, 0x0033, 0x0031, 0x65E5, 0xFFFF, 0x0067, 0x0061, 0x006C, 0x0067, 0x0061, 0x006C, 0xFFFF }; unsigned short unac_data496[] = { 0xFFFF, 0xA641, 0xA641, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA643, 0xA643, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA645, 0xA645, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA647, 0xA647, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data497[] = { 0xFFFF, 0xA649, 0xA649, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA64B, 0xA64B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA64D, 0xA64D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA64F, 0xA64F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data498[] = { 0xFFFF, 0xA651, 0xA651, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA653, 0xA653, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA655, 0xA655, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA657, 0xA657, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data499[] = { 0xFFFF, 0xA659, 0xA659, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA65B, 0xA65B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA65D, 0xA65D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA65F, 0xA65F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data500[] = { 0xFFFF, 0xA661, 0xA661, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA663, 0xA663, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA665, 0xA665, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA667, 0xA667, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data501[] = { 0xFFFF, 0xA669, 0xA669, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA66B, 0xA66B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA66D, 0xA66D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data502[] = { 0xFFFF, 0xA681, 0xA681, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA683, 0xA683, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA685, 0xA685, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA687, 0xA687, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data503[] = { 0xFFFF, 0xA689, 0xA689, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA68B, 0xA68B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA68D, 0xA68D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA68F, 0xA68F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data504[] = { 0xFFFF, 0xA691, 0xA691, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA693, 0xA693, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA695, 0xA695, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA697, 0xA697, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data505[] = { 0xFFFF, 0xA699, 0xA699, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA69B, 0xA69B, 0xFFFF, 0xFFFF, 0xFFFF, 0x044A, 0x044A, 0xFFFF, 0x044C, 0x044C, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data506[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA723, 0xA723, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA725, 0xA725, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA727, 0xA727, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data507[] = { 0xFFFF, 0xA729, 0xA729, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA72B, 0xA72B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA72D, 0xA72D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA72F, 0xA72F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data508[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA733, 0xA733, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA735, 0xA735, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA737, 0xA737, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data509[] = { 0xFFFF, 0xA739, 0xA739, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA73B, 0xA73B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA73D, 0xA73D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA73F, 0xA73F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data510[] = { 0xFFFF, 0xA741, 0xA741, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA743, 0xA743, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA745, 0xA745, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA747, 0xA747, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data511[] = { 0xFFFF, 0xA749, 0xA749, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA74B, 0xA74B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA74D, 0xA74D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA74F, 0xA74F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data512[] = { 0xFFFF, 0xA751, 0xA751, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA753, 0xA753, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA755, 0xA755, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA757, 0xA757, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data513[] = { 0xFFFF, 0xA759, 0xA759, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA75B, 0xA75B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA75D, 0xA75D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA75F, 0xA75F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data514[] = { 0xFFFF, 0xA761, 0xA761, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA763, 0xA763, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA765, 0xA765, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA767, 0xA767, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data515[] = { 0xFFFF, 0xA769, 0xA769, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA76B, 0xA76B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA76D, 0xA76D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA76F, 0xA76F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data516[] = { 0xA76F, 0xA76F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data517[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA77A, 0xA77A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA77C, 0xA77C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1D79, 0x1D79, 0xFFFF, 0xA77F, 0xA77F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data518[] = { 0xFFFF, 0xA781, 0xA781, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA783, 0xA783, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA785, 0xA785, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA787, 0xA787, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data519[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA78C, 0xA78C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0265, 0x0265, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data520[] = { 0xFFFF, 0xA791, 0xA791, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA793, 0xA793, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA797, 0xA797, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data521[] = { 0xFFFF, 0xA799, 0xA799, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA79B, 0xA79B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA79D, 0xA79D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA79F, 0xA79F, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data522[] = { 0xFFFF, 0xA7A1, 0xA7A1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7A3, 0xA7A3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7A5, 0xA7A5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7A7, 0xA7A7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data523[] = { 0xFFFF, 0xA7A9, 0xA7A9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0266, 0x0266, 0xFFFF, 0x025C, 0x025C, 0xFFFF, 0x0261, 0x0261, 0xFFFF, 0x026C, 0x026C, 0xFFFF, 0x026A, 0x026A, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data524[] = { 0xFFFF, 0x029E, 0x029E, 0xFFFF, 0x0287, 0x0287, 0xFFFF, 0x029D, 0x029D, 0xFFFF, 0xAB53, 0xAB53, 0xFFFF, 0xA7B5, 0xA7B5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7B7, 0xA7B7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data525[] = { 0xFFFF, 0xA7B9, 0xA7B9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7BB, 0xA7BB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7BD, 0xA7BD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7BF, 0xA7BF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data526[] = { 0xFFFF, 0xA7C1, 0xA7C1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7C3, 0xA7C3, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA794, 0xA794, 0xFFFF, 0x0282, 0x0282, 0xFFFF, 0x1D8E, 0x1D8E, 0xFFFF, 0xA7C8, 0xA7C8 }; unsigned short unac_data527[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7CA, 0xA7CA, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data528[] = { 0xFFFF, 0xA7D1, 0xA7D1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA7D7, 0xA7D7, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data529[] = { 0xFFFF, 0xA7D9, 0xA7D9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data530[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0043, 0x0063, 0xFFFF, 0x0046, 0x0066, 0xFFFF, 0x0051, 0x0071, 0xFFFF, 0xFFFF, 0xA7F6, 0xA7F6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data531[] = { 0x0126, 0x0127, 0xFFFF, 0x0153, 0x0153, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data532[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data533[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data534[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data535[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data536[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data537[] = { 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF }; unsigned short unac_data538[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xA727, 0xA727, 0xFFFF, 0xAB37, 0xAB37, 0xFFFF, 0x026B, 0x026B, 0xFFFF, 0xAB52, 0xAB52, 0xFFFF }; unsigned short unac_data539[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x028D, 0x028D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data540[] = { 0xFFFF, 0x13A0, 0x13A0, 0xFFFF, 0x13A1, 0x13A1, 0xFFFF, 0x13A2, 0x13A2, 0xFFFF, 0x13A3, 0x13A3, 0xFFFF, 0x13A4, 0x13A4, 0xFFFF, 0x13A5, 0x13A5, 0xFFFF, 0x13A6, 0x13A6, 0xFFFF, 0x13A7, 0x13A7 }; unsigned short unac_data541[] = { 0xFFFF, 0x13A8, 0x13A8, 0xFFFF, 0x13A9, 0x13A9, 0xFFFF, 0x13AA, 0x13AA, 0xFFFF, 0x13AB, 0x13AB, 0xFFFF, 0x13AC, 0x13AC, 0xFFFF, 0x13AD, 0x13AD, 0xFFFF, 0x13AE, 0x13AE, 0xFFFF, 0x13AF, 0x13AF }; unsigned short unac_data542[] = { 0xFFFF, 0x13B0, 0x13B0, 0xFFFF, 0x13B1, 0x13B1, 0xFFFF, 0x13B2, 0x13B2, 0xFFFF, 0x13B3, 0x13B3, 0xFFFF, 0x13B4, 0x13B4, 0xFFFF, 0x13B5, 0x13B5, 0xFFFF, 0x13B6, 0x13B6, 0xFFFF, 0x13B7, 0x13B7 }; unsigned short unac_data543[] = { 0xFFFF, 0x13B8, 0x13B8, 0xFFFF, 0x13B9, 0x13B9, 0xFFFF, 0x13BA, 0x13BA, 0xFFFF, 0x13BB, 0x13BB, 0xFFFF, 0x13BC, 0x13BC, 0xFFFF, 0x13BD, 0x13BD, 0xFFFF, 0x13BE, 0x13BE, 0xFFFF, 0x13BF, 0x13BF }; unsigned short unac_data544[] = { 0xFFFF, 0x13C0, 0x13C0, 0xFFFF, 0x13C1, 0x13C1, 0xFFFF, 0x13C2, 0x13C2, 0xFFFF, 0x13C3, 0x13C3, 0xFFFF, 0x13C4, 0x13C4, 0xFFFF, 0x13C5, 0x13C5, 0xFFFF, 0x13C6, 0x13C6, 0xFFFF, 0x13C7, 0x13C7 }; unsigned short unac_data545[] = { 0xFFFF, 0x13C8, 0x13C8, 0xFFFF, 0x13C9, 0x13C9, 0xFFFF, 0x13CA, 0x13CA, 0xFFFF, 0x13CB, 0x13CB, 0xFFFF, 0x13CC, 0x13CC, 0xFFFF, 0x13CD, 0x13CD, 0xFFFF, 0x13CE, 0x13CE, 0xFFFF, 0x13CF, 0x13CF }; unsigned short unac_data546[] = { 0xFFFF, 0x13D0, 0x13D0, 0xFFFF, 0x13D1, 0x13D1, 0xFFFF, 0x13D2, 0x13D2, 0xFFFF, 0x13D3, 0x13D3, 0xFFFF, 0x13D4, 0x13D4, 0xFFFF, 0x13D5, 0x13D5, 0xFFFF, 0x13D6, 0x13D6, 0xFFFF, 0x13D7, 0x13D7 }; unsigned short unac_data547[] = { 0xFFFF, 0x13D8, 0x13D8, 0xFFFF, 0x13D9, 0x13D9, 0xFFFF, 0x13DA, 0x13DA, 0xFFFF, 0x13DB, 0x13DB, 0xFFFF, 0x13DC, 0x13DC, 0xFFFF, 0x13DD, 0x13DD, 0xFFFF, 0x13DE, 0x13DE, 0xFFFF, 0x13DF, 0x13DF }; unsigned short unac_data548[] = { 0xFFFF, 0x13E0, 0x13E0, 0xFFFF, 0x13E1, 0x13E1, 0xFFFF, 0x13E2, 0x13E2, 0xFFFF, 0x13E3, 0x13E3, 0xFFFF, 0x13E4, 0x13E4, 0xFFFF, 0x13E5, 0x13E5, 0xFFFF, 0x13E6, 0x13E6, 0xFFFF, 0x13E7, 0x13E7 }; unsigned short unac_data549[] = { 0xFFFF, 0x13E8, 0x13E8, 0xFFFF, 0x13E9, 0x13E9, 0xFFFF, 0x13EA, 0x13EA, 0xFFFF, 0x13EB, 0x13EB, 0xFFFF, 0x13EC, 0x13EC, 0xFFFF, 0x13ED, 0x13ED, 0xFFFF, 0x13EE, 0x13EE, 0xFFFF, 0x13EF, 0x13EF }; unsigned short unac_data550[] = { 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data551[] = { 0x8C48, 0x8C48, 0xFFFF, 0x66F4, 0x66F4, 0xFFFF, 0x8ECA, 0x8ECA, 0xFFFF, 0x8CC8, 0x8CC8, 0xFFFF, 0x6ED1, 0x6ED1, 0xFFFF, 0x4E32, 0x4E32, 0xFFFF, 0x53E5, 0x53E5, 0xFFFF, 0x9F9C, 0x9F9C, 0xFFFF }; unsigned short unac_data552[] = { 0x9F9C, 0x9F9C, 0xFFFF, 0x5951, 0x5951, 0xFFFF, 0x91D1, 0x91D1, 0xFFFF, 0x5587, 0x5587, 0xFFFF, 0x5948, 0x5948, 0xFFFF, 0x61F6, 0x61F6, 0xFFFF, 0x7669, 0x7669, 0xFFFF, 0x7F85, 0x7F85, 0xFFFF }; unsigned short unac_data553[] = { 0x863F, 0x863F, 0xFFFF, 0x87BA, 0x87BA, 0xFFFF, 0x88F8, 0x88F8, 0xFFFF, 0x908F, 0x908F, 0xFFFF, 0x6A02, 0x6A02, 0xFFFF, 0x6D1B, 0x6D1B, 0xFFFF, 0x70D9, 0x70D9, 0xFFFF, 0x73DE, 0x73DE, 0xFFFF }; unsigned short unac_data554[] = { 0x843D, 0x843D, 0xFFFF, 0x916A, 0x916A, 0xFFFF, 0x99F1, 0x99F1, 0xFFFF, 0x4E82, 0x4E82, 0xFFFF, 0x5375, 0x5375, 0xFFFF, 0x6B04, 0x6B04, 0xFFFF, 0x721B, 0x721B, 0xFFFF, 0x862D, 0x862D, 0xFFFF }; unsigned short unac_data555[] = { 0x9E1E, 0x9E1E, 0xFFFF, 0x5D50, 0x5D50, 0xFFFF, 0x6FEB, 0x6FEB, 0xFFFF, 0x85CD, 0x85CD, 0xFFFF, 0x8964, 0x8964, 0xFFFF, 0x62C9, 0x62C9, 0xFFFF, 0x81D8, 0x81D8, 0xFFFF, 0x881F, 0x881F, 0xFFFF }; unsigned short unac_data556[] = { 0x5ECA, 0x5ECA, 0xFFFF, 0x6717, 0x6717, 0xFFFF, 0x6D6A, 0x6D6A, 0xFFFF, 0x72FC, 0x72FC, 0xFFFF, 0x90CE, 0x90CE, 0xFFFF, 0x4F86, 0x4F86, 0xFFFF, 0x51B7, 0x51B7, 0xFFFF, 0x52DE, 0x52DE, 0xFFFF }; unsigned short unac_data557[] = { 0x64C4, 0x64C4, 0xFFFF, 0x6AD3, 0x6AD3, 0xFFFF, 0x7210, 0x7210, 0xFFFF, 0x76E7, 0x76E7, 0xFFFF, 0x8001, 0x8001, 0xFFFF, 0x8606, 0x8606, 0xFFFF, 0x865C, 0x865C, 0xFFFF, 0x8DEF, 0x8DEF, 0xFFFF }; unsigned short unac_data558[] = { 0x9732, 0x9732, 0xFFFF, 0x9B6F, 0x9B6F, 0xFFFF, 0x9DFA, 0x9DFA, 0xFFFF, 0x788C, 0x788C, 0xFFFF, 0x797F, 0x797F, 0xFFFF, 0x7DA0, 0x7DA0, 0xFFFF, 0x83C9, 0x83C9, 0xFFFF, 0x9304, 0x9304, 0xFFFF }; unsigned short unac_data559[] = { 0x9E7F, 0x9E7F, 0xFFFF, 0x8AD6, 0x8AD6, 0xFFFF, 0x58DF, 0x58DF, 0xFFFF, 0x5F04, 0x5F04, 0xFFFF, 0x7C60, 0x7C60, 0xFFFF, 0x807E, 0x807E, 0xFFFF, 0x7262, 0x7262, 0xFFFF, 0x78CA, 0x78CA, 0xFFFF }; unsigned short unac_data560[] = { 0x8CC2, 0x8CC2, 0xFFFF, 0x96F7, 0x96F7, 0xFFFF, 0x58D8, 0x58D8, 0xFFFF, 0x5C62, 0x5C62, 0xFFFF, 0x6A13, 0x6A13, 0xFFFF, 0x6DDA, 0x6DDA, 0xFFFF, 0x6F0F, 0x6F0F, 0xFFFF, 0x7D2F, 0x7D2F, 0xFFFF }; unsigned short unac_data561[] = { 0x7E37, 0x7E37, 0xFFFF, 0x964B, 0x964B, 0xFFFF, 0x52D2, 0x52D2, 0xFFFF, 0x808B, 0x808B, 0xFFFF, 0x51DC, 0x51DC, 0xFFFF, 0x51CC, 0x51CC, 0xFFFF, 0x7A1C, 0x7A1C, 0xFFFF, 0x7DBE, 0x7DBE, 0xFFFF }; unsigned short unac_data562[] = { 0x83F1, 0x83F1, 0xFFFF, 0x9675, 0x9675, 0xFFFF, 0x8B80, 0x8B80, 0xFFFF, 0x62CF, 0x62CF, 0xFFFF, 0x6A02, 0x6A02, 0xFFFF, 0x8AFE, 0x8AFE, 0xFFFF, 0x4E39, 0x4E39, 0xFFFF, 0x5BE7, 0x5BE7, 0xFFFF }; unsigned short unac_data563[] = { 0x6012, 0x6012, 0xFFFF, 0x7387, 0x7387, 0xFFFF, 0x7570, 0x7570, 0xFFFF, 0x5317, 0x5317, 0xFFFF, 0x78FB, 0x78FB, 0xFFFF, 0x4FBF, 0x4FBF, 0xFFFF, 0x5FA9, 0x5FA9, 0xFFFF, 0x4E0D, 0x4E0D, 0xFFFF }; unsigned short unac_data564[] = { 0x6CCC, 0x6CCC, 0xFFFF, 0x6578, 0x6578, 0xFFFF, 0x7D22, 0x7D22, 0xFFFF, 0x53C3, 0x53C3, 0xFFFF, 0x585E, 0x585E, 0xFFFF, 0x7701, 0x7701, 0xFFFF, 0x8449, 0x8449, 0xFFFF, 0x8AAA, 0x8AAA, 0xFFFF }; unsigned short unac_data565[] = { 0x6BBA, 0x6BBA, 0xFFFF, 0x8FB0, 0x8FB0, 0xFFFF, 0x6C88, 0x6C88, 0xFFFF, 0x62FE, 0x62FE, 0xFFFF, 0x82E5, 0x82E5, 0xFFFF, 0x63A0, 0x63A0, 0xFFFF, 0x7565, 0x7565, 0xFFFF, 0x4EAE, 0x4EAE, 0xFFFF }; unsigned short unac_data566[] = { 0x5169, 0x5169, 0xFFFF, 0x51C9, 0x51C9, 0xFFFF, 0x6881, 0x6881, 0xFFFF, 0x7CE7, 0x7CE7, 0xFFFF, 0x826F, 0x826F, 0xFFFF, 0x8AD2, 0x8AD2, 0xFFFF, 0x91CF, 0x91CF, 0xFFFF, 0x52F5, 0x52F5, 0xFFFF }; unsigned short unac_data567[] = { 0x5442, 0x5442, 0xFFFF, 0x5973, 0x5973, 0xFFFF, 0x5EEC, 0x5EEC, 0xFFFF, 0x65C5, 0x65C5, 0xFFFF, 0x6FFE, 0x6FFE, 0xFFFF, 0x792A, 0x792A, 0xFFFF, 0x95AD, 0x95AD, 0xFFFF, 0x9A6A, 0x9A6A, 0xFFFF }; unsigned short unac_data568[] = { 0x9E97, 0x9E97, 0xFFFF, 0x9ECE, 0x9ECE, 0xFFFF, 0x529B, 0x529B, 0xFFFF, 0x66C6, 0x66C6, 0xFFFF, 0x6B77, 0x6B77, 0xFFFF, 0x8F62, 0x8F62, 0xFFFF, 0x5E74, 0x5E74, 0xFFFF, 0x6190, 0x6190, 0xFFFF }; unsigned short unac_data569[] = { 0x6200, 0x6200, 0xFFFF, 0x649A, 0x649A, 0xFFFF, 0x6F23, 0x6F23, 0xFFFF, 0x7149, 0x7149, 0xFFFF, 0x7489, 0x7489, 0xFFFF, 0x79CA, 0x79CA, 0xFFFF, 0x7DF4, 0x7DF4, 0xFFFF, 0x806F, 0x806F, 0xFFFF }; unsigned short unac_data570[] = { 0x8F26, 0x8F26, 0xFFFF, 0x84EE, 0x84EE, 0xFFFF, 0x9023, 0x9023, 0xFFFF, 0x934A, 0x934A, 0xFFFF, 0x5217, 0x5217, 0xFFFF, 0x52A3, 0x52A3, 0xFFFF, 0x54BD, 0x54BD, 0xFFFF, 0x70C8, 0x70C8, 0xFFFF }; unsigned short unac_data571[] = { 0x88C2, 0x88C2, 0xFFFF, 0x8AAA, 0x8AAA, 0xFFFF, 0x5EC9, 0x5EC9, 0xFFFF, 0x5FF5, 0x5FF5, 0xFFFF, 0x637B, 0x637B, 0xFFFF, 0x6BAE, 0x6BAE, 0xFFFF, 0x7C3E, 0x7C3E, 0xFFFF, 0x7375, 0x7375, 0xFFFF }; unsigned short unac_data572[] = { 0x4EE4, 0x4EE4, 0xFFFF, 0x56F9, 0x56F9, 0xFFFF, 0x5BE7, 0x5BE7, 0xFFFF, 0x5DBA, 0x5DBA, 0xFFFF, 0x601C, 0x601C, 0xFFFF, 0x73B2, 0x73B2, 0xFFFF, 0x7469, 0x7469, 0xFFFF, 0x7F9A, 0x7F9A, 0xFFFF }; unsigned short unac_data573[] = { 0x8046, 0x8046, 0xFFFF, 0x9234, 0x9234, 0xFFFF, 0x96F6, 0x96F6, 0xFFFF, 0x9748, 0x9748, 0xFFFF, 0x9818, 0x9818, 0xFFFF, 0x4F8B, 0x4F8B, 0xFFFF, 0x79AE, 0x79AE, 0xFFFF, 0x91B4, 0x91B4, 0xFFFF }; unsigned short unac_data574[] = { 0x96B8, 0x96B8, 0xFFFF, 0x60E1, 0x60E1, 0xFFFF, 0x4E86, 0x4E86, 0xFFFF, 0x50DA, 0x50DA, 0xFFFF, 0x5BEE, 0x5BEE, 0xFFFF, 0x5C3F, 0x5C3F, 0xFFFF, 0x6599, 0x6599, 0xFFFF, 0x6A02, 0x6A02, 0xFFFF }; unsigned short unac_data575[] = { 0x71CE, 0x71CE, 0xFFFF, 0x7642, 0x7642, 0xFFFF, 0x84FC, 0x84FC, 0xFFFF, 0x907C, 0x907C, 0xFFFF, 0x9F8D, 0x9F8D, 0xFFFF, 0x6688, 0x6688, 0xFFFF, 0x962E, 0x962E, 0xFFFF, 0x5289, 0x5289, 0xFFFF }; unsigned short unac_data576[] = { 0x677B, 0x677B, 0xFFFF, 0x67F3, 0x67F3, 0xFFFF, 0x6D41, 0x6D41, 0xFFFF, 0x6E9C, 0x6E9C, 0xFFFF, 0x7409, 0x7409, 0xFFFF, 0x7559, 0x7559, 0xFFFF, 0x786B, 0x786B, 0xFFFF, 0x7D10, 0x7D10, 0xFFFF }; unsigned short unac_data577[] = { 0x985E, 0x985E, 0xFFFF, 0x516D, 0x516D, 0xFFFF, 0x622E, 0x622E, 0xFFFF, 0x9678, 0x9678, 0xFFFF, 0x502B, 0x502B, 0xFFFF, 0x5D19, 0x5D19, 0xFFFF, 0x6DEA, 0x6DEA, 0xFFFF, 0x8F2A, 0x8F2A, 0xFFFF }; unsigned short unac_data578[] = { 0x5F8B, 0x5F8B, 0xFFFF, 0x6144, 0x6144, 0xFFFF, 0x6817, 0x6817, 0xFFFF, 0x7387, 0x7387, 0xFFFF, 0x9686, 0x9686, 0xFFFF, 0x5229, 0x5229, 0xFFFF, 0x540F, 0x540F, 0xFFFF, 0x5C65, 0x5C65, 0xFFFF }; unsigned short unac_data579[] = { 0x6613, 0x6613, 0xFFFF, 0x674E, 0x674E, 0xFFFF, 0x68A8, 0x68A8, 0xFFFF, 0x6CE5, 0x6CE5, 0xFFFF, 0x7406, 0x7406, 0xFFFF, 0x75E2, 0x75E2, 0xFFFF, 0x7F79, 0x7F79, 0xFFFF, 0x88CF, 0x88CF, 0xFFFF }; unsigned short unac_data580[] = { 0x88E1, 0x88E1, 0xFFFF, 0x91CC, 0x91CC, 0xFFFF, 0x96E2, 0x96E2, 0xFFFF, 0x533F, 0x533F, 0xFFFF, 0x6EBA, 0x6EBA, 0xFFFF, 0x541D, 0x541D, 0xFFFF, 0x71D0, 0x71D0, 0xFFFF, 0x7498, 0x7498, 0xFFFF }; unsigned short unac_data581[] = { 0x85FA, 0x85FA, 0xFFFF, 0x96A3, 0x96A3, 0xFFFF, 0x9C57, 0x9C57, 0xFFFF, 0x9E9F, 0x9E9F, 0xFFFF, 0x6797, 0x6797, 0xFFFF, 0x6DCB, 0x6DCB, 0xFFFF, 0x81E8, 0x81E8, 0xFFFF, 0x7ACB, 0x7ACB, 0xFFFF }; unsigned short unac_data582[] = { 0x7B20, 0x7B20, 0xFFFF, 0x7C92, 0x7C92, 0xFFFF, 0x72C0, 0x72C0, 0xFFFF, 0x7099, 0x7099, 0xFFFF, 0x8B58, 0x8B58, 0xFFFF, 0x4EC0, 0x4EC0, 0xFFFF, 0x8336, 0x8336, 0xFFFF, 0x523A, 0x523A, 0xFFFF }; unsigned short unac_data583[] = { 0x5207, 0x5207, 0xFFFF, 0x5EA6, 0x5EA6, 0xFFFF, 0x62D3, 0x62D3, 0xFFFF, 0x7CD6, 0x7CD6, 0xFFFF, 0x5B85, 0x5B85, 0xFFFF, 0x6D1E, 0x6D1E, 0xFFFF, 0x66B4, 0x66B4, 0xFFFF, 0x8F3B, 0x8F3B, 0xFFFF }; unsigned short unac_data584[] = { 0x884C, 0x884C, 0xFFFF, 0x964D, 0x964D, 0xFFFF, 0x898B, 0x898B, 0xFFFF, 0x5ED3, 0x5ED3, 0xFFFF, 0x5140, 0x5140, 0xFFFF, 0x55C0, 0x55C0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data585[] = { 0x585A, 0x585A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6674, 0x6674, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x51DE, 0x51DE, 0xFFFF, 0x732A, 0x732A, 0xFFFF, 0x76CA, 0x76CA, 0xFFFF }; unsigned short unac_data586[] = { 0x793C, 0x793C, 0xFFFF, 0x795E, 0x795E, 0xFFFF, 0x7965, 0x7965, 0xFFFF, 0x798F, 0x798F, 0xFFFF, 0x9756, 0x9756, 0xFFFF, 0x7CBE, 0x7CBE, 0xFFFF, 0x7FBD, 0x7FBD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data587[] = { 0x8612, 0x8612, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x8AF8, 0x8AF8, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x9038, 0x9038, 0xFFFF, 0x90FD, 0x90FD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data588[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x98EF, 0x98EF, 0xFFFF, 0x98FC, 0x98FC, 0xFFFF, 0x9928, 0x9928, 0xFFFF, 0x9DB4, 0x9DB4, 0xFFFF, 0x90DE, 0x90DE, 0xFFFF, 0x96B7, 0x96B7, 0xFFFF }; unsigned short unac_data589[] = { 0x4FAE, 0x4FAE, 0xFFFF, 0x50E7, 0x50E7, 0xFFFF, 0x514D, 0x514D, 0xFFFF, 0x52C9, 0x52C9, 0xFFFF, 0x52E4, 0x52E4, 0xFFFF, 0x5351, 0x5351, 0xFFFF, 0x559D, 0x559D, 0xFFFF, 0x5606, 0x5606, 0xFFFF }; unsigned short unac_data590[] = { 0x5668, 0x5668, 0xFFFF, 0x5840, 0x5840, 0xFFFF, 0x58A8, 0x58A8, 0xFFFF, 0x5C64, 0x5C64, 0xFFFF, 0x5C6E, 0x5C6E, 0xFFFF, 0x6094, 0x6094, 0xFFFF, 0x6168, 0x6168, 0xFFFF, 0x618E, 0x618E, 0xFFFF }; unsigned short unac_data591[] = { 0x61F2, 0x61F2, 0xFFFF, 0x654F, 0x654F, 0xFFFF, 0x65E2, 0x65E2, 0xFFFF, 0x6691, 0x6691, 0xFFFF, 0x6885, 0x6885, 0xFFFF, 0x6D77, 0x6D77, 0xFFFF, 0x6E1A, 0x6E1A, 0xFFFF, 0x6F22, 0x6F22, 0xFFFF }; unsigned short unac_data592[] = { 0x716E, 0x716E, 0xFFFF, 0x722B, 0x722B, 0xFFFF, 0x7422, 0x7422, 0xFFFF, 0x7891, 0x7891, 0xFFFF, 0x793E, 0x793E, 0xFFFF, 0x7949, 0x7949, 0xFFFF, 0x7948, 0x7948, 0xFFFF, 0x7950, 0x7950, 0xFFFF }; unsigned short unac_data593[] = { 0x7956, 0x7956, 0xFFFF, 0x795D, 0x795D, 0xFFFF, 0x798D, 0x798D, 0xFFFF, 0x798E, 0x798E, 0xFFFF, 0x7A40, 0x7A40, 0xFFFF, 0x7A81, 0x7A81, 0xFFFF, 0x7BC0, 0x7BC0, 0xFFFF, 0x7DF4, 0x7DF4, 0xFFFF }; unsigned short unac_data594[] = { 0x7E09, 0x7E09, 0xFFFF, 0x7E41, 0x7E41, 0xFFFF, 0x7F72, 0x7F72, 0xFFFF, 0x8005, 0x8005, 0xFFFF, 0x81ED, 0x81ED, 0xFFFF, 0x8279, 0x8279, 0xFFFF, 0x8279, 0x8279, 0xFFFF, 0x8457, 0x8457, 0xFFFF }; unsigned short unac_data595[] = { 0x8910, 0x8910, 0xFFFF, 0x8996, 0x8996, 0xFFFF, 0x8B01, 0x8B01, 0xFFFF, 0x8B39, 0x8B39, 0xFFFF, 0x8CD3, 0x8CD3, 0xFFFF, 0x8D08, 0x8D08, 0xFFFF, 0x8FB6, 0x8FB6, 0xFFFF, 0x9038, 0x9038, 0xFFFF }; unsigned short unac_data596[] = { 0x96E3, 0x96E3, 0xFFFF, 0x97FF, 0x97FF, 0xFFFF, 0x983B, 0x983B, 0xFFFF, 0x6075, 0x6075, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x8218, 0x8218, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data597[] = { 0x4E26, 0x4E26, 0xFFFF, 0x51B5, 0x51B5, 0xFFFF, 0x5168, 0x5168, 0xFFFF, 0x4F80, 0x4F80, 0xFFFF, 0x5145, 0x5145, 0xFFFF, 0x5180, 0x5180, 0xFFFF, 0x52C7, 0x52C7, 0xFFFF, 0x52FA, 0x52FA, 0xFFFF }; unsigned short unac_data598[] = { 0x559D, 0x559D, 0xFFFF, 0x5555, 0x5555, 0xFFFF, 0x5599, 0x5599, 0xFFFF, 0x55E2, 0x55E2, 0xFFFF, 0x585A, 0x585A, 0xFFFF, 0x58B3, 0x58B3, 0xFFFF, 0x5944, 0x5944, 0xFFFF, 0x5954, 0x5954, 0xFFFF }; unsigned short unac_data599[] = { 0x5A62, 0x5A62, 0xFFFF, 0x5B28, 0x5B28, 0xFFFF, 0x5ED2, 0x5ED2, 0xFFFF, 0x5ED9, 0x5ED9, 0xFFFF, 0x5F69, 0x5F69, 0xFFFF, 0x5FAD, 0x5FAD, 0xFFFF, 0x60D8, 0x60D8, 0xFFFF, 0x614E, 0x614E, 0xFFFF }; unsigned short unac_data600[] = { 0x6108, 0x6108, 0xFFFF, 0x618E, 0x618E, 0xFFFF, 0x6160, 0x6160, 0xFFFF, 0x61F2, 0x61F2, 0xFFFF, 0x6234, 0x6234, 0xFFFF, 0x63C4, 0x63C4, 0xFFFF, 0x641C, 0x641C, 0xFFFF, 0x6452, 0x6452, 0xFFFF }; unsigned short unac_data601[] = { 0x6556, 0x6556, 0xFFFF, 0x6674, 0x6674, 0xFFFF, 0x6717, 0x6717, 0xFFFF, 0x671B, 0x671B, 0xFFFF, 0x6756, 0x6756, 0xFFFF, 0x6B79, 0x6B79, 0xFFFF, 0x6BBA, 0x6BBA, 0xFFFF, 0x6D41, 0x6D41, 0xFFFF }; unsigned short unac_data602[] = { 0x6EDB, 0x6EDB, 0xFFFF, 0x6ECB, 0x6ECB, 0xFFFF, 0x6F22, 0x6F22, 0xFFFF, 0x701E, 0x701E, 0xFFFF, 0x716E, 0x716E, 0xFFFF, 0x77A7, 0x77A7, 0xFFFF, 0x7235, 0x7235, 0xFFFF, 0x72AF, 0x72AF, 0xFFFF }; unsigned short unac_data603[] = { 0x732A, 0x732A, 0xFFFF, 0x7471, 0x7471, 0xFFFF, 0x7506, 0x7506, 0xFFFF, 0x753B, 0x753B, 0xFFFF, 0x761D, 0x761D, 0xFFFF, 0x761F, 0x761F, 0xFFFF, 0x76CA, 0x76CA, 0xFFFF, 0x76DB, 0x76DB, 0xFFFF }; unsigned short unac_data604[] = { 0x76F4, 0x76F4, 0xFFFF, 0x774A, 0x774A, 0xFFFF, 0x7740, 0x7740, 0xFFFF, 0x78CC, 0x78CC, 0xFFFF, 0x7AB1, 0x7AB1, 0xFFFF, 0x7BC0, 0x7BC0, 0xFFFF, 0x7C7B, 0x7C7B, 0xFFFF, 0x7D5B, 0x7D5B, 0xFFFF }; unsigned short unac_data605[] = { 0x7DF4, 0x7DF4, 0xFFFF, 0x7F3E, 0x7F3E, 0xFFFF, 0x8005, 0x8005, 0xFFFF, 0x8352, 0x8352, 0xFFFF, 0x83EF, 0x83EF, 0xFFFF, 0x8779, 0x8779, 0xFFFF, 0x8941, 0x8941, 0xFFFF, 0x8986, 0x8986, 0xFFFF }; unsigned short unac_data606[] = { 0x8996, 0x8996, 0xFFFF, 0x8ABF, 0x8ABF, 0xFFFF, 0x8AF8, 0x8AF8, 0xFFFF, 0x8ACB, 0x8ACB, 0xFFFF, 0x8B01, 0x8B01, 0xFFFF, 0x8AFE, 0x8AFE, 0xFFFF, 0x8AED, 0x8AED, 0xFFFF, 0x8B39, 0x8B39, 0xFFFF }; unsigned short unac_data607[] = { 0x8B8A, 0x8B8A, 0xFFFF, 0x8D08, 0x8D08, 0xFFFF, 0x8F38, 0x8F38, 0xFFFF, 0x9072, 0x9072, 0xFFFF, 0x9199, 0x9199, 0xFFFF, 0x9276, 0x9276, 0xFFFF, 0x967C, 0x967C, 0xFFFF, 0x96E3, 0x96E3, 0xFFFF }; unsigned short unac_data608[] = { 0x9756, 0x9756, 0xFFFF, 0x97DB, 0x97DB, 0xFFFF, 0x97FF, 0x97FF, 0xFFFF, 0x980B, 0x980B, 0xFFFF, 0x983B, 0x983B, 0xFFFF, 0x9B12, 0x9B12, 0xFFFF, 0x9F9C, 0x9F9C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data609[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3B9D, 0x3B9D, 0xFFFF, 0x4018, 0x4018, 0xFFFF, 0x4039, 0x4039, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data610[] = { 0x9F43, 0x9F43, 0xFFFF, 0x9F8E, 0x9F8E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data611[] = { 0x0066, 0x0066, 0x0066, 0x0066, 0x0066, 0x0066, 0x0066, 0x0069, 0x0066, 0x0069, 0x0066, 0x0069, 0x0066, 0x006C, 0x0066, 0x006C, 0x0066, 0x006C, 0x0066, 0x0066, 0x0069, 0x0066, 0x0066, 0x0069, 0x0066, 0x0066, 0x0069, 0x0066, 0x0066, 0x006C, 0x0066, 0x0066, 0x006C, 0x0066, 0x0066, 0x006C, 0x0074, 0x0073, 0x0074, 0x0073, 0x0073, 0x0074, 0x0073, 0x0074, 0x0073, 0x0074, 0x0073, 0x0074, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data612[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0574, 0x0576, 0x0574, 0x0576, 0x0574, 0x0576, 0x0574, 0x0565, 0x0574, 0x0565, 0x0574, 0x0565, 0x0574, 0x056B, 0x0574, 0x056B, 0x0574, 0x056B, 0x057E, 0x0576, 0x057E, 0x0576, 0x057E, 0x0576, 0x0574, 0x056D, 0x0574, 0x056D, 0x0574, 0x056D }; unsigned short unac_data613[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x05D9, 0x05D9, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x05F2, 0x05F2, 0xFFFF }; unsigned short unac_data614[] = { 0x05E2, 0x05E2, 0xFFFF, 0x05D0, 0x05D0, 0xFFFF, 0x05D3, 0x05D3, 0xFFFF, 0x05D4, 0x05D4, 0xFFFF, 0x05DB, 0x05DB, 0xFFFF, 0x05DC, 0x05DC, 0xFFFF, 0x05DD, 0x05DD, 0xFFFF, 0x05E8, 0x05E8, 0xFFFF }; unsigned short unac_data615[] = { 0x05EA, 0x05EA, 0xFFFF, 0x002B, 0x002B, 0xFFFF, 0x05E9, 0x05E9, 0xFFFF, 0x05E9, 0x05E9, 0xFFFF, 0x05E9, 0x05E9, 0xFFFF, 0x05E9, 0x05E9, 0xFFFF, 0x05D0, 0x05D0, 0xFFFF, 0x05D0, 0x05D0, 0xFFFF }; unsigned short unac_data616[] = { 0x05D0, 0x05D0, 0xFFFF, 0x05D1, 0x05D1, 0xFFFF, 0x05D2, 0x05D2, 0xFFFF, 0x05D3, 0x05D3, 0xFFFF, 0x05D4, 0x05D4, 0xFFFF, 0x05D5, 0x05D5, 0xFFFF, 0x05D6, 0x05D6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data617[] = { 0x05D8, 0x05D8, 0xFFFF, 0x05D9, 0x05D9, 0xFFFF, 0x05DA, 0x05DA, 0xFFFF, 0x05DB, 0x05DB, 0xFFFF, 0x05DC, 0x05DC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x05DE, 0x05DE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data618[] = { 0x05E0, 0x05E0, 0xFFFF, 0x05E1, 0x05E1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x05E3, 0x05E3, 0xFFFF, 0x05E4, 0x05E4, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x05E6, 0x05E6, 0xFFFF, 0x05E7, 0x05E7, 0xFFFF }; unsigned short unac_data619[] = { 0x05E8, 0x05E8, 0xFFFF, 0x05E9, 0x05E9, 0xFFFF, 0x05EA, 0x05EA, 0xFFFF, 0x05D5, 0x05D5, 0xFFFF, 0x05D1, 0x05D1, 0xFFFF, 0x05DB, 0x05DB, 0xFFFF, 0x05E4, 0x05E4, 0xFFFF, 0x05D0, 0x05DC, 0x05D0, 0x05DC, 0xFFFF }; unsigned short unac_data620[] = { 0x0671, 0x0671, 0xFFFF, 0x0671, 0x0671, 0xFFFF, 0x067B, 0x067B, 0xFFFF, 0x067B, 0x067B, 0xFFFF, 0x067B, 0x067B, 0xFFFF, 0x067B, 0x067B, 0xFFFF, 0x067E, 0x067E, 0xFFFF, 0x067E, 0x067E, 0xFFFF }; unsigned short unac_data621[] = { 0x067E, 0x067E, 0xFFFF, 0x067E, 0x067E, 0xFFFF, 0x0680, 0x0680, 0xFFFF, 0x0680, 0x0680, 0xFFFF, 0x0680, 0x0680, 0xFFFF, 0x0680, 0x0680, 0xFFFF, 0x067A, 0x067A, 0xFFFF, 0x067A, 0x067A, 0xFFFF }; unsigned short unac_data622[] = { 0x067A, 0x067A, 0xFFFF, 0x067A, 0x067A, 0xFFFF, 0x067F, 0x067F, 0xFFFF, 0x067F, 0x067F, 0xFFFF, 0x067F, 0x067F, 0xFFFF, 0x067F, 0x067F, 0xFFFF, 0x0679, 0x0679, 0xFFFF, 0x0679, 0x0679, 0xFFFF }; unsigned short unac_data623[] = { 0x0679, 0x0679, 0xFFFF, 0x0679, 0x0679, 0xFFFF, 0x06A4, 0x06A4, 0xFFFF, 0x06A4, 0x06A4, 0xFFFF, 0x06A4, 0x06A4, 0xFFFF, 0x06A4, 0x06A4, 0xFFFF, 0x06A6, 0x06A6, 0xFFFF, 0x06A6, 0x06A6, 0xFFFF }; unsigned short unac_data624[] = { 0x06A6, 0x06A6, 0xFFFF, 0x06A6, 0x06A6, 0xFFFF, 0x0684, 0x0684, 0xFFFF, 0x0684, 0x0684, 0xFFFF, 0x0684, 0x0684, 0xFFFF, 0x0684, 0x0684, 0xFFFF, 0x0683, 0x0683, 0xFFFF, 0x0683, 0x0683, 0xFFFF }; unsigned short unac_data625[] = { 0x0683, 0x0683, 0xFFFF, 0x0683, 0x0683, 0xFFFF, 0x0686, 0x0686, 0xFFFF, 0x0686, 0x0686, 0xFFFF, 0x0686, 0x0686, 0xFFFF, 0x0686, 0x0686, 0xFFFF, 0x0687, 0x0687, 0xFFFF, 0x0687, 0x0687, 0xFFFF }; unsigned short unac_data626[] = { 0x0687, 0x0687, 0xFFFF, 0x0687, 0x0687, 0xFFFF, 0x068D, 0x068D, 0xFFFF, 0x068D, 0x068D, 0xFFFF, 0x068C, 0x068C, 0xFFFF, 0x068C, 0x068C, 0xFFFF, 0x068E, 0x068E, 0xFFFF, 0x068E, 0x068E, 0xFFFF }; unsigned short unac_data627[] = { 0x0688, 0x0688, 0xFFFF, 0x0688, 0x0688, 0xFFFF, 0x0698, 0x0698, 0xFFFF, 0x0698, 0x0698, 0xFFFF, 0x0691, 0x0691, 0xFFFF, 0x0691, 0x0691, 0xFFFF, 0x06A9, 0x06A9, 0xFFFF, 0x06A9, 0x06A9, 0xFFFF }; unsigned short unac_data628[] = { 0x06A9, 0x06A9, 0xFFFF, 0x06A9, 0x06A9, 0xFFFF, 0x06AF, 0x06AF, 0xFFFF, 0x06AF, 0x06AF, 0xFFFF, 0x06AF, 0x06AF, 0xFFFF, 0x06AF, 0x06AF, 0xFFFF, 0x06B3, 0x06B3, 0xFFFF, 0x06B3, 0x06B3, 0xFFFF }; unsigned short unac_data629[] = { 0x06B3, 0x06B3, 0xFFFF, 0x06B3, 0x06B3, 0xFFFF, 0x06B1, 0x06B1, 0xFFFF, 0x06B1, 0x06B1, 0xFFFF, 0x06B1, 0x06B1, 0xFFFF, 0x06B1, 0x06B1, 0xFFFF, 0x06BA, 0x06BA, 0xFFFF, 0x06BA, 0x06BA, 0xFFFF }; unsigned short unac_data630[] = { 0x06BB, 0x06BB, 0xFFFF, 0x06BB, 0x06BB, 0xFFFF, 0x06BB, 0x06BB, 0xFFFF, 0x06BB, 0x06BB, 0xFFFF, 0x06D5, 0x06D5, 0xFFFF, 0x06D5, 0x06D5, 0xFFFF, 0x06C1, 0x06C1, 0xFFFF, 0x06C1, 0x06C1, 0xFFFF }; unsigned short unac_data631[] = { 0x06C1, 0x06C1, 0xFFFF, 0x06C1, 0x06C1, 0xFFFF, 0x06BE, 0x06BE, 0xFFFF, 0x06BE, 0x06BE, 0xFFFF, 0x06BE, 0x06BE, 0xFFFF, 0x06BE, 0x06BE, 0xFFFF, 0x06D2, 0x06D2, 0xFFFF, 0x06D2, 0x06D2, 0xFFFF }; unsigned short unac_data632[] = { 0x06D2, 0x06D2, 0xFFFF, 0x06D2, 0x06D2, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data633[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x06AD, 0x06AD, 0xFFFF, 0x06AD, 0x06AD, 0xFFFF, 0x06AD, 0x06AD, 0xFFFF, 0x06AD, 0x06AD, 0xFFFF, 0x06C7, 0x06C7, 0xFFFF }; unsigned short unac_data634[] = { 0x06C7, 0x06C7, 0xFFFF, 0x06C6, 0x06C6, 0xFFFF, 0x06C6, 0x06C6, 0xFFFF, 0x06C8, 0x06C8, 0xFFFF, 0x06C8, 0x06C8, 0xFFFF, 0x06C7, 0x0674, 0x06C7, 0x0674, 0xFFFF, 0x06CB, 0x06CB, 0xFFFF, 0x06CB, 0x06CB, 0xFFFF }; unsigned short unac_data635[] = { 0x06C5, 0x06C5, 0xFFFF, 0x06C5, 0x06C5, 0xFFFF, 0x06C9, 0x06C9, 0xFFFF, 0x06C9, 0x06C9, 0xFFFF, 0x06D0, 0x06D0, 0xFFFF, 0x06D0, 0x06D0, 0xFFFF, 0x06D0, 0x06D0, 0xFFFF, 0x06D0, 0x06D0, 0xFFFF }; unsigned short unac_data636[] = { 0x0649, 0x0649, 0xFFFF, 0x0649, 0x0649, 0xFFFF, 0x0627, 0x064A, 0x0627, 0x064A, 0xFFFF, 0x0627, 0x064A, 0x0627, 0x064A, 0xFFFF, 0x06D5, 0x064A, 0x06D5, 0x064A, 0xFFFF, 0x06D5, 0x064A, 0x06D5, 0x064A, 0xFFFF, 0x0648, 0x064A, 0x0648, 0x064A, 0xFFFF, 0x0648, 0x064A, 0x0648, 0x064A, 0xFFFF }; unsigned short unac_data637[] = { 0x06C7, 0x064A, 0x06C7, 0x064A, 0xFFFF, 0x06C7, 0x064A, 0x06C7, 0x064A, 0xFFFF, 0x06C6, 0x064A, 0x06C6, 0x064A, 0xFFFF, 0x06C6, 0x064A, 0x06C6, 0x064A, 0xFFFF, 0x06C8, 0x064A, 0x06C8, 0x064A, 0xFFFF, 0x06C8, 0x064A, 0x06C8, 0x064A, 0xFFFF, 0x06D0, 0x064A, 0x06D0, 0x064A, 0xFFFF, 0x06D0, 0x064A, 0x06D0, 0x064A, 0xFFFF }; unsigned short unac_data638[] = { 0x06D0, 0x064A, 0x06D0, 0x064A, 0xFFFF, 0x0649, 0x064A, 0x0649, 0x064A, 0xFFFF, 0x0649, 0x064A, 0x0649, 0x064A, 0xFFFF, 0x0649, 0x064A, 0x0649, 0x064A, 0xFFFF, 0x06CC, 0x06CC, 0xFFFF, 0x06CC, 0x06CC, 0xFFFF, 0x06CC, 0x06CC, 0xFFFF, 0x06CC, 0x06CC, 0xFFFF }; unsigned short unac_data639[] = { 0x062C, 0x064A, 0x062C, 0x064A, 0xFFFF, 0x062D, 0x064A, 0x062D, 0x064A, 0xFFFF, 0x0645, 0x064A, 0x0645, 0x064A, 0xFFFF, 0x0649, 0x064A, 0x0649, 0x064A, 0xFFFF, 0x064A, 0x064A, 0x064A, 0x064A, 0xFFFF, 0x0628, 0x062C, 0x0628, 0x062C, 0xFFFF, 0x0628, 0x062D, 0x0628, 0x062D, 0xFFFF, 0x0628, 0x062E, 0x0628, 0x062E, 0xFFFF }; unsigned short unac_data640[] = { 0x0628, 0x0645, 0x0628, 0x0645, 0xFFFF, 0x0628, 0x0649, 0x0628, 0x0649, 0xFFFF, 0x0628, 0x064A, 0x0628, 0x064A, 0xFFFF, 0x062A, 0x062C, 0x062A, 0x062C, 0xFFFF, 0x062A, 0x062D, 0x062A, 0x062D, 0xFFFF, 0x062A, 0x062E, 0x062A, 0x062E, 0xFFFF, 0x062A, 0x0645, 0x062A, 0x0645, 0xFFFF, 0x062A, 0x0649, 0x062A, 0x0649, 0xFFFF }; unsigned short unac_data641[] = { 0x062A, 0x064A, 0x062A, 0x064A, 0xFFFF, 0x062B, 0x062C, 0x062B, 0x062C, 0xFFFF, 0x062B, 0x0645, 0x062B, 0x0645, 0xFFFF, 0x062B, 0x0649, 0x062B, 0x0649, 0xFFFF, 0x062B, 0x064A, 0x062B, 0x064A, 0xFFFF, 0x062C, 0x062D, 0x062C, 0x062D, 0xFFFF, 0x062C, 0x0645, 0x062C, 0x0645, 0xFFFF, 0x062D, 0x062C, 0x062D, 0x062C, 0xFFFF }; unsigned short unac_data642[] = { 0x062D, 0x0645, 0x062D, 0x0645, 0xFFFF, 0x062E, 0x062C, 0x062E, 0x062C, 0xFFFF, 0x062E, 0x062D, 0x062E, 0x062D, 0xFFFF, 0x062E, 0x0645, 0x062E, 0x0645, 0xFFFF, 0x0633, 0x062C, 0x0633, 0x062C, 0xFFFF, 0x0633, 0x062D, 0x0633, 0x062D, 0xFFFF, 0x0633, 0x062E, 0x0633, 0x062E, 0xFFFF, 0x0633, 0x0645, 0x0633, 0x0645, 0xFFFF }; unsigned short unac_data643[] = { 0x0635, 0x062D, 0x0635, 0x062D, 0xFFFF, 0x0635, 0x0645, 0x0635, 0x0645, 0xFFFF, 0x0636, 0x062C, 0x0636, 0x062C, 0xFFFF, 0x0636, 0x062D, 0x0636, 0x062D, 0xFFFF, 0x0636, 0x062E, 0x0636, 0x062E, 0xFFFF, 0x0636, 0x0645, 0x0636, 0x0645, 0xFFFF, 0x0637, 0x062D, 0x0637, 0x062D, 0xFFFF, 0x0637, 0x0645, 0x0637, 0x0645, 0xFFFF }; unsigned short unac_data644[] = { 0x0638, 0x0645, 0x0638, 0x0645, 0xFFFF, 0x0639, 0x062C, 0x0639, 0x062C, 0xFFFF, 0x0639, 0x0645, 0x0639, 0x0645, 0xFFFF, 0x063A, 0x062C, 0x063A, 0x062C, 0xFFFF, 0x063A, 0x0645, 0x063A, 0x0645, 0xFFFF, 0x0641, 0x062C, 0x0641, 0x062C, 0xFFFF, 0x0641, 0x062D, 0x0641, 0x062D, 0xFFFF, 0x0641, 0x062E, 0x0641, 0x062E, 0xFFFF }; unsigned short unac_data645[] = { 0x0641, 0x0645, 0x0641, 0x0645, 0xFFFF, 0x0641, 0x0649, 0x0641, 0x0649, 0xFFFF, 0x0641, 0x064A, 0x0641, 0x064A, 0xFFFF, 0x0642, 0x062D, 0x0642, 0x062D, 0xFFFF, 0x0642, 0x0645, 0x0642, 0x0645, 0xFFFF, 0x0642, 0x0649, 0x0642, 0x0649, 0xFFFF, 0x0642, 0x064A, 0x0642, 0x064A, 0xFFFF, 0x0643, 0x0627, 0x0643, 0x0627, 0xFFFF }; unsigned short unac_data646[] = { 0x0643, 0x062C, 0x0643, 0x062C, 0xFFFF, 0x0643, 0x062D, 0x0643, 0x062D, 0xFFFF, 0x0643, 0x062E, 0x0643, 0x062E, 0xFFFF, 0x0643, 0x0644, 0x0643, 0x0644, 0xFFFF, 0x0643, 0x0645, 0x0643, 0x0645, 0xFFFF, 0x0643, 0x0649, 0x0643, 0x0649, 0xFFFF, 0x0643, 0x064A, 0x0643, 0x064A, 0xFFFF, 0x0644, 0x062C, 0x0644, 0x062C, 0xFFFF }; unsigned short unac_data647[] = { 0x0644, 0x062D, 0x0644, 0x062D, 0xFFFF, 0x0644, 0x062E, 0x0644, 0x062E, 0xFFFF, 0x0644, 0x0645, 0x0644, 0x0645, 0xFFFF, 0x0644, 0x0649, 0x0644, 0x0649, 0xFFFF, 0x0644, 0x064A, 0x0644, 0x064A, 0xFFFF, 0x0645, 0x062C, 0x0645, 0x062C, 0xFFFF, 0x0645, 0x062D, 0x0645, 0x062D, 0xFFFF, 0x0645, 0x062E, 0x0645, 0x062E, 0xFFFF }; unsigned short unac_data648[] = { 0x0645, 0x0645, 0x0645, 0x0645, 0xFFFF, 0x0645, 0x0649, 0x0645, 0x0649, 0xFFFF, 0x0645, 0x064A, 0x0645, 0x064A, 0xFFFF, 0x0646, 0x062C, 0x0646, 0x062C, 0xFFFF, 0x0646, 0x062D, 0x0646, 0x062D, 0xFFFF, 0x0646, 0x062E, 0x0646, 0x062E, 0xFFFF, 0x0646, 0x0645, 0x0646, 0x0645, 0xFFFF, 0x0646, 0x0649, 0x0646, 0x0649, 0xFFFF }; unsigned short unac_data649[] = { 0x0646, 0x064A, 0x0646, 0x064A, 0xFFFF, 0x0647, 0x062C, 0x0647, 0x062C, 0xFFFF, 0x0647, 0x0645, 0x0647, 0x0645, 0xFFFF, 0x0647, 0x0649, 0x0647, 0x0649, 0xFFFF, 0x0647, 0x064A, 0x0647, 0x064A, 0xFFFF, 0x064A, 0x062C, 0x064A, 0x062C, 0xFFFF, 0x064A, 0x062D, 0x064A, 0x062D, 0xFFFF, 0x064A, 0x062E, 0x064A, 0x062E, 0xFFFF }; unsigned short unac_data650[] = { 0x064A, 0x0645, 0x064A, 0x0645, 0xFFFF, 0x064A, 0x0649, 0x064A, 0x0649, 0xFFFF, 0x064A, 0x064A, 0x064A, 0x064A, 0xFFFF, 0x0630, 0x0630, 0xFFFF, 0x0631, 0x0631, 0xFFFF, 0x0649, 0x0649, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF }; unsigned short unac_data651[] = { 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0631, 0x064A, 0x0631, 0x064A, 0xFFFF, 0x0632, 0x064A, 0x0632, 0x064A, 0xFFFF, 0x0645, 0x064A, 0x0645, 0x064A, 0xFFFF, 0x0646, 0x064A, 0x0646, 0x064A, 0xFFFF }; unsigned short unac_data652[] = { 0x0649, 0x064A, 0x0649, 0x064A, 0xFFFF, 0x064A, 0x064A, 0x064A, 0x064A, 0xFFFF, 0x0628, 0x0631, 0x0628, 0x0631, 0xFFFF, 0x0628, 0x0632, 0x0628, 0x0632, 0xFFFF, 0x0628, 0x0645, 0x0628, 0x0645, 0xFFFF, 0x0628, 0x0646, 0x0628, 0x0646, 0xFFFF, 0x0628, 0x0649, 0x0628, 0x0649, 0xFFFF, 0x0628, 0x064A, 0x0628, 0x064A, 0xFFFF }; unsigned short unac_data653[] = { 0x062A, 0x0631, 0x062A, 0x0631, 0xFFFF, 0x062A, 0x0632, 0x062A, 0x0632, 0xFFFF, 0x062A, 0x0645, 0x062A, 0x0645, 0xFFFF, 0x062A, 0x0646, 0x062A, 0x0646, 0xFFFF, 0x062A, 0x0649, 0x062A, 0x0649, 0xFFFF, 0x062A, 0x064A, 0x062A, 0x064A, 0xFFFF, 0x062B, 0x0631, 0x062B, 0x0631, 0xFFFF, 0x062B, 0x0632, 0x062B, 0x0632, 0xFFFF }; unsigned short unac_data654[] = { 0x062B, 0x0645, 0x062B, 0x0645, 0xFFFF, 0x062B, 0x0646, 0x062B, 0x0646, 0xFFFF, 0x062B, 0x0649, 0x062B, 0x0649, 0xFFFF, 0x062B, 0x064A, 0x062B, 0x064A, 0xFFFF, 0x0641, 0x0649, 0x0641, 0x0649, 0xFFFF, 0x0641, 0x064A, 0x0641, 0x064A, 0xFFFF, 0x0642, 0x0649, 0x0642, 0x0649, 0xFFFF, 0x0642, 0x064A, 0x0642, 0x064A, 0xFFFF }; unsigned short unac_data655[] = { 0x0643, 0x0627, 0x0643, 0x0627, 0xFFFF, 0x0643, 0x0644, 0x0643, 0x0644, 0xFFFF, 0x0643, 0x0645, 0x0643, 0x0645, 0xFFFF, 0x0643, 0x0649, 0x0643, 0x0649, 0xFFFF, 0x0643, 0x064A, 0x0643, 0x064A, 0xFFFF, 0x0644, 0x0645, 0x0644, 0x0645, 0xFFFF, 0x0644, 0x0649, 0x0644, 0x0649, 0xFFFF, 0x0644, 0x064A, 0x0644, 0x064A, 0xFFFF }; unsigned short unac_data656[] = { 0x0645, 0x0627, 0x0645, 0x0627, 0xFFFF, 0x0645, 0x0645, 0x0645, 0x0645, 0xFFFF, 0x0646, 0x0631, 0x0646, 0x0631, 0xFFFF, 0x0646, 0x0632, 0x0646, 0x0632, 0xFFFF, 0x0646, 0x0645, 0x0646, 0x0645, 0xFFFF, 0x0646, 0x0646, 0x0646, 0x0646, 0xFFFF, 0x0646, 0x0649, 0x0646, 0x0649, 0xFFFF, 0x0646, 0x064A, 0x0646, 0x064A, 0xFFFF }; unsigned short unac_data657[] = { 0x0649, 0x0649, 0xFFFF, 0x064A, 0x0631, 0x064A, 0x0631, 0xFFFF, 0x064A, 0x0632, 0x064A, 0x0632, 0xFFFF, 0x064A, 0x0645, 0x064A, 0x0645, 0xFFFF, 0x064A, 0x0646, 0x064A, 0x0646, 0xFFFF, 0x064A, 0x0649, 0x064A, 0x0649, 0xFFFF, 0x064A, 0x064A, 0x064A, 0x064A, 0xFFFF, 0x062C, 0x064A, 0x062C, 0x064A, 0xFFFF }; unsigned short unac_data658[] = { 0x062D, 0x064A, 0x062D, 0x064A, 0xFFFF, 0x062E, 0x064A, 0x062E, 0x064A, 0xFFFF, 0x0645, 0x064A, 0x0645, 0x064A, 0xFFFF, 0x0647, 0x064A, 0x0647, 0x064A, 0xFFFF, 0x0628, 0x062C, 0x0628, 0x062C, 0xFFFF, 0x0628, 0x062D, 0x0628, 0x062D, 0xFFFF, 0x0628, 0x062E, 0x0628, 0x062E, 0xFFFF, 0x0628, 0x0645, 0x0628, 0x0645, 0xFFFF }; unsigned short unac_data659[] = { 0x0628, 0x0647, 0x0628, 0x0647, 0xFFFF, 0x062A, 0x062C, 0x062A, 0x062C, 0xFFFF, 0x062A, 0x062D, 0x062A, 0x062D, 0xFFFF, 0x062A, 0x062E, 0x062A, 0x062E, 0xFFFF, 0x062A, 0x0645, 0x062A, 0x0645, 0xFFFF, 0x062A, 0x0647, 0x062A, 0x0647, 0xFFFF, 0x062B, 0x0645, 0x062B, 0x0645, 0xFFFF, 0x062C, 0x062D, 0x062C, 0x062D, 0xFFFF }; unsigned short unac_data660[] = { 0x062C, 0x0645, 0x062C, 0x0645, 0xFFFF, 0x062D, 0x062C, 0x062D, 0x062C, 0xFFFF, 0x062D, 0x0645, 0x062D, 0x0645, 0xFFFF, 0x062E, 0x062C, 0x062E, 0x062C, 0xFFFF, 0x062E, 0x0645, 0x062E, 0x0645, 0xFFFF, 0x0633, 0x062C, 0x0633, 0x062C, 0xFFFF, 0x0633, 0x062D, 0x0633, 0x062D, 0xFFFF, 0x0633, 0x062E, 0x0633, 0x062E, 0xFFFF }; unsigned short unac_data661[] = { 0x0633, 0x0645, 0x0633, 0x0645, 0xFFFF, 0x0635, 0x062D, 0x0635, 0x062D, 0xFFFF, 0x0635, 0x062E, 0x0635, 0x062E, 0xFFFF, 0x0635, 0x0645, 0x0635, 0x0645, 0xFFFF, 0x0636, 0x062C, 0x0636, 0x062C, 0xFFFF, 0x0636, 0x062D, 0x0636, 0x062D, 0xFFFF, 0x0636, 0x062E, 0x0636, 0x062E, 0xFFFF, 0x0636, 0x0645, 0x0636, 0x0645, 0xFFFF }; unsigned short unac_data662[] = { 0x0637, 0x062D, 0x0637, 0x062D, 0xFFFF, 0x0638, 0x0645, 0x0638, 0x0645, 0xFFFF, 0x0639, 0x062C, 0x0639, 0x062C, 0xFFFF, 0x0639, 0x0645, 0x0639, 0x0645, 0xFFFF, 0x063A, 0x062C, 0x063A, 0x062C, 0xFFFF, 0x063A, 0x0645, 0x063A, 0x0645, 0xFFFF, 0x0641, 0x062C, 0x0641, 0x062C, 0xFFFF, 0x0641, 0x062D, 0x0641, 0x062D, 0xFFFF }; unsigned short unac_data663[] = { 0x0641, 0x062E, 0x0641, 0x062E, 0xFFFF, 0x0641, 0x0645, 0x0641, 0x0645, 0xFFFF, 0x0642, 0x062D, 0x0642, 0x062D, 0xFFFF, 0x0642, 0x0645, 0x0642, 0x0645, 0xFFFF, 0x0643, 0x062C, 0x0643, 0x062C, 0xFFFF, 0x0643, 0x062D, 0x0643, 0x062D, 0xFFFF, 0x0643, 0x062E, 0x0643, 0x062E, 0xFFFF, 0x0643, 0x0644, 0x0643, 0x0644, 0xFFFF }; unsigned short unac_data664[] = { 0x0643, 0x0645, 0x0643, 0x0645, 0xFFFF, 0x0644, 0x062C, 0x0644, 0x062C, 0xFFFF, 0x0644, 0x062D, 0x0644, 0x062D, 0xFFFF, 0x0644, 0x062E, 0x0644, 0x062E, 0xFFFF, 0x0644, 0x0645, 0x0644, 0x0645, 0xFFFF, 0x0644, 0x0647, 0x0644, 0x0647, 0xFFFF, 0x0645, 0x062C, 0x0645, 0x062C, 0xFFFF, 0x0645, 0x062D, 0x0645, 0x062D, 0xFFFF }; unsigned short unac_data665[] = { 0x0645, 0x062E, 0x0645, 0x062E, 0xFFFF, 0x0645, 0x0645, 0x0645, 0x0645, 0xFFFF, 0x0646, 0x062C, 0x0646, 0x062C, 0xFFFF, 0x0646, 0x062D, 0x0646, 0x062D, 0xFFFF, 0x0646, 0x062E, 0x0646, 0x062E, 0xFFFF, 0x0646, 0x0645, 0x0646, 0x0645, 0xFFFF, 0x0646, 0x0647, 0x0646, 0x0647, 0xFFFF, 0x0647, 0x062C, 0x0647, 0x062C, 0xFFFF }; unsigned short unac_data666[] = { 0x0647, 0x0645, 0x0647, 0x0645, 0xFFFF, 0x0647, 0x0647, 0xFFFF, 0x064A, 0x062C, 0x064A, 0x062C, 0xFFFF, 0x064A, 0x062D, 0x064A, 0x062D, 0xFFFF, 0x064A, 0x062E, 0x064A, 0x062E, 0xFFFF, 0x064A, 0x0645, 0x064A, 0x0645, 0xFFFF, 0x064A, 0x0647, 0x064A, 0x0647, 0xFFFF, 0x0645, 0x064A, 0x0645, 0x064A, 0xFFFF }; unsigned short unac_data667[] = { 0x0647, 0x064A, 0x0647, 0x064A, 0xFFFF, 0x0628, 0x0645, 0x0628, 0x0645, 0xFFFF, 0x0628, 0x0647, 0x0628, 0x0647, 0xFFFF, 0x062A, 0x0645, 0x062A, 0x0645, 0xFFFF, 0x062A, 0x0647, 0x062A, 0x0647, 0xFFFF, 0x062B, 0x0645, 0x062B, 0x0645, 0xFFFF, 0x062B, 0x0647, 0x062B, 0x0647, 0xFFFF, 0x0633, 0x0645, 0x0633, 0x0645, 0xFFFF }; unsigned short unac_data668[] = { 0x0633, 0x0647, 0x0633, 0x0647, 0xFFFF, 0x0634, 0x0645, 0x0634, 0x0645, 0xFFFF, 0x0634, 0x0647, 0x0634, 0x0647, 0xFFFF, 0x0643, 0x0644, 0x0643, 0x0644, 0xFFFF, 0x0643, 0x0645, 0x0643, 0x0645, 0xFFFF, 0x0644, 0x0645, 0x0644, 0x0645, 0xFFFF, 0x0646, 0x0645, 0x0646, 0x0645, 0xFFFF, 0x0646, 0x0647, 0x0646, 0x0647, 0xFFFF }; unsigned short unac_data669[] = { 0x064A, 0x0645, 0x064A, 0x0645, 0xFFFF, 0x064A, 0x0647, 0x064A, 0x0647, 0xFFFF, 0x0640, 0x0640, 0xFFFF, 0x0640, 0x0640, 0xFFFF, 0x0640, 0x0640, 0xFFFF, 0x0637, 0x0649, 0x0637, 0x0649, 0xFFFF, 0x0637, 0x064A, 0x0637, 0x064A, 0xFFFF, 0x0639, 0x0649, 0x0639, 0x0649, 0xFFFF }; unsigned short unac_data670[] = { 0x0639, 0x064A, 0x0639, 0x064A, 0xFFFF, 0x063A, 0x0649, 0x063A, 0x0649, 0xFFFF, 0x063A, 0x064A, 0x063A, 0x064A, 0xFFFF, 0x0633, 0x0649, 0x0633, 0x0649, 0xFFFF, 0x0633, 0x064A, 0x0633, 0x064A, 0xFFFF, 0x0634, 0x0649, 0x0634, 0x0649, 0xFFFF, 0x0634, 0x064A, 0x0634, 0x064A, 0xFFFF, 0x062D, 0x0649, 0x062D, 0x0649, 0xFFFF }; unsigned short unac_data671[] = { 0x062D, 0x064A, 0x062D, 0x064A, 0xFFFF, 0x062C, 0x0649, 0x062C, 0x0649, 0xFFFF, 0x062C, 0x064A, 0x062C, 0x064A, 0xFFFF, 0x062E, 0x0649, 0x062E, 0x0649, 0xFFFF, 0x062E, 0x064A, 0x062E, 0x064A, 0xFFFF, 0x0635, 0x0649, 0x0635, 0x0649, 0xFFFF, 0x0635, 0x064A, 0x0635, 0x064A, 0xFFFF, 0x0636, 0x0649, 0x0636, 0x0649, 0xFFFF }; unsigned short unac_data672[] = { 0x0636, 0x064A, 0x0636, 0x064A, 0xFFFF, 0x0634, 0x062C, 0x0634, 0x062C, 0xFFFF, 0x0634, 0x062D, 0x0634, 0x062D, 0xFFFF, 0x0634, 0x062E, 0x0634, 0x062E, 0xFFFF, 0x0634, 0x0645, 0x0634, 0x0645, 0xFFFF, 0x0634, 0x0631, 0x0634, 0x0631, 0xFFFF, 0x0633, 0x0631, 0x0633, 0x0631, 0xFFFF, 0x0635, 0x0631, 0x0635, 0x0631, 0xFFFF }; unsigned short unac_data673[] = { 0x0636, 0x0631, 0x0636, 0x0631, 0xFFFF, 0x0637, 0x0649, 0x0637, 0x0649, 0xFFFF, 0x0637, 0x064A, 0x0637, 0x064A, 0xFFFF, 0x0639, 0x0649, 0x0639, 0x0649, 0xFFFF, 0x0639, 0x064A, 0x0639, 0x064A, 0xFFFF, 0x063A, 0x0649, 0x063A, 0x0649, 0xFFFF, 0x063A, 0x064A, 0x063A, 0x064A, 0xFFFF, 0x0633, 0x0649, 0x0633, 0x0649, 0xFFFF }; unsigned short unac_data674[] = { 0x0633, 0x064A, 0x0633, 0x064A, 0xFFFF, 0x0634, 0x0649, 0x0634, 0x0649, 0xFFFF, 0x0634, 0x064A, 0x0634, 0x064A, 0xFFFF, 0x062D, 0x0649, 0x062D, 0x0649, 0xFFFF, 0x062D, 0x064A, 0x062D, 0x064A, 0xFFFF, 0x062C, 0x0649, 0x062C, 0x0649, 0xFFFF, 0x062C, 0x064A, 0x062C, 0x064A, 0xFFFF, 0x062E, 0x0649, 0x062E, 0x0649, 0xFFFF }; unsigned short unac_data675[] = { 0x062E, 0x064A, 0x062E, 0x064A, 0xFFFF, 0x0635, 0x0649, 0x0635, 0x0649, 0xFFFF, 0x0635, 0x064A, 0x0635, 0x064A, 0xFFFF, 0x0636, 0x0649, 0x0636, 0x0649, 0xFFFF, 0x0636, 0x064A, 0x0636, 0x064A, 0xFFFF, 0x0634, 0x062C, 0x0634, 0x062C, 0xFFFF, 0x0634, 0x062D, 0x0634, 0x062D, 0xFFFF, 0x0634, 0x062E, 0x0634, 0x062E, 0xFFFF }; unsigned short unac_data676[] = { 0x0634, 0x0645, 0x0634, 0x0645, 0xFFFF, 0x0634, 0x0631, 0x0634, 0x0631, 0xFFFF, 0x0633, 0x0631, 0x0633, 0x0631, 0xFFFF, 0x0635, 0x0631, 0x0635, 0x0631, 0xFFFF, 0x0636, 0x0631, 0x0636, 0x0631, 0xFFFF, 0x0634, 0x062C, 0x0634, 0x062C, 0xFFFF, 0x0634, 0x062D, 0x0634, 0x062D, 0xFFFF, 0x0634, 0x062E, 0x0634, 0x062E, 0xFFFF }; unsigned short unac_data677[] = { 0x0634, 0x0645, 0x0634, 0x0645, 0xFFFF, 0x0633, 0x0647, 0x0633, 0x0647, 0xFFFF, 0x0634, 0x0647, 0x0634, 0x0647, 0xFFFF, 0x0637, 0x0645, 0x0637, 0x0645, 0xFFFF, 0x0633, 0x062C, 0x0633, 0x062C, 0xFFFF, 0x0633, 0x062D, 0x0633, 0x062D, 0xFFFF, 0x0633, 0x062E, 0x0633, 0x062E, 0xFFFF, 0x0634, 0x062C, 0x0634, 0x062C, 0xFFFF }; unsigned short unac_data678[] = { 0x0634, 0x062D, 0x0634, 0x062D, 0xFFFF, 0x0634, 0x062E, 0x0634, 0x062E, 0xFFFF, 0x0637, 0x0645, 0x0637, 0x0645, 0xFFFF, 0x0638, 0x0645, 0x0638, 0x0645, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data679[] = { 0x062A, 0x062C, 0x0645, 0x062A, 0x062C, 0x0645, 0xFFFF, 0x062A, 0x062D, 0x062C, 0x062A, 0x062D, 0x062C, 0xFFFF, 0x062A, 0x062D, 0x062C, 0x062A, 0x062D, 0x062C, 0xFFFF, 0x062A, 0x062D, 0x0645, 0x062A, 0x062D, 0x0645, 0xFFFF, 0x062A, 0x062E, 0x0645, 0x062A, 0x062E, 0x0645, 0xFFFF, 0x062A, 0x0645, 0x062C, 0x062A, 0x0645, 0x062C, 0xFFFF, 0x062A, 0x0645, 0x062D, 0x062A, 0x0645, 0x062D, 0xFFFF, 0x062A, 0x0645, 0x062E, 0x062A, 0x0645, 0x062E, 0xFFFF }; unsigned short unac_data680[] = { 0x062C, 0x0645, 0x062D, 0x062C, 0x0645, 0x062D, 0xFFFF, 0x062C, 0x0645, 0x062D, 0x062C, 0x0645, 0x062D, 0xFFFF, 0x062D, 0x0645, 0x064A, 0x062D, 0x0645, 0x064A, 0xFFFF, 0x062D, 0x0645, 0x0649, 0x062D, 0x0645, 0x0649, 0xFFFF, 0x0633, 0x062D, 0x062C, 0x0633, 0x062D, 0x062C, 0xFFFF, 0x0633, 0x062C, 0x062D, 0x0633, 0x062C, 0x062D, 0xFFFF, 0x0633, 0x062C, 0x0649, 0x0633, 0x062C, 0x0649, 0xFFFF, 0x0633, 0x0645, 0x062D, 0x0633, 0x0645, 0x062D, 0xFFFF }; unsigned short unac_data681[] = { 0x0633, 0x0645, 0x062D, 0x0633, 0x0645, 0x062D, 0xFFFF, 0x0633, 0x0645, 0x062C, 0x0633, 0x0645, 0x062C, 0xFFFF, 0x0633, 0x0645, 0x0645, 0x0633, 0x0645, 0x0645, 0xFFFF, 0x0633, 0x0645, 0x0645, 0x0633, 0x0645, 0x0645, 0xFFFF, 0x0635, 0x062D, 0x062D, 0x0635, 0x062D, 0x062D, 0xFFFF, 0x0635, 0x062D, 0x062D, 0x0635, 0x062D, 0x062D, 0xFFFF, 0x0635, 0x0645, 0x0645, 0x0635, 0x0645, 0x0645, 0xFFFF, 0x0634, 0x062D, 0x0645, 0x0634, 0x062D, 0x0645, 0xFFFF }; unsigned short unac_data682[] = { 0x0634, 0x062D, 0x0645, 0x0634, 0x062D, 0x0645, 0xFFFF, 0x0634, 0x062C, 0x064A, 0x0634, 0x062C, 0x064A, 0xFFFF, 0x0634, 0x0645, 0x062E, 0x0634, 0x0645, 0x062E, 0xFFFF, 0x0634, 0x0645, 0x062E, 0x0634, 0x0645, 0x062E, 0xFFFF, 0x0634, 0x0645, 0x0645, 0x0634, 0x0645, 0x0645, 0xFFFF, 0x0634, 0x0645, 0x0645, 0x0634, 0x0645, 0x0645, 0xFFFF, 0x0636, 0x062D, 0x0649, 0x0636, 0x062D, 0x0649, 0xFFFF, 0x0636, 0x062E, 0x0645, 0x0636, 0x062E, 0x0645, 0xFFFF }; unsigned short unac_data683[] = { 0x0636, 0x062E, 0x0645, 0x0636, 0x062E, 0x0645, 0xFFFF, 0x0637, 0x0645, 0x062D, 0x0637, 0x0645, 0x062D, 0xFFFF, 0x0637, 0x0645, 0x062D, 0x0637, 0x0645, 0x062D, 0xFFFF, 0x0637, 0x0645, 0x0645, 0x0637, 0x0645, 0x0645, 0xFFFF, 0x0637, 0x0645, 0x064A, 0x0637, 0x0645, 0x064A, 0xFFFF, 0x0639, 0x062C, 0x0645, 0x0639, 0x062C, 0x0645, 0xFFFF, 0x0639, 0x0645, 0x0645, 0x0639, 0x0645, 0x0645, 0xFFFF, 0x0639, 0x0645, 0x0645, 0x0639, 0x0645, 0x0645, 0xFFFF }; unsigned short unac_data684[] = { 0x0639, 0x0645, 0x0649, 0x0639, 0x0645, 0x0649, 0xFFFF, 0x063A, 0x0645, 0x0645, 0x063A, 0x0645, 0x0645, 0xFFFF, 0x063A, 0x0645, 0x064A, 0x063A, 0x0645, 0x064A, 0xFFFF, 0x063A, 0x0645, 0x0649, 0x063A, 0x0645, 0x0649, 0xFFFF, 0x0641, 0x062E, 0x0645, 0x0641, 0x062E, 0x0645, 0xFFFF, 0x0641, 0x062E, 0x0645, 0x0641, 0x062E, 0x0645, 0xFFFF, 0x0642, 0x0645, 0x062D, 0x0642, 0x0645, 0x062D, 0xFFFF, 0x0642, 0x0645, 0x0645, 0x0642, 0x0645, 0x0645, 0xFFFF }; unsigned short unac_data685[] = { 0x0644, 0x062D, 0x0645, 0x0644, 0x062D, 0x0645, 0xFFFF, 0x0644, 0x062D, 0x064A, 0x0644, 0x062D, 0x064A, 0xFFFF, 0x0644, 0x062D, 0x0649, 0x0644, 0x062D, 0x0649, 0xFFFF, 0x0644, 0x062C, 0x062C, 0x0644, 0x062C, 0x062C, 0xFFFF, 0x0644, 0x062C, 0x062C, 0x0644, 0x062C, 0x062C, 0xFFFF, 0x0644, 0x062E, 0x0645, 0x0644, 0x062E, 0x0645, 0xFFFF, 0x0644, 0x062E, 0x0645, 0x0644, 0x062E, 0x0645, 0xFFFF, 0x0644, 0x0645, 0x062D, 0x0644, 0x0645, 0x062D, 0xFFFF }; unsigned short unac_data686[] = { 0x0644, 0x0645, 0x062D, 0x0644, 0x0645, 0x062D, 0xFFFF, 0x0645, 0x062D, 0x062C, 0x0645, 0x062D, 0x062C, 0xFFFF, 0x0645, 0x062D, 0x0645, 0x0645, 0x062D, 0x0645, 0xFFFF, 0x0645, 0x062D, 0x064A, 0x0645, 0x062D, 0x064A, 0xFFFF, 0x0645, 0x062C, 0x062D, 0x0645, 0x062C, 0x062D, 0xFFFF, 0x0645, 0x062C, 0x0645, 0x0645, 0x062C, 0x0645, 0xFFFF, 0x0645, 0x062E, 0x062C, 0x0645, 0x062E, 0x062C, 0xFFFF, 0x0645, 0x062E, 0x0645, 0x0645, 0x062E, 0x0645, 0xFFFF }; unsigned short unac_data687[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0645, 0x062C, 0x062E, 0x0645, 0x062C, 0x062E, 0xFFFF, 0x0647, 0x0645, 0x062C, 0x0647, 0x0645, 0x062C, 0xFFFF, 0x0647, 0x0645, 0x0645, 0x0647, 0x0645, 0x0645, 0xFFFF, 0x0646, 0x062D, 0x0645, 0x0646, 0x062D, 0x0645, 0xFFFF, 0x0646, 0x062D, 0x0649, 0x0646, 0x062D, 0x0649, 0xFFFF, 0x0646, 0x062C, 0x0645, 0x0646, 0x062C, 0x0645, 0xFFFF }; unsigned short unac_data688[] = { 0x0646, 0x062C, 0x0645, 0x0646, 0x062C, 0x0645, 0xFFFF, 0x0646, 0x062C, 0x0649, 0x0646, 0x062C, 0x0649, 0xFFFF, 0x0646, 0x0645, 0x064A, 0x0646, 0x0645, 0x064A, 0xFFFF, 0x0646, 0x0645, 0x0649, 0x0646, 0x0645, 0x0649, 0xFFFF, 0x064A, 0x0645, 0x0645, 0x064A, 0x0645, 0x0645, 0xFFFF, 0x064A, 0x0645, 0x0645, 0x064A, 0x0645, 0x0645, 0xFFFF, 0x0628, 0x062E, 0x064A, 0x0628, 0x062E, 0x064A, 0xFFFF, 0x062A, 0x062C, 0x064A, 0x062A, 0x062C, 0x064A, 0xFFFF }; unsigned short unac_data689[] = { 0x062A, 0x062C, 0x0649, 0x062A, 0x062C, 0x0649, 0xFFFF, 0x062A, 0x062E, 0x064A, 0x062A, 0x062E, 0x064A, 0xFFFF, 0x062A, 0x062E, 0x0649, 0x062A, 0x062E, 0x0649, 0xFFFF, 0x062A, 0x0645, 0x064A, 0x062A, 0x0645, 0x064A, 0xFFFF, 0x062A, 0x0645, 0x0649, 0x062A, 0x0645, 0x0649, 0xFFFF, 0x062C, 0x0645, 0x064A, 0x062C, 0x0645, 0x064A, 0xFFFF, 0x062C, 0x062D, 0x0649, 0x062C, 0x062D, 0x0649, 0xFFFF, 0x062C, 0x0645, 0x0649, 0x062C, 0x0645, 0x0649, 0xFFFF }; unsigned short unac_data690[] = { 0x0633, 0x062E, 0x0649, 0x0633, 0x062E, 0x0649, 0xFFFF, 0x0635, 0x062D, 0x064A, 0x0635, 0x062D, 0x064A, 0xFFFF, 0x0634, 0x062D, 0x064A, 0x0634, 0x062D, 0x064A, 0xFFFF, 0x0636, 0x062D, 0x064A, 0x0636, 0x062D, 0x064A, 0xFFFF, 0x0644, 0x062C, 0x064A, 0x0644, 0x062C, 0x064A, 0xFFFF, 0x0644, 0x0645, 0x064A, 0x0644, 0x0645, 0x064A, 0xFFFF, 0x064A, 0x062D, 0x064A, 0x064A, 0x062D, 0x064A, 0xFFFF, 0x064A, 0x062C, 0x064A, 0x064A, 0x062C, 0x064A, 0xFFFF }; unsigned short unac_data691[] = { 0x064A, 0x0645, 0x064A, 0x064A, 0x0645, 0x064A, 0xFFFF, 0x0645, 0x0645, 0x064A, 0x0645, 0x0645, 0x064A, 0xFFFF, 0x0642, 0x0645, 0x064A, 0x0642, 0x0645, 0x064A, 0xFFFF, 0x0646, 0x062D, 0x064A, 0x0646, 0x062D, 0x064A, 0xFFFF, 0x0642, 0x0645, 0x062D, 0x0642, 0x0645, 0x062D, 0xFFFF, 0x0644, 0x062D, 0x0645, 0x0644, 0x062D, 0x0645, 0xFFFF, 0x0639, 0x0645, 0x064A, 0x0639, 0x0645, 0x064A, 0xFFFF, 0x0643, 0x0645, 0x064A, 0x0643, 0x0645, 0x064A, 0xFFFF }; unsigned short unac_data692[] = { 0x0646, 0x062C, 0x062D, 0x0646, 0x062C, 0x062D, 0xFFFF, 0x0645, 0x062E, 0x064A, 0x0645, 0x062E, 0x064A, 0xFFFF, 0x0644, 0x062C, 0x0645, 0x0644, 0x062C, 0x0645, 0xFFFF, 0x0643, 0x0645, 0x0645, 0x0643, 0x0645, 0x0645, 0xFFFF, 0x0644, 0x062C, 0x0645, 0x0644, 0x062C, 0x0645, 0xFFFF, 0x0646, 0x062C, 0x062D, 0x0646, 0x062C, 0x062D, 0xFFFF, 0x062C, 0x062D, 0x064A, 0x062C, 0x062D, 0x064A, 0xFFFF, 0x062D, 0x062C, 0x064A, 0x062D, 0x062C, 0x064A, 0xFFFF }; unsigned short unac_data693[] = { 0x0645, 0x062C, 0x064A, 0x0645, 0x062C, 0x064A, 0xFFFF, 0x0641, 0x0645, 0x064A, 0x0641, 0x0645, 0x064A, 0xFFFF, 0x0628, 0x062D, 0x064A, 0x0628, 0x062D, 0x064A, 0xFFFF, 0x0643, 0x0645, 0x0645, 0x0643, 0x0645, 0x0645, 0xFFFF, 0x0639, 0x062C, 0x0645, 0x0639, 0x062C, 0x0645, 0xFFFF, 0x0635, 0x0645, 0x0645, 0x0635, 0x0645, 0x0645, 0xFFFF, 0x0633, 0x062E, 0x064A, 0x0633, 0x062E, 0x064A, 0xFFFF, 0x0646, 0x062C, 0x064A, 0x0646, 0x062C, 0x064A, 0xFFFF }; unsigned short unac_data694[] = { 0x0635, 0x0644, 0x06D2, 0x0635, 0x0644, 0x06D2, 0xFFFF, 0x0642, 0x0644, 0x06D2, 0x0642, 0x0644, 0x06D2, 0xFFFF, 0x0627, 0x0644, 0x0644, 0x0647, 0x0627, 0x0644, 0x0644, 0x0647, 0xFFFF, 0x0627, 0x0643, 0x0628, 0x0631, 0x0627, 0x0643, 0x0628, 0x0631, 0xFFFF, 0x0645, 0x062D, 0x0645, 0x062F, 0x0645, 0x062D, 0x0645, 0x062F, 0xFFFF, 0x0635, 0x0644, 0x0639, 0x0645, 0x0635, 0x0644, 0x0639, 0x0645, 0xFFFF, 0x0631, 0x0633, 0x0648, 0x0644, 0x0631, 0x0633, 0x0648, 0x0644, 0xFFFF, 0x0639, 0x0644, 0x064A, 0x0647, 0x0639, 0x0644, 0x064A, 0x0647, 0xFFFF }; unsigned short unac_data695[] = { 0x0648, 0x0633, 0x0644, 0x0645, 0x0648, 0x0633, 0x0644, 0x0645, 0xFFFF, 0x0635, 0x0644, 0x0649, 0x0635, 0x0644, 0x0649, 0xFFFF, 0x0635, 0x0644, 0x0649, 0x0020, 0x0627, 0x0644, 0x0644, 0x0647, 0x0020, 0x0639, 0x0644, 0x064A, 0x0647, 0x0020, 0x0648, 0x0633, 0x0644, 0x0645, 0x0635, 0x0644, 0x0649, 0x0020, 0x0627, 0x0644, 0x0644, 0x0647, 0x0020, 0x0639, 0x0644, 0x064A, 0x0647, 0x0020, 0x0648, 0x0633, 0x0644, 0x0645, 0xFFFF, 0x062C, 0x0644, 0x0020, 0x062C, 0x0644, 0x0627, 0x0644, 0x0647, 0x062C, 0x0644, 0x0020, 0x062C, 0x0644, 0x0627, 0x0644, 0x0647, 0xFFFF, 0x0631, 0x06CC, 0x0627, 0x0644, 0x0631, 0x06CC, 0x0627, 0x0644, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data696[] = { 0x002C, 0x002C, 0xFFFF, 0x3001, 0x3001, 0xFFFF, 0x3002, 0x3002, 0xFFFF, 0x003A, 0x003A, 0xFFFF, 0x003B, 0x003B, 0xFFFF, 0x0021, 0x0021, 0xFFFF, 0x003F, 0x003F, 0xFFFF, 0x3016, 0x3016, 0xFFFF }; unsigned short unac_data697[] = { 0x3017, 0x3017, 0xFFFF, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data698[] = { 0x002E, 0x002E, 0x002E, 0x002E, 0xFFFF, 0x2014, 0x2014, 0xFFFF, 0x2013, 0x2013, 0xFFFF, 0x005F, 0x005F, 0xFFFF, 0x005F, 0x005F, 0xFFFF, 0x0028, 0x0028, 0xFFFF, 0x0029, 0x0029, 0xFFFF, 0x007B, 0x007B, 0xFFFF }; unsigned short unac_data699[] = { 0x007D, 0x007D, 0xFFFF, 0x3014, 0x3014, 0xFFFF, 0x3015, 0x3015, 0xFFFF, 0x3010, 0x3010, 0xFFFF, 0x3011, 0x3011, 0xFFFF, 0x300A, 0x300A, 0xFFFF, 0x300B, 0x300B, 0xFFFF, 0x3008, 0x3008, 0xFFFF }; unsigned short unac_data700[] = { 0x3009, 0x3009, 0xFFFF, 0x300C, 0x300C, 0xFFFF, 0x300D, 0x300D, 0xFFFF, 0x300E, 0x300E, 0xFFFF, 0x300F, 0x300F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x005B, 0x005B, 0xFFFF }; unsigned short unac_data701[] = { 0x005D, 0x005D, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x005F, 0x005F, 0xFFFF, 0x005F, 0x005F, 0xFFFF, 0x005F, 0x005F, 0xFFFF }; unsigned short unac_data702[] = { 0x002C, 0x002C, 0xFFFF, 0x3001, 0x3001, 0xFFFF, 0x002E, 0x002E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x003B, 0x003B, 0xFFFF, 0x003A, 0x003A, 0xFFFF, 0x003F, 0x003F, 0xFFFF, 0x0021, 0x0021, 0xFFFF }; unsigned short unac_data703[] = { 0x2014, 0x2014, 0xFFFF, 0x0028, 0x0028, 0xFFFF, 0x0029, 0x0029, 0xFFFF, 0x007B, 0x007B, 0xFFFF, 0x007D, 0x007D, 0xFFFF, 0x3014, 0x3014, 0xFFFF, 0x3015, 0x3015, 0xFFFF, 0x0023, 0x0023, 0xFFFF }; unsigned short unac_data704[] = { 0x0026, 0x0026, 0xFFFF, 0x002A, 0x002A, 0xFFFF, 0x002B, 0x002B, 0xFFFF, 0x002D, 0x002D, 0xFFFF, 0x003C, 0x003C, 0xFFFF, 0x003E, 0x003E, 0xFFFF, 0x003D, 0x003D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data705[] = { 0x005C, 0x005C, 0xFFFF, 0x0024, 0x0024, 0xFFFF, 0x0025, 0x0025, 0xFFFF, 0x0040, 0x0040, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data706[] = { 0x0020, 0x0020, 0xFFFF, 0x0640, 0x0640, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0640, 0x0640, 0xFFFF }; unsigned short unac_data707[] = { 0x0020, 0x0020, 0xFFFF, 0x0640, 0x0640, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0640, 0x0640, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0640, 0x0640, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x0640, 0x0640, 0xFFFF }; unsigned short unac_data708[] = { 0x0621, 0x0621, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0648, 0x0648, 0xFFFF, 0x0648, 0x0648, 0xFFFF, 0x0627, 0x0627, 0xFFFF }; unsigned short unac_data709[] = { 0x0627, 0x0627, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0627, 0x0627, 0xFFFF, 0x0628, 0x0628, 0xFFFF }; unsigned short unac_data710[] = { 0x0628, 0x0628, 0xFFFF, 0x0628, 0x0628, 0xFFFF, 0x0628, 0x0628, 0xFFFF, 0x0629, 0x0629, 0xFFFF, 0x0629, 0x0629, 0xFFFF, 0x062A, 0x062A, 0xFFFF, 0x062A, 0x062A, 0xFFFF, 0x062A, 0x062A, 0xFFFF }; unsigned short unac_data711[] = { 0x062A, 0x062A, 0xFFFF, 0x062B, 0x062B, 0xFFFF, 0x062B, 0x062B, 0xFFFF, 0x062B, 0x062B, 0xFFFF, 0x062B, 0x062B, 0xFFFF, 0x062C, 0x062C, 0xFFFF, 0x062C, 0x062C, 0xFFFF, 0x062C, 0x062C, 0xFFFF }; unsigned short unac_data712[] = { 0x062C, 0x062C, 0xFFFF, 0x062D, 0x062D, 0xFFFF, 0x062D, 0x062D, 0xFFFF, 0x062D, 0x062D, 0xFFFF, 0x062D, 0x062D, 0xFFFF, 0x062E, 0x062E, 0xFFFF, 0x062E, 0x062E, 0xFFFF, 0x062E, 0x062E, 0xFFFF }; unsigned short unac_data713[] = { 0x062E, 0x062E, 0xFFFF, 0x062F, 0x062F, 0xFFFF, 0x062F, 0x062F, 0xFFFF, 0x0630, 0x0630, 0xFFFF, 0x0630, 0x0630, 0xFFFF, 0x0631, 0x0631, 0xFFFF, 0x0631, 0x0631, 0xFFFF, 0x0632, 0x0632, 0xFFFF }; unsigned short unac_data714[] = { 0x0632, 0x0632, 0xFFFF, 0x0633, 0x0633, 0xFFFF, 0x0633, 0x0633, 0xFFFF, 0x0633, 0x0633, 0xFFFF, 0x0633, 0x0633, 0xFFFF, 0x0634, 0x0634, 0xFFFF, 0x0634, 0x0634, 0xFFFF, 0x0634, 0x0634, 0xFFFF }; unsigned short unac_data715[] = { 0x0634, 0x0634, 0xFFFF, 0x0635, 0x0635, 0xFFFF, 0x0635, 0x0635, 0xFFFF, 0x0635, 0x0635, 0xFFFF, 0x0635, 0x0635, 0xFFFF, 0x0636, 0x0636, 0xFFFF, 0x0636, 0x0636, 0xFFFF, 0x0636, 0x0636, 0xFFFF }; unsigned short unac_data716[] = { 0x0636, 0x0636, 0xFFFF, 0x0637, 0x0637, 0xFFFF, 0x0637, 0x0637, 0xFFFF, 0x0637, 0x0637, 0xFFFF, 0x0637, 0x0637, 0xFFFF, 0x0638, 0x0638, 0xFFFF, 0x0638, 0x0638, 0xFFFF, 0x0638, 0x0638, 0xFFFF }; unsigned short unac_data717[] = { 0x0638, 0x0638, 0xFFFF, 0x0639, 0x0639, 0xFFFF, 0x0639, 0x0639, 0xFFFF, 0x0639, 0x0639, 0xFFFF, 0x0639, 0x0639, 0xFFFF, 0x063A, 0x063A, 0xFFFF, 0x063A, 0x063A, 0xFFFF, 0x063A, 0x063A, 0xFFFF }; unsigned short unac_data718[] = { 0x063A, 0x063A, 0xFFFF, 0x0641, 0x0641, 0xFFFF, 0x0641, 0x0641, 0xFFFF, 0x0641, 0x0641, 0xFFFF, 0x0641, 0x0641, 0xFFFF, 0x0642, 0x0642, 0xFFFF, 0x0642, 0x0642, 0xFFFF, 0x0642, 0x0642, 0xFFFF }; unsigned short unac_data719[] = { 0x0642, 0x0642, 0xFFFF, 0x0643, 0x0643, 0xFFFF, 0x0643, 0x0643, 0xFFFF, 0x0643, 0x0643, 0xFFFF, 0x0643, 0x0643, 0xFFFF, 0x0644, 0x0644, 0xFFFF, 0x0644, 0x0644, 0xFFFF, 0x0644, 0x0644, 0xFFFF }; unsigned short unac_data720[] = { 0x0644, 0x0644, 0xFFFF, 0x0645, 0x0645, 0xFFFF, 0x0645, 0x0645, 0xFFFF, 0x0645, 0x0645, 0xFFFF, 0x0645, 0x0645, 0xFFFF, 0x0646, 0x0646, 0xFFFF, 0x0646, 0x0646, 0xFFFF, 0x0646, 0x0646, 0xFFFF }; unsigned short unac_data721[] = { 0x0646, 0x0646, 0xFFFF, 0x0647, 0x0647, 0xFFFF, 0x0647, 0x0647, 0xFFFF, 0x0647, 0x0647, 0xFFFF, 0x0647, 0x0647, 0xFFFF, 0x0648, 0x0648, 0xFFFF, 0x0648, 0x0648, 0xFFFF, 0x0649, 0x0649, 0xFFFF }; unsigned short unac_data722[] = { 0x0649, 0x0649, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0x064A, 0x064A, 0xFFFF, 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF, 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF, 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF }; unsigned short unac_data723[] = { 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF, 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF, 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF, 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF, 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data724[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0021, 0x0021, 0xFFFF, 0x0022, 0x0022, 0xFFFF, 0x0023, 0x0023, 0xFFFF, 0x0024, 0x0024, 0xFFFF, 0x0025, 0x0025, 0xFFFF, 0x0026, 0x0026, 0xFFFF, 0x0027, 0x0027, 0xFFFF }; unsigned short unac_data725[] = { 0x0028, 0x0028, 0xFFFF, 0x0029, 0x0029, 0xFFFF, 0x002A, 0x002A, 0xFFFF, 0x002B, 0x002B, 0xFFFF, 0x002C, 0x002C, 0xFFFF, 0x002D, 0x002D, 0xFFFF, 0x002E, 0x002E, 0xFFFF, 0x002F, 0x002F, 0xFFFF }; unsigned short unac_data726[] = { 0x0038, 0x0038, 0xFFFF, 0x0039, 0x0039, 0xFFFF, 0x003A, 0x003A, 0xFFFF, 0x003B, 0x003B, 0xFFFF, 0x003C, 0x003C, 0xFFFF, 0x003D, 0x003D, 0xFFFF, 0x003E, 0x003E, 0xFFFF, 0x003F, 0x003F, 0xFFFF }; unsigned short unac_data727[] = { 0x0040, 0x0040, 0xFFFF, 0x0041, 0x0061, 0xFF41, 0x0042, 0x0062, 0xFF42, 0x0043, 0x0063, 0xFF43, 0x0044, 0x0064, 0xFF44, 0x0045, 0x0065, 0xFF45, 0x0046, 0x0066, 0xFF46, 0x0047, 0x0067, 0xFF47 }; unsigned short unac_data728[] = { 0x0048, 0x0068, 0xFF48, 0x0049, 0x0069, 0xFF49, 0x004A, 0x006A, 0xFF4A, 0x004B, 0x006B, 0xFF4B, 0x004C, 0x006C, 0xFF4C, 0x004D, 0x006D, 0xFF4D, 0x004E, 0x006E, 0xFF4E, 0x004F, 0x006F, 0xFF4F }; unsigned short unac_data729[] = { 0x0050, 0x0070, 0xFF50, 0x0051, 0x0071, 0xFF51, 0x0052, 0x0072, 0xFF52, 0x0053, 0x0073, 0xFF53, 0x0054, 0x0074, 0xFF54, 0x0055, 0x0075, 0xFF55, 0x0056, 0x0076, 0xFF56, 0x0057, 0x0077, 0xFF57 }; unsigned short unac_data730[] = { 0x0058, 0x0078, 0xFF58, 0x0059, 0x0079, 0xFF59, 0x005A, 0x007A, 0xFF5A, 0x005B, 0x005B, 0xFFFF, 0x005C, 0x005C, 0xFFFF, 0x005D, 0x005D, 0xFFFF, 0x005E, 0x005E, 0xFFFF, 0x005F, 0x005F, 0xFFFF }; unsigned short unac_data731[] = { 0x0060, 0x0060, 0xFFFF, 0x0061, 0x0061, 0xFFFF, 0x0062, 0x0062, 0xFFFF, 0x0063, 0x0063, 0xFFFF, 0x0064, 0x0064, 0xFFFF, 0x0065, 0x0065, 0xFFFF, 0x0066, 0x0066, 0xFFFF, 0x0067, 0x0067, 0xFFFF }; unsigned short unac_data732[] = { 0x0068, 0x0068, 0xFFFF, 0x0069, 0x0069, 0xFFFF, 0x006A, 0x006A, 0xFFFF, 0x006B, 0x006B, 0xFFFF, 0x006C, 0x006C, 0xFFFF, 0x006D, 0x006D, 0xFFFF, 0x006E, 0x006E, 0xFFFF, 0x006F, 0x006F, 0xFFFF }; unsigned short unac_data733[] = { 0x0070, 0x0070, 0xFFFF, 0x0071, 0x0071, 0xFFFF, 0x0072, 0x0072, 0xFFFF, 0x0073, 0x0073, 0xFFFF, 0x0074, 0x0074, 0xFFFF, 0x0075, 0x0075, 0xFFFF, 0x0076, 0x0076, 0xFFFF, 0x0077, 0x0077, 0xFFFF }; unsigned short unac_data734[] = { 0x0078, 0x0078, 0xFFFF, 0x0079, 0x0079, 0xFFFF, 0x007A, 0x007A, 0xFFFF, 0x007B, 0x007B, 0xFFFF, 0x007C, 0x007C, 0xFFFF, 0x007D, 0x007D, 0xFFFF, 0x007E, 0x007E, 0xFFFF, 0x2985, 0x2985, 0xFFFF }; unsigned short unac_data735[] = { 0x2986, 0x2986, 0xFFFF, 0x3002, 0x3002, 0xFFFF, 0x300C, 0x300C, 0xFFFF, 0x300D, 0x300D, 0xFFFF, 0x3001, 0x3001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data736[] = { 0x1160, 0x1160, 0xFFFF, 0x1100, 0x1100, 0xFFFF, 0x1101, 0x1101, 0xFFFF, 0x11AA, 0x11AA, 0xFFFF, 0x1102, 0x1102, 0xFFFF, 0x11AC, 0x11AC, 0xFFFF, 0x11AD, 0x11AD, 0xFFFF, 0x1103, 0x1103, 0xFFFF }; unsigned short unac_data737[] = { 0x110C, 0x110C, 0xFFFF, 0x110D, 0x110D, 0xFFFF, 0x110E, 0x110E, 0xFFFF, 0x110F, 0x110F, 0xFFFF, 0x1110, 0x1110, 0xFFFF, 0x1111, 0x1111, 0xFFFF, 0x1112, 0x1112, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data738[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1161, 0x1161, 0xFFFF, 0x1162, 0x1162, 0xFFFF, 0x1163, 0x1163, 0xFFFF, 0x1164, 0x1164, 0xFFFF, 0x1165, 0x1165, 0xFFFF, 0x1166, 0x1166, 0xFFFF }; unsigned short unac_data739[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1167, 0x1167, 0xFFFF, 0x1168, 0x1168, 0xFFFF, 0x1169, 0x1169, 0xFFFF, 0x116A, 0x116A, 0xFFFF, 0x116B, 0x116B, 0xFFFF, 0x116C, 0x116C, 0xFFFF }; unsigned short unac_data740[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x116D, 0x116D, 0xFFFF, 0x116E, 0x116E, 0xFFFF, 0x116F, 0x116F, 0xFFFF, 0x1170, 0x1170, 0xFFFF, 0x1171, 0x1171, 0xFFFF, 0x1172, 0x1172, 0xFFFF }; unsigned short unac_data741[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1173, 0x1173, 0xFFFF, 0x1174, 0x1174, 0xFFFF, 0x1175, 0x1175, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data742[] = { 0x00A2, 0x00A2, 0xFFFF, 0x00A3, 0x00A3, 0xFFFF, 0x00AC, 0x00AC, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x00A6, 0x00A6, 0xFFFF, 0x00A5, 0x00A5, 0xFFFF, 0x20A9, 0x20A9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short unac_data743[] = { 0x2502, 0x2502, 0xFFFF, 0x2190, 0x2190, 0xFFFF, 0x2191, 0x2191, 0xFFFF, 0x2192, 0x2192, 0xFFFF, 0x2193, 0x2193, 0xFFFF, 0x25A0, 0x25A0, 0xFFFF, 0x25CB, 0x25CB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; unsigned short* unac_data_table[UNAC_BLOCK_COUNT] = { unac_data0, unac_data1, unac_data2, unac_data3, unac_data4, unac_data5, unac_data6, unac_data7, unac_data8, unac_data9, unac_data10, unac_data11, unac_data12, unac_data13, unac_data14, unac_data15, unac_data16, unac_data17, unac_data18, unac_data19, unac_data20, unac_data21, unac_data22, unac_data23, unac_data24, unac_data25, unac_data26, unac_data27, unac_data28, unac_data29, unac_data30, unac_data31, unac_data32, unac_data33, unac_data34, unac_data35, unac_data36, unac_data37, unac_data38, unac_data39, unac_data40, unac_data41, unac_data42, unac_data43, unac_data44, unac_data45, unac_data46, unac_data47, unac_data48, unac_data49, unac_data50, unac_data51, unac_data52, unac_data53, unac_data54, unac_data55, unac_data56, unac_data57, unac_data58, unac_data59, unac_data60, unac_data61, unac_data62, unac_data63, unac_data64, unac_data65, unac_data66, unac_data67, unac_data68, unac_data69, unac_data70, unac_data71, unac_data72, unac_data73, unac_data74, unac_data75, unac_data76, unac_data77, unac_data78, unac_data79, unac_data80, unac_data81, unac_data82, unac_data83, unac_data84, unac_data85, unac_data86, unac_data87, unac_data88, unac_data89, unac_data90, unac_data91, unac_data92, unac_data93, unac_data94, unac_data95, unac_data96, unac_data97, unac_data98, unac_data99, unac_data100, unac_data101, unac_data102, unac_data103, unac_data104, unac_data105, unac_data106, unac_data107, unac_data108, unac_data109, unac_data110, unac_data111, unac_data112, unac_data113, unac_data114, unac_data115, unac_data116, unac_data117, unac_data118, unac_data119, unac_data120, unac_data121, unac_data122, unac_data123, unac_data124, unac_data125, unac_data126, unac_data127, unac_data128, unac_data129, unac_data130, unac_data131, unac_data132, unac_data133, unac_data134, unac_data135, unac_data136, unac_data137, unac_data138, unac_data139, unac_data140, unac_data141, unac_data142, unac_data143, unac_data144, unac_data145, unac_data146, unac_data147, unac_data148, unac_data149, unac_data150, unac_data151, unac_data152, unac_data153, unac_data154, unac_data155, unac_data156, unac_data157, unac_data158, unac_data159, unac_data160, unac_data161, unac_data162, unac_data163, unac_data164, unac_data165, unac_data166, unac_data167, unac_data168, unac_data169, unac_data170, unac_data171, unac_data172, unac_data173, unac_data174, unac_data175, unac_data176, unac_data177, unac_data178, unac_data179, unac_data180, unac_data181, unac_data182, unac_data183, unac_data184, unac_data185, unac_data186, unac_data187, unac_data188, unac_data189, unac_data190, unac_data191, unac_data192, unac_data193, unac_data194, unac_data195, unac_data196, unac_data197, unac_data198, unac_data199, unac_data200, unac_data201, unac_data202, unac_data203, unac_data204, unac_data205, unac_data206, unac_data207, unac_data208, unac_data209, unac_data210, unac_data211, unac_data212, unac_data213, unac_data214, unac_data215, unac_data216, unac_data217, unac_data218, unac_data219, unac_data220, unac_data221, unac_data222, unac_data223, unac_data224, unac_data225, unac_data226, unac_data227, unac_data228, unac_data229, unac_data230, unac_data231, unac_data232, unac_data233, unac_data234, unac_data235, unac_data236, unac_data237, unac_data238, unac_data239, unac_data240, unac_data241, unac_data242, unac_data243, unac_data244, unac_data245, unac_data246, unac_data247, unac_data248, unac_data249, unac_data250, unac_data251, unac_data252, unac_data253, unac_data254, unac_data255, unac_data256, unac_data257, unac_data258, unac_data259, unac_data260, unac_data261, unac_data262, unac_data263, unac_data264, unac_data265, unac_data266, unac_data267, unac_data268, unac_data269, unac_data270, unac_data271, unac_data272, unac_data273, unac_data274, unac_data275, unac_data276, unac_data277, unac_data278, unac_data279, unac_data280, unac_data281, unac_data282, unac_data283, unac_data284, unac_data285, unac_data286, unac_data287, unac_data288, unac_data289, unac_data290, unac_data291, unac_data292, unac_data293, unac_data294, unac_data295, unac_data296, unac_data297, unac_data298, unac_data299, unac_data300, unac_data301, unac_data302, unac_data303, unac_data304, unac_data305, unac_data306, unac_data307, unac_data308, unac_data309, unac_data310, unac_data311, unac_data312, unac_data313, unac_data314, unac_data315, unac_data316, unac_data317, unac_data318, unac_data319, unac_data320, unac_data321, unac_data322, unac_data323, unac_data324, unac_data325, unac_data326, unac_data327, unac_data328, unac_data329, unac_data330, unac_data331, unac_data332, unac_data333, unac_data334, unac_data335, unac_data336, unac_data337, unac_data338, unac_data339, unac_data340, unac_data341, unac_data342, unac_data343, unac_data344, unac_data345, unac_data346, unac_data347, unac_data348, unac_data349, unac_data350, unac_data351, unac_data352, unac_data353, unac_data354, unac_data355, unac_data356, unac_data357, unac_data358, unac_data359, unac_data360, unac_data361, unac_data362, unac_data363, unac_data364, unac_data365, unac_data366, unac_data367, unac_data368, unac_data369, unac_data370, unac_data371, unac_data372, unac_data373, unac_data374, unac_data375, unac_data376, unac_data377, unac_data378, unac_data379, unac_data380, unac_data381, unac_data382, unac_data383, unac_data384, unac_data385, unac_data386, unac_data387, unac_data388, unac_data389, unac_data390, unac_data391, unac_data392, unac_data393, unac_data394, unac_data395, unac_data396, unac_data397, unac_data398, unac_data399, unac_data400, unac_data401, unac_data402, unac_data403, unac_data404, unac_data405, unac_data406, unac_data407, unac_data408, unac_data409, unac_data410, unac_data411, unac_data412, unac_data413, unac_data414, unac_data415, unac_data416, unac_data417, unac_data418, unac_data419, unac_data420, unac_data421, unac_data422, unac_data423, unac_data424, unac_data425, unac_data426, unac_data427, unac_data428, unac_data429, unac_data430, unac_data431, unac_data432, unac_data433, unac_data434, unac_data435, unac_data436, unac_data437, unac_data438, unac_data439, unac_data440, unac_data441, unac_data442, unac_data443, unac_data444, unac_data445, unac_data446, unac_data447, unac_data448, unac_data449, unac_data450, unac_data451, unac_data452, unac_data453, unac_data454, unac_data455, unac_data456, unac_data457, unac_data458, unac_data459, unac_data460, unac_data461, unac_data462, unac_data463, unac_data464, unac_data465, unac_data466, unac_data467, unac_data468, unac_data469, unac_data470, unac_data471, unac_data472, unac_data473, unac_data474, unac_data475, unac_data476, unac_data477, unac_data478, unac_data479, unac_data480, unac_data481, unac_data482, unac_data483, unac_data484, unac_data485, unac_data486, unac_data487, unac_data488, unac_data489, unac_data490, unac_data491, unac_data492, unac_data493, unac_data494, unac_data495, unac_data496, unac_data497, unac_data498, unac_data499, unac_data500, unac_data501, unac_data502, unac_data503, unac_data504, unac_data505, unac_data506, unac_data507, unac_data508, unac_data509, unac_data510, unac_data511, unac_data512, unac_data513, unac_data514, unac_data515, unac_data516, unac_data517, unac_data518, unac_data519, unac_data520, unac_data521, unac_data522, unac_data523, unac_data524, unac_data525, unac_data526, unac_data527, unac_data528, unac_data529, unac_data530, unac_data531, unac_data532, unac_data533, unac_data534, unac_data535, unac_data536, unac_data537, unac_data538, unac_data539, unac_data540, unac_data541, unac_data542, unac_data543, unac_data544, unac_data545, unac_data546, unac_data547, unac_data548, unac_data549, unac_data550, unac_data551, unac_data552, unac_data553, unac_data554, unac_data555, unac_data556, unac_data557, unac_data558, unac_data559, unac_data560, unac_data561, unac_data562, unac_data563, unac_data564, unac_data565, unac_data566, unac_data567, unac_data568, unac_data569, unac_data570, unac_data571, unac_data572, unac_data573, unac_data574, unac_data575, unac_data576, unac_data577, unac_data578, unac_data579, unac_data580, unac_data581, unac_data582, unac_data583, unac_data584, unac_data585, unac_data586, unac_data587, unac_data588, unac_data589, unac_data590, unac_data591, unac_data592, unac_data593, unac_data594, unac_data595, unac_data596, unac_data597, unac_data598, unac_data599, unac_data600, unac_data601, unac_data602, unac_data603, unac_data604, unac_data605, unac_data606, unac_data607, unac_data608, unac_data609, unac_data610, unac_data611, unac_data612, unac_data613, unac_data614, unac_data615, unac_data616, unac_data617, unac_data618, unac_data619, unac_data620, unac_data621, unac_data622, unac_data623, unac_data624, unac_data625, unac_data626, unac_data627, unac_data628, unac_data629, unac_data630, unac_data631, unac_data632, unac_data633, unac_data634, unac_data635, unac_data636, unac_data637, unac_data638, unac_data639, unac_data640, unac_data641, unac_data642, unac_data643, unac_data644, unac_data645, unac_data646, unac_data647, unac_data648, unac_data649, unac_data650, unac_data651, unac_data652, unac_data653, unac_data654, unac_data655, unac_data656, unac_data657, unac_data658, unac_data659, unac_data660, unac_data661, unac_data662, unac_data663, unac_data664, unac_data665, unac_data666, unac_data667, unac_data668, unac_data669, unac_data670, unac_data671, unac_data672, unac_data673, unac_data674, unac_data675, unac_data676, unac_data677, unac_data678, unac_data679, unac_data680, unac_data681, unac_data682, unac_data683, unac_data684, unac_data685, unac_data686, unac_data687, unac_data688, unac_data689, unac_data690, unac_data691, unac_data692, unac_data693, unac_data694, unac_data695, unac_data696, unac_data697, unac_data698, unac_data699, unac_data700, unac_data701, unac_data702, unac_data703, unac_data704, unac_data705, unac_data706, unac_data707, unac_data708, unac_data709, unac_data710, unac_data711, unac_data712, unac_data713, unac_data714, unac_data715, unac_data716, unac_data717, unac_data718, unac_data719, unac_data720, unac_data721, unac_data722, unac_data723, unac_data724, unac_data725, unac_data726, unac_data727, unac_data728, unac_data729, unac_data730, unac_data731, unac_data732, unac_data733, unac_data734, unac_data735, unac_data736, unac_data737, unac_data738, unac_data739, unac_data740, unac_data741, unac_data742, unac_data743 }; /* Generated by builder. Do not modify. End tables */ /* * Debug level. See unac.h for a detailed discussion of the * values. */ static int debug_level = UNAC_DEBUG_LOW; #ifdef UNAC_DEBUG_AVAILABLE /* * Default debug function, printing on stderr. */ static void debug_doprint_default(const char* message, void* data) { fprintf(stderr, "%s", message); } /* * Default doprint handler is debug_doprint. */ static unac_debug_print_t debug_doprint = debug_doprint_default; /* * Default app data is null. */ static void* debug_appdata = (void*)0; /* * Generate a debug message (arguments ala printf) and * send it to the doprint handler. */ #define DEBUG debug_print("%s:%d: ", __FILE__, __LINE__), debug_print #define DEBUG_APPEND debug_print static void debug_print(const char* message, ...) { #define UNAC_MAXIMUM_MESSAGE_SIZE 512 /* * UNAC_MAXIMUM_MESSAGE_SIZE is supposedly enough but I * do trust some vsnprintf implementations to be bugous. */ char unac_message_buffer[UNAC_MAXIMUM_MESSAGE_SIZE+1] = { '\0' }; va_list args; va_start(args, message); if(vsnprintf(unac_message_buffer, UNAC_MAXIMUM_MESSAGE_SIZE, message, args) < 0) { char tmp[UNAC_MAXIMUM_MESSAGE_SIZE]; sprintf(tmp, "[message larger than %d, truncated]", UNAC_MAXIMUM_MESSAGE_SIZE); debug_doprint(tmp, debug_appdata); } va_end(args); unac_message_buffer[UNAC_MAXIMUM_MESSAGE_SIZE] = '\0'; debug_doprint(unac_message_buffer, debug_appdata); } void unac_debug_callback(int level, unac_debug_print_t function, void* data) { debug_level = level; if(function) debug_doprint = function; debug_appdata = data; } #else /* UNAC_DEBUG_AVAILABLE */ #define DEBUG #define DEBUG_APPEND #endif /* UNAC_DEBUG_AVAILABLE */ /* 0 1 2 are the offsets from base in the position table, keep the value! */ #define UNAC_UNAC 0 #define UNAC_UNACFOLD 1 #define UNAC_FOLD 2 int unacmaybefold_string_utf16(const char* in, size_t in_length, char** outp, size_t* out_lengthp, int what) { char* out; size_t out_size; size_t out_length; size_t i; out_size = in_length > 0 ? in_length : 1024; out = *outp; out = (char*)realloc(out, out_size + 1); if (nullptr == out) { if(debug_level >= UNAC_DEBUG_LOW) DEBUG("realloc %d bytes failed\n", out_size+1); /* *outp is still valid. Let the caller free it */ return -1; } out_length = 0; for(i = 0; i < in_length; i += 2) { unsigned short c; unsigned short* p; size_t l; size_t k; c = (in[i] << 8) | (in[i + 1] & 0xff); /* * Lookup the tables for decomposition information */ #ifdef BUILDING_RECOLL // Exception unac/fold values set by user. There should be 3 arrays for // unac/fold/unac+fold. For now there is only one array, which used to // be set for unac+fold, and is mostly or only used to prevent diacritics // removal for some chars and languages where it should not be done. // In conformance with current usage, but incorrectly, we do the following // things for the special chars depending on the operation requested: // - unaccenting: do nothing (copy original char) // - unac+fold: use table // - fold: use the unicode data. string trans; if (what != UNAC_FOLD && except_trans.size() != 0 && is_except_char(c, trans)) { if (what == UNAC_UNAC) { // Unaccent only. Do nothing p = nullptr; l = 0; } else { // Has to be UNAC_UNACFOLD: use table p = (unsigned short *)trans.c_str(); l = trans.size() / 2; } } else { #endif /* BUILDING_RECOLL */ unac_uf_char_utf16_(c, p, l, what) #ifdef BUILDING_RECOLL } #endif /* BUILDING_RECOLL */ /* * Explain what's done in great detail */ if(debug_level == UNAC_DEBUG_HIGH) { unsigned short index = unac_indexes[(c) >> UNAC_BLOCK_SHIFT]; unsigned char position = (c) & UNAC_BLOCK_MASK; DEBUG("unac_data%d[%d] & unac_positions[%d][%d]: ", index, unac_positions[index][position], index, position+1); DEBUG_APPEND("0x%04x => ", (c)); if(l == 0) { DEBUG_APPEND("untouched\n"); } else { size_t i; for(i = 0; i < l; i++) DEBUG_APPEND("0x%04x ", p[i]); DEBUG_APPEND("\n"); } } /* * Make sure there is enough space to hold the decomposition * Note: a previous realloc may have succeeded, which means that *outp * is not valid any more. We have to do the freeing and zero out *outp */ if(out_length + ((l + 1) * 2) > out_size) { char *saved; out_size += ((l + 1) * 2) + 1024; saved = out; out = (char *)realloc(out, out_size); if(out == 0) { if(debug_level >= UNAC_DEBUG_LOW) DEBUG("realloc %d bytes failed\n", out_size); free(saved); *outp = 0; return -1; } } if(l > 0) { /* l == 1 && *p == 0 is the special case generated for mark characters (which may be found if the input is already in decomposed form. Output nothing */ if (l != 1 || *p != 0) { /* * If there is a decomposition, insert it in the output * string. */ for(k = 0; k < l; k++) { out[out_length++] = (p[k] >> 8) & 0xff; out[out_length++] = (p[k] & 0xff); } } } else { /* * If there is no decomposition leave it unchanged */ out[out_length++] = in[i]; out[out_length++] = in[i + 1]; } } *outp = out; *out_lengthp = out_length; (*outp)[*out_lengthp] = '\0'; return 0; } int unac_string_utf16(const char* in, size_t in_length, char** outp, size_t* out_lengthp) { return unacmaybefold_string_utf16(in, in_length, outp, out_lengthp, UNAC_UNAC); } int unacfold_string_utf16(const char* in, size_t in_length, char** outp, size_t* out_lengthp) { return unacmaybefold_string_utf16(in, in_length, outp, out_lengthp, UNAC_UNACFOLD); } int fold_string_utf16(const char* in, size_t in_length, char** outp, size_t* out_lengthp) { return unacmaybefold_string_utf16(in, in_length, outp, out_lengthp, UNAC_FOLD); } static const char *utf16be = "UTF-16BE"; static iconv_t u8tou16_cd = (iconv_t)-1; static iconv_t u16tou8_cd = (iconv_t)-1; #ifdef __cplusplus static std::mutex o_unac_mutex; #endif /* * Convert buffer containing string encoded in charset into * a string in charset and return it in buffer . The * points to a malloced string large enough to hold the conversion result. * It is the responsibility of the caller to free this array. * The out string is always null terminated. */ static int convert(const char* from, const char* to, const char* in, size_t in_length, char** outp, size_t* out_lengthp) { int ret = -1; iconv_t cd; char* out; size_t out_remain; size_t out_size; char* out_base; int from_utf16, from_utf8, to_utf16, to_utf8, u8tou16, u16tou8; const char space[] = { 0x00, 0x20 }; #ifdef __cplusplus std::unique_lock lock(o_unac_mutex); #endif if (!strcmp(utf16be, from)) { from_utf8 = 0; from_utf16 = 1; } else if (!strcasecmp("UTF-8", from)) { from_utf8 = 1; from_utf16 = 0; } else { from_utf8 = from_utf16 = 0; } if (!strcmp(utf16be, to)) { to_utf8 = 0; to_utf16 = 1; } else if (!strcasecmp("UTF-8", to)) { to_utf8 = 1; to_utf16 = 0; } else { to_utf8 = to_utf16 = 0; } u16tou8 = from_utf16 && to_utf8; u8tou16 = from_utf8 && to_utf16; out_size = in_length > 0 ? in_length : 1024; out = *outp; out = (char *)realloc(out, out_size + 1); if(out == 0) { /* *outp still valid, no freeing */ if(debug_level >= UNAC_DEBUG_LOW) DEBUG("realloc %d bytes failed\n", out_size+1); goto out; } out_remain = out_size; out_base = out; if (u8tou16) { if (u8tou16_cd == (iconv_t)-1) { if((u8tou16_cd = iconv_open(to, from)) == (iconv_t)-1) { goto out; } } else { iconv(u8tou16_cd, 0, 0, 0, 0); } cd = u8tou16_cd; } else if (u16tou8) { if (u16tou8_cd == (iconv_t)-1) { if((u16tou8_cd = iconv_open(to, from)) == (iconv_t)-1) { goto out; } } else { iconv(u16tou8_cd, 0, 0, 0, 0); } cd = u16tou8_cd; } else { if((cd = iconv_open(to, from)) == (iconv_t)-1) { goto out; } } do { if(iconv(cd, (ICONV_CONST char **) &in, &in_length, &out, &out_remain) == (size_t)-1) { switch(errno) { case EILSEQ: /* * If an illegal sequence is found in the context of unac_string * it means the unaccented version of a character contains * a sequence that cannot be mapped back to the original charset. * For instance, the 1/4 character in ISO-8859-1 is decomposed * in three characters including the FRACTION SLASH (2044) which * have no equivalent in the ISO-8859-1 map. One can argue that * the conversions tables should map it to the regular / character * or that a entry should be associated with it. * * To cope with this situation, convert silently transform all * illegal sequences (EILSEQ) into a SPACE character 0x0020. * * In the general conversion case this behaviour is not desirable. * However, it is not the responsibility of this program to cope * with inconsistencies of the Unicode description and a bug report * should be submited to Unicode so that they can fix the problem. * */ if (from_utf16) { const char* tmp = space; size_t tmp_length = 2; if (iconv(cd, (ICONV_CONST char **)&tmp, &tmp_length, &out, &out_remain) == (size_t)-1) { if(errno == E2BIG) { /* fall thru to the E2BIG case below */; } else { goto out; } } else { /* The offending character was replaced by a SPACE, skip it. */ in += 2; in_length -= 2; /* And continue conversion. */ break; } } else { goto out; } case E2BIG: { /* * The output does not fit in the current out buffer, enlarge it. */ size_t length = out - out_base; out_size *= 2; { char *saved = out_base; /* +1 for null */ out_base = (char *)realloc(out_base, out_size + 1); if (out_base == 0) { /* *outp potentially not valid any more. Free here, * and zero out */ if(debug_level >= UNAC_DEBUG_LOW) DEBUG("realloc %d bytes failed\n", out_size+1); free(saved); *outp = 0; goto out; } } out = out_base + length; out_remain = out_size - length; } break; default: goto out; break; } } } while(in_length > 0); if (!u8tou16 && !u16tou8) iconv_close(cd); *outp = out_base; *out_lengthp = out - out_base; (*outp)[*out_lengthp] = '\0'; ret = 0; out: return ret; } int unacmaybefold_string(const char* charset, const char* in, size_t in_length, char** outp, size_t* out_lengthp, int what) { /* * When converting an empty string, skip everything but alloc the * buffer if NULL pointer. */ if (in_length <= 0) { if(!*outp) { if ((*outp = (char*)malloc(32)) == 0) return -1; } (*outp)[0] = '\0'; *out_lengthp = 0; } else { char* utf16 = 0; size_t utf16_length = 0; char* utf16_unaccented = 0; size_t utf16_unaccented_length = 0; if(convert(charset, utf16be, in, in_length, &utf16, &utf16_length) < 0) { return -1; } unacmaybefold_string_utf16(utf16, utf16_length, &utf16_unaccented, &utf16_unaccented_length, what); free(utf16); if(convert(utf16be, charset, utf16_unaccented, utf16_unaccented_length, outp, out_lengthp) < 0) { return -1; } free(utf16_unaccented); } return 0; } int unac_string( const char* charset, const char* in, size_t in_length, char** outp, size_t* out_lengthp) { return unacmaybefold_string(charset, in, in_length, outp, out_lengthp, UNAC_UNAC); } int unacfold_string( const char* charset, const char* in, size_t in_length, char** outp, size_t* out_lengthp) { return unacmaybefold_string(charset, in, in_length, outp, out_lengthp, UNAC_UNACFOLD); } int fold_string( const char* charset, const char* in, size_t in_length, char** outp, size_t* out_lengthp) { return unacmaybefold_string(charset, in, in_length, outp, out_lengthp, UNAC_FOLD); } const char* unac_version(void) { return UNAC_VERSION; } #ifdef BUILDING_RECOLL void unac_set_except_translations(const char *spectrans) { except_trans.clear(); if (!spectrans || !spectrans[0]) return; // The translation tables out of Unicode are in machine byte order (we // just let the compiler read the values). // For the translation part, we need to choose our encoding in accordance ) // (16BE or 16LE depending on processor) // On the contrary, the source char is always to be compared to // the input text, which is encoded in UTF-16BE ... What a mess. static const char *machinecoding = 0; bool littleendian = true; if (machinecoding == 0) { const char* charshort = "\001\002"; short *ip = (short *)charshort; if (*ip == 0x0102) { littleendian = false; machinecoding = "UTF-16BE"; } else { littleendian = true; machinecoding = "UTF-16LE"; } } vector vtrans; stringToStrings(spectrans, vtrans); for (const auto& trans : vtrans) { /* Convert the whole thing to utf-16be/le according to endianness */ char *out = 0; size_t outsize; if (convert("UTF-8", machinecoding, trans.c_str(), trans.size(), &out, &outsize) != 0 || outsize < 2) continue; /* The source char must be utf-16be as this is what we convert the input text to for internal processing */ unsigned short ch; if (littleendian) ch = (out[1] << 8) | (out[0] & 0xff); else ch = (out[0] << 8) | (out[1] & 0xff); except_trans[ch] = string((const char *)(out + 2), outsize-2); free(out); } } #endif /* BUILDING_RECOLL */ recoll-1.36.1/unac/unac_version.h0000644000175000017500000000003514410615043013571 00000000000000#define UNAC_VERSION "1.7.0" recoll-1.36.1/unac/COPYING0000644000175000017500000004310614410615043011766 00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. recoll-1.36.1/unac/unac.cpp0000644000175000017500000000002214410615043012353 00000000000000#include "unac.c" recoll-1.36.1/unac/README.recoll0000644000175000017500000000061114410615043013064 00000000000000This is a stripped down and modified version of unac-1.7.0 You can find the full original distribution at http://www.senga.org/unac/ You can find the full version used by Recoll at the following url: http://bitbucket.org/medoc/recoll/src/tip/unac/ See AUTHORS and COPYING for tributes, etc. Dont bother Loic Dachary about bugs in this version, you can find the culprit here: jfd@recoll.org recoll-1.36.1/unac/README0000644000175000017500000000626414410615043011617 00000000000000$Header: /cvsroot/unac/unac/README,v 1.5 2002/09/02 10:40:09 loic Exp $ What is it ? ------------ unac is a C library that removes accents from characters, regardless of the character set (ISO-8859-15, ISO-CELTIC, KOI8-RU...) as long as iconv(3) is able to convert it into UTF-16 (Unicode). For instance the string t will become ete. It provides a command line interface (unaccent) that removes accents from an input flow or a string given in argument. When using the library function or the command, the charset of the input must be specified. The input is converted to UTF-16 using iconv(3), accents are removed and the result is converted back to the original charset. The iconv -l command on GNU/Linux will show all charset supported. Where is the documentation ? ---------------------------- The manual page of the unaccent command : man unaccent. The manual page of the unac library : man unac. How to install it ? ------------------- For OS that are not GNU/Linux we recommend to use the iconv library provided by Bruno Haible at ftp://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.8.tar.gz. ./configure [--with-iconv=/my/local] make all make check make install How to link with unac ? ------------------------- Assuming you've installed unac in the /usr/local directory use something similar to the following: In the sources: ... #include ... On the command line: cc -I/usr/local/include -o prog prog.cc -L/usr/local/lib -lunac Where can I download it ? ------------------------- The main distribution site is http://www.senga.org/unac/. What is the license ? --------------------- unac is distributed under the GNU GPL, as found at http://www.gnu.org/licenses/gpl.txt. Unicode data files are under the following license, which is compatible with the GNU GPL: http://www.unicode.org/Public/3.2-Update/UnicodeData-3.2.0.html#UCD_Terms UCD Terms of Use Disclaimer The Unicode Character Database is provided as is by Unicode, Inc. No claims are made as to fitness for any particular purpose. No warranties of any kind are expressed or implied. The recipient agrees to determine applicability of information provided. If this file has been purchased on magnetic or optical media from Unicode, Inc., the sole remedy for any claim will be exchange of defective media within 90 days of receipt. This disclaimer is applicable for all other data files accompanying the Unicode Character Database, some of which have been compiled by the Unicode Consortium, and some of which have been supplied by other sources. Limitations on Rights to Redistribute This Data Recipient is granted the right to make copies in any form for internal distribution and to freely use the information supplied in the creation of products supporting the Unicode(TM) Standard. The files in the Unicode Character Database can be redistributed to third parties or other organizations (whether for profit or not) as long as this notice and the disclaimer notice are retained. Information can be extracted from these files and used in documentation or programs, as long as there is an accompanying notice indicating the source. Loic Dachary loic@senga.org http://www.senga.org/ recoll-1.36.1/compile0000755000175000017500000001635014521160720011364 00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2021 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN* | MSYS*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/* | msys/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: recoll-1.36.1/configure.ac0000644000175000017500000004543314515753273012316 00000000000000AC_INIT([Recoll],[m4_esyscmd_s(cat RECOLL-VERSION.txt)]) AC_CONFIG_HEADERS([common/autoconfig.h]) AH_BOTTOM([#include "conf_post.h"]) AC_PREREQ([2.69]) AC_CONFIG_SRCDIR(index/recollindex.cpp) AM_INIT_AUTOMAKE([1.10 no-define subdir-objects foreign]) AC_DISABLE_STATIC LT_INIT AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/iconv.m4]) AM_ICONV INCICONV=$CPPFLAGS LIBICONV=$LTLIBICONV AC_PROG_CXX # AC_PROG_CXX used to set CXX to C when no compiler was found, but now it's # g++. So actually try to build a program to verify the compiler. if test C$CXX = C ; then AC_MSG_ERROR([C++ compiler needed. Please install one (ie: gnu g++)]) fi AC_LANG_PUSH([C++]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[rcl_link_ok=yes],[rcl_link_ok=no]) if test "$rcl_link_ok" = "no" ; then AC_MSG_ERROR([No working C++ compiler was found]) fi AC_LANG_POP([C++]) AC_CANONICAL_HOST # We want librecoll.so to have no undefined symbols because it makes our # life easier when building the loadable modules for, e.g. Python Can't # make sense of the libtool -no-undefined flag. It seems to do nothing on # Linux. Otoh, -Wl,--no-undefined or -z,defs is not portable (e.g. does not # work on the mac). So set this link flag as a system-dependant value here case $host_os in linux*) NO_UNDEF_LINK_FLAG=-Wl,--no-undefined ;; esac AC_PROG_YACC LT_INIT AC_SYS_LARGEFILE # OpenBSD needs sys/param.h for mount.h to compile AC_CHECK_HEADERS([sys/param.h, spawn.h]) AC_CHECK_HEADERS([sys/mount.h sys/statfs.h sys/statvfs.h sys/vfs.h malloc.h malloc/malloc.h], [], [], [#ifdef HAVE_SYS_PARAM_H # include #endif ]) # Used with glibc for feature detection AC_CHECK_HEADERS([features.h]) AC_CHECK_FUNCS([posix_spawn setrlimit kqueue vsnprintf malloc_trim posix_fadvise]) AC_CHECK_FUNCS(mkdtemp) if test "x$ac_cv_func_posix_spawn" = xyes; then : AC_ARG_ENABLE(posix_spawn, AS_HELP_STRING([--enable-posix_spawn],[Enable the use of posix_spawn().]), posixSpawnEnabled=$enableval, posixSpawnEnabled=no) fi if test X$posixSpawnEnabled = Xyes ; then AC_DEFINE(USE_POSIX_SPAWN, 1, [Use posix_spawn()]) fi AC_CHECK_LIB([pthread], [pthread_create], [], []) AC_SEARCH_LIBS([dlopen], [dl], [], []) if test X$ac_cv_search_function != Xno ; then AC_DEFINE(HAVE_DLOPEN, 1, [dlopen function is available]) fi AC_CHECK_LIB([z], [zlibVersion], [], [AC_MSG_ERROR([libz not found])]) ############# Putenv AC_MSG_CHECKING(for type of string parameter to putenv) AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ putenv((const char *)0); ]])],[rcl_putenv_string_const="1"],[rcl_putenv_string_const="0"]) if test X$rcl_putenv_string_const = X1 ; then AC_DEFINE(PUTENV_ARG_CONST, 1, [putenv parameter is const]) fi AC_LANG_POP([C++]) PKG_CHECK_MODULES([XSLT], [libxslt], [], AC_MSG_ERROR([libxslt])) # Use specific 'file' command ? (Useful on solaris to specify # /usr/local/bin/file instead of the system's which doesn't understand '-i' AC_ARG_WITH(file-command, AS_HELP_STRING([--with-file-command],[Specify version of 'file' command (ie: --with-file-command=/usr/local/bin/file)]), withFileCommand=$withval, withFileCommand=file) case $withFileCommand in file) AC_PATH_PROG(fileProg, file);; *) fileProg=$withFileCommand;; esac if test ! -x "$fileProg"; then AC_MSG_ERROR([$fileProg does not exist or is not executable]) fi AC_DEFINE_UNQUOTED(FILE_PROG, "$fileProg", [Path to the file program]) # Use aspell to provide spelling expansions ? # The default is yes. If we do find an aspell installation, we use it. Else # we do compile the aspell module using an internal copy of aspell.h # Only --with-aspell=no will completely disable aspell support AC_ARG_WITH(aspell, AS_HELP_STRING([--without-aspell],[Disable use of aspell spelling package to provide term expansion to other spellings]), withAspell=$withval, withAspell=yes) case $withAspell in no);; yes) AC_PATH_PROG(aspellProg, aspell) ;; *) # The argument should be the path to the aspell program aspellProg=$withAspell ;; esac if test X$withAspell != Xno ; then AC_DEFINE(RCL_USE_ASPELL, 1, [Compile the aspell interface]) if test X$aspellProg != X ; then aspellBase=`dirname $aspellProg` aspellBase=`dirname $aspellBase` AC_DEFINE_UNQUOTED(ASPELL_PROG, "$aspellProg", [Path to the aspell program]) fi fi if test -f /usr/include/sys/inotify.h -o -f /usr/include/linux/inotify.h; then inot_default=yes else inot_default=no fi # Real time monitoring with inotify AC_ARG_WITH(inotify, AS_HELP_STRING([--with-inotify],[Use inotify for almost real time indexing of modified files (the default is yes on Linux).]), withInotify=$withval, withInotify=$inot_default) if test X$withInotify != Xno ; then AC_MSG_NOTICE([enabled support for inotify monitoring]) AC_DEFINE(RCL_MONITOR, 1, [Real time monitoring option]) AC_DEFINE(RCL_USE_INOTIFY, 1, [Compile the inotify interface]) else AC_MSG_NOTICE([inotify not found, inotify monitoring disabled]) fi # Real time monitoring with FAM AC_ARG_WITH(fam, AS_HELP_STRING([--with-fam],[Use File Alteration Monitor for almost real time indexing of modified files. Give the fam/gamin library as argument (ie: /usr/lib/libfam.so) if configure does not find the right one.]), withFam=$withval, withFam=yes) if test X$withFam != Xno -a X$withInotify != Xno ; then AC_MSG_NOTICE([FAM support enabled but inotify support also enabled. Disabling FAM support and using inotify]) withFam=no fi famLib="" case $withFam in no);; yes) for dir in /usr/local/lib ${libdir};do if test -f $dir/libfam.so ; then famLib=$dir/libfam.so;break;fi done if test X$famLib = X ; then AC_MSG_NOTICE([FAM library not found, disabling FAM and real time indexing support]) withFam=no fi ;; *) # The argument should be the path to the fam library famLib=$withFam ;; esac if test X$withFam != Xno ; then AC_DEFINE(RCL_MONITOR, 1, [Real time monitoring option]) AC_DEFINE(RCL_USE_FAM, 1, [Compile the fam interface]) if test X$famLib != X ; then famLibDir=`dirname $famLib` famBase=`dirname $famLibDir` famBLib=`basename $famLib .so | sed -e s/lib//` if test ! -f $famBase/include/fam.h ; then AC_MSG_ERROR([fam.h not found in $famBase/include. Specify --with-fam=no to disable fam support]) fi LIBFAM="-L$famLibDir -l$famBLib" AC_MSG_NOTICE([fam library directive: $LIBFAM]) AC_DEFINE_UNQUOTED(FAM_INCLUDE, "$famBase/include/fam.h", [Path to the fam api include file]) else AC_MSG_ERROR([fam library not found]) fi fi # Enable use of threads in the indexing pipeline. # Disabled by default on OS X as this actually hurts performance. # Also disabled on Windows (which does not use configure, see autoconfig-win.h) case ${host_os} in darwin*) AC_ARG_ENABLE(idxthreads, [--enable-idxthreads Enable multithread indexing.], idxthreadsEnabled=$enableval, idxthreadsEnabled=no) ;; *) AC_ARG_ENABLE(idxthreads, [--disable-idxthreads Disable multithread indexing.], idxthreadsEnabled=$enableval, idxthreadsEnabled=yes) ;; esac AM_CONDITIONAL(NOTHREADS, [test X$idxthreadsEnabled = Xno]) if test X$idxthreadsEnabled = Xyes ; then AC_DEFINE(IDX_THREADS, 1, [Use multiple threads for indexing]) fi AC_ARG_ENABLE(publiclib, AS_HELP_STRING([--disable-publiclib], [Do not install the recoll library in $libdir: use a private directory instead.]), publiclibEnabled=$enableval, publiclibEnabled=yes) AM_CONDITIONAL(PUBLIC_LIB, [test X$publiclibEnabled = Xyes]) if test X$publiclibEnabled = Xyes; then RECOLL_PUBLIC_LIB=1 else RECOLL_PUBLIC_LIB=0 fi # Enable CamelCase word splitting. This is optional because it causes # problems with phrases: with camelcase enabled, "MySQL manual" # will be matched by "MySQL manual" and "my sql manual" but not # "mysql manual" (which would need increased slack as manual is now at pos # 2 instead of 1 AC_ARG_ENABLE(camelcase, AS_HELP_STRING([--enable-camelcase], [Enable splitting camelCase words. This is not enabled by default as it makes phrase matches more difficult: you need to use matching case in the phrase query to get a match. Ie querying for "MySQL manual" and "my sql manual" are the same, but not the same as "mysql manual" (in phrases only and you could raise the phrase slack to get a match).]), camelcaseEnabled=$enableval, camelcaseEnabled=no) if test X$camelcaseEnabled = Xyes ; then AC_DEFINE(RCL_SPLIT_CAMELCASE, 1, [Split camelCase words]) fi AC_ARG_ENABLE(testmains, AS_HELP_STRING([--enable-testmains],[Enable building small test drivers. These are not unit tests.]), buildtestmains=$enableval, buildtestmains=no) AM_CONDITIONAL([COND_TESTMAINS], [test "$buildtestmains" = yes]) AC_ARG_ENABLE(rclgrep, AS_HELP_STRING([--enable-rclgrep],[Enable building the index-less search tool.]), buildrclgrep=$enableval, buildrclgrep=no) AM_CONDITIONAL([COND_RCLGREP], [test "$buildrclgrep" = yes]) # Disable building the python module. AC_ARG_ENABLE(python-module, AS_HELP_STRING([--disable-python-module],[Do not build the Python module.]), pythonEnabled=$enableval, pythonEnabled=yes) AM_CONDITIONAL(MAKEPYTHON, [test X$pythonEnabled = Xyes]) # Disable building the libchm python wrapper AC_ARG_ENABLE(python-chm, AS_HELP_STRING([--disable-python-chm], [Do not build the libchm Python wrapper.]), pythonChmEnabled=$enableval, pythonChmEnabled=yes) if test X$pythonChmEnabled = Xyes; then AC_CHECK_LIB([chm], [chm_resolve_object], [], [AC_MSG_ERROR([--enable-python-chm is set but libchm is not found])]) fi AM_CONDITIONAL(MAKEPYTHONCHM, [test X$pythonChmEnabled = Xyes]) # Disable building the aspell python wrapper. This DISABLES aspell usage in recoll. The default # aspell "pipe" interface is inadequate for recoll use because it does not list suggestions for # words which are part of the dictionary, which is usually the case for common mispellings (which # are bound to be found somewhere in the document set). AC_ARG_ENABLE(python-aspell, AS_HELP_STRING([--disable-python-aspell], [Do not build the aspell Python wrapper.]), pythonAspellEnabled=$enableval, pythonAspellEnabled=yes) if test X$pythonAspellEnabled = Xyes; then AC_CHECK_LIB([aspell], [aspell_speller_suggest], [], [AC_MSG_ERROR([--enable-python-aspell is set but libaspell is not found])]) fi AM_CONDITIONAL(MAKEPYTHONASPELL, [test X$pythonAspellEnabled = Xyes]) AC_ARG_ENABLE(indexer, AS_HELP_STRING([--disable-indexer],[Disable building the recollindex indexer.]), enableINDEXER=$enableval, enableINDEXER="yes") AM_CONDITIONAL(MAKEINDEXER, [test X$enableINDEXER = Xyes]) AC_ARG_ENABLE(xadump, AS_HELP_STRING([--enable-xadump],[Enable building the xadump low level Xapian access program.]), enableXADUMP=$enableval, enableXADUMP="no") AM_CONDITIONAL(MAKEXADUMP, [test X$enableXADUMP = Xyes]) AC_ARG_ENABLE(ext4-birthtime, AS_HELP_STRING([--enable-ext4-birthtime],[Enable indexing to support birthtime metadata for ext4 file systems.]), enableBirthtime=$enableval, enableBirthtime=no) AM_CONDITIONAL([COND_BTIME], [test "$enableBirthtime" = yes]) if test x"$enableBirthtime" = xyes; then DEF_EXT4_BIRTH_TIME=1 else DEF_EXT4_BIRTH_TIME=0 fi AC_ARG_ENABLE(userdoc, AS_HELP_STRING([--disable-userdoc],[Disable building the user manual. (Avoids the need for docbook xml/xsl files and TeX tools.]), enableUserdoc=$enableval, enableUserdoc="yes") AM_CONDITIONAL(MAKEUSERDOC, [test X$enableUserdoc = Xyes]) #### QT AC_ARG_ENABLE(qtgui, AS_HELP_STRING([--disable-qtgui],[Disable the QT-based graphical user interface.]), enableQT=$enableval, enableQT="yes") AM_CONDITIONAL(MAKEQT, [test X$enableQT = Xyes]) AC_ARG_ENABLE(recollq, AS_HELP_STRING([--enable-recollq],[Enable building the recollq command line query tool (recoll -t without need for Qt). This is done by default if --disable-qtgui is set but this option enables forcing it.]), enableRECOLLQ=$enableval, enableRECOLLQ="no") if test X"$enableRECOLLQ" != X ; then AM_CONDITIONAL(MAKECMDLINE, [test X$enableRECOLLQ = Xyes]) else AM_CONDITIONAL(MAKECMDLINE, [test X$enableQT = Xno]) fi if test X$enableQT = Xyes ; then if test X$QTDIR != X ; then PATH=$PATH:$QTDIR/bin export PATH fi if test X$QMAKE = X ; then QMAKE=qmake fi case $QMAKE in */*) QMAKEPATH=$QMAKE;; *) AC_PATH_PROG([QMAKEPATH], $QMAKE, NOTFOUND);; esac if test X$QMAKEPATH = XNOTFOUND ; then AC_MSG_ERROR([Cannot find the qmake program. Maybe you need to install qt development files and tools and/or set the QTDIR environment variable?]) fi QMAKE=$QMAKEPATH QTGUI=qtgui ##### Using Qt webkit for reslist display? Else Qt textbrowser AC_ARG_ENABLE(webkit, AS_HELP_STRING([--disable-webkit],[Disable use of qt-webkit (only meaningful if qtgui is enabled).]), enableWebkit=$enableval, enableWebkit="yes") if test "$enableWebkit" = "yes" ; then QMAKE_ENABLE_WEBKIT="" QMAKE_DISABLE_WEBKIT="#" else QMAKE_ENABLE_WEBKIT="#" QMAKE_DISABLE_WEBKIT="" fi AC_ARG_ENABLE(webengine, AS_HELP_STRING([--enable-webengine],[Enable use of qt-webengine (only meaningful if qtgui is enabled), in place or qt-webkit.]), enableWebengine=$enableval, enableWebengine="no") if test "$enableWebengine" = "yes" ; then QMAKE_ENABLE_WEBENGINE="" QMAKE_DISABLE_WEBENGINE="#" QMAKE_ENABLE_WEBKIT="#" QMAKE_DISABLE_WEBKIT="" else QMAKE_ENABLE_WEBENGINE="#" QMAKE_DISABLE_WEBENGINE="" fi ##### Using QZeitGeist lib ? Default no for now AC_ARG_WITH(qzeitgeist, AS_HELP_STRING([--with-qzeitgeist],[Enable the use of the qzeitgeist library to send zeitgeist events.]), withQZeitgeist=$withval, withQZeitgeist="no") case "$withQZeitgeist" in no) LIBQZEITGEIST=;; yes) LIBQZEITGEIST=-lqzeitgeist;; *) LIBQZEITGEIST=$withQZeitgeist;; esac if test "$withQZeitgeist" != "no" ; then QMAKE_ENABLE_ZEITGEIST="" QMAKE_DISABLE_ZEITGEIST="#" else QMAKE_ENABLE_ZEITGEIST="#" QMAKE_DISABLE_ZEITGEIST="" fi # Retain debugging symbols in GUI recoll ? This makes it enormous (~50MB) AC_ARG_ENABLE(guidebug, AS_HELP_STRING([--enable-guidebug],[Generate and retain debug symbols in GUI program (makes the file very big).]), enableGuiDebug=$enableval, enableGuiDebug="no") if test "$enableGuiDebug" = "yes" ; then QMAKE_ENABLE_GUIDEBUG="" else QMAKE_ENABLE_GUIDEBUG="#" fi AC_CONFIG_FILES($QTGUI/recoll.pro) ##################### End QT stuff fi dnl Borrow a macro definition from pkg.config, dnl for older installs that lack it. m4_ifndef([PKG_CHECK_VAR], [ dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR ]) ### Systemd AC_ARG_WITH([systemd], AS_HELP_STRING([--without-systemd],[Disable installation of the systemd unit files.])) AC_ARG_WITH([system-unit-dir], AS_HELP_STRING([--with-system-unit-dir=DIR],[Install location for systemd system unit files]), [SYSTEMD_SYSTEM_UNIT_DIR="$withval"], [PKG_CHECK_VAR([SYSTEMD_SYSTEM_UNIT_DIR], [systemd], [systemdsystemunitdir])]) AC_ARG_WITH([user-unit-dir], AS_HELP_STRING([--with-user-unit-dir=DIR],[Install location for systemd user unit files]), [SYSTEMD_USER_UNIT_DIR="$withval"], [PKG_CHECK_VAR([SYSTEMD_USER_UNIT_DIR], [systemd], [systemduserunitdir])]) if test X$enableINDEXER = Xno -o "x$SYSTEMD_SYSTEM_UNIT_DIR" = "x" -o \ "x$SYSTEMD_USER_UNIT_DIR" = "x"; then with_systemd="no" fi AM_CONDITIONAL([INSTALL_SYSTEMD_UNITS], [test "X$with_systemd" != "Xno"]) ### X11: this is needed for the session monitoring code (in recollindex -m) AC_ARG_ENABLE(x11mon, AS_HELP_STRING([--disable-x11mon],[Disable recollindex support for X11 session monitoring.]), enableX11mon=$enableval, enableX11mon="yes") if test X$enableINDEXER = Xno ; then enableX11mon=no else if test X$withInotify = Xno -a X$withFam = Xno ; then enableX11mon=no fi fi if test "$enableX11mon" = yes ; then AC_PATH_XTRA X_LIBX11=-lX11 else AC_DEFINE(DISABLE_X11MON, 1, [No X11 session monitoring support]) X_LIBX11="" fi #echo X_CFLAGS "'$X_CFLAGS'" X_PRE_LIBS "'$X_PRE_LIBS'" X_LIBS \ # "'$X_LIBS'" X_LIBX11 "'$X_LIBX11'" X_EXTRA_LIBS "'$X_EXTRA_LIBS'" # Check if anything needs Xapian. We also need to build the shared lib if this is the case. # Useful if building rclgrep alone for example. xapian_needed=yes if test X$buildtestmains = Xno -a X$pythonEnabled = Xno -a X$enableINDEXER = Xno \ -a X$enableXADUMP = Xno -a X$enableQT = Xno -a X$enableRECOLLQ = Xno ; then xapian_needed=no fi if test X$xapian_needed = Xyes; then PKG_CHECK_MODULES([XAPIAN], xapian-core, [], AC_MSG_ERROR([libxapian])) fi AM_CONDITIONAL([MAKE_RECOLL_LIB], [test X$xapian_needed = Xyes]) # For communicating the value of RECOLL_DATADIR to non-make-based # subpackages like python-recoll, we have to expand prefix in here, because # things like "datadir = ${prefix}/share" (which is what we'd get by # expanding @datadir@) don't mean a thing in Python... I guess we could # have a piece of shell-script text to be substituted into and executed by # setup.py for getting the value of pkgdatadir, but really... m_prefix=$prefix test "X$m_prefix" = "XNONE" && m_prefix=/usr/local m_datadir=${m_prefix}/share RECOLL_DATADIR=${m_datadir}/recoll RCLVERSION=$PACKAGE_VERSION RCLLIBVERSION=$RCLVERSION AC_SUBST(NO_UNDEF_LINK_FLAG) AC_SUBST(RECOLL_DATADIR) AC_SUBST(RECOLL_PUBLIC_LIB) AC_SUBST(DEF_EXT4_BIRTH_TIME) AC_SUBST(X_CFLAGS) AC_SUBST(X_PRE_LIBS) AC_SUBST(X_LIBS) AC_SUBST(X_LIBX11) AC_SUBST(X_EXTRA_LIBS) AC_SUBST(INCICONV) AC_SUBST(LIBICONV) AC_SUBST(XAPIAN_LIBS) AC_SUBST(XAPIAN_CFLAGS) AC_SUBST(LIBFAM) AC_SUBST(QMAKE) AC_SUBST(QTGUI) AC_SUBST(QMAKE_ENABLE_WEBKIT) AC_SUBST(QMAKE_DISABLE_WEBKIT) AC_SUBST(QMAKE_ENABLE_WEBENGINE) AC_SUBST(QMAKE_DISABLE_WEBENGINE) AC_SUBST(QMAKE_ENABLE_GUIDEBUG) AC_SUBST(QMAKE_DISABLE_GUIDEBUG) AC_SUBST(QMAKE_ENABLE_ZEITGEIST) AC_SUBST(QMAKE_DISABLE_ZEITGEIST) AC_SUBST(LIBQZEITGEIST) AC_SUBST(RCLVERSION) AC_SUBST(RCLLIBVERSION) AC_SUBST(XSLT_CFLAGS) AC_SUBST(XSLT_LIBS) AC_SUBST([SYSTEMD_SYSTEM_UNIT_DIR]) AC_SUBST([SYSTEMD_USER_UNIT_DIR]) AC_CONFIG_FILES([Makefile python/recoll/setup.py python/recoll/setup.cfg python/pychm/setup.py python/pychm/setup.cfg python/pyaspell/setup.py]) if test X$buildtestmains = Xyes ; then AC_CONFIG_FILES([testmains/Makefile]) fi if test X$buildrclgrep = Xyes ; then AC_CONFIG_FILES([rclgrep/Makefile]) fi AC_OUTPUT recoll-1.36.1/missing0000755000175000017500000001533614521160720011410 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2021 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: recoll-1.36.1/aspell/0000755000175000017500000000000014521161750011345 500000000000000recoll-1.36.1/aspell/rclaspell.cpp0000644000175000017500000002737414520460563013772 00000000000000/* Copyright (C) 2006-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #ifdef RCL_USE_ASPELL #include "rclaspell.h" #include #include #include #include #include #include "safeunistd.h" #include "pathut.h" #include "execmd.h" #include "log.h" #include "unacpp.h" #include "rclutil.h" #include "smallut.h" using namespace std; // Private rclaspell data class AspellData { public: string m_execbuild; vector m_execspell; ExecCmd m_speller; #ifdef _WIN32 string m_datadir; #endif string m_addCreateParam; }; Aspell::Aspell(const RclConfig *cnf) : m_config(cnf) { } Aspell::~Aspell() { deleteZ(m_data); } bool Aspell::init(string &reason) { deleteZ(m_data); // Language: we get this from the configuration, else from the NLS // environment. The aspell language names used for selecting language // definition files (used to create dictionaries) are like en, fr if (!m_config->getConfParam("aspellLanguage", m_lang) || m_lang.empty()) { string lang = "en"; const char *cp; if ((cp = getenv("LC_ALL"))) lang = cp; else if ((cp = getenv("LANG"))) lang = cp; if (!lang.compare("C")) lang = "en"; m_lang = lang.substr(0, lang.find_first_of("_")); if (!m_lang.compare("ja")) { // Aspell has no support for Japanese. We substitute // english, as Japanese users often have texts with // interspersed english words or english texts. Japanese // parts of the text won't be sent to aspell (check // Rcl::Db::isSpellingCandidate()) m_lang = "en"; } } m_data = new AspellData; m_config->getConfParam("aspellAddCreateParam", m_data->m_addCreateParam); #ifdef _WIN32 m_data->m_datadir = path_cat( path_pkgdatadir(), "filters/aspell-installed/mingw32/lib/aspell-0.60"); if (m_data->m_addCreateParam.empty()) { m_data->m_addCreateParam = string("--local-data-dir=") + path_cat(m_config->getConfDir(), "aspell"); } #endif // WIN32 const char *aspell_prog_from_env = getenv("ASPELL_PROG"); if (aspell_prog_from_env && access(aspell_prog_from_env, X_OK) == 0) { m_data->m_execbuild = aspell_prog_from_env; } #ifdef ASPELL_PROG if (m_data->m_execbuild.empty()) { string cmd = m_config->findFilter(ASPELL_PROG); LOGDEB("rclaspell::init: findFilter returns " << cmd << endl); if (path_isabsolute(cmd)) { m_data->m_execbuild.swap(cmd); } } #endif // ASPELL_PROG if (m_data->m_execbuild.empty()) { ExecCmd::which("aspell", m_data->m_execbuild); } if (m_data->m_execbuild.empty()) { reason = "aspell program not found or not executable"; deleteZ(m_data); return false; } // At the moment, no idea how to distribute the pyaspell Python extension on MacOS. // So Recoll still makes suggestions (using the aspell command) when a search fails, // but can't widen searches to common orthographic neighbours. #ifndef __APPLE__ m_data->m_execspell = { "rclaspell-sugg.py", string("--lang=") + m_lang, "--encoding=utf-8", #ifdef _WIN32 string("--data-dir=") + m_data->m_datadir, #endif string("--master=") + dicPath(), "--sug-mode=fast", "--mode=none", }; if (!m_data->m_addCreateParam.empty()) { m_data->m_execspell.push_back(m_data->m_addCreateParam); } m_data->m_execspell.push_back("pipe"); m_config->processFilterCmd(m_data->m_execspell); #else // __APPLE__ -> m_data->m_execspell = { m_data->m_execbuild, string("--lang=") + m_lang, "--encoding=utf-8", string("--master=") + dicPath(), "--sug-mode=fast", "--mode=none", }; if (!m_data->m_addCreateParam.empty()) { m_data->m_execspell.push_back(m_data->m_addCreateParam); } m_data->m_execspell.push_back("pipe"); #endif return true; } bool Aspell::ok() const { return nullptr != m_data; } string Aspell::dicPath() { string ccdir = m_config->getAspellcacheDir(); return path_cat(ccdir, string("aspdict.") + m_lang + string(".rws")); } // The data source for the create dictionary aspell command. We walk // the term list, filtering out things that are probably not words. // Note that the manual for the current version (0.60) of aspell // states that utf-8 is not well supported, so that we should maybe // also filter all 8bit chars. Info is contradictory, so we only // filter out CJK which is definitely not supported (katakana would // make sense though, but currently no support). class AspExecPv : public ExecCmdProvide { public: string *m_input; // pointer to string used as input buffer to command Rcl::TermIter *m_tit; Rcl::Db &m_db; AspExecPv(string *i, Rcl::TermIter *tit, Rcl::Db &db) : m_input(i), m_tit(tit), m_db(db) {} void newData() { while (m_db.termWalkNext(m_tit, *m_input)) { LOGDEB2("Aspell::buildDict: term: [" << (m_input) << "]\n" ); if (!Rcl::Db::isSpellingCandidate(*m_input)) { LOGDEB2("Aspell::buildDict: SKIP\n" ); continue; } if (!o_index_stripchars) { string lower; if (!unacmaybefold(*m_input, lower, "UTF-8", UNACOP_FOLD)) continue; m_input->swap(lower); } // Got a non-empty sort-of appropriate term, let's send it to // aspell LOGDEB2("Apell::buildDict: SEND\n" ); m_input->append("\n"); return; } // End of data. Tell so. Exec will close cmd. m_input->erase(); } }; bool Aspell::buildDict(Rcl::Db &db, string &reason) { if (!ok()) return false; // We create the dictionary by executing the aspell command: // aspell --lang=[lang] create master [dictApath] string cmdstring(m_data->m_execbuild); ExecCmd aspell; vector args; args.push_back(string("--lang=")+ m_lang); cmdstring += string(" ") + string("--lang=") + m_lang; args.push_back("--encoding=utf-8"); cmdstring += string(" ") + "--encoding=utf-8"; #ifdef _WIN32 args.push_back(string("--data-dir=") + m_data->m_datadir); #endif if (!m_data->m_addCreateParam.empty()) { args.push_back(m_data->m_addCreateParam); cmdstring += string(" ") + m_data->m_addCreateParam; } args.push_back("create"); cmdstring += string(" ") + "create"; args.push_back("master"); cmdstring += string(" ") + "master"; args.push_back(dicPath()); cmdstring += string(" ") + dicPath(); // Have to disable stderr, as numerous messages about bad strings are // printed. We'd like to keep errors about missing databases though, so // make it configurable for diags bool keepStderr = false; m_config->getConfParam("aspellKeepStderr", &keepStderr); if (!keepStderr) aspell.setStderr("/dev/null"); Rcl::TermIter *tit = db.termWalkOpen(); if (nullptr == tit) { reason = "termWalkOpen failed\n"; return false; } string termbuf; AspExecPv pv(&termbuf, tit, db); aspell.setProvide(&pv); if (aspell.doexec(m_data->m_execbuild, args, &termbuf)) { ExecCmd cmd; args.clear(); args.push_back("dicts"); string dicts; bool hasdict = false; if (cmd.doexec(m_data->m_execbuild, args, nullptr, &dicts)) { vector vdicts; stringToTokens(dicts, vdicts, "\n\r\t "); if (find(vdicts.begin(), vdicts.end(), m_lang) != vdicts.end()) { hasdict = true; } } if (hasdict) { reason = string("\naspell dictionary creation command [") + cmdstring; reason += string( "] failed. Reason unknown.\n" "Try to set aspellKeepStderr = 1 in recoll.conf, and execute \n" "the indexing command in a terminal to see the aspell " "diagnostic output.\n"); } else { reason = string("aspell dictionary creation command failed:\n") + cmdstring + "\n" "One possible reason might be missing language " "data files for lang = " + m_lang + ". Maybe try to execute the command by hand for a better diag."; } return false; } db.termWalkClose(tit); return true; } bool Aspell::make_speller(string& reason) { if (!ok()) return false; if (m_data->m_speller.getChildPid() > 0) return true; LOGDEB("Starting aspell command [" << stringsToString(m_data->m_execspell) << "]\n"); if (m_data->m_speller.startExec(m_data->m_execspell, true, true) != 0) { reason += "Can't start aspell: " + stringsToString(m_data->m_execspell); return false; } // Read initial line from aspell: version etc. string line; if (m_data->m_speller.getline(line, 2) <= 0) { reason += "Aspell: failed reading initial line"; m_data->m_speller.zapChild(); return false; } LOGDEB("rclaspell: aspell initial answer: [" << line << "]\n"); return true; } bool Aspell::suggest( Rcl::Db &db, const string &_term, vector& suggestions, string& reason) { LOGDEB("Aspell::suggest: term [" << _term << "]\n"); if (!ok() || !make_speller(reason)) return false; string mterm(_term); if (mterm.empty()) return true; //?? if (!Rcl::Db::isSpellingCandidate(mterm)) { LOGDEB0("Aspell::suggest: [" << mterm << " not spelling candidate, return empty/true\n"); return true; } if (!o_index_stripchars) { string lower; if (!unacmaybefold(mterm, lower, "UTF-8", UNACOP_FOLD)) { LOGERR("Aspell::check : cant lowercase input\n"); return false; } mterm.swap(lower); } m_data->m_speller.send(mterm + "\n"); std::string line; if (m_data->m_speller.getline(line, 3) <= 0) { reason.append("Aspell error: "); return false; } LOGDEB1("ASPELL: got answer: " << line << "\n"); string empty; if (m_data->m_speller.getline(empty, 1) <= 0) { reason.append("Aspell: failed reading final empty line\n"); return false; } if (line[0] == '*' || line[0] == '#') { // Word is in dictionary, or there are no suggestions return true; } string::size_type colon; // Aspell suggestions line: & original count offset: miss, miss, … if (line[0] != '&' || (colon = line.find(':')) == string::npos || colon == line.size()-1) { // ?? reason.append("Aspell: bad answer line: "); reason.append(line); return false; } std::vector words; stringSplitString(line.substr(colon + 2), words, ", "); for (const auto& word : words) { if (db.termExists(word)) suggestions.push_back(word); } return true; } #endif // RCL_USE_ASPELL recoll-1.36.1/aspell/rclaspell.h0000644000175000017500000000433314410615043013416 00000000000000/* Copyright (C) 2006-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RCLASPELL_H_INCLUDED_ #define _RCLASPELL_H_INCLUDED_ /* autoconfig.h must be included before this file */ #ifdef RCL_USE_ASPELL /** * Aspell speller interface class. * * Aspell is used to let the user find about spelling variations that may * exist in the document set for a given word. * A specific aspell dictionary is created out of all the terms in the * xapian index, and we then use it to expand a term to spelling neighbours. */ #include #include #include "rclconfig.h" #include "rcldb.h" class AspellData; class Aspell { public: Aspell(const RclConfig *cnf); ~Aspell(); Aspell(const Aspell &) = delete; Aspell& operator=(const Aspell &) = delete; /** Check health */ bool ok() const; /** Find the aspell command and shared library, init function pointers */ bool init(std::string &reason); /** Build dictionary out of index term list. This is done at the end * of an indexing pass. */ bool buildDict(Rcl::Db &db, std::string &reason); /** Return a list of possible expansions for a given word */ bool suggest(Rcl::Db &db, const std::string& term, std::vector &suggestions, std::string &reason); private: std::string dicPath(); const RclConfig *m_config; std::string m_lang; AspellData *m_data{nullptr}; bool make_speller(std::string& reason); }; #endif /* RCL_USE_ASPELL */ #endif /* _RCLASPELL_H_INCLUDED_ */ recoll-1.36.1/depcomp0000755000175000017500000005602014521160720011361 00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2021 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: recoll-1.36.1/AUTHORS0000644000175000017500000000131614427373216011065 000000000000002023-04: This file should have been created long ago. I have not spent a lot of time researching, and only listed recent contributions. Please contact me if you feel like being listed here. == Primary developer: Jean-Francois Dockes == Contributions: * Using the Linux statx call and file Birth Date for indexing and searching (commits 3e20e093, 6a8810b6, 0a08689f, 8f46009e, 840ae091) : ** Zhijun Li ** Jianye Shi ** Biao Dong ** (Ding (Beijing) Intelligent Technology Co. Ltd) * shenlebantongying, (cmake build) * Frank Dana (multiple bug fixes and improvements) * Paul S Hahn (openai.whisper support for transcribing audio files) recoll-1.36.1/internfile/0000755000175000017500000000000014521161750012224 500000000000000recoll-1.36.1/internfile/mh_mbox.cpp0000644000175000017500000004571314410615043014307 00000000000000/* Copyright (C) 2005-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include "cstr.h" #include "mimehandler.h" #include "log.h" #include "mh_mbox.h" #include "smallut.h" #include "rclconfig.h" #include "md5ut.h" #include "conftree.h" #include "pathut.h" using namespace std; // Define maximum message size for safety. 100MB would seem reasonable static unsigned int max_mbox_member_size = 100 * 1024 * 1024; // The mbox format uses lines beginning with 'From ' as separator. // Mailers are supposed to quote any other lines beginning with // 'From ', turning it into '>From '. This should make it easy to detect // message boundaries by matching a '^From ' regular expression // Unfortunately this quoting is quite often incorrect in the real world. // // The rest of the format for the line is somewhat variable, but there will // be a 4 digit year somewhere... // The canonic format is the following, with a 24 characters date: // From toto@tutu.com Sat Sep 30 16:44:06 2000 // This resulted into the pattern for versions up to 1.9.0: // "^From .* [1-2][0-9][0-9][0-9]$" // // Some mailers add a time zone to the date, this is non-"standard", // but happens, like in: // From toto@truc.com Sat Sep 30 16:44:06 2000 -0400 // // This is taken into account in the new regexp, which also matches more // of the date format, to catch a few actual issues like // From http://www.itu.int/newsroom/press/releases/1998/NP-2.html: // Note that this *should* have been quoted. // // http://www.qmail.org/man/man5/mbox.html seems to indicate that the // fact that From_ is normally preceded by a blank line should not be // used, but we do it anyway (for now). // The same source indicates that arbitrary data can follow the date field // // A variety of pathologic From_ lines: // Bad date format: // From uucp Wed May 22 11:28 GMT 1996 // Added timezone at the end (ok, part of the "any data" after the date) // From qian2@fas.harvard.edu Sat Sep 30 16:44:06 2000 -0400 // Emacs VM botch ? Adds tz between hour and year // From dockes Wed Feb 23 10:31:20 +0100 2005 // From dockes Fri Dec 1 20:36:39 +0100 2006 // The modified regexp gives the exact same results on the ietf mail archive // and my own's. // Update, 2008-08-29: some old? Thunderbird versions apparently use a date // in "Date: " header format, like: From - Mon, 8 May 2006 10:57:32 // This was added as an alternative format. By the way it also fools "mail" and // emacs-vm, Recoll is not alone // Update: 2009-11-27: word after From may be quoted string: From "john bull" static const string frompat{ "^From[ ]+([^ ]+|\"[^\"]+\")[ ]+" // 'From (toto@tutu|"john bull") ' "[[:alpha:]]{3}[ ]+[[:alpha:]]{3}[ ]+[0-3 ][0-9][ ]+" // Fri Oct 26 "[0-2][0-9]:[0-5][0-9](:[0-5][0-9])?[ ]+" // Time, seconds optional "([^ ]+[ ]+)?" // Optional tz "[12][0-9][0-9][0-9]" // Year, unanchored, more data may follow "|" // Or standard mail Date: header format "^From[ ]+[^ ]+[ ]+" // From toto@tutu "[[:alpha:]]{3},[ ]+[0-3]?[0-9][ ]+[[:alpha:]]{3}[ ]+" // Mon, 8 May "[12][0-9][0-9][0-9][ ]+" // Year "[0-2][0-9]:[0-5][0-9](:[0-5][0-9])?" // Time, secs optional }; // Extreme thunderbird brokiness. Will sometimes use From lines // exactly like: From ^M (From followed by space and eol). We only // test for this if QUIRKS_TBIRD is set static const string miniTbirdFrom{"^From $"}; static SimpleRegexp fromregex(frompat, SimpleRegexp::SRE_NOSUB); static SimpleRegexp minifromregex(miniTbirdFrom, SimpleRegexp::SRE_NOSUB); static std::mutex o_mcache_mutex; /** * Handles a cache for message numbers to offset translations. Permits direct * accesses inside big folders instead of having to scan up to the right place * * Message offsets are saved to files stored under cfg(mboxcachedir), default * confdir/mboxcache. Mbox files smaller than cfg(mboxcacheminmbs) are not * cached. * Cache files are named as the md5 of the file UDI, which is kept in * the first block for possible collision detection. The 64 bits * offsets for all message "From_" lines follow. The format is purely * binary, values are not even byte-swapped to be proc-idependant. */ #define M_o_b1size 1024 class MboxCache { public: MboxCache() { // Can't access rclconfig here, we're a static object, would // have to make sure it's initialized. } ~MboxCache() {} int64_t get_offset(RclConfig *config, const string& udi, int msgnum, int64_t filesize) { LOGDEB0("MboxCache::get_offset: udi [" << udi << "] msgnum " << msgnum << "\n"); if (!ok(config)) { LOGDEB("MboxCache::get_offset: init failed\n"); return -1; } std::unique_lock locker(o_mcache_mutex); string fn = makefilename(udi); ifstream instream(fn.c_str(), std::ifstream::binary); if (!instream.good()) { if (filesize > m_minfsize) { LOGSYSERR("MboxCache::get_offset", "open", fn); } else { LOGDEB("MboxCache::get_offset: no cache for " << fn << endl); } return -1; } char blk1[M_o_b1size]; instream.read(blk1, M_o_b1size); if (!instream.good()) { LOGSYSERR("MboxCache::get_offset", "read blk1", ""); return -1; } ConfSimple cf(string(blk1, M_o_b1size)); string fudi; if (!cf.get("udi", fudi) || fudi.compare(udi)) { LOGINFO("MboxCache::get_offset:badudi fn " << fn << " udi [" << udi << "], fudi [" << fudi << "]\n"); return -1; } LOGDEB1("MboxCache::get_offset: reading offsets file at offs " << cacheoffset(msgnum) << "\n"); instream.seekg(cacheoffset(msgnum)); if (!instream.good()) { LOGSYSERR("MboxCache::get_offset", "seek", lltodecstr(cacheoffset(msgnum))); return -1; } int64_t offset = -1; instream.read((char*)&offset, sizeof(int64_t)); if (!instream.good()) { LOGSYSERR("MboxCache::get_offset", "read", ""); return -1; } LOGDEB0("MboxCache::get_offset: ret " << offset << "\n"); return offset; } // Save array of offsets for a given file, designated by Udi void put_offsets(RclConfig *config, const string& udi, int64_t fsize, vector& offs) { LOGDEB0("MboxCache::put_offsets: " << offs.size() << " offsets\n"); if (!ok(config) || !maybemakedir()) return; if (fsize < m_minfsize) { LOGDEB0("MboxCache::put_offsets: fsize " << fsize << " < minsize " << m_minfsize << endl); return; } std::unique_lock locker(o_mcache_mutex); string fn = makefilename(udi); std::ofstream os(fn.c_str(), std::ios::out|std::ios::binary); if (!os.good()) { LOGSYSERR("MboxCache::put_offsets", "open", fn); return; } string blk1("udi="); blk1.append(udi); blk1.append(cstr_newline); blk1.resize(M_o_b1size, 0); os.write(blk1.c_str(), M_o_b1size); if (!os.good()) { LOGSYSERR("MboxCache::put_offsets", "write blk1", ""); return; } for (const auto& off : offs) { os.write((char*)&off, sizeof(int64_t)); if (!os.good()) { LOGSYSERR("MboxCache::put_offsets", "write", ""); return; } } os.flush(); if (!os.good()) { LOGSYSERR("MboxCache::put_offsets", "flush", ""); return; } } // Check state, possibly initialize bool ok(RclConfig *config) { std::unique_lock locker(o_mcache_mutex); if (m_minfsize == -1) return false; if (!m_ok) { int minmbs = 5; config->getConfParam("mboxcacheminmbs", &minmbs); if (minmbs < 0) { // minmbs set to negative to disable cache m_minfsize = -1; return false; } m_minfsize = minmbs * 1000 * 1000; m_dir = config->getMboxcacheDir(); m_ok = true; } return m_ok; } private: bool m_ok{false}; // Place where we store things string m_dir; // Don't cache smaller files. If -1, don't do anything. int64_t m_minfsize{0}; // Create the cache directory if it does not exist bool maybemakedir() { if (!path_makepath(m_dir, 0700)) { LOGSYSERR("MboxCache::maybemakedir", "path_makepath", m_dir); return false; } return true; } // Compute file name from udi string makefilename(const string& udi) { string digest, xdigest; MD5String(udi, digest); MD5HexPrint(digest, xdigest); return path_cat(m_dir, xdigest); } // Compute offset in cache file for the mbox offset of msgnum // Msgnums are from 1 int64_t cacheoffset(int msgnum) { return M_o_b1size + (msgnum-1) * sizeof(int64_t); } }; static class MboxCache o_mcache; static const string cstr_keyquirks("mhmboxquirks"); enum Quirks {MBOXQUIRK_TBIRD=1}; class MimeHandlerMbox::Internal { public: Internal(MimeHandlerMbox *p) : pthis(p) {} std::string fn; // File name std::string ipath; ifstream instream; int msgnum{0}; // Current message number in folder. Starts at 1 int64_t lineno{0}; // debug int64_t fsize{0}; std::vector offsets; int quirks; MimeHandlerMbox *pthis; bool tryUseCache(int mtarg); }; MimeHandlerMbox::MimeHandlerMbox(RclConfig *cnf, const std::string& id) : RecollFilter(cnf, id) { m = new Internal(this); string smbs; m_config->getConfParam("mboxmaxmsgmbs", smbs); if (!smbs.empty()) { max_mbox_member_size = (unsigned int)atoi(smbs.c_str()) *1024*1024; } LOGDEB0("MimeHandlerMbox::MimeHandlerMbox: max_mbox_member_size (MB): " << max_mbox_member_size / (1024*1024) << endl); } MimeHandlerMbox::~MimeHandlerMbox() { if (m) { clear(); delete m; } } void MimeHandlerMbox::clear_impl() { m->fn.erase(); m->ipath.erase(); // We used to use m->instream = ifstream() which fails with some compilers, as the copy // constructor is marked deleted in standard c++ (works with many compilers though). if (m->instream.is_open()) { m->instream.close(); } m->instream.clear(); m->msgnum = 0; m->lineno = 0; m->fsize = 0; m->offsets.clear(); m->quirks = 0; } bool MimeHandlerMbox::skip_to_document(const std::string& ipath) { m->ipath = ipath; return true; } bool MimeHandlerMbox::set_document_file_impl(const string&, const string &fn) { LOGDEB("MimeHandlerMbox::set_document_file(" << fn << ")\n"); clear_impl(); m->fn = fn; m->instream.open(fn.c_str(), std::ifstream::binary); if (!m->instream.good()) { LOGSYSERR("MimeHandlerMail::set_document_file", "ifstream", fn); return false; } // TBD #if 0 && defined O_NOATIME && O_NOATIME != 0 if (fcntl(fileno((FILE *)m->vfp), F_SETFL, O_NOATIME) < 0) { // perror("fcntl"); } #endif m->fsize = path_filesize(fn); m_havedoc = true; // Check for location-based quirks: string quirks; if (m_config && m_config->getConfParam(cstr_keyquirks, quirks)) { if (quirks == "tbird") { LOGDEB("MimeHandlerMbox: setting quirks TBIRD\n"); m->quirks |= MBOXQUIRK_TBIRD; } } // And double check for thunderbird string tbirdmsf = fn + ".msf"; if (!(m->quirks & MBOXQUIRK_TBIRD) && path_exists(tbirdmsf)) { LOGDEB("MimeHandlerMbox: detected unconf'd tbird mbox in "<< fn <<"\n"); m->quirks |= MBOXQUIRK_TBIRD; } return true; } bool MimeHandlerMbox::Internal::tryUseCache(int mtarg) { bool cachefound = false; string line; int64_t off; LOGDEB0("MimeHandlerMbox::next_doc: mtarg " << mtarg << " m_udi[" << pthis->m_udi << "]\n"); if (pthis->m_udi.empty()) { goto out; } if ((off = o_mcache.get_offset(pthis->m_config, pthis->m_udi, mtarg, fsize)) < 0) { goto out; } instream.seekg(off); if (!instream.good()) { LOGSYSERR("tryUseCache", "seekg", ""); goto out; } getline(instream, line, '\n'); if (!instream.good()) { LOGSYSERR("tryUseCache", "getline", ""); goto out; } LOGDEB1("MimeHandlerMbox::tryUseCache:getl ok. line:[" << line << "]\n"); if ((fromregex(line) || ((quirks & MBOXQUIRK_TBIRD) && minifromregex(line))) ) { LOGDEB0("MimeHandlerMbox: Cache: From_ Ok\n"); instream.seekg(off); msgnum = mtarg -1; cachefound = true; } else { LOGDEB0("MimeHandlerMbox: cache: regex failed for [" << line << "]\n"); } out: if (!cachefound) { // No cached result: scan. instream.seekg(0); msgnum = 0; } return cachefound; } bool MimeHandlerMbox::next_document() { if (!m->instream.good()) { LOGERR("MimeHandlerMbox::next_document: not open\n"); return false; } if (!m_havedoc) { return false; } int mtarg = 0; if (!m->ipath.empty()) { sscanf(m->ipath.c_str(), "%d", &mtarg); } else if (m_forPreview) { // Can't preview an mbox. LOGDEB("MimeHandlerMbox::next_document: can't preview folders!\n"); return false; } LOGDEB0("MimeHandlerMbox::next_document: fn " << m->fn << ", msgnum " << m->msgnum << " mtarg " << mtarg << " \n"); if (mtarg == 0) mtarg = -1; // If we are called to retrieve a specific message, try to use the // offsets cache to try and position to the right header. bool storeoffsets = true; if (mtarg > 0) { storeoffsets = !m->tryUseCache(mtarg); } int64_t message_end = 0; bool iseof = false; bool hademptyline = true; string& msgtxt = m_metaData[cstr_dj_keycontent]; msgtxt.erase(); string line; for (;;) { message_end = m->instream.tellg(); getline(m->instream, line, '\n'); if (!m->instream.good()) { ifstream::iostate st = m->instream.rdstate(); if (st & std::ifstream::eofbit) { LOGDEB0("MimeHandlerMbox:next: eof at " << message_end << endl); } else { if (st & std::ifstream::failbit) { LOGDEB0("MimeHandlerMbox:next: failbit\n"); LOGSYSERR("MimeHandlerMbox:next:", "", ""); } if (st & std::ifstream::badbit) { LOGDEB0("MimeHandlerMbox:next: badbit\n"); LOGSYSERR("MimeHandlerMbox:next:", "", ""); } if (st & std::ifstream::goodbit) { LOGDEB1("MimeHandlerMbox:next: good\n"); } } iseof = true; m->msgnum++; break; } m->lineno++; rtrimstring(line, "\r\n"); int ll = line.size(); LOGDEB2("mhmbox:next: hadempty " << hademptyline << " lineno " << m->lineno << " ll " << ll << " Line: [" << line << "]\n"); if (hademptyline) { if (ll > 0) { // Non-empty line with empty line flag set, reset flag // and check regex. if (!(m->quirks & MBOXQUIRK_TBIRD)) { // Tbird sometimes ommits the empty line, so avoid // resetting state (initially true) and hope for // the best hademptyline = false; } /* The 'F' compare is redundant but it improves performance A LOT */ if (line[0] == 'F' && ( fromregex(line) || ((m->quirks & MBOXQUIRK_TBIRD) && minifromregex(line))) ) { LOGDEB1("MimeHandlerMbox: msgnum " << m->msgnum << ", From_ at line " << m->lineno << " foffset " << message_end << " line: [" << line << "]\n"); if (storeoffsets) { m->offsets.push_back(message_end); } m->msgnum++; if ((mtarg <= 0 && m->msgnum > 1) || (mtarg > 0 && m->msgnum > mtarg)) { // Got message, go do something with it break; } // From_ lines are not part of messages continue; } } } else if (ll <= 0) { hademptyline = true; } if (mtarg <= 0 || m->msgnum == mtarg) { // Accumulate message lines line += '\n'; msgtxt += line; if (msgtxt.size() > max_mbox_member_size) { LOGERR("mh_mbox: huge message (more than " << max_mbox_member_size/(1024*1024) << " MB) inside " << m->fn << ", giving up\n"); return false; } } } LOGDEB2("Message text length " << msgtxt.size() << "\n"); LOGDEB2("Message text: [" << msgtxt << "]\n"); char buf[20]; // m->msgnum was incremented when hitting the next From_ or eof, so the data // is for m->msgnum - 1 sprintf(buf, "%d", m->msgnum - 1); m_metaData[cstr_dj_keyipath] = buf; m_metaData[cstr_dj_keymt] = "message/rfc822"; if (iseof) { LOGDEB2("MimeHandlerMbox::next: eof hit\n"); m_havedoc = false; if (!m_udi.empty() && storeoffsets) { o_mcache.put_offsets(m_config, m_udi, m->fsize, m->offsets); } } return msgtxt.empty() ? false : true; } recoll-1.36.1/internfile/myhtmlparse.cpp0000644000175000017500000005356714427373216015244 00000000000000/* This file was copied from omega-0.8.5->1.2.6 and modified */ /* myhtmlparse.cc: subclass of HtmlParser for extracting text * * ----START-LICENCE---- * Copyright 1999,2000,2001 BrightStation PLC * Copyright 2002,2003,2004 Olly Betts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * -----END-LICENCE----- */ #include #ifdef _WIN32 // Local implementation in windows directory #include "strptime.h" #endif #include #include #include #include "cstr.h" #include "myhtmlparse.h" #include "indextext.h" // for lowercase_term() #include "mimeparse.h" #include "smallut.h" #include "cancelcheck.h" #include "log.h" #include "transcode.h" #include "rclutil.h" using std::string; using std::map; static const string cstr_html_charset("charset"); static const string cstr_html_content("content"); inline static bool p_notdigit(char c) { return !isdigit(static_cast(c)); } inline static bool p_notxdigit(char c) { return !isxdigit(static_cast(c)); } inline static bool p_notalnum(char c) { return !isalnum(static_cast(c)); } /* * The following array was taken from Estraier. Estraier was * written by Mikio Hirabayashi. * Copyright (C) 2003-2004 Mikio Hirabayashi * The version where this comes from * is covered by the GNU licence, as this file.*/ static const char *epairs[] = { /* basic symbols */ "amp", "&", "lt", "<", "gt", ">", "quot", "\"", "apos", "'", /* ISO-8859-1 */ "nbsp", "\xc2\xa0", "iexcl", "\xc2\xa1", "cent", "\xc2\xa2", "pound", "\xc2\xa3", "curren", "\xc2\xa4", "yen", "\xc2\xa5", "brvbar", "\xc2\xa6", "sect", "\xc2\xa7", "uml", "\xc2\xa8", "copy", "\xc2\xa9", "ordf", "\xc2\xaa", "laquo", "\xc2\xab", "not", "\xc2\xac", "shy", "\xc2\xad", "reg", "\xc2\xae", "macr", "\xc2\xaf", "deg", "\xc2\xb0", "plusmn", "\xc2\xb1", "sup2", "\xc2\xb2", "sup3", "\xc2\xb3", "acute", "\xc2\xb4", "micro", "\xc2\xb5", "para", "\xc2\xb6", "middot", "\xc2\xb7", "cedil", "\xc2\xb8", "sup1", "\xc2\xb9", "ordm", "\xc2\xba", "raquo", "\xc2\xbb", "frac14", "\xc2\xbc", "frac12", "\xc2\xbd", "frac34", "\xc2\xbe", "iquest", "\xc2\xbf", "Agrave", "\xc3\x80", "Aacute", "\xc3\x81", "Acirc", "\xc3\x82", "Atilde", "\xc3\x83", "Auml", "\xc3\x84", "Aring", "\xc3\x85", "AElig", "\xc3\x86", "Ccedil", "\xc3\x87", "Egrave", "\xc3\x88", "Eacute", "\xc3\x89", "Ecirc", "\xc3\x8a", "Euml", "\xc3\x8b", "Igrave", "\xc3\x8c", "Iacute", "\xc3\x8d", "Icirc", "\xc3\x8e", "Iuml", "\xc3\x8f", "ETH", "\xc3\x90", "Ntilde", "\xc3\x91", "Ograve", "\xc3\x92", "Oacute", "\xc3\x93", "Ocirc", "\xc3\x94", "Otilde", "\xc3\x95", "Ouml", "\xc3\x96", "times", "\xc3\x97", "Oslash", "\xc3\x98", "Ugrave", "\xc3\x99", "Uacute", "\xc3\x9a", "Ucirc", "\xc3\x9b", "Uuml", "\xc3\x9c", "Yacute", "\xc3\x9d", "THORN", "\xc3\x9e", "szlig", "\xc3\x9f", "agrave", "\xc3\xa0", "aacute", "\xc3\xa1", "acirc", "\xc3\xa2", "atilde", "\xc3\xa3", "auml", "\xc3\xa4", "aring", "\xc3\xa5", "aelig", "\xc3\xa6", "ccedil", "\xc3\xa7", "egrave", "\xc3\xa8", "eacute", "\xc3\xa9", "ecirc", "\xc3\xaa", "euml", "\xc3\xab", "igrave", "\xc3\xac", "iacute", "\xc3\xad", "icirc", "\xc3\xae", "iuml", "\xc3\xaf", "eth", "\xc3\xb0", "ntilde", "\xc3\xb1", "ograve", "\xc3\xb2", "oacute", "\xc3\xb3", "ocirc", "\xc3\xb4", "otilde", "\xc3\xb5", "ouml", "\xc3\xb6", "divide", "\xc3\xb7", "oslash", "\xc3\xb8", "ugrave", "\xc3\xb9", "uacute", "\xc3\xba", "ucirc", "\xc3\xbb", "uuml", "\xc3\xbc", "yacute", "\xc3\xbd", "thorn", "\xc3\xbe", "yuml", "\xc3\xbf", /* ISO-10646 */ "fnof", "\xc6\x92", "Alpha", "\xce\x91", "Beta", "\xce\x92", "Gamma", "\xce\x93", "Delta", "\xce\x94", "Epsilon", "\xce\x95", "Zeta", "\xce\x96", "Eta", "\xce\x97", "Theta", "\xce\x98", "Iota", "\xce\x99", "Kappa", "\xce\x9a", "Lambda", "\xce\x9b", "Mu", "\xce\x9c", "Nu", "\xce\x9d", "Xi", "\xce\x9e", "Omicron", "\xce\x9f", "Pi", "\xce\xa0", "Rho", "\xce\xa1", "Sigma", "\xce\xa3", "Tau", "\xce\xa4", "Upsilon", "\xce\xa5", "Phi", "\xce\xa6", "Chi", "\xce\xa7", "Psi", "\xce\xa8", "Omega", "\xce\xa9", "alpha", "\xce\xb1", "beta", "\xce\xb2", "gamma", "\xce\xb3", "delta", "\xce\xb4", "epsilon", "\xce\xb5", "zeta", "\xce\xb6", "eta", "\xce\xb7", "theta", "\xce\xb8", "iota", "\xce\xb9", "kappa", "\xce\xba", "lambda", "\xce\xbb", "mu", "\xce\xbc", "nu", "\xce\xbd", "xi", "\xce\xbe", "omicron", "\xce\xbf", "pi", "\xcf\x80", "rho", "\xcf\x81", "sigmaf", "\xcf\x82", "sigma", "\xcf\x83", "tau", "\xcf\x84", "upsilon", "\xcf\x85", "phi", "\xcf\x86", "chi", "\xcf\x87", "psi", "\xcf\x88", "omega", "\xcf\x89", "thetasym", "\xcf\x91", "upsih", "\xcf\x92", "piv", "\xcf\x96", "bull", "\xe2\x80\xa2", "hellip", "\xe2\x80\xa6", "prime", "\xe2\x80\xb2", "Prime", "\xe2\x80\xb3", "oline", "\xe2\x80\xbe", "frasl", "\xe2\x81\x84", "weierp", "\xe2\x84\x98", "image", "\xe2\x84\x91", "real", "\xe2\x84\x9c", "trade", "\xe2\x84\xa2", "alefsym", "\xe2\x84\xb5", "larr", "\xe2\x86\x90", "uarr", "\xe2\x86\x91", "rarr", "\xe2\x86\x92", "darr", "\xe2\x86\x93", "harr", "\xe2\x86\x94", "crarr", "\xe2\x86\xb5", "lArr", "\xe2\x87\x90", "uArr", "\xe2\x87\x91", "rArr", "\xe2\x87\x92", "dArr", "\xe2\x87\x93", "hArr", "\xe2\x87\x94", "forall", "\xe2\x88\x80", "part", "\xe2\x88\x82", "exist", "\xe2\x88\x83", "empty", "\xe2\x88\x85", "nabla", "\xe2\x88\x87", "isin", "\xe2\x88\x88", "notin", "\xe2\x88\x89", "ni", "\xe2\x88\x8b", "prod", "\xe2\x88\x8f", "sum", "\xe2\x88\x91", "minus", "\xe2\x88\x92", "lowast", "\xe2\x88\x97", "radic", "\xe2\x88\x9a", "prop", "\xe2\x88\x9d", "infin", "\xe2\x88\x9e", "ang", "\xe2\x88\xa0", "and", "\xe2\x88\xa7", "or", "\xe2\x88\xa8", "cap", "\xe2\x88\xa9", "cup", "\xe2\x88\xaa", "int", "\xe2\x88\xab", "there4", "\xe2\x88\xb4", "sim", "\xe2\x88\xbc", "cong", "\xe2\x89\x85", "asymp", "\xe2\x89\x88", "ne", "\xe2\x89\xa0", "equiv", "\xe2\x89\xa1", "le", "\xe2\x89\xa4", "ge", "\xe2\x89\xa5", "sub", "\xe2\x8a\x82", "sup", "\xe2\x8a\x83", "nsub", "\xe2\x8a\x84", "sube", "\xe2\x8a\x86", "supe", "\xe2\x8a\x87", "oplus", "\xe2\x8a\x95", "otimes", "\xe2\x8a\x97", "perp", "\xe2\x8a\xa5", "sdot", "\xe2\x8b\x85", "lceil", "\xe2\x8c\x88", "rceil", "\xe2\x8c\x89", "lfloor", "\xe2\x8c\x8a", "rfloor", "\xe2\x8c\x8b", "lang", "\xe2\x8c\xa9", "rang", "\xe2\x8c\xaa", "loz", "\xe2\x97\x8a", "spades", "\xe2\x99\xa0", "clubs", "\xe2\x99\xa3", "hearts", "\xe2\x99\xa5", "diams", "\xe2\x99\xa6", "OElig", "\xc5\x92", "oelig", "\xc5\x93", "Scaron", "\xc5\xa0", "scaron", "\xc5\xa1", "Yuml", "\xc5\xb8", "circ", "\xcb\x86", "tilde", "\xcb\x9c", "ensp", "\xe2\x80\x82", "emsp", "\xe2\x80\x83", "thinsp", "\xe2\x80\x89", "zwnj", "\xe2\x80\x8c", "zwj", "\xe2\x80\x8d", "lrm", "\xe2\x80\x8e", "rlm", "\xe2\x80\x8f", "ndash", "\xe2\x80\x93", "mdash", "\xe2\x80\x94", "lsquo", "\xe2\x80\x98", "rsquo", "\xe2\x80\x99", "sbquo", "\xe2\x80\x9a", "ldquo", "\xe2\x80\x9c", "rdquo", "\xe2\x80\x9d", "bdquo", "\xe2\x80\x9e", "dagger", "\xe2\x80\xa0", "Dagger", "\xe2\x80\xa1", "permil", "\xe2\x80\xb0", "lsaquo", "\xe2\x80\xb9", "rsaquo", "\xe2\x80\xba", "euro", "\xe2\x82\xac", NULL, NULL }; map my_named_ents; class NamedEntsInitializer { public: NamedEntsInitializer() { for (int i = 0;;) { const char *ent; const char *val; ent = epairs[i++]; if (ent == 0) break; val = epairs[i++]; if (val == 0) break; my_named_ents[string(ent)] = val; } } }; static NamedEntsInitializer namedEntsInitializerInstance; MyHtmlParser::MyHtmlParser() : in_script_tag(false), in_style_tag(false), in_pre_tag(false), in_title_tag(false), pending_space(false), indexing_allowed(true) { // The default html document charset is iso-8859-1. We'll update // this value from the encoding tag if found. Actually use cp1252 which // is a superset charset = "CP1252"; } void MyHtmlParser::decode_entities(string &s) { LOGDEB2("MyHtmlParser::decode_entities\n"); // This has no meaning whatsoever if the character encoding is unknown, // so don't do it. If charset known, caller has converted text to utf-8, // and this is also how we translate entities // if (tocharset != "utf-8") // return; // We need a const_iterator version of s.end() - otherwise the // find() and find_if() templates don't work... string::const_iterator amp = s.begin(), s_end = s.end(); while ((amp = find(amp, s_end, '&')) != s_end) { unsigned int val = 0; string::const_iterator end, p = amp + 1; string subs; if (p != s_end && *p == '#') { p++; if (p != s_end && (*p == 'x' || *p == 'X')) { // hex p++; end = find_if(p, s_end, p_notxdigit); sscanf(s.substr(p - s.begin(), end - p).c_str(), "%x", &val); } else { // number end = find_if(p, s_end, p_notdigit); val = atoi(s.substr(p - s.begin(), end - p).c_str()); } } else { end = find_if(p, s_end, p_notalnum); string code = s.substr(p - s.begin(), end - p); map::const_iterator i; i = my_named_ents.find(code); if (i != my_named_ents.end()) subs = i->second; } if (end < s_end && *end == ';') end++; if (val) { // The code is the code position for a unicode char. We need // to translate it to an utf-8 string. string utf16be; utf16be += char(val / 256); utf16be += char(val % 256); transcode(utf16be, subs, "UTF-16BE", "UTF-8"); } if (subs.length() > 0) { string::size_type amp_pos = amp - s.begin(); s.replace(amp_pos, end - amp, subs); s_end = s.end(); // We've modified the string, so the iterators are no longer // valid... amp = s.begin() + amp_pos + subs.length(); } else { amp = end; } } } // Compress whitespace and suppress newlines // Note that we independently add some newlines to the output text in the // tag processing code. Like this, the preview looks a bit more like what a // browser would display. // We keep whitespace inside

 tags
void
MyHtmlParser::process_text(const string &text)
{
    LOGDEB2("process_text: title " << in_title_tag << " script " <<
            in_script_tag << " style " << in_style_tag << " pre " <<
            in_pre_tag << " pending_space " << pending_space << " txt [" <<
            text << "]\n");
    CancelCheck::instance().checkCancel();

    if (!in_script_tag && !in_style_tag) {
        if (in_title_tag) {
            titledump += text;
        } else if (!in_pre_tag) {
            string::size_type b = 0;
            bool only_space = true;
            while ((b = text.find_first_not_of(WHITESPACE, b)) != string::npos) {
                only_space = false;
                // If space specifically needed or chunk begins with
                // whitespace, add exactly one space
                if (pending_space || b != 0) {
                    dump += ' ';
                }
                pending_space = true;
                string::size_type e = text.find_first_of(WHITESPACE, b);
                if (e == string::npos) {
                    dump += text.substr(b);
                    pending_space = false;
                    break;
                }
                dump += text.substr(b, e - b);
                b = e + 1;
            }
            if (only_space)
                pending_space = true;
        } else {
            if (pending_space)
                dump += ' ';
            dump += text;
        }
    }
}

bool
MyHtmlParser::opening_tag(const string &tag)
{
    LOGDEB2("opening_tag: [" << tag << "]\n");
#if 0
    cout << "TAG: " << tag << ": " << endl;
    map::const_iterator x;
    for (x = p.begin(); x != p.end(); x++) {
        cout << "  " << x->first << " -> '" << x->second << "'" << endl;
    }
#endif
    if (tag.empty()) return true;
    switch (tag[0]) {
    case 'a':
        if (tag == "address") pending_space = true;
        break;
    case 'b':
        // body: some bad docs have several opening body tags and
        // even text before the body is displayed by Opera and
        // Firefox.  We used to reset the dump each time we saw a
        // body tag, but I can't see any reason to do so.

        if (tag == "blockquote" || tag == "br") {
            dump += '\n';
            pending_space = true;
        }
        break;
    case 'c':
        if (tag == "center") pending_space = true;
        break;
    case 'd':
        if (tag == "dd" || tag == "dir" || tag == "div" || tag == "dl" ||
            tag == "dt") pending_space = true;
        if (tag == "dt")
            dump += '\n';
        break;
    case 'e':
        if (tag == "embed") pending_space = true;
        break;
    case 'f':
        if (tag == "fieldset" || tag == "form") pending_space = true;
        break;
    case 'h':
        // hr, and h1, ..., h6
        if (tag.length() == 2 && strchr("r123456", tag[1])) {
            dump += '\n';
            pending_space = true;
        }
        break;
    case 'i':
        if (tag == "iframe" || tag == "img" || tag == "isindex" ||
            tag == "input") pending_space = true;
        break;
    case 'k':
        if (tag == "keygen") pending_space = true;
        break;
    case 'l':
        if (tag == "legend" || tag == "li" || tag == "listing") {
            dump += '\n';
            pending_space = true;
        }
        break;
    case 'm':
        if (tag == "meta") {
            string content;
            if (get_parameter(cstr_html_content, content)) {
                string name;
                if (get_parameter("name", name)) {
                    lowercase_term(name);
                    if (name == "date") {
                        // Specific to Recoll filters.
                        decode_entities(content);
                        struct tm tm;
                        memset(&tm, 0, sizeof(tm));
                        if (strptime(content.c_str(), 
                                     " %Y-%m-%d %H:%M:%S ", &tm) ||
                            strptime(content.c_str(), 
                                     "%Y-%m-%dT%H:%M:%S", &tm)
                            ) {
                            char ascuxtime[100];
                            sprintf(ascuxtime, "%ld", (long)mktime(&tm));
                            dmtime = ascuxtime;
                        }
                    } else if (name == "robots") {
                    } else {
                        string markup;
                        bool ishtml = false;
                        if (get_parameter("markup", markup)) {
                            if (!stringlowercmp("html", markup)) {
                                ishtml = true;
                            }
                        }
                        decode_entities(content);
                        if (ishtml && 
                            content.compare(0, cstr_fldhtm.size(),
                                            cstr_fldhtm)) {
                            content.insert(0, cstr_fldhtm);
                        }
                        addmeta(meta, name, content);
                    }
                } 
                string hdr;
                if (get_parameter("http-equiv", hdr)) {
                    lowercase_term(hdr);
                    if (hdr == "content-type") {
                        MimeHeaderValue p;
                        parseMimeHeaderValue(content, p);
                        map::const_iterator k;
                        if ((k = p.params.find(cstr_html_charset)) != 
                            p.params.end()) {
                            charset = k->second;
                            if (!charset.empty() && 
                                !samecharset(charset, fromcharset)) {
                                LOGDEB1("Doc http-equiv charset '" << charset <<
                                        "' differs from dir deflt '" <<
                                        fromcharset << "'\n");
                                throw false;
                            }
                        }
                    }
                }
            }
            string newcharset;
            if (get_parameter(cstr_html_charset, newcharset)) {
                // HTML5 added: 
                lowercase_term(newcharset);
                charset = newcharset;
                if (!charset.empty() && 
                    !samecharset(charset, fromcharset)) {
                    LOGDEB1("Doc html5 charset '" << charset <<
                            "' differs from dir deflt '"< If the  is open, do
//    > something with the text (that is, don't throw up). Else, things are
//    > too weird, throw an error. We don't get called if the parser finds
//    > a closing body tag (exception gets thrown by closing_tag())
// But we don't throw any more. Whatever text we've extracted up to now is
// better than nothing.
void
MyHtmlParser::do_eof()
{
}

recoll-1.36.1/internfile/mimehandler.cpp0000644000175000017500000003504614427373216015154 00000000000000/*
 *   Copyright 2004 J.F.Dockes
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "cstr.h"
#include "mimehandler.h"
#include "log.h"
#include "rclconfig.h"
#include "smallut.h"
#include "md5ut.h"
#include "mh_exec.h"
#include "mh_execm.h"
#include "mh_html.h"
#include "mh_mail.h"
#include "mh_mbox.h"
#include "mh_text.h"
#include "mh_symlink.h"
#include "mh_unknown.h"
#include "mh_null.h"
#include "mh_xslt.h"
#include "rcldoc.h"
#include "rclutil.h"
#include "conftree.h"

using namespace std;

// Performance help: we use a pool of already known and created
// handlers. There can be several instances for a given mime type
// (think email attachment in email message: 2 rfc822 handlers are
// needed simulteanously)
static multimap  o_handlers;
static list::iterator> o_hlru;
typedef list::iterator>::iterator hlruit_tp;

static std::mutex o_handlers_mutex;

static const unsigned int max_handlers_cache_size = 100;

/* Look for mime handler in pool */
static RecollFilter *getMimeHandlerFromCache(const string& key)
{
    std::unique_lock locker(o_handlers_mutex);
    string xdigest;
    MD5HexPrint(key, xdigest);
    LOGDEB("getMimeHandlerFromCache: " << xdigest << " cache size " <<
           o_handlers.size() << "\n");

    multimap::iterator it = o_handlers.find(key);
    if (it != o_handlers.end()) {
        RecollFilter *h = it->second;
        hlruit_tp it1 = find(o_hlru.begin(), o_hlru.end(), it);
        if (it1 != o_hlru.end()) {
            o_hlru.erase(it1);
        } else {
            LOGERR("getMimeHandlerFromCache: lru position not found\n");
        }
        o_handlers.erase(it);
        LOGDEB("getMimeHandlerFromCache: " << xdigest << " found size " <<
               o_handlers.size() << "\n");
        return h;
    }
    LOGDEB("getMimeHandlerFromCache: " << xdigest << " not found\n");
    return 0;
}

/* Return mime handler to pool */
void returnMimeHandler(RecollFilter *handler)
{
    typedef multimap::value_type value_type;

    if (handler == 0) {
        LOGERR("returnMimeHandler: bad parameter\n");
        return;
    }
    handler->clear();

    std::unique_lock locker(o_handlers_mutex);

    LOGDEB("returnMimeHandler: returning filter for " <<
           handler->get_mime_type() << " cache size " << o_handlers.size() <<
           "\n");

    // Limit pool size. The pool can grow quite big because there are
    // many filter types, each of which can be used in several copies
    // at the same time either because it occurs several times in a
    // stack (ie mail attachment to mail), or because several threads
    // are processing the same mime type at the same time.
    multimap::iterator it;
    if (o_handlers.size() >= max_handlers_cache_size) {
        static int once = 1;
        if (once) {
            once = 0;
            for (it = o_handlers.begin(); it != o_handlers.end(); it++) {
                LOGDEB1("Cache full. key: " << it->first << "\n");
            }
            LOGDEB1("Cache LRU size: " << o_hlru.size() << "\n");
        }
        if (o_hlru.size() > 0) {
            it = o_hlru.back();
            o_hlru.pop_back();
            delete it->second;
            o_handlers.erase(it);
        }
    }
    it = o_handlers.insert(value_type(handler->get_id(), handler));
    o_hlru.push_front(it);
}

void clearMimeHandlerCache()
{
    LOGDEB("clearMimeHandlerCache()\n");
    multimap::iterator it;
    std::unique_lock locker(o_handlers_mutex);
    for (it = o_handlers.begin(); it != o_handlers.end(); it++) {
        delete it->second;
    }
    o_handlers.clear();
    TempFile::tryRemoveAgain();
}

/** For mime types set as "internal" in mimeconf: 
 * create appropriate handler object. */
static RecollFilter *mhFactory(RclConfig *config, const string &mimeOrParams,
                               bool nobuild, string& id)
{
    LOGDEB1("mhFactory(" << mimeOrParams << ")\n");
    vector lparams;
    stringToStrings(mimeOrParams, lparams);
    if (lparams.empty()) {
        // ??
        return nullptr;
    }
    string lmime(lparams[0]);
    stringtolower(lmime);
    if (cstr_textplain == lmime) {
        LOGDEB2("mhFactory(" << lmime << "): returning MimeHandlerText\n");
        MD5String("MimeHandlerText", id);
        return nobuild ? 0 : new MimeHandlerText(config, id);
    } else if (cstr_texthtml == lmime) {
        LOGDEB2("mhFactory(" << lmime << "): returning MimeHandlerHtml\n");
        MD5String("MimeHandlerHtml", id);
        return nobuild ? 0 : new MimeHandlerHtml(config, id);
    } else if ("text/x-mail" == lmime) {
        LOGDEB2("mhFactory(" << lmime << "): returning MimeHandlerMbox\n");
        MD5String("MimeHandlerMbox", id);
        return nobuild ? 0 : new MimeHandlerMbox(config, id);
    } else if ("message/rfc822" == lmime) {
        LOGDEB2("mhFactory(" << lmime << "): returning MimeHandlerMail\n");
        MD5String("MimeHandlerMail", id);
        return nobuild ? 0 : new MimeHandlerMail(config, id);
    } else if ("inode/symlink" == lmime) {
        LOGDEB2("mhFactory(" << lmime << "): returning MimeHandlerSymlink\n");
        MD5String("MimeHandlerSymlink", id);
        return nobuild ? 0 : new MimeHandlerSymlink(config, id);
    } else if ("application/x-zerosize" == lmime) {
        LOGDEB("mhFactory(" << lmime << "): returning MimeHandlerNull\n");
        MD5String("MimeHandlerNull", id);
        return nobuild ? 0 : new MimeHandlerNull(config, id);
    } else if (lmime.find("text/") == 0) {
        // Try to handle unknown text/xx as text/plain. This only happen if the text/xx was defined
        // as "internal" in mimeconf.
        // This is historic, because the natural way with the current code would be to specify
        // "internal text/plain" instead of just "internal"...
        // Also see rclconfig for the textunknownisplain configuration variable which allows
        // processing all unknown text/* as text/plain.
        LOGDEB2("mhFactory(" << lmime << "): returning MimeHandlerText(x)\n");
        MD5String("MimeHandlerText", id);
        return nobuild ? 0 : new MimeHandlerText(config, id);
    } else if ("xsltproc" == lmime) {
        // XML Types processed with one or several xslt style sheets.
        MD5String(mimeOrParams, id);
        return nobuild ? 0 : new MimeHandlerXslt(config, id, lparams);
    } else {
        // We should not get there. It means that "internal" was set
        // as a handler in mimeconf for a mime type we actually can't
        // handle.
        LOGERR("mhFactory: mime type [" << lmime <<
               "] set as internal but unknown\n");
        MD5String("MimeHandlerUnknown", id);
        return nobuild ? 0 : new MimeHandlerUnknown(config, id);
    }
}

static const string cstr_mh_charset("charset");
static const string cstr_mh_maxseconds("maxseconds");
/**
 * Create a filter that executes an external program or script
 * A filter def can look like:
 *      someprog -v -t " h i j";charset= xx; mimetype=yy
 * A semi-colon list of attr=value pairs may come after the exec spec.
 * This list is treated by replacing semi-colons with newlines and building
 * a confsimple. This is done quite brutally and we don't support having
 * a ';' inside a quoted string for now. Can't see a use for it.
 */
MimeHandlerExec *mhExecFactory(RclConfig *cfg, const string& mtype, string& hs,
                               bool multiple, const string& id)
{
    ConfSimple attrs;
    string cmdstr;

    if (!cfg->valueSplitAttributes(hs, cmdstr, attrs)) {
        LOGERR("mhExecFactory: bad config line for [" <<
               mtype << "]: [" << hs << "]\n");
        return 0;
    }

    // Split command name and args, and build exec object
    vector cmdtoks;
    stringToStrings(cmdstr, cmdtoks);
    if (cmdtoks.empty()) {
        LOGERR("mhExecFactory: bad config line for [" << mtype <<
               "]: [" << hs << "]\n");
        return 0;
    }
    if (!cfg->processFilterCmd(cmdtoks)) {
        return nullptr;
    }
    MimeHandlerExec *h = multiple ? new MimeHandlerExecMultiple(cfg, id) :
        new MimeHandlerExec(cfg, id);
    h->params = cmdtoks;

    // Handle additional attributes. We substitute the semi-colons
    // with newlines and use a ConfSimple
    string value;
    if (attrs.get(cstr_mh_charset, value)) 
        h->cfgFilterOutputCharset = stringtolower((const string&)value);
    if (attrs.get(cstr_dj_keymt, value))
        h->cfgFilterOutputMtype = stringtolower((const string&)value);
    if (attrs.get(cstr_mh_maxseconds, value)) {
        h->setmaxseconds(atoi(value.c_str()));
    }
    LOGDEB2("mhExecFactory:mt [" << mtype << "] cfgmt [" <<
            h->cfgFilterOutputMtype << "] cfgcs ["<cfgFilterOutputCharset <<
            "] cmd: [" << stringsToString(h->params) << "]\n");
    return h;
}

/* Get handler/filter object for given mime type: */
RecollFilter *getMimeHandler(const string &mtype, RclConfig *cfg,
                             bool filtertypes, const std::string& fn)
{
    LOGDEB("getMimeHandler: mtype [" << mtype << "] filtertypes " <<
           filtertypes << "\n");
    RecollFilter *h = 0;

    // Get handler definition for mime type. We do this even if an
    // appropriate handler object may be in the cache.
    // This is fast, and necessary to conform to the
    // configuration, (ie: text/html might be filtered out by
    // indexedmimetypes but an html handler could still be in the
    // cache because it was needed by some other interning stack).
    string hs;
    hs = cfg->getMimeHandlerDef(mtype, filtertypes, fn);
    string id;

    if (!hs.empty()) { 
        // Got a handler definition line
        // Break definition into type (internal/exec/execm) 
        // and name/command string 
        string::size_type b1 = hs.find_first_of(" \t");
        string handlertype = hs.substr(0, b1);
        string cmdstr;
        if (b1 != string::npos) {
            cmdstr = hs.substr(b1);
            trimstring(cmdstr);
        }
        bool internal = !stringlowercmp("internal", handlertype);
        if (internal) {
            // For internal types let the factory compute the cache id
            mhFactory(cfg, cmdstr.empty() ? mtype : cmdstr, true, id);
        } else {
            // exec/execm: use the md5 of the def line
            MD5String(hs, id);
        }

        // Do we already have a handler object in the cache ?
        h = getMimeHandlerFromCache(id);
        if (h != 0)
            goto out;

        LOGDEB2("getMimeHandler: " << mtype << " not in cache\n");
        if (internal) {
            // If there is a parameter after "internal" it's the mime
            // type to use, or the further qualifier (e.g. style sheet
            // name for xslt types). This is so that we can have bogus
            // mime types like text/x-purple-html-log (for ie:
            // specific icon) and still use the html filter on
            // them. This is partly redundant with the
            // localfields/rclaptg, but better? (and the latter will
            // probably go away at some point in the future?).
            LOGDEB2("handlertype internal, cmdstr [" << cmdstr << "]\n");
            h = mhFactory(cfg, cmdstr.empty() ? mtype : cmdstr, false, id);
            goto out;
        } else if (!stringlowercmp("dll", handlertype)) {
        } else {
            if (cmdstr.empty()) {
                LOGERR("getMimeHandler: bad line for " << mtype << ": " <<
                       hs << "\n");
                goto out;
            }
            if (!stringlowercmp("exec", handlertype)) {
                h = mhExecFactory(cfg, mtype, cmdstr, false, id);
                goto out;
            } else if (!stringlowercmp("execm", handlertype)) {
                h = mhExecFactory(cfg, mtype, cmdstr, true, id);
                goto out;
            } else {
                LOGERR("getMimeHandler: bad line for " << mtype << ": " <<
                       hs << "\n");
                goto out;
            }
        }
    } else {
        // No identified mime type, or no handler associated.
        // Unhandled files are either ignored or their name and
        // generic metadata is indexed, depending on configuration
        bool indexunknown = false;
        cfg->getConfParam("indexallfilenames", &indexunknown);
        if (indexunknown) {
            MD5String("MimeHandlerUnknown", id);
            if ((h = getMimeHandlerFromCache(id)) == 0)
                h = new MimeHandlerUnknown(cfg, id);
        }
        goto out;
    }

out:
    if (h) {
        h->set_property(RecollFilter::DEFAULT_CHARSET, cfg->getDefCharset());
        // In multithread context, and in case this handler is out
        // from the cache, it may have a config pointer belonging to
        // another thread. Fix it.
        h->setConfig(cfg);
    }
    return h;
}

/// Can this mime type be interned (according to config) ?
bool canIntern(const std::string mtype, RclConfig *cfg)
{
    if (mtype.empty())
        return false;
    string hs = cfg->getMimeHandlerDef(mtype);
    if (hs.empty())
        return false;
    return true;
}
/// Same, getting MIME from doc
bool canIntern(Rcl::Doc *doc, RclConfig *cfg)
{
    if (doc) {
        return canIntern(doc->mimetype, cfg);
    }
    return false;
}

/// Can this MIME type be opened (has viewer def) ?
bool canOpen(Rcl::Doc *doc, RclConfig *cfg, bool useall)
{
    if (!doc) {
        return false;
    }
    string apptag;
    doc->getmeta(Rcl::Doc::keyapptg, &apptag);
    return !cfg->getMimeViewerDef(doc->mimetype, apptag, useall).empty();
}

string RecollFilter::metadataAsString()
{
    string s;
    for (const auto& ent : m_metaData) {
        if (ent.first == "content")
            continue;
        s += ent.first + "->" + ent.second + "\n";
    }
    return s;
}
recoll-1.36.1/internfile/uncomp.h0000644000175000017500000000402514410615043013613 00000000000000/* Copyright (C) 2013 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _UNCOMP_H_INCLUDED_
#define _UNCOMP_H_INCLUDED_

#include 
#include 
#include 

#include "pathut.h"
#include "rclutil.h"

/// Uncompression script interface.
class Uncomp {
public:
    explicit Uncomp(bool docache = false);
    ~Uncomp();
    Uncomp(const Uncomp&) = delete;
    Uncomp& operator=(const Uncomp&) = delete;

    /** Uncompress the input file into a temporary one, by executing the
     * script given as input. 
     * Return the path to the uncompressed file (which is inside a 
     * temporary directory).
     */
    bool uncompressfile(const std::string& ifn, 
                        const std::vector& cmdv,
                        std::string& tfile);
    static void clearcache();
    
private:
    TempDir *m_dir{0};
    std::string   m_tfile;
    std::string   m_srcpath;
    bool m_docache;

    class UncompCache {
    public:
        UncompCache() {}
        ~UncompCache() {
            delete m_dir;
        }
        UncompCache(const UncompCache&) = delete;
        UncompCache& operator=(const UncompCache&) = delete;
        std::mutex m_lock;
        TempDir *m_dir{0};
        std::string   m_tfile;
        std::string   m_srcpath;
    };
    static UncompCache o_cache;
};

#endif /* _UNCOMP_H_INCLUDED_ */
recoll-1.36.1/internfile/mh_execm.cpp0000644000175000017500000003040314427373216014444 00000000000000/* Copyright (C) 2005 J.F.Dockes 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 
#include 
#include "safesyswait.h"
#include 
#include 

#include "cstr.h"
#include "mh_execm.h"
#include "mh_html.h"
#include "log.h"
#include "cancelcheck.h"
#include "smallut.h"
#include "md5ut.h"
#include "rclconfig.h"
#include "mimetype.h"
#include "idfile.h"
#include "rclutil.h"
#include "idxdiags.h"

using namespace std;

bool MimeHandlerExecMultiple::startCmd()
{
    LOGDEB1("MimeHandlerExecMultiple::startCmd\n");
    if (params.empty()) {
        // Hu ho
        LOGERR("MHExecMultiple::startCmd: empty params\n");
        m_reason = "RECFILTERROR BADCONFIG";
        return false;
    }

    // Command name
    string cmd = params.front();
    
    m_maxmemberkb = 50000;
    m_config->getConfParam("membermaxkbs", &m_maxmemberkb);
    ostringstream oss;
    oss << "RECOLL_FILTER_MAXMEMBERKB=" << m_maxmemberkb;
    m_cmd.putenv(oss.str());

    m_cmd.putenv("RECOLL_CONFDIR", m_config->getConfDir());
    m_cmd.putenv(m_forPreview ? "RECOLL_FILTER_FORPREVIEW=yes" : "RECOLL_FILTER_FORPREVIEW=no");

    m_cmd.setrlimit_as(m_filtermaxmbytes);
    m_adv.setmaxsecs(m_filtermaxseconds);
    m_cmd.setAdvise(&m_adv);
    std::string errfile;
    m_config->getConfParam("helperlogfilename", errfile);
    if (!errfile.empty()) {
        m_cmd.setStderr(errfile);
    }

    // Build parameter list: delete cmd name
    vectormyparams(params.begin() + 1, params.end());

    if (m_cmd.startExec(cmd, myparams, 1, 1) < 0) {
        IdxDiags::theDiags().record(IdxDiags::MissingHelper, m_fn);
        m_reason = string("RECFILTERROR HELPERNOTFOUND ") + cmd;
        missingHelper = true;
        whatHelper = cmd;
        return false;
    }
    return true;
}

// Note: data is not used if this is the "document:" field: it goes
// directly to m_metaData[cstr_dj_keycontent] to avoid an extra copy
// 
// Messages are made of data elements. Each element is like:
// name: len\ndata
// An empty line signals the end of the message, so the whole thing
// would look like:
// Name1: Len1\nData1Name2: Len2\nData2\n
bool MimeHandlerExecMultiple::readDataElement(string& name, string &data)
{
    string ibuf;

    // Read name and length
    if (m_cmd.getline(ibuf) <= 0) {
        LOGERR("MHExecMultiple: getline error\n");
        return false;
    }
    
    LOGDEB1("MHEM:rde: line [" << ibuf << "]\n");

    // Empty line (end of message) ?
    if (!ibuf.compare("\n")) {
        LOGDEB1("MHExecMultiple: Got empty line\n");
        name.clear();
        return true;
    }

    // Filters will sometimes abort before entering the real protocol, ie if
    // a module can't be loaded. Check the special filter error first word:
    std::string::size_type pos;
    if ((pos = ibuf.find("RECFILTERROR ")) == 0) {
        m_reason = ibuf;
        if (ibuf.find("HELPERNOTFOUND") != string::npos) {
            IdxDiags::theDiags().record(IdxDiags::MissingHelper, m_fn);
            missingHelper = true;
            whatHelper = ibuf.substr(pos);
        }
        return false;
    }

    // We're expecting something like Name: len\n
    vector tokens;
    stringToTokens(ibuf, tokens);
    if (tokens.size() != 2) {
        LOGERR("MHExecMultiple: bad line in filter output: [" << ibuf << "]\n");
        return false;
    }
    vector::iterator it = tokens.begin();
    name = *it++;
    string& slen = *it;
    int len;
    if (sscanf(slen.c_str(), "%d", &len) != 1) {
        LOGERR("MHExecMultiple: bad line in filter output: [" << ibuf << "]\n");
        return false;
    }

    if (len / 1024 > m_maxmemberkb) {
        LOGERR("MHExecMultiple: data len > maxmemberkb\n");
        return false;
    }
    
    // Hack: check for 'Document:' and read directly the document data
    // to m_metaData[cstr_dj_keycontent] to avoid an extra copy of the bulky
    // piece
    string *datap = &data;
    if (!stringlowercmp("document:", name)) {
        datap = &m_metaData[cstr_dj_keycontent];
    } else {
        datap = &data;
    }

    // Read element data
    datap->erase();
    if (len > 0 && m_cmd.receive(*datap, len) != len) {
        LOGERR("MHExecMultiple: expected " << len << " data bytes, got " << datap->length() << "\n");
        return false;
    }
    LOGDEB1("MHExecMe:rdDtElt got: name [" << name << "] len " << len <<
            "value [" << (datap->size() > 100 ? (datap->substr(0, 100) + " ...") : *datap) << "\n");
    return true;
}

bool MimeHandlerExecMultiple::next_document()
{
    LOGDEB("MimeHandlerExecMultiple::next_document(): [" << m_fn << "]\n");
    if (m_havedoc == false)
        return false;

    if (missingHelper) {
        LOGDEB("MHExecMultiple::next_document(): helper known missing\n");
        m_reason = whatHelper;
        return false;
    }

    if (m_cmd.getChildPid() <= 0 && !startCmd()) {
        return false;
    }

    m_metaData.clear();
    
    // Send request to child process. This maybe the first/only
    // request for a given file, or a continuation request. We send an
    // empty file name in the latter case.
    // We also compute the file md5 before starting the extraction:
    // under Windows, we may not be able to do it while the file
    // is opened by the filter.
    ostringstream obuf;
    string file_md5;
    if (m_filefirst) {
        if (!m_forPreview && !m_nomd5) {
            string md5, xmd5, reason;
            if (MD5File(m_fn, md5, &reason)) {
                file_md5 = MD5HexPrint(md5, xmd5);
            } else {
                LOGERR("MimeHandlerExecM: cant compute md5 for [" << m_fn <<"]: " << reason << "\n");
            }
        }
        obuf << "filename: " << m_fn.length() << "\n" << m_fn;
        // m_filefirst is set to true by set_document_file()
        m_filefirst = false;
    } else {
        obuf << "filename: " << 0 << "\n";
    }
    if (!m_ipath.empty()) {
        LOGDEB("next_doc: sending ipath " << m_ipath.length() << " val [" << m_ipath << "]\n");
        obuf << "ipath: " << m_ipath.length() << "\n" << m_ipath;
    }
    if (!m_dfltInputCharset.empty()) {
        obuf << "dflincs: " << m_dfltInputCharset.length() << "\n" << m_dfltInputCharset;
    }
    obuf << "mimetype: " << m_mimeType.length() << "\n" << m_mimeType;
    obuf << "\n";
    if (m_cmd.send(obuf.str()) < 0) {
        m_cmd.zapChild();
        LOGERR("MHExecMultiple: send error\n");
        return false;
    }

    m_adv.reset();

    // Read answer (multiple elements)
    LOGDEB1("MHExecMultiple: reading answer\n");
    bool eofnext_received = false;
    bool eofnow_received = false;
    bool fileerror_received = false;
    bool subdocerror_received = false;
    string ipath;
    string mtype;
    string charset;
    for (int loop=0;;loop++) {
        string name, data;
        try {
            if (!readDataElement(name, data)) {
                m_cmd.zapChild();
                return false;
            }
        } catch (HandlerTimeout) {
            LOGINFO("MHExecMultiple: timeout\n");
            m_cmd.zapChild();
            return false;
        } catch (CancelExcept) {
            LOGINFO("MHExecMultiple: interrupt\n");
            m_cmd.zapChild();
            return false;
        }
        if (name.empty())
            break;
        if (!stringlowercmp("eofnext:", name)) {
            LOGDEB0("MHExecMultiple: got EOFNEXT\n");
            eofnext_received = true;
        } else if (!stringlowercmp("eofnow:", name)) {
            LOGDEB0("MHExecMultiple: got EOFNOW\n");
            eofnow_received = true;
        } else if (!stringlowercmp("fileerror:", name)) {
            LOGDEB0("MHExecMultiple: got FILEERROR\n");
            fileerror_received = true;
        } else if (!stringlowercmp("subdocerror:", name)) {
            LOGDEB0("MHExecMultiple: got SUBDOCERROR\n");
            subdocerror_received = true;
        } else if (!stringlowercmp("ipath:", name)) {
            ipath = data;
            LOGDEB0("MHExecMultiple: got ipath [" << data << "]\n");
        } else if (!stringlowercmp("charset:", name)) {
            charset = data;
            LOGDEB0("MHExecMultiple: got charset [" << data << "]\n");
        } else if (!stringlowercmp("mimetype:", name)) {
            mtype = data;
            LOGDEB0("MHExecMultiple: got mimetype [" << data << "]\n");
        } else {
            string nm = stringtolower((const string&)name);
            trimstring(nm, ":");
            LOGDEB0("MHExecMultiple: got [" << nm << "] -> [" << data << "]\n");
            addmeta(m_metaData, nm, data);
        }
        if (loop == 200) {
            // ?? 
            LOGERR("MHExecMultiple: handler sent more than 200 attributes\n");
            return false;
        }
    }

    if (eofnow_received || fileerror_received) {
        // No more docs
        m_havedoc = false;
        return false;
    }
    if (subdocerror_received) {
        return false;
    }

    // It used to be that eof could be signalled just by an empty document, but
    // this was wrong. Empty documents can be found ie in zip files and should 
    // not be interpreted as eof.
    if (m_metaData[cstr_dj_keycontent].empty()) {
        LOGDEB0("MHExecMultiple: got empty document inside [" << m_fn << "]: [" << ipath << "]\n");
    }

    if (!ipath.empty()) {
        // If this has an ipath, it is an internal doc from a
        // multi-document file. In this case, either the filter
        // supplies the mimetype, or the ipath MUST be a filename-like
        // string which we can use to compute a mime type
        m_metaData[cstr_dj_keyipath] = ipath;
        if (mtype.empty()) {
            LOGDEB0("MHExecMultiple: no mime type from filter, using ipath for a guess\n");
            mtype = mimetype(ipath, m_config, false);
            if (mtype.empty()) {
                // mimetype() won't call idFile when there is no file. Do it
                mtype = idFileMem(m_metaData[cstr_dj_keycontent]);
                if (mtype.empty()) {
                    // Note this happens for example for directory zip members
                    // We could recognize them by the end /, but wouldn't know
                    // what to do with them anyway.
                    LOGINFO("MHExecMultiple: cant guess mime type\n");
                    mtype = "application/octet-stream";
                }
            }
            /* If we identify text/plain from the suffix (as opposed
               to the handler setting the type), we use text/plain1
               instead. As directed in mimeconf, this will cause the
               text handler to be applied (instead of internfile just
               ending things there), allowing splitting and default
               charset conversions. */
            if (mtype == "text/plain") {
                mtype = "text/plain1";
            }
        }
        m_metaData[cstr_dj_keymt] = mtype;
        if (!m_forPreview) {
            string md5, xmd5;
            MD5String(m_metaData[cstr_dj_keycontent], md5);
            m_metaData[cstr_dj_keymd5] = MD5HexPrint(md5, xmd5);
        }
    } else {
        // "Self" document.
        m_metaData[cstr_dj_keymt] = mtype.empty() ? cstr_texthtml : mtype;
        m_metaData.erase(cstr_dj_keyipath);
        if (!m_forPreview) {
            m_metaData[cstr_dj_keymd5] = file_md5;
        }
    }

    handle_cs(m_metaData[cstr_dj_keymt], charset);

    if (eofnext_received)
        m_havedoc = false;

    LOGDEB0("MHExecMultiple: returning " << m_metaData[cstr_dj_keycontent].size() <<
            " bytes of content, mtype [" << m_metaData[cstr_dj_keymt] <<
            "] charset [" << m_metaData[cstr_dj_keycharset] << "]\n");
    LOGDEB2("MHExecMultiple: metadata: \n" << metadataAsString());
    return true;
}
recoll-1.36.1/internfile/Filter.h0000644000175000017500000001606714410615043013550 00000000000000/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifndef _DIJON_FILTER_H
#define _DIJON_FILTER_H

#include 
#include 
#include 
#include 

class RclConfig;

namespace Dijon {

/// Document handler interface.
///
/// Document handler can either translate the text format
/// (e.g. msdoc->text/plain), or/and extract subdocuments from
/// multidocument formats (e.g. mbox->message, message->attachments,
/// zip etc.)
class Filter {
public:
    Filter() {}
    virtual ~Filter() {}
    /// Filter objects cannot be copied.
    Filter(const Filter &other) = delete;
    Filter& operator=(const Filter& other) = delete;

    // Allow me to access the general config. This is a borrowed
    // pointer. It has to be read/write, but don't delete.
    virtual void setConfig(RclConfig *) = 0;

    /// Returns the MIME type handled by the filter.
    std::string get_mime_type(void) const {
        return m_mimeType;
    }

    /** Supported input types */
    typedef enum {DOCUMENT_DATA = 0, DOCUMENT_STRING, DOCUMENT_FILE_NAME, 
                  DOCUMENT_URI} DataInput;

    virtual bool is_data_input_ok(DataInput input) const = 0;

    /* Properties to be set prior to actual operation */
    typedef enum {
        // Source encoding to be used for reading/transcoding the
        // original data if there is no other way to find
        // (e.g. for text/plain files)
        DEFAULT_CHARSET = 0,
        // Either "view" or "index". Some implementations produce
        // slightly different data (e.g. avoiding repeating some
        // text in index mode)
        OPERATING_MODE,
        // Unique document identifier. This can be useful if the
        // filter wants to manage a persistent cache (e.g. mh_mbox)
        DJF_UDI
    } Properties;

    /** Sets a property, prior to calling set_document_XXX().
     * Returns false if the property or value is not supported. */
    virtual bool set_property(Properties prop_name, 
                              const std::string &prop_value) = 0;

    /** (Re)initializes the filter with the given data.
     * Caller should ensure the given pointer is valid until the
     * Filter object is destroyed, as some filters may not need to
     * do a deep copy of the data.
     * Call next_document() to position the filter onto the first document.
     * Returns false if this input is not supported or an error occurred.
     */
    virtual bool set_document_data(const std::string& mtype, 
                                   const char *data_ptr, 
                                   size_t data_length) = 0;

    /** (Re)initializes the filter with the given data.
     * Call next_document() to position the filter onto the first document.
     * Returns false if this input is not supported or an error occurred.
     */
    virtual bool set_document_string(const std::string& mtype, 
                                     const std::string &data_str) = 0;

    /** (Re)initializes the filter with the given file.
     * Call next_document() to position the filter onto the first document.
     * Returns false if this input is not supported or an error occurred.
     */
    virtual bool set_document_file(const std::string& mtype, 
                                   const std::string &file_path) = 0;

    /** (Re)initializes the filter with the given URI.
     * Call next_document() to position the filter onto the first document.
     * Returns false if this input is not supported or an error occurred.
     * No implementation supports this at the moment.
     */
    virtual bool set_document_uri(const std::string& mtype, 
                                  const std::string &uri) = 0;

    /** Set the document size meta_data element. This is the size
        of the immediate containing file (ie, a .doc, a .odt), not
        the size of, ie, a containing archive or .gz nor the size
        of the extracted text. This is set externally, because the
        surrounding code quite often has a better idea about it
        (having created a temp file, etc.), and this saves more
        stat() calls The value is stored inside metaData, docsize
        key
    */
    virtual void set_docsize(int64_t size) = 0;

    // Going from one nested document to the next.

    /** Returns true if there are nested documents left to extract.
     * Returns false if the end of the parent document was reached
     * or an error occurred.
     */
    virtual bool has_documents(void) const = 0;

    /** Moves to the next nested document.
     * Returns false if there are none left.
     */ 
    virtual bool next_document(void) = 0;

    /** Skips to the nested document with the given ipath.
     * Returns false if no such document exists.
     */
    virtual bool skip_to_document(const std::string &ipath) = 0;

    // Accessing documents' contents.

    /// Returns the message for the most recent error that has occurred.
    virtual std::string get_error(void) const = 0;

    /** Returns a dictionary of metadata extracted from the current document.
     * Metadata fields may include one or more of the following :
     * content, title, ipath, mimetype, language, charset, author, creator,
     * publisher, modificationdate, creationdate, size
     * Special considerations apply :
     * - content may contain binary data, watch out !
     * - ipath is an internal path to the nested document that can be
     * later passed to skip_to_document(). It may be empty if the parent
     * document's type doesn't allow embedding, in which case the filter
     * should only return one document.
     * - mimetype should be text/plain if the document could be handled
     * internally, empty if unknown. If any other value, it is expected
     * that the client application can pass the nested document's content
     * to another filter that supports this particular type.
     */
    virtual const std::map&
    get_meta_data(void) const {
        return m_metaData;
    }

    virtual void clear() {
        m_metaData.clear();
    }
    // Hack: is this the special version used for unknown types?
    virtual bool is_unknown() {
        return false;
    }

protected:
    /// The MIME type handled by the filter.
    std::string m_mimeType;

    /// Current Metadata dictionary. For multi-document files, this
    /// may be rebuilt for each sub-document. See common/cstr.h for
    /// the common key definitions.
    std::map m_metaData;

};
}

#endif // _DIJON_FILTER_H
recoll-1.36.1/internfile/mh_execm.h0000644000175000017500000001262514410615043014104 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _MH_EXECM_H_INCLUDED_
#define _MH_EXECM_H_INCLUDED_

#include "mh_exec.h"
#include "execmd.h"

/** 
 * Turn external document into internal one by executing an external filter.
 *
 * The command to execute, and its parameters, are stored in the "params" 
 * which is built in mimehandler.cpp out of data from the mimeconf file.
 *
 * This version uses persistent filters which can handle multiple requests 
 * without exiting (both multiple files and multiple documents per file), 
 * with a simple question/response protocol.
 *
 * The data is exchanged in TLV fashion, in a way that should be
 * usable in most script languages. The basic unit of data has one line 
 * with a data type and a count (both ASCII), followed by the data. A
 * 'message' is made of one or several units or tags and ends with one empty
 * line. 
 * 
 * Example from recollindex (the message begins before 'Filename' and has
 * 'Filename' and 'Ipath' tags):
 * 
Filename: 24
/my/home/mail/somefolderIpath: 2
22


#include 
#include 

#include "mimehandler.h"

namespace Binc {
class MimeDocument;
class MimePart;
}

class MHMailAttach;

/** 
 * Process a mail message (rfc822) into internal documents.
 */
class MimeHandlerMail : public RecollFilter {
public:
    MimeHandlerMail(RclConfig *cnf, const std::string &id);
    virtual ~MimeHandlerMail();
    MimeHandlerMail(const MimeHandlerMail&) = delete;
    MimeHandlerMail& operator=(const MimeHandlerMail&) = delete;
    virtual bool is_data_input_ok(DataInput input) const override {
        return (input == DOCUMENT_FILE_NAME || input == DOCUMENT_STRING);
    }
    virtual bool next_document() override;
    virtual bool skip_to_document(const std::string& ipath) override;
    virtual void clear_impl() override;

protected:
    virtual bool set_document_file_impl(const std::string& mt,
                                        const std::string& file_path) override;
    virtual bool set_document_string_impl(const std::string& mt,
                                          const std::string& data) override;

private:
    bool processMsg(Binc::MimePart *doc, int depth);
    void walkmime(Binc::MimePart* doc, int depth);
    bool processAttach();
    Binc::MimeDocument     *m_bincdoc;
    int                     m_fd;
    std::stringstream      *m_stream;

    // Current index in parts. starts at -1 for self, then index into
    // attachments
    int                     m_idx; 
    // Start of actual text (after the reprinted headers. This is for 
    // generating a semi-meaningful "abstract")
    std::string::size_type       m_startoftext; 
    std::string                  m_subject; 
    std::vector  m_attachments;
    // Additional headers to be processed as per config + field name translation
    std::map      m_addProcdHdrs; 
};

class MHMailAttach {
public:
    std::string m_contentType;
    std::string m_filename;
    std::string m_charset;
    std::string m_contentTransferEncoding;
    Binc::MimePart *m_part;
};

#endif /* _MAIL_H_INCLUDED_ */
recoll-1.36.1/internfile/mh_mbox.h0000644000175000017500000000323114410615043013741 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _MBOX_H_INCLUDED_
#define _MBOX_H_INCLUDED_

#include 
#include 
#include 

#include "mimehandler.h"

/** 
 * Translate a mail folder file into internal documents (also works
 * for maildir files). This has to keep state while parsing a mail folder
 * file. 
 */
class MimeHandlerMbox : public RecollFilter {
public:
    MimeHandlerMbox(RclConfig *cnf, const std::string& id);
    virtual ~MimeHandlerMbox();
    MimeHandlerMbox(const MimeHandlerMbox&) = delete;
    MimeHandlerMbox& operator=(const MimeHandlerMbox&) = delete;
    virtual bool next_document() override;
    virtual bool skip_to_document(const std::string& ipath) override;
    virtual void clear_impl() override;

protected:
    virtual bool set_document_file_impl(const std::string&, const std::string&) override;

    class Internal;
private:
    Internal *m{nullptr};
};

#endif /* _MBOX_H_INCLUDED_ */
recoll-1.36.1/internfile/mh_text.cpp0000644000175000017500000001460214410615043014317 00000000000000/* Copyright (C) 2005 J.F.Dockes 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 
#include 
#include "safefcntl.h"
#include 
#include "safeunistd.h"

#include 
#include 

#include "cstr.h"
#include "mh_text.h"
#include "log.h"
#include "readfile.h"
#include "md5ut.h"
#include "rclconfig.h"
#include "pxattr.h"
#include "pathut.h"

using namespace std;

void MimeHandlerText::getparams()
{
    m_config->getConfParam("textfilemaxmbs", &m_maxmbs);

    // Text file page size: if set, we split text files into
    // multiple documents
    int ps = 1000;
    m_config->getConfParam("textfilepagekbs", &ps);
    if (ps != -1) {
        ps *= 1024;
        m_paging = true;
    } else {
        m_paging = false;
    }
    m_pagesz = size_t(ps);
    m_offs = 0;
}

// Process a plain text file
bool MimeHandlerText::set_document_file_impl(const string&, const string &fn)
{
    LOGDEB("MimeHandlerText::set_document_file: [" << fn << "] offs " <<
           m_offs << "\n");

    m_fn = fn;
    // file size for oversize check
    m_totlen = path_filesize(m_fn);
    if (m_totlen < 0) {
        LOGERR("MimeHandlerText::set_document_file: stat " << m_fn <<
               " errno " << errno << "\n");
        return false;
    }

    // Check for charset defined in extended attribute as per:
    // http://freedesktop.org/wiki/CommonExtendedAttributes
    pxattr::get(m_fn, "charset", &m_charsetfromxattr);

    getparams();
    if (m_maxmbs != -1 && m_totlen / (1024*1024) > m_maxmbs) {
        LOGINF("MimeHandlerText: file too big (textfilemaxmbs=" << m_maxmbs <<
               "), contents will not be indexed: " << fn << endl);
    } else {
        if (!readnext()) {
            return false;
        } 
    }
    m_havedoc = true;
    return true;
}

bool MimeHandlerText::set_document_string_impl(const string&, const string& otext)
{
    m_fn.clear();
    m_totlen = otext.size();

    getparams();
    if (m_maxmbs != -1 && m_totlen / (1024*1024) > m_maxmbs) {
        LOGINF("MimeHandlerText: text too big (textfilemaxmbs=" << m_maxmbs <<
               "), contents will not be indexed\n");
    } else {
        if (!m_paging || (m_totlen <= (int64_t)m_pagesz)) {
            // Avoid copy for texts smaller than page size
            m_paging = false;
            m_text = otext;
            m_offs = m_totlen;
        } else {
            m_alltext = otext;
            readnext();
        }
    }
        
    m_havedoc = true;
    return true;
}

bool MimeHandlerText::skip_to_document(const string& ipath)
{
    char *endptr;
    int64_t t = strtoll(ipath.c_str(), &endptr, 10);
    if (endptr == ipath.c_str()) {
        LOGERR("MimeHandlerText::skip_to_document: bad ipath offs ["  <<
               ipath << "]\n");
        return false;
    }
    m_offs = t;
    readnext();
    return true;
}

bool MimeHandlerText::next_document()
{
    LOGDEB("MimeHandlerText::next_document: m_havedoc "  << m_havedoc << "\n");

    if (m_havedoc == false)
        return false;

    if (m_charsetfromxattr.empty())
        m_metaData[cstr_dj_keyorigcharset] = m_dfltInputCharset;
    else 
        m_metaData[cstr_dj_keyorigcharset] = m_charsetfromxattr;

    m_metaData[cstr_dj_keymt] = cstr_textplain;

    size_t srclen = m_text.length();
    if (!m_forPreview) {
        string md5, xmd5;
        MD5String(m_text, md5);
        m_metaData[cstr_dj_keymd5] = MD5HexPrint(md5, xmd5);
    }
    m_metaData[cstr_dj_keycontent].swap(m_text);

    // We transcode even if defcharset is supposedly already utf-8:
    // this validates the encoding.
    // txtdcode() truncates the text if transcoding fails
    (void)txtdcode("mh_text");

    // If the text length is 0 (the file is empty or oversize), or we are 
    // not paging, we're done
    if (srclen == 0 || !m_paging) {
        m_havedoc = false;
        return true;
    } else {
        // Paging: set ipath then read next chunk. 

        int64_t start_offset = m_offs - srclen;
        string buf = lltodecstr(start_offset);

        // Don't set ipath for the first chunk to avoid having 2
        // records for small files (one for the file, one for the
        // first chunk). This is a hack. The right thing to do would
        // be to use a different mtype for files over the page size,
        // and keep text/plain only for smaller files.
        if (start_offset != 0)
            m_metaData[cstr_dj_keyipath] = buf;

        readnext();

        // This ensures that the first chunk (offs==srclen) of a
        // multi-chunk file does have an ipath. Else it stands for the
        // whole file (see just above), which used to be the case but
        // does not seem right
        if (m_havedoc)
            m_metaData[cstr_dj_keyipath] = buf;

        return true;
    }
}

bool MimeHandlerText::readnext()
{
    string reason;
    m_text.clear();
    if (!m_fn.empty()) {
        if (!file_to_string(m_fn, m_text, m_offs, m_pagesz, &reason)) {
            LOGERR("MimeHandlerText: can't read file: "  << reason << "\n" );
            m_havedoc = false;
            return false;
        }
    } else {
        m_text = m_alltext.substr((size_t)m_offs, m_pagesz);
    }

    if (m_text.length() == 0) {
        // EOF
        m_havedoc = false;
        return true;
    }

    // If possible try to adjust the chunk to end right after a line
    // Don't do this for the last chunk. Last chunk of exactly the
    // page size might be unduly split, no big deal
    if (m_text.length() == m_pagesz && m_text.back() != '\n' &&
        m_text.back() != '\r') {
        string::size_type pos = m_text.find_last_of("\n\r");
        if (pos != string::npos && pos != 0) {
            m_text.erase(pos);
        }
    }
    m_offs += m_text.length();
    return true;
}
recoll-1.36.1/internfile/mimehandler.h0000644000175000017500000001354314427373216014617 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _MIMEHANDLER_H_INCLUDED_
#define _MIMEHANDLER_H_INCLUDED_

#include 
#include 
#include 

#include "Filter.h"
#include "smallut.h"

class RclConfig;

class RecollFilter : public Dijon::Filter {
public:
    RecollFilter(RclConfig *config, const std::string& id)
        : m_config(config), m_id(id) {
    }
    virtual ~RecollFilter() {}
    RecollFilter(const RecollFilter&) = delete;
    RecollFilter& operator=(const RecollFilter&) = delete;

    virtual void setConfig(RclConfig *config) override {
        m_config = config;
    }

    virtual bool set_property(Properties p, const std::string &v) override {
        switch (p) {
        case DJF_UDI: 
            m_udi = v;
            break;
        case DEFAULT_CHARSET: 
            m_dfltInputCharset = v;
            break;
        case OPERATING_MODE: 
            if (!v.empty() && v[0] == 'v') 
                m_forPreview = true; 
            else 
                m_forPreview = false;
            break;
        }
        return true;
    }

    // We don't use this for now
    virtual bool set_document_uri(const std::string& mtype, const std::string &) override {
        m_mimeType = mtype;
        return false;
    }

    virtual bool set_document_file(const std::string& mtype,const std::string &file_path) override {
        m_mimeType = mtype;
        return set_document_file_impl(mtype, file_path);
    }

    virtual bool set_document_string(const std::string& mtype,const std::string &contents) override{
        m_mimeType = mtype;
        return set_document_string_impl(mtype, contents);
    }
    
    virtual bool set_document_data(const std::string& mtype, const char *cp, size_t sz) override {
        return set_document_string(mtype, std::string(cp, sz));
    }

    virtual void set_docsize(int64_t size) override {
        m_docsize = size;
    }

    virtual int64_t get_docsize() const {
        return m_docsize;
    }

    virtual bool has_documents() const override {
        return m_havedoc;
    }

    // Most doc types are single-doc
    virtual bool skip_to_document(const std::string& s) override {
        if (s.empty())
            return true;
        return false;
    }

    virtual bool is_data_input_ok(DataInput input) const override {
        if (input == DOCUMENT_FILE_NAME)
            return true;
        return false;
    }

    virtual std::string get_error() const override {
        return m_reason;
    }

    virtual const std::string& get_id() const {
        return m_id;
    }

    // Classes which need to do local work in clear() need
    // to implement clear_impl()
    virtual void clear() final {
        clear_impl();
        Dijon::Filter::clear();
        m_forPreview = m_havedoc = false;
        m_dfltInputCharset.clear();
        m_reason.clear();
    }
    virtual void clear_impl() {}
    
    // This only makes sense if the contents are currently txt/plain
    // It converts from keyorigcharset to UTF-8 and sets keycharset.
    bool txtdcode(const std::string& who);

    std::string metadataAsString();
protected:

    // We provide default implementation as not all handlers need both methods
    virtual bool set_document_file_impl(const std::string&,
                                        const std::string&) {
        return m_havedoc = true;
    }

    virtual bool set_document_string_impl(const std::string&,
                                          const std::string&) {
        return m_havedoc = true;
    }
    
    bool preview() {
        return m_forPreview;
    }

    RclConfig *m_config;
    bool   m_forPreview{false};
    std::string m_dfltInputCharset;
    std::string m_reason;
    bool   m_havedoc{false};
    std::string m_udi; // May be set by creator as a hint
    // m_id is and md5 of the filter definition line (from mimeconf) and
    // is used when fetching/returning filters to / from the cache.
    std::string m_id;
    int64_t m_docsize{0}; // Size of the top document
};

/**
 * Return indexing handler object for the given mime type. The returned 
 * pointer should be passed to returnMimeHandler() for recycling, after use.
 * @param mtyp input mime type, ie text/plain
 * @param cfg  the recoll config object to be used
 * @param filtertypes decide if we should restrict to types in 
 *     indexedmimetypes (if this is set at all).
 */
extern RecollFilter *getMimeHandler(const std::string &mtyp, RclConfig *cfg,
                                    bool filtertypes, const std::string& fn = std::string());

/// Free up filter for reuse (you can also delete it)
extern void returnMimeHandler(RecollFilter *);

/// Clean up cache at the end of an indexing pass. For people who use
/// the GUI to index: avoid all those filter processes forever hanging
/// off recoll.
extern void clearMimeHandlerCache();

namespace Rcl {
class Doc;
}
/// Can this mime type be interned ?
extern bool canIntern(const std::string mimetype, RclConfig *cfg);
/// Same, getting MIME from doc
extern bool canIntern(Rcl::Doc *doc, RclConfig *cfg);
/// Can this MIME type be opened (has viewer def) ?
extern bool canOpen(Rcl::Doc *doc, RclConfig *cfg, bool useall=false);

#endif /* _MIMEHANDLER_H_INCLUDED_ */
recoll-1.36.1/internfile/txtdcode.cpp0000644000175000017500000001274114410615043014467 00000000000000/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 

#include "cstr.h"
#include "transcode.h"
#include "mimehandler.h"
#include "log.h"
#include "smallut.h"
#include "rclutil.h"
#include "listmem.h"

#ifdef _WIN32
#include "utf8iter.h"
#endif

using std::string;

// Called after decoding from utf-8 failed. Handle the common case
// where this is a good old 8bit-encoded text document left-over when
// the locale was switched to utf-8. We try to guess a charset
// according to the locale language and use it. This is a very rough
// heuristic, but may be better than discarding the data. 
// If we still get a significant number of decode errors, the doc is
// quite probably binary, so just fail.
// Note that we could very well get a wrong transcoding (e.g. between
// iso-8859 variations), there is no way to detect it.
static bool alternate_decode(const string& in, string& out, string& ocs)
{
    int ecnt;
    if (samecharset(ocs, cstr_utf8)) {
        string lang = localelang();
        string code = langtocode(lang);
        LOGDEB("RecollFilter::txtdcode: trying alternate decode from " <<
               code << "\n");
        bool ret = transcode(in, out, code, cstr_utf8, &ecnt);
        if (ecnt > 5)
            ret = false;
        if (ret) {
            ocs = code;
        }
        return ret;
    } else {
        // Give a try to utf-8 anyway, as this is self-detecting. This
        // handles UTF-8 docs in a non-utf-8 environment. Note that
        // this will almost never be called, as most encodings are
        // unable to detect errors so that the first try at
        // transcoding will have succeeded and alternate_decode() will
        // not be called at all.
        // 
        // To avoid this, we would have to attempt an utf-8 decode
        // first, but this is a costly proposition as we don't know
        // how much data to test, so need to test all (the beginning
        // of the text could be ascii even if there are 8-bit chars
        // later).
        bool ret = transcode(in, out, cstr_utf8, cstr_utf8, &ecnt);
        return ecnt > 5 ? false : ret;
    }
}

static string bomtocode(const string& itext)
{
#if 0
    std::ostringstream strm;
    listmem(strm, itext.c_str(), MIN(itext.size(), 8));
    LOGDEB("txtdcode:bomtocode: input " << strm.str() << "\n");
#endif

    const unsigned char *utxt = (const unsigned char *)itext.c_str();
    if (itext.size() >= 3 && utxt[0] == 0xEF && utxt[1] == 0xBB &&
        utxt[2] == 0xBF) {
        LOGDEB("txtdcode:bomtocode: UTF-8\n");
        return "UTF-8";
    } else if (itext.size() >= 2 && utxt[0] == 0xFE && utxt[1] == 0xFF) {
        return "UTF-16BE";
    } else if (itext.size() >= 2 && utxt[0] == 0xFF && utxt[1] == 0xFE) {
        return "UTF-16LE";
    } else if (itext.size() >= 4 && utxt[0] == 0 && utxt[1] == 0 &&
               utxt[2] == 0xFE && utxt[3] == 0xFF) {
        return "UTF-32BE";
    } else if (itext.size() >= 4 && utxt[3] == 0 && utxt[2] == 0 &&
               utxt[1] == 0xFE && utxt[0] == 0xFF) {
        return "UTF-32LE";
    } else {
        return string();
    }
}

bool RecollFilter::txtdcode(const string& who)
{
    if (m_metaData[cstr_dj_keymt].compare(cstr_textplain)) {
        LOGERR(who << "::txtdcode: called on non txt/plain: " <<
               m_metaData[cstr_dj_keymt] << "\n");
        return false;
    }

    string& ocs = m_metaData[cstr_dj_keyorigcharset];
    string& itext = m_metaData[cstr_dj_keycontent];
    LOGDEB(who << "::txtdcode: "  << itext.size() << " bytes from ["  <<
           ocs << "] to UTF-8\n");
    int ecnt;
    string otext;

#ifdef _WIN32
    // Under Windows the environment charset will usually not be
    // utf-8. We check if the text is actually utf-8. This is worth
    // it, else the conversion from 8-bit is going to succeed if the
    // text is already utf-8, and produce bogus data.
    if (utf8check(itext) >= 0) {
        m_metaData[cstr_dj_keycharset] = cstr_utf8;
        return true;
    }
#endif

    string bomfromcode = bomtocode(itext);
    if (!bomfromcode.empty()) {
        LOGDEB(who << "::txtdcode: " << " input charset changed from " <<
               ocs << " to " << bomfromcode << " from BOM detection\n");
        ocs = bomfromcode;
    }
    
    bool ret = transcode(itext, otext, ocs, cstr_utf8, &ecnt);
    if (!ret || ecnt > int(itext.size() / 100)) {
        LOGERR(who << "::txtdcode: transcode " << itext.size() <<
               " bytes to UTF-8 failed for input charset [" << ocs <<
               "] ret " << ret << " ecnt "  << ecnt << "\n");

        ret = alternate_decode(itext, otext, ocs);

        if (!ret) {
            LOGDEB("txtdcode: failed. Doc is not text?\n" );
            itext.erase();
            return false;
        }
    }

    itext.swap(otext);
    m_metaData[cstr_dj_keycharset] = cstr_utf8;
    return true;
}
recoll-1.36.1/internfile/mh_exec.cpp0000644000175000017500000002056514427373216014277 00000000000000/* Copyright (C) 2005-2023 J.F.Dockes 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 
#include 
#include 

#include "safesyswait.h"

#include "cstr.h"
#include "execmd.h"
#include "mh_exec.h"
#include "mh_html.h"
#include "log.h"
#include "cancelcheck.h"
#include "smallut.h"
#include "md5ut.h"
#include "rclconfig.h"
#include "idxdiags.h"
#include "pathut.h"

using namespace std;

MEAdv::MEAdv(int maxsecs) 
    : m_filtermaxseconds(maxsecs) 
{
    m_start = time(0L);
}
void MEAdv::reset()
{
    m_start = time(0L);

}

void MEAdv::newData(int n) 
{
    PRETEND_USE(n);
    LOGDEB2("MHExec:newData(" << n << ")\n");
    if (m_filtermaxseconds > 0 && time(0L) - m_start > m_filtermaxseconds) {
        LOGERR("MimeHandlerExec: filter timeout (" << m_filtermaxseconds << " S)\n");
        throw HandlerTimeout();
    }
    // If a cancel request was set by the signal handler (or by us
    // just above), this will raise an exception. Another approach
    // would be to call ExeCmd::setCancel().
    CancelCheck::instance().checkCancel();
}


MimeHandlerExec::MimeHandlerExec(RclConfig *cnf, const std::string& id)
    : RecollFilter(cnf, id)
{
    m_config->getConfParam("filtermaxseconds", &m_filtermaxseconds);
    m_config->getConfParam("filtermaxmbytes", &m_filtermaxmbytes);
}

bool MimeHandlerExec::set_document_file_impl(const std::string& mt, const std::string &file_path)
{
    // Check if we should compute an MD5 for the file. Excessively bulky ones are excluded, which
    // only has consequences for duplicates detection (no big).
    // Exclusion can be based either on the script name or MIME type.

    // Exclusion based on script name. Can't do this in the constructor because the script name is
    // not set yet. Do it once on first call
    unordered_set nomd5tps;
    bool tpsread(false);
    
    if (false == m_hnomd5init) {
        m_hnomd5init = true;
        if (m_config->getConfParam("nomd5types", &nomd5tps)) {
            tpsread = true;
            if (!nomd5tps.empty()) {
                if (params.size() && nomd5tps.find(path_getsimple(params[0])) != nomd5tps.end()) {
                    m_handlernomd5 = true;
                }
                // On Windows the 1st param may be a script interpreter name (e.g. "Python"), and
                // the script name is 2nd. Actually this is not used anymore because we now rely on
                // script names extensions to start interpreters. Kept around anyway.
                if (params.size()>1 && nomd5tps.find(path_getsimple(params[1])) != nomd5tps.end()) {
                    m_handlernomd5 = true;
                }
            }
        }
    }

    m_nomd5 = m_handlernomd5;

    if (!m_nomd5) {
        // Check for MD5 exclusion based on MIME type.
        if (!tpsread) {
            m_config->getConfParam("nomd5types", &nomd5tps);
        }
        for (const auto& nomd5tp : nomd5tps) {
            if (fnmatch(nomd5tp.c_str(), mt.c_str(), FNM_PATHNAME) == 0) {
                m_nomd5 = true;
                break;
            }
        }
    }
    
    m_fn = file_path;
    m_havedoc = true;
    return true;
}

bool MimeHandlerExec::skip_to_document(const string& ipath) 
{
    LOGDEB("MimeHandlerExec:skip_to_document: [" << ipath << "]\n");
    m_ipath = ipath;
    return true;
}

// Execute an external program to translate a file from its native
// format to text or html.
bool MimeHandlerExec::next_document()
{
    if (m_havedoc == false)
        return false;
    m_havedoc = false;
    if (missingHelper) {
        LOGDEB("MimeHandlerExec::next_document(): helper known missing\n");
        m_reason = whatHelper;
        return false;
    }

    if (params.empty()) {
        // Hu ho
        LOGERR("MimeHandlerExec::next_document: empty params\n");
        m_reason = "RECFILTERROR BADCONFIG";
        return false;
    }

    // Command name
    string cmd = params.front();
    
    // Build parameter vector: delete cmd name and add the file name
    vectormyparams(params.begin() + 1, params.end());
    myparams.push_back(m_fn);
    if (!m_ipath.empty())
        myparams.push_back(m_ipath);

    // Execute command, store the output
    string& output = m_metaData[cstr_dj_keycontent];
    output.erase();
    ExecCmd mexec;
    MEAdv adv(m_filtermaxseconds);
    mexec.setAdvise(&adv);
    mexec.putenv("RECOLL_CONFDIR", m_config->getConfDir());
    mexec.putenv(m_forPreview ? "RECOLL_FILTER_FORPREVIEW=yes" : "RECOLL_FILTER_FORPREVIEW=no");
    mexec.setrlimit_as(m_filtermaxmbytes);
    std::string errfile;
    m_config->getConfParam("helperlogfilename", errfile);
    if (!errfile.empty()) {
        mexec.setStderr(errfile);
    }
    int status;
    try {
        status = mexec.doexec(cmd, myparams, 0, &output);
    } catch (HandlerTimeout) {
        LOGERR("MimeHandlerExec: handler timeout\n" );
        status = 0x110f;
    } catch (CancelExcept) {
        LOGERR("MimeHandlerExec: cancelled\n" );
        status = 0x110f;
    }

    if (status) {
        LOGERR("MimeHandlerExec: command status 0x" <<
               std::hex << status << std::dec << " for " << cmd << "\n");
        if (WIFEXITED(status) && WEXITSTATUS(status) == 127) {
            // That's how execmd signals a failed exec (most probably
            // a missing command). Let'hope no filter uses the same value as
            // an exit status... Disable myself permanently and signal the 
            // missing cmd.
            missingHelper = true;
            m_reason = string("RECFILTERROR HELPERNOTFOUND ") + cmd;
            whatHelper = m_reason;
            IdxDiags::theDiags().record(IdxDiags::MissingHelper, m_fn);
        } else if (output.find("RECFILTERROR") == 0) {
            // If the output string begins with RECFILTERROR, then it's 
            // interpretable error information out from a recoll script
            m_reason = output;
            std::string::size_type pos;
            if ((pos = output.find("RECFILTERROR ")) == 0) {
                if (output.find("HELPERNOTFOUND") != string::npos) {
                    IdxDiags::theDiags().record(IdxDiags::MissingHelper, m_fn);
                    missingHelper = true;
                    whatHelper = output.substr(pos);
                }
            }
        }
        return false;
    }

    finaldetails();
    return true;
}

void MimeHandlerExec::handle_cs(const string& mt, const string& icharset)
{
    string charset(icharset);

    // cfgFilterOutputCharset comes from the mimeconf filter
    // definition line and defaults to UTF-8 if empty. If the value is
    // "default", we use the default input charset value defined in
    // recoll.conf (which may vary depending on directory)
    if (charset.empty()) {
        charset = cfgFilterOutputCharset.empty() ? cstr_utf8 : cfgFilterOutputCharset;
        if (!stringlowercmp("default", charset)) {
            charset = m_dfltInputCharset;
        }
    }
    m_metaData[cstr_dj_keyorigcharset] = charset;

    // If this is text/plain transcode_to/check utf-8
    if (!mt.compare(cstr_textplain)) {
        (void)txtdcode("mh_exec/m");
    } else {
        m_metaData[cstr_dj_keycharset] = charset;
    }
}

void MimeHandlerExec::finaldetails()
{
    // The default output mime type is html, but it may be defined
    // otherwise in the filter definition.
    m_metaData[cstr_dj_keymt] = cfgFilterOutputMtype.empty() ? cstr_texthtml : cfgFilterOutputMtype;

    if (!m_forPreview && !m_nomd5) {
        string md5, xmd5, reason;
        if (MD5File(m_fn, md5, &reason)) {
            m_metaData[cstr_dj_keymd5] = MD5HexPrint(md5, xmd5);
        } else {
            LOGERR("MimeHandlerExec: cant compute md5 for [" << m_fn << "]: " << reason << "\n");
        }
    }

    handle_cs(m_metaData[cstr_dj_keymt]);
}

recoll-1.36.1/internfile/mh_xslt.cpp0000644000175000017500000002745514463114077014350 00000000000000/* Copyright (C) 2005 J.F.Dockes 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#if defined(HAVE_MALLOC_H)
#include 
#elif defined(HAVE_MALLOC_MALLOC_H)
#include 
#endif

#include 
#include 
#include 
#include 
#include 

#include "cstr.h"
#include "mh_xslt.h"
#include "log.h"
#include "smallut.h"
#include "md5ut.h"
#include "rclconfig.h"
#include "readfile.h"
#include "pathut.h"

using namespace std;

// Do we need this? It would need to be called from recollinit
// Call once, not reentrant
// xmlInitParser();
// LIBXML_TEST_VERSION;
// Probably not:    xmlCleanupParser();
        

class FileScanXML : public FileScanDo {
public:
    FileScanXML(const string& fn) : m_fn(fn) {}
    virtual ~FileScanXML() {
        if (ctxt) {
            xmlFreeParserCtxt(ctxt);
            // This should not be necessary (done by free), but see
            // http://xmlsoft.org/xmlmem.html#Compacting The
            // malloc_trim() and mallopt() doc seems to be a bit
            // misleading, there is probably a frag size under which
            // free() does not try to malloc_trim() at all
#ifdef HAVE_MALLOC_TRIM
            malloc_trim(0);
#endif /* HAVE_MALLOC_TRIM */
        }
    }

    xmlDocPtr getDoc() {
        int ret;
        if ((ret = xmlParseChunk(ctxt, nullptr, 0, 1))) {
            xmlError *error = xmlGetLastError();
            LOGERR("FileScanXML: final xmlParseChunk failed with error " <<
                   ret << " error: " <<
                   (error ? error->message :
                    " null return from xmlGetLastError()") << "\n");
            return nullptr;
        }
        return ctxt->myDoc;
    }

    virtual bool init(int64_t, string *) {
        ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, m_fn.c_str());
        if (ctxt == nullptr) {
            LOGERR("FileScanXML: xmlCreatePushParserCtxt failed\n");
            return false;
        } else {
            return true;
        }
    }
    
    virtual bool data(const char *buf, int cnt, string*) {
        if (0) {
            string dt(buf, cnt);
            LOGDEB1("FileScanXML: data: cnt " << cnt << " data " << dt << endl);
        } else {
            LOGDEB1("FileScanXML: data: cnt " << cnt << endl);
        }            
        int ret;
        if ((ret = xmlParseChunk(ctxt, buf, cnt, 0))) {
            xmlError *error = xmlGetLastError();
            LOGERR("FileScanXML: xmlParseChunk failed with error " <<
                   ret << " for [" << buf << "] error " <<
                   (error ? error->message :
                    " null return from xmlGetLastError()") << "\n");
            return false;
        } else {
            LOGDEB1("xmlParseChunk ok (sent " << cnt << " bytes)\n");
            return true;
        }
    }

private:
    xmlParserCtxtPtr ctxt{nullptr};
    string m_fn;
};

class MimeHandlerXslt::Internal {
public:
    Internal(MimeHandlerXslt *_p)
        : p(_p) {}
    ~Internal() {
        for (auto& entry : metaOrAllSS) {
            xsltFreeStylesheet(entry.second);
        }
        for (auto& entry : bodySS) {
            xsltFreeStylesheet(entry.second);
        }
    }

    xsltStylesheet *prepare_stylesheet(const string& ssnm);
    bool process_doc_or_string(bool forpv, const string& fn, const string& data);
    bool apply_stylesheet(
        const string& fn, const string& member, const string& data,
        xsltStylesheet *ssp, string& result, string *md5p);

    MimeHandlerXslt *p;
    bool ok{false};

    // Pairs of zip archive member names and style sheet names for the
    // metadata, and map of style sheets refd by their names.
    // Exception: there can be a single entry which does meta and
    // body, in which case bodymembers/bodySS are empty.
    vector> metaMembers;
    map  metaOrAllSS;
    // Same for body data
    vector> bodyMembers;
    map bodySS;
    string result;
    string filtersdir;
};

MimeHandlerXslt::~MimeHandlerXslt()
{
    delete m;
}

MimeHandlerXslt::MimeHandlerXslt(RclConfig *cnf, const std::string& id,
                                 const std::vector& params)
    : RecollFilter(cnf, id), m(new Internal(this))
{
    LOGDEB("MimeHandlerXslt: params: " << stringsToString(params) << endl);
    m->filtersdir = path_cat(cnf->getDatadir(), "filters");

    xmlSubstituteEntitiesDefault(0);
    xmlLoadExtDtdDefaultValue = 0;

    // params can be "xslt stylesheetall" or
    // "xslt meta/body memberpath stylesheetnm [... ... ...] ...
    if (params.size() == 2) {
        auto ss = m->prepare_stylesheet(params[1]);
        if (ss) {
            m->ok = true;
            m->metaOrAllSS[""] = ss;
        }
    } else if (params.size() > 3 && params.size() % 3 == 1) {
        auto it = params.begin();
        it++;
        while (it != params.end()) {
            // meta/body membername ssname
            const string& tp = *it++;
            const string& znm = *it++;
            const string& ssnm = *it++;
            vector> *mbrv;
            map *ssmp;
            if (tp == "meta") {
                mbrv = &m->metaMembers;
                ssmp = &m->metaOrAllSS;
            } else if (tp == "body") {
                mbrv = &m->bodyMembers;
                ssmp = &m->bodySS;
            } else {
                LOGERR("MimeHandlerXslt: bad member type " << tp << endl);
                return;
            }
            if (ssmp->find(ssnm) == ssmp->end()) {
                auto ss = m->prepare_stylesheet(ssnm);
                if (nullptr == ss) {
                    return;
                }
                ssmp->insert({ssnm, ss});
            }
            mbrv->push_back({znm, ssnm});
        }
        m->ok = true;
    } else {
        LOGERR("MimeHandlerXslt: constructor with wrong param vector: " <<
               stringsToString(params) << endl);
    }
}

xsltStylesheet *MimeHandlerXslt::Internal::prepare_stylesheet(const string& ssnm)
{
    string ssfn = path_cat(filtersdir, ssnm);
    FileScanXML XMLstyle(ssfn);
    string reason;
    if (!file_scan(ssfn, &XMLstyle, &reason)) {
        LOGERR("MimeHandlerXslt: file_scan failed for style sheet " <<
               ssfn << " : " << reason << endl);
        return nullptr;
    }
    xmlDoc *stl = XMLstyle.getDoc();
    if (stl == nullptr) {
        LOGERR("MimeHandlerXslt: getDoc failed for style sheet " <<
               ssfn << endl);
        return nullptr;
    }
    return xsltParseStylesheetDoc(stl);
}

bool MimeHandlerXslt::Internal::apply_stylesheet(
    const string& fn, const string& member, const string& data,
    xsltStylesheet *ssp, string& result, string *md5p)
{
    FileScanXML XMLdoc(fn);
    string md5, reason;
    bool res;
    if (!fn.empty()) {
        if (member.empty()) {
            res = file_scan(fn, &XMLdoc, 0, -1, &reason, md5p);
        } else {
            res = file_scan(fn, member, &XMLdoc, &reason);
        }
    } else {
        if (member.empty()) {
            res = string_scan(data.c_str(), data.size(), &XMLdoc, &reason, md5p);
        } else {
            res = string_scan(data.c_str(), data.size(), member, &XMLdoc,
                              &reason);
        }
    }
    if (!res) {
        LOGERR("MimeHandlerXslt::set_document_: file_scan failed for "<<
               fn << " " << member << " : " << reason << endl);
        return false;
    }

    xmlDocPtr doc = XMLdoc.getDoc();
    if (nullptr == doc) {
        LOGERR("MimeHandlerXslt::set_document_: no parsed doc\n");
        return false;
    }
    xmlDocPtr transformed = xsltApplyStylesheet(ssp, doc, NULL);
    if (nullptr == transformed) {
        LOGERR("MimeHandlerXslt::set_document_: xslt transform failed\n");
        xmlFreeDoc(doc);
        return false;
    }
    xmlChar *outstr;
    int outlen;
    xsltSaveResultToString(&outstr, &outlen, transformed, ssp);
    result = string((const char*)outstr, outlen);
    xmlFree(outstr);
    xmlFreeDoc(transformed);
    xmlFreeDoc(doc);
    return true;
}

bool MimeHandlerXslt::Internal::process_doc_or_string(
    bool forpreview, const string& fn, const string& data)
{
    p->m_metaData[cstr_dj_keycharset] = cstr_utf8;
    if (bodySS.empty()) {
        auto ssp = metaOrAllSS.find("");
        if (ssp == metaOrAllSS.end()) {
            LOGERR("MimeHandlerXslt::process: no style sheet !\n");
            return false;
        }
        string md5;
        if (apply_stylesheet(fn, string(), data, ssp->second, result,
                             forpreview ? nullptr : &md5)) {
            if (!forpreview) {
                p->m_metaData[cstr_dj_keymd5] = md5;
            }
            return true;
        }
        return false;
    } else {
        result = "\n\n";
        for (auto& member : metaMembers) {
            auto it = metaOrAllSS.find(member.second);
            if (it == metaOrAllSS.end()) {
                LOGERR("MimeHandlerXslt::process: no style sheet found for " <<
                       member.first << ":" << member.second << "!\n");
                return false;
            }
            string part;
            if (!apply_stylesheet(fn, member.first, data, it->second, part, nullptr)) {
                return false;
            }
            result += part;
        }
        result += "\n\n";
        
        for (auto& member : bodyMembers) {
            auto it = bodySS.find(member.second);
            if (it == bodySS.end()) {
                LOGERR("MimeHandlerXslt::process: no style sheet found for " <<
                       member.first << ":" << member.second << "!\n");
                return false;
            }
            string part;
            if (!apply_stylesheet(fn, member.first, data, it->second, part, nullptr)) {
                return false;
            }
            result += part;
        }
        result += "";
    }
    return true;
}

bool MimeHandlerXslt::set_document_file_impl(const string&, const string &fn)
{
    LOGDEB0("MimeHandlerXslt::set_document_file_: fn: " << fn << endl);
    if (!m || !m->ok) {
        return false;
    }
    bool ret = m->process_doc_or_string(m_forPreview, fn, string());
    if (ret) {
        m_havedoc = true;
    }
    return ret;
}

bool MimeHandlerXslt::set_document_string_impl(const string&, const string& txt)
{
    LOGDEB0("MimeHandlerXslt::set_document_string_\n");
    if (!m || !m->ok) {
        return false;
    }
    bool ret = m->process_doc_or_string(m_forPreview, string(), txt);
    if (ret) {
        m_havedoc = true;
    }
    return ret;
}

bool MimeHandlerXslt::next_document()
{
    if (!m || !m->ok) {
        return false;
    }
    if (m_havedoc == false)
        return false;
    m_havedoc = false;
    m_metaData[cstr_dj_keymt] = cstr_texthtml;
    m_metaData[cstr_dj_keycontent].swap(m->result);
    LOGDEB1("MimeHandlerXslt::next_document: result: [" <<
            m_metaData[cstr_dj_keycontent] << "]\n");
    return true;
}

void MimeHandlerXslt::clear_impl()
{
    m_havedoc = false;
    m->result.clear();
}
recoll-1.36.1/internfile/mh_html.h0000644000175000017500000000401014410615043013734 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _HTML_H_INCLUDED_
#define _HTML_H_INCLUDED_

#include 

#include "mimehandler.h"

/**
 * Convert html to utf-8 text and extract whatever metadata we can find.
 */
class MimeHandlerHtml : public RecollFilter {
public:
    MimeHandlerHtml(RclConfig *cnf, const std::string& id) 
        : RecollFilter(cnf, id) {
    }
    virtual ~MimeHandlerHtml() {}
    MimeHandlerHtml(const MimeHandlerHtml&) = delete;
    MimeHandlerHtml& operator=(const MimeHandlerHtml&) = delete;

    virtual bool is_data_input_ok(DataInput input) const override {
        if (input == DOCUMENT_FILE_NAME || input == DOCUMENT_STRING)
            return true;
        return false;
    }
    virtual bool next_document() override;
    const std::string& get_html() {
        return m_html;
    }
    virtual void clear_impl() override {
        m_filename.erase();
        m_html.erase();
    }
protected:
    virtual bool set_document_file_impl(const std::string& mt,
                                        const std::string &file_path) override;
    virtual bool set_document_string_impl(const std::string& mt,
                                          const std::string &data) override;

private:
    std::string m_filename;
    std::string m_html;
};

#endif /* _HTML_H_INCLUDED_ */
recoll-1.36.1/internfile/mh_text.h0000644000175000017500000000477014410615043013771 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _MH_TEXT_H_INCLUDED_
#define _MH_TEXT_H_INCLUDED_

#include 
#include 

#include 

#include "mimehandler.h"

/**
 * Handler for text/plain files. 
 *
 * Maybe try to guess charset, or use default, then transcode to utf8
 */
class MimeHandlerText : public RecollFilter {
public:
    MimeHandlerText(RclConfig *cnf, const std::string& id) 
        : RecollFilter(cnf, id), m_paging(false), m_offs(0), m_pagesz(0) {
    }
    virtual ~MimeHandlerText() {}
    MimeHandlerText(const MimeHandlerText&) = delete;
    MimeHandlerText& operator=(const MimeHandlerText&) = delete;

    virtual bool is_data_input_ok(DataInput input) const override {
        if (input == DOCUMENT_FILE_NAME || input == DOCUMENT_STRING)
            return true;
        return false;
    }
    virtual bool next_document() override;
    virtual bool skip_to_document(const std::string& s) override;
    virtual void clear_impl() override {
        m_paging = false;
        m_text.clear(); 
        m_fn.clear();
        m_offs = 0;
        m_pagesz = 0;
        m_charsetfromxattr.clear();
    }
    
protected:
    virtual bool set_document_file_impl(const std::string& mt,
                                        const std::string &file_path) override;
    virtual bool set_document_string_impl(const std::string&,
                                          const std::string&) override;

private:
    bool   m_paging{false};
    std::string m_text;
    std::string m_alltext;
    std::string m_fn;
    int64_t m_offs{0}; // Offset of next read in file if we're paging
    int64_t m_totlen{0};
    size_t m_pagesz{0};
    int m_maxmbs{20};
    std::string m_charsetfromxattr; 

    bool readnext();
    void getparams();
};

#endif /* _MH_TEXT_H_INCLUDED_ */
recoll-1.36.1/internfile/mh_null.h0000644000175000017500000000424314410615043013752 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _MH_NULL_H_INCLUDED_
#define _MH_NULL_H_INCLUDED_

#include 
#include "cstr.h"
#include "mimehandler.h"

/// Null input handler always returning empty data.
///
/// It may make sense in some cases to set this null filter (no output)
/// instead of using recoll_noindex or leaving the default filter in
/// case one doesn't want to install it: this will avoid endless retries
/// to reindex the affected files, as recoll will think it has succeeded
/// indexing them. Downside: the files won't be indexed when one
/// actually installs the real filter, will need a -z
/// Actually used for empty files.
/// Associated to application/x-zerosize, so use the following in mimeconf:
///     = internal application/x-zerosize
class MimeHandlerNull : public RecollFilter {
public:
    MimeHandlerNull(RclConfig *cnf, const std::string& id) 
        : RecollFilter(cnf, id) {}
    virtual ~MimeHandlerNull() = default;
    MimeHandlerNull(const MimeHandlerNull&) = delete;
    MimeHandlerNull& operator=(const MimeHandlerNull&) = delete;

    virtual bool is_data_input_ok(DataInput) const {
        return true;
    }
    
    virtual bool next_document() {
        if (m_havedoc == false)
            return false;
        m_havedoc = false; 
        m_metaData[cstr_dj_keycontent] = cstr_null;
        m_metaData[cstr_dj_keymt] = cstr_textplain;
        return true;
    }
};

#endif /* _MH_NULL_H_INCLUDED_ */
recoll-1.36.1/internfile/mh_mail.cpp0000644000175000017500000005474014427373216014277 00000000000000/* Copyright (C) 2005 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 
#include 
#include 
#include "safeunistd.h"
#include 
#include 
#include "safesysstat.h"

#include 
#include 

#include "cstr.h"
#include "mimehandler.h"
#include "readfile.h"
#include "transcode.h"
#include "mimeparse.h"
#include "mh_mail.h"
#include "log.h"
#include "smallut.h"
#include "mh_html.h"
#include "rclconfig.h"
#include "mimetype.h"
#include "md5ut.h"

// binc imap mime definitions
#include "mime.h"

using namespace std;

static const int maxdepth = 20;
static const string cstr_mail_charset("charset");

MimeHandlerMail::MimeHandlerMail(RclConfig *cnf, const string &id) 
    : RecollFilter(cnf, id), m_bincdoc(0), m_fd(-1), m_stream(0), m_idx(-1)
{
    // Look for additional headers to be processed as per config:
    vector hdrnames = m_config->getFieldSectNames("mail");
    if (hdrnames.empty())
        return;
    for (const auto& nm : hdrnames) {
        (void)m_config->getFieldConfParam(nm, "mail", m_addProcdHdrs[nm]);
    }
}

MimeHandlerMail::~MimeHandlerMail() 
{
    if (m_fd >= 0) {
        close(m_fd);
        m_fd = -1;
    }
}

void MimeHandlerMail::clear_impl()
{
    delete m_bincdoc; m_bincdoc = 0;
    if (m_fd >= 0) {
        close(m_fd);
        m_fd = -1;
    }
    delete m_stream; m_stream = 0;
    m_idx = -1;
    m_startoftext = 0;
    m_subject.erase();
    for (auto attp : m_attachments) {
        delete attp;
    }
    m_attachments.clear();
}

bool MimeHandlerMail::set_document_file_impl(const string&, const string &fn)
{
    LOGDEB("MimeHandlerMail::set_document_file(" << fn << ")\n");
    if (m_fd >= 0) {
        close(m_fd);
        m_fd = -1;
    }

    if (!m_forPreview) {
        // Yes, we read the file twice. It would be possible in theory
        // to add the md5 computation to the mime analysis, but ...
        string md5, xmd5, reason;
        if (MD5File(fn, md5, &reason)) {
            m_metaData[cstr_dj_keymd5] = MD5HexPrint(md5, xmd5);
        } else {
            LOGERR("MimeHandlerMail: md5 [" << fn << "]: " << reason << "\n");
        }
    }
    m_fd = open(fn.c_str(), 0);
    if (m_fd < 0) {
        LOGERR("MimeHandlerMail::set_document_file: open(" << fn <<
               ") errno " << errno << "\n");
        return false;
    }
#if defined O_NOATIME && O_NOATIME != 0
    if (fcntl(m_fd, F_SETFL, O_NOATIME) < 0) {
        // perror("fcntl");
    }
#endif
    delete m_bincdoc;
    m_bincdoc = new Binc::MimeDocument;
    m_bincdoc->parseFull(m_fd);
    if (!m_bincdoc->isHeaderParsed() && !m_bincdoc->isAllParsed()) {
        LOGERR("MimeHandlerMail::mkDoc: mime parse error for " << fn << "\n");
        return false;
    }
    m_havedoc = true;
    return true;
}

bool MimeHandlerMail::set_document_string_impl(const string&,
                                               const string& msgtxt)
{
    LOGDEB1("MimeHandlerMail::set_document_string\n");
    LOGDEB2("Message text: [" << msgtxt << "]\n");
    delete m_stream;

    if (!m_forPreview) {
        string md5, xmd5;
        MD5String(msgtxt, md5);
        m_metaData[cstr_dj_keymd5] = MD5HexPrint(md5, xmd5);
    }

    if ((m_stream = new stringstream(msgtxt)) == 0 || !m_stream->good()) {
        LOGERR("MimeHandlerMail::set_document_string: stream create error."
               "msgtxt.size() " << msgtxt.size() << "\n");
        return false;
    }
    delete m_bincdoc;
    if ((m_bincdoc = new Binc::MimeDocument) == 0) {
        LOGERR("MimeHandlerMail::set_doc._string: new Binc:Document failed. "
               "Out of memory?");
        return false;
    }
    m_bincdoc->parseFull(*m_stream);
    if (!m_bincdoc->isHeaderParsed() && !m_bincdoc->isAllParsed()) {
        LOGERR("MimeHandlerMail::set_document_string: mime parse error\n");
        return false;
    }
    m_havedoc = true;
    return true;
}

bool MimeHandlerMail::skip_to_document(const string& ipath) 
{
    LOGDEB("MimeHandlerMail::skip_to_document(" << ipath << ")\n");
    if (m_idx == -1) {
        // No decoding done yet. If ipath is null need do nothing
        if (ipath.empty() || ipath == "-1")
            return true;
        // ipath points to attachment: need to decode message
        if (!next_document()) {
            LOGERR("MimeHandlerMail::skip_to_doc: next_document failed\n");
            return false;
        }
    }
    m_idx = atoi(ipath.c_str());
    return true;
}

bool MimeHandlerMail::next_document()
{
    LOGDEB("MimeHandlerMail::next_document m_idx " << m_idx << " m_havedoc " <<
           m_havedoc << "\n");
    if (!m_havedoc)
        return false;
    bool res = false;

    if (m_idx == -1) {
        m_metaData[cstr_dj_keymt] = cstr_textplain;
        res = processMsg(m_bincdoc, 0);
        LOGDEB1("MimeHandlerMail::next_document: mt " <<
                m_metaData[cstr_dj_keymt] << ", att cnt " <<
                m_attachments.size() << "\n");
        const string& txt = m_metaData[cstr_dj_keycontent];
        if (m_startoftext < txt.size())
            m_metaData[cstr_dj_keyabstract] = 
                truncate_to_word(txt.substr(m_startoftext), 250);
        if (m_attachments.size() > 0) {
            m_metaData[cstr_dj_keyanc] = "t";
        }
    } else {
        m_metaData[cstr_dj_keyabstract].clear();
        res = processAttach();
    }
    m_idx++;
    m_havedoc = m_idx < (int)m_attachments.size();
    if (!m_havedoc) {
        m_reason = "Subdocument index too high";
    }
    return res;
}

// Decode according to content transfer encoding. May actually do nothing,
// which will be indicated by the *respp argument pointing to the original 
// text on exit
static bool decodeBody(const string& cte, // Content transfer encoding
                       const string& body, // Source text
                       string& decoded,   // Decoded text if actual decoding
                       const string** respp // Decoding Indicator 
    )
{
    // By default, there is no encoding (7bit,8bit,raw). Also in case of 
    // decoding error
    *respp = &body;

    if (!stringlowercmp("quoted-printable", cte)) {
        if (!qp_decode(body, decoded)) {
            LOGERR("decodeBody: quoted-printable decoding failed !\n");
            LOGDEB("      Body: \n" << body << "\n");
            return false;
        }
        *respp = &decoded;
    } else if (!stringlowercmp("base64", cte)) {
        if (!base64_decode(body, decoded)) {
            // base64 encoding errors are actually relatively common
            LOGERR("decodeBody: base64 decoding failed !\n");
            LOGDEB("      Body: \n" << body << "\n");
            return false;
        }
        *respp = &decoded;
    }
    return true;
}

bool MimeHandlerMail::processAttach()
{
    LOGDEB("MimeHandlerMail::processAttach() m_idx " << m_idx << "\n");
    if (!m_havedoc)
        return false;
    if (m_idx >= (int)m_attachments.size()) {
        m_havedoc = false;
        return false;
    }
    MHMailAttach *att = m_attachments[m_idx];

    m_metaData[cstr_dj_keymt] = att->m_contentType;
    m_metaData[cstr_dj_keyorigcharset] = att->m_charset;
    m_metaData[cstr_dj_keycharset] = att->m_charset;
    m_metaData[cstr_dj_keyfn] = att->m_filename;
    m_metaData[cstr_dj_keytitle] = att->m_filename + "  (" + m_subject + ")";
    LOGDEB1("  processAttach:ct [" << att->m_contentType << "] cs [" <<
            att->m_charset << "] fn [" << att->m_filename << "]\n");

    // Erase current content and replace
    string& body = m_metaData[cstr_dj_keycontent];
    body.clear();
    att->m_part->getBody(body, 0, att->m_part->bodylength);
    {
        string decoded;
        const string *bdp;
        if (!decodeBody(att->m_contentTransferEncoding, body, decoded, &bdp)) {
            return false;
        }
        if (bdp != &body)
            body.swap(decoded);
    }

    // Special case for application/octet-stream: try to better
    // identify content, using file name if set
    if (m_metaData[cstr_dj_keymt] == "application/octet-stream" &&
        !m_metaData[cstr_dj_keyfn].empty()) {
        string mt = mimetype(m_metaData[cstr_dj_keyfn], m_config, false);
        if (!mt.empty()) 
            m_metaData[cstr_dj_keymt] = mt;
    }

    // Special case for text/plain content. Internfile should deal
    // with this but it expects text/plain to be utf-8 already, so we
    // handle the transcoding if needed. Same kind of issue for the MD5
    if (m_metaData[cstr_dj_keymt] == cstr_textplain) {
        if (!txtdcode("MimeHandlerMail::processAttach")) {
            body.clear();
        } else if (!m_forPreview) {
            string md5, xmd5;
            MD5String(body, md5);
            m_metaData[cstr_dj_keymd5] = MD5HexPrint(md5, xmd5);
        }
    }

    // Ipath
    char nbuf[20];
    sprintf(nbuf, "%d", m_idx);
    m_metaData[cstr_dj_keyipath] = nbuf;

    return true;
}

// Transform a single message into a document. The subject becomes the
// title, and any simple body part with a content-type of text or html
// and content-disposition inline gets concatenated as text.
// 
// If depth is not zero, we're called recursively for an
// message/rfc822 part and we must not touch the doc fields except the
// text
bool MimeHandlerMail::processMsg(Binc::MimePart *doc, int depth)
{
    LOGDEB2("MimeHandlerMail::processMsg: depth " << depth << "\n");
    if (depth++ >= maxdepth) {
        // Have to stop somewhere
        LOGINFO("MimeHandlerMail::processMsg: maxdepth " << maxdepth <<
                " exceeded\n");
        // Return true anyway, better to index partially than not at all
        return true;
    }
        
    // Handle some headers. 
    string& text = m_metaData[cstr_dj_keycontent];
    Binc::HeaderItem hi;
    string decoded;
    if (doc->h.getFirstHeader("From", hi)) {
        rfc2047_decode(hi.getValue(), decoded);
        if (preview())
            text += string("From: ");
        text += decoded + cstr_newline;
        if (depth == 1) {
            m_metaData[cstr_dj_keyauthor] = decoded;
        }
    }
    if (doc->h.getFirstHeader("To", hi)) {
        rfc2047_decode(hi.getValue(), decoded);
        if (preview())
            text += string("To: ");
        text += decoded + cstr_newline;
        if (depth == 1) {
            m_metaData[cstr_dj_keyrecipient] = decoded;
        }
    }
    if (doc->h.getFirstHeader("Cc", hi)) {
        rfc2047_decode(hi.getValue(), decoded);
        if (preview())
            text += string("Cc: ");
        text += decoded + cstr_newline;
        if (depth == 1) {
            m_metaData[cstr_dj_keyrecipient] += " " + decoded;
        }
    }
#if 0 // 2022-10: this does not appear to be used anywhere
// cstr.h DEF_CSTR(dj_keymsgid, "msgid");
    if (doc->h.getFirstHeader("Message-Id", hi)) {
        if (depth == 1) {
            m_metaData[cstr_dj_keymsgid] =  hi.getValue();
            trimstring(m_metaData[cstr_dj_keymsgid], "<>");
        }
    }
#endif
    if (doc->h.getFirstHeader("Date", hi)) {
        rfc2047_decode(hi.getValue(), decoded);
        if (depth == 1) {
            time_t t = rfc2822DateToUxTime(decoded);
            if (t != (time_t)-1) {
                char ascuxtime[100];
                sprintf(ascuxtime, "%ld", (long)t);
                m_metaData[cstr_dj_keymd] = ascuxtime;
            } else {
                // Leave mtime field alone, ftime will be used instead.
                LOGDEB("rfc2822Date...: failed: [" << decoded << "]\n");
            }
        }
        if (preview())
            text += string("Date: ");
        text += decoded + cstr_newline;
    }
    if (doc->h.getFirstHeader("Subject", hi)) {
        rfc2047_decode(hi.getValue(), decoded);
        if (depth == 1) {
            m_metaData[cstr_dj_keytitle] = decoded;
            m_subject = decoded;
        }
        if (preview())
            text += string("Subject: ");
        text += decoded + cstr_newline;
    }

    // Check for the presence of configured additional headers and possibly
    // add them to the metadata (with appropriate field name).
    if (!m_addProcdHdrs.empty()) {
        for (auto& it : m_addProcdHdrs) {
            if (!it.second.empty() && doc->h.getFirstHeader(it.first, hi)) {
                rfc2047_decode(hi.getValue(), m_metaData[it.second]);
            }
        }
    }

    text += '\n';
    m_startoftext = text.size();
    LOGDEB2("MimeHandlerMail::processMsg:ismultipart " <<
            doc->isMultipart() << " mime subtype '"<getSubType()<< "'\n");
    walkmime(doc, depth);

    LOGDEB2("MimeHandlerMail::processMsg:text:[" <<
            m_metaData[cstr_dj_keycontent] << "]\n");
    return true;
}

// Recursively walk the message mime parts and concatenate all the
// inline html or text that we find anywhere.  
//
// RFC2046 reminder: 
// Top level media types: 
//      Simple:    text, image, audio, video, application, 
//      Composite: multipart, message.
// 
// multipart can be mixed, signed, alternative, parallel, digest.
// message/rfc822 may also be of interest.
void MimeHandlerMail::walkmime(Binc::MimePart* doc, int depth)
{
    LOGDEB2("MimeHandlerMail::walkmime: depth " << depth << "\n");
    if (depth++ >= maxdepth) {
        LOGINFO("walkmime: max depth (" << maxdepth << ") exceeded\n");
        return;
    }

    string& out = m_metaData[cstr_dj_keycontent];

    if (doc->isMultipart()) {
        LOGDEB2("walkmime: ismultipart " << doc->isMultipart() <<
                " subtype '" << doc->getSubType() << "'\n");
        // We only handle alternative, related and mixed (no digests). 
        std::vector::iterator it;

        if (!stringicmp("mixed", doc->getSubType()) || 
            !stringicmp("signed", doc->getSubType()) ||
            !stringicmp("related", doc->getSubType())) {
            // Multipart mixed and related:  process each part.
            for (it = doc->members.begin(); it != doc->members.end();it++) {
                walkmime(&(*it), depth);
            }

        } else if (!stringicmp("alternative", doc->getSubType())) {
            // Multipart/alternative: look for a text/plain part, then html.
            // Process if found
            std::vector::iterator ittxt, ithtml;
            ittxt = ithtml = doc->members.end();
            int i = 1;
            for (it = doc->members.begin(); 
                 it != doc->members.end(); it++, i++) {
                // Get and parse content-type header
                Binc::HeaderItem hi;
                if (!it->h.getFirstHeader("Content-Type", hi)) {
                    LOGDEB("walkmime:no ctent-type header for part "<members.end() && ittxt->bodylength) {
                LOGDEB2("walkmime: alternative: choose text/plain. Size: " <<
                        ittxt->bodylength << endl);
                walkmime(&(*ittxt), depth);
            } else if (ithtml != doc->members.end()) {
                LOGDEB2("walkmime: alternative: choose text/html.\n");
                walkmime(&(*ithtml), depth);
            }
        }
        return;
    } 
    
    // Part is not multipart: it must be either simple or message. Take
    // a look at interesting headers and a possible filename parameter

    // Get and parse content-type header.
    Binc::HeaderItem hi;
    string ctt = cstr_textplain;
    if (doc->h.getFirstHeader("Content-Type", hi)) {
        ctt = hi.getValue();
    }
    LOGDEB2("walkmime:content-type: " << ctt << "\n");
    MimeHeaderValue content_type;
    parseMimeHeaderValue(ctt, content_type);
            
    // Get and parse Content-Disposition header
    string ctd = "inline";
    if (doc->h.getFirstHeader("Content-Disposition", hi)) {
        ctd = hi.getValue();
    }
    MimeHeaderValue content_disposition;
    parseMimeHeaderValue(ctd, content_disposition);
    LOGDEB2("Content_disposition:[" << content_disposition.value << "]\n");
    string dispindic;
    if (stringlowercmp("inline", content_disposition.value))
        dispindic = "Attachment";
    else 
        dispindic = "Inline";

    // See if we have a filename.
    string filename;
    map::const_iterator it;
    it = content_disposition.params.find(string("filename"));
    if (it != content_disposition.params.end())
        filename = it->second;
    if (filename.empty()) {
        it = content_type.params.find(string("name"));
        if (it != content_type.params.end())
            filename = it->second;
    }

    // Note: I have never seen anything useful in the Content-Description field.
    // So it's not processed. Use rfc2047 to decode if we ever do it.
        
    if (doc->isMessageRFC822()) {
        LOGDEB2("walkmime: message/RFC822 part\n");
        
        // The first part is the already parsed message.  Call
        // processMsg instead of walkmime so that mail headers get
        // printed. The depth will tell it what to do
        if (doc->members.empty()) {
            //??
            return;
        }
        out += "\n";
        if (m_forPreview)
            out += "[" + dispindic + " " + content_type.value + ": ";
        out += filename;
        if (m_forPreview)
            out += "]";
        out += "\n\n";
        processMsg(&doc->members[0], depth);
        return;
    }

    // "Simple" part. 
    LOGDEB2("walkmime: simple  part\n");
    // Normally the default charset is us-ascii. But it happens that 8
    // bit chars exist in a message that is stated as us-ascii. Ie the
    // mailer used by yahoo support ('KANA') does this. We could
    // convert to iso-8859 only if the transfer-encoding is 8 bit, or
    // test for actual 8 bit chars, but what the heck, le'ts use
    // 8859-1 (actually CP1252 which is compatible, but with more
    // useful chars) as default.
    string charset;
    it = content_type.params.find(cstr_mail_charset);
    if (it != content_type.params.end())
        charset = it->second;
    if (charset.empty() || 
        !stringlowercmp("us-ascii", charset) || 
        !stringlowercmp("default", charset) || 
        !stringlowercmp("x-user-defined", charset) || 
        !stringlowercmp("x-unknown", charset) || 
        !stringlowercmp("unknown", charset) ) {
        if (!m_config->getConfParam("maildefcharset", charset))
            charset = "CP1252";
    }

    // Content transfer encoding
    string cte = "7bit";
    if (doc->h.getFirstHeader("Content-Transfer-Encoding", hi)) {
        cte = hi.getValue();
    } 

    // If the Content-Disposition is not inline, we treat it as
    // attachment, as per rfc2183. 
    // If it is inline but not text or html, same thing.
    // Some early MIME msgs have "text" instead of "text/plain" as type
    if (stringlowercmp("inline", content_disposition.value) ||
        (stringlowercmp(cstr_textplain, content_type.value) && 
         stringlowercmp("text", content_type.value) && 
         stringlowercmp("text/html", content_type.value)) ) {
        if (!filename.empty()) {
            out += "\n";
            if (m_forPreview)
                out += "[" + dispindic + " " + content_type.value + ": ";
            out += filename;
            if (m_forPreview)
                out += "]";
            out += "\n\n";
        }
        MHMailAttach *att = new MHMailAttach;
        if (att == 0) {
            LOGERR("Out of memory\n");
            return;
        }
        att->m_contentType = content_type.value;
        stringtolower(att->m_contentType);
        att->m_filename = filename;
        att->m_charset = charset;
        att->m_contentTransferEncoding = cte;
        att->m_part = doc;
        LOGDEB("walkmime: attachmnt: ct [" << att->m_contentType <<
               "] cte [" << att->m_contentTransferEncoding << "] cs [" <<
               att->m_charset << "] fn [" << filename << "]\n");
        m_attachments.push_back(att);
        return;
    }

    // We are dealing with an inline part of text/plain or text/html
    // type. We can't just return a text or html subdoc and let the
    // filter stack work: this would create another subdocument, but
    // we want instead to decode a body part of this message document.

    LOGDEB2("walkmime: final: body start offset " <<
            doc->getBodyStartOffset()<<", length "<getBodyLength()<<"\n");
    string body;
    doc->getBody(body, 0, doc->bodylength);
    {
        string decoded;
        const string *bdp;
        if (!decodeBody(cte, body, decoded, &bdp)) {
            LOGERR("MimeHandlerMail::walkmime: failed decoding body\n");
        }
        if (bdp != &body)
            body.swap(decoded);
    }

    // Handle html stripping and transcoding to utf8
    if (!stringlowercmp("text/html", content_type.value)) {
        MimeHandlerHtml mh(m_config, "1234");
        mh.set_property(Dijon::Filter::OPERATING_MODE, 
                        m_forPreview ? "view" : "index");
        mh.set_property(Dijon::Filter::DEFAULT_CHARSET, charset);
        mh.set_document_string("text/html", body);
        mh.next_document();
        map::const_iterator it = 
            mh.get_meta_data().find(cstr_dj_keycontent);
        if (it != mh.get_meta_data().end())
            out += it->second;
    } else {
        string utf8;
        // Transcode to utf-8 
        LOGDEB1("walkmime: transcoding from " << charset << " to UTF-8\n");
        if (!transcode(body, utf8, charset, cstr_utf8)) {
            LOGERR("walkmime: transcode failed from cs '" << charset <<
                   "' to UTF-8\n");
            out += body;
        } else {
            out += utf8;
        }
    }

    if (out.length() && out[out.length()-1] != '\n')
        out += '\n';
    
    LOGDEB2("walkmime: out now: [" << out << "]\n");
}
recoll-1.36.1/internfile/myhtmlparse.h0000644000175000017500000000472714427373216014703 00000000000000/* This file was copied from omega-0.8.5 and modified */

/* myhtmlparse.h: subclass of HtmlParser for extracting text
 *
 * ----START-LICENCE----
 * Copyright 1999,2000,2001 BrightStation PLC
 * Copyright 2002,2003,2004 Olly Betts
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
 * USA
 * -----END-LICENCE-----
 */

#include 
#include 

#include "htmlparse.h"

// FIXME: Should we include \xa0 which is non-breaking space in iso-8859-1, but
// not in all charsets and perhaps spans of all \xa0 should become a single
// \xa0?
#define WHITESPACE " \t\n\r"

class MyHtmlParser : public HtmlParser {
public:
    bool in_script_tag;
    bool in_style_tag;
    bool in_pre_tag;
    bool in_title_tag;
    bool pending_space;
    std::map meta;
    std::string dump, dmtime, titledump;
    // This is the charset our caller thinks the doc used (initially
    // comes from the environment/configuration, used as source for
    // conversion to utf-8)
    std::string fromcharset; 
    // This is the charset it was supposedly converted to (always
    // utf-8 in fact, except if conversion utterly failed)
    std::string tocharset; 
    // charset is declared by HtmlParser. It is the charset from the
    // document: default, then from html or xml header.
    // string charset; 

    bool indexing_allowed;

    void process_text(const std::string &text);
    bool opening_tag(const std::string &tag);
    bool closing_tag(const std::string &tag);
    void do_eof();
    void decode_entities(std::string &s);
    void reset_charsets() {fromcharset = tocharset = "";}
    void set_charsets(const std::string& f, const std::string& t) {
        fromcharset = f;
        tocharset = t;
    }
    // Return charset as determined from html
    const std::string& get_charset() {return charset;}

    MyHtmlParser();
};
recoll-1.36.1/internfile/extrameta.h0000644000175000017500000000347514410615043014314 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _REAPXATTRS_H_INCLUDED_
#define _REAPXATTRS_H_INCLUDED_

#include "autoconfig.h"

/** Extended attributes processing helper functions */

#include 
#include 

class RclConfig;
namespace Rcl {class Doc;};

/** Read external attributes, possibly ignore some or change the names
   according to the fields configuration */
extern void reapXAttrs(const RclConfig* config, const std::string& path, 
               std::map& xfields);

/** Turn the pre-processed extended file attributes into doc fields */
extern void docFieldsFromXattrs(
    RclConfig *cfg, const std::map& xfields, 
    Rcl::Doc& doc);

/** Get metadata by executing commands */
extern void reapMetaCmds(RclConfig* config, const std::string& path, 
             std::map& xfields);

/** Turn the pre-processed ext cmd metadata into doc fields */
extern void docFieldsFromMetaCmds(
    RclConfig *cfg, const std::map& xfields, 
    Rcl::Doc& doc);

#endif /* _REAPXATTRS_H_INCLUDED_ */
recoll-1.36.1/internfile/mh_exec.h0000644000175000017500000001140514410615043013722 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _MH_EXEC_H_INCLUDED_
#define _MH_EXEC_H_INCLUDED_

#include 
#include 

#include "mimehandler.h"
#include "execmd.h"

class HandlerTimeout {};
    
/** 
 * Turn external document into internal one by executing an external command.
 *
 * The command to execute, and its parameters, are defined in the mimeconf
 * configuration file, and stored by mimehandler.cpp in the object when it is
 * built. This data is not reset by a clear() call.
 *
 * The output MIME type (typically text/plain or text/html) and output
 * character set are also defined in mimeconf. The default is text/html, utf-8
 *
 * The command will write the document text to stdout. Its only way to
 * set metadata is through "meta" tags if the output MIME is
 * text/html.
 *
 * As any RecollFilter, a MimeHandlerExec object can be reset
 * by calling clear(), and will stay initialised for the same mtype
 * (cmd, params etc.)
 */
class MimeHandlerExec : public RecollFilter {
public:
    ///////////////////////
    // Members not reset by clear(). params, cfgFilterOutputMtype and 
    // cfgFilterOutputCharset
    // define what I am.  missingHelper is a permanent error
    // (no use to try and execute over and over something that's not
    // here).

    // Parameters: this has been built by our creator, from config file 
    // data. We always add the file name at the end before actual execution
    std::vector params;
    // Filter output type. The default for ext. filters is to output html, 
    // but some don't, in which case the type is defined in the config.
    std::string cfgFilterOutputMtype;
    // Output character set if the above type is not text/html. For
    // those filters, the output charset has to be known: ie set by a command
    // line option.
    std::string cfgFilterOutputCharset; 
    bool missingHelper{false};
    std::string whatHelper;
    // Resource management values

    // The filtermaxseconds default is set in the constructor by
    // querying the recoll.conf configuration variable. It can be
    // changed by the filter creation code in mimehandler.cpp if a
    // maxseconds parameter is set on the mimeconf line.
    int m_filtermaxseconds{900};
    int m_filtermaxmbytes{0};
    ////////////////

    MimeHandlerExec(RclConfig *cnf, const std::string& id);

    virtual void setmaxseconds(int seconds) {
        m_filtermaxseconds = seconds;
    }
    
    virtual bool next_document() override;
    virtual bool skip_to_document(const std::string& ipath) override;

    virtual void clear_impl() override {
        m_fn.erase(); 
        m_ipath.erase();
    }

protected:
    virtual bool set_document_file_impl(
        const std::string& mt, const std::string& file_path) override;

    std::string m_fn;
    std::string m_ipath;
    // md5 computation excluded by handler name: can't change after init
    bool m_handlernomd5{false};
    bool m_hnomd5init{false};
    // If md5 not excluded by handler name, allow/forbid depending on mime
    bool m_nomd5{false};
    
    // Set the character set field and possibly transcode text/plain
    // output.
    // 
    // @param mt the MIME type. A constant for mh_exec, but may depend on the
    //    subdocument entry for mh_execm.
    // @param charset Document character set. A constant (empty
    //      parameter) for mh_exec (we use the value defined in mimeconf),
    //      possibly sent from the command for mh_execm.
    virtual void handle_cs(const std::string& mt, 
                           const std::string& charset = std::string());

private:
    virtual void finaldetails();
};


// This is called periodically by ExeCmd when it is waiting for data,
// or when it does receive some. We may choose to interrupt the
// command.
class MEAdv : public ExecCmdAdvise {
public:
    MEAdv(int maxsecs = 900);
    // Reset start time to now
    void reset();
    void setmaxsecs(int maxsecs) {
        m_filtermaxseconds = maxsecs;
    }
    void newData(int n);
private:
    time_t m_start;
    int m_filtermaxseconds;
};

#endif /* _MH_EXEC_H_INCLUDED_ */
recoll-1.36.1/internfile/mh_unknown.h0000644000175000017500000000330114427373216014504 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _MH_UNKNOWN_H_INCLUDED_
#define _MH_UNKNOWN_H_INCLUDED_

#include 

#include "cstr.h"
#include "mimehandler.h"

/**
 * Handler for files with no content handler: does nothing.
 *
 */
class MimeHandlerUnknown : public RecollFilter {
public:
    MimeHandlerUnknown(RclConfig *cnf, const std::string& id) 
        : RecollFilter(cnf, id) {
    }
    virtual ~MimeHandlerUnknown() {}
    MimeHandlerUnknown(const MimeHandlerUnknown&) = delete;
    MimeHandlerUnknown& operator=(const MimeHandlerUnknown&) = delete;
    virtual bool is_data_input_ok(DataInput) const {
        return true;
    }
    
    virtual bool next_document() {
        if (m_havedoc == false)
            return false;
        m_havedoc = false; 
        m_metaData[cstr_dj_keycontent] = cstr_null;
        m_metaData[cstr_dj_keymt] = cstr_textplain;
        return true;
    }
    virtual bool is_unknown() {return true;}
};

#endif /* _MH_UNKNOWN_H_INCLUDED_ */
recoll-1.36.1/internfile/internfile.cpp0000644000175000017500000012561614502557755015037 00000000000000/* Copyright (C) 2004-2019 J.F.Dockes 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "autoconfig.h"

#include 
#include 
#include 
#include "safefcntl.h"
#include 
#include "safeunistd.h"

#include 
#include 
#include 
#include 

using namespace std;

#include "cstr.h"
#include "internfile.h"
#include "rcldoc.h"
#include "mimetype.h"
#include "log.h"
#include "mimehandler.h"
#include "execmd.h"
#include "pathut.h"
#include "rclconfig.h"
#include "mh_html.h"
#include "fileudi.h"
#include "cancelcheck.h"
#include "copyfile.h"
#include "fetcher.h"
#include "extrameta.h"
#include "uncomp.h"

// The internal path element separator. This can't be the same as the rcldb 
// file to ipath separator : "|"
// We replace it with a control char if it comes out of a filter (ie:
// rclzip.py or rclchm.py can do this). If you want the SOH control char
// inside an ipath, you're out of luck (and a bit weird).
static const string cstr_isep(":");

static const char cchar_colon_repl = '\x01';
static string colon_hide(const string& in)
{
    string out;
    for (string::const_iterator it = in.begin(); it != in.end(); it++) {
        out += *it == ':' ? cchar_colon_repl : *it;
    }
    return out;
}
static string colon_restore(const string& in)
{
    string out;
    for (string::const_iterator it = in.begin(); it != in.end(); it++) {
        out += *it == cchar_colon_repl ? ':' : *it;
    }
    return out;
}

// This is used when the user wants to retrieve a search result doc's parent
// (ie message having a given attachment)
bool FileInterner::getEnclosingUDI(const Rcl::Doc &doc, string& udi)
{
    LOGDEB("FileInterner::getEnclosingUDI(): url [" << doc.url <<"] ipath [" << doc.ipath << "]\n");
    string eipath = doc.ipath;
    string::size_type colon;
    if (eipath.empty())
        return false;
    if ((colon =  eipath.find_last_of(cstr_isep)) != string::npos) {
        eipath.erase(colon);
    } else {
        eipath.erase();
    }
    
    make_udi(url_gpath(doc.idxurl.empty() ? doc.url : doc.idxurl), eipath, udi);
    return true;
}

string FileInterner::getLastIpathElt(const string& ipath)
{
    string::size_type sep;
    if ((sep =  ipath.find_last_of(cstr_isep)) != string::npos) {
        return ipath.substr(sep + 1);
    } else {
        return ipath;
    }
}

bool FileInterner::ipathContains(const string& parent, const string& child)
{
    return child.find(parent) == 0 &&
        child.find(cstr_isep, parent.size()) == parent.size();
}

// Constructor: identify the input file, possibly create an
// uncompressed temporary copy, and create the top filter for the
// uncompressed file type.
//
// Empty handler on return says that we're in error, this will be
// processed by the first call to internfile().
// Split into "constructor calls init()" to allow use from other constructor
FileInterner::FileInterner(const string &fn, const struct PathStat& stp,
                           RclConfig *cnf, int flags, const string *imime)
{
    LOGDEB0("FileInterner::FileInterner(fn=" << fn << ")\n");
    if (fn.empty()) {
        LOGERR("FileInterner::FileInterner: empty file name!\n");
        return;
    }
    initcommon(cnf, flags);
    init(fn, stp, cnf, flags, imime);
}

// Note that we always succeed (set m_ok = true), except in internal
// inconsistency cases (which could just as well abort()).  Errors
// will be detected when internfile() is called. This is done so that
// our caller creates a doc record in all cases (with an error-flagged
// signature), so that the appropriate retry choices can be made. This
// used to not be the case, and was changed because this was the
// simplest way to solve the retry issues (simpler than changing the
// caller in e.g. fsindexer).
void FileInterner::init(const string &f, const struct PathStat& stp,
                        RclConfig *cnf, int flags, const string *imime)
{
    if (f.empty()) {
        LOGERR("FileInterner::init: empty file name!\n");
        return;
    }
    m_fn = f;

    // Compute udi for the input file. This is used by filters which
    // manage some kind of cache.  Indexing by udi makes things easier
    // because they sometimes get a temp as actual input.
    string udi;
    make_udi(f, cstr_null, udi);

    cnf->setKeyDir(path_getfather(m_fn));

    string l_mime;
    bool usfci = false;
    cnf->getConfParam("usesystemfilecommand", &usfci);

    // In general, even when the input mime type is set (when
    // previewing), we can't use it: it's the type for the actual
    // document, but this can be part of a compound document, and
    // we're dealing with the top level file here, or this could be a
    // compressed file. The flag tells us we really can use it
    // (e.g. the web indexer sets it).
    if (flags & FIF_doUseInputMimetype) {
        if (!imime) {
            LOGERR("FileInterner:: told to use null imime\n");
            return;
        }
        l_mime = *imime;
    } else {
        LOGDEB("FileInterner::init fn [" << f << "] mime [" <<
               (imime ? imime->c_str() : "(null)") << "] preview " <<
               m_forPreview << "\n");

        // Run mime type identification in any case (see comment above).
        l_mime = mimetype(m_fn, m_cfg, usfci, stp);

        // If identification fails, try to use the input parameter. This
        // is then normally not a compressed type (it's the mime type from
        // the db), and is only set when previewing, not for indexing
        if (l_mime.empty() && imime)
            l_mime = *imime;
    }

    int64_t docsize = stp.pst_size;

    if (!l_mime.empty()) {
        // Has mime: check for a compressed file. If so, create a
        // temporary uncompressed file, and rerun the mime type
        // identification, then do the rest with the temp file.
        vectorucmd;
        if (m_cfg->getUncompressor(l_mime, ucmd)) {
            // Check for compressed size limit
            int maxkbs = -1;
            if (!m_cfg->getConfParam("compressedfilemaxkbs", &maxkbs) ||
                maxkbs < 0 || stp.pst_type == PathStat::PST_INVALID ||
                int(stp.pst_size / 1024) < maxkbs) {
                if (!m_uncomp->uncompressfile(m_fn, ucmd, m_tfile)) {
                    m_ok = true;
                    return;
                }
                LOGDEB1("FileInterner:: after ucomp: tfile " << m_tfile <<"\n");
                m_fn = m_tfile;
                // Stat the uncompressed file, mainly to get the size
                struct PathStat ucstat;
                if (path_fileprops(m_fn, &ucstat) != 0) {
                    LOGERR("FileInterner: can't stat the uncompressed file[" <<
                           m_fn << "] errno " << errno << "\n");
                    m_ok = true;
                    return;
                } else {
                    docsize = ucstat.pst_size;
                }
                l_mime = mimetype(m_fn, m_cfg, usfci, ucstat);
                if (l_mime.empty() && imime)
                    l_mime = *imime;
            } else {
                LOGINFO("FileInterner:: " << m_fn << " over size limit " << maxkbs << " kbs\n");
            }
        }
    }

    if (l_mime.empty()) {
        // No mime type. We let it through as config may warrant that
        // we index all file names
        LOGDEB0("FileInterner:: no mime: [" << m_fn << "]\n");
    }

    // Get fields computed from extended attributes. We use the
    // original file, not the m_fn which may be the uncompressed temp
    // file
    if (!m_noxattrs)
        reapXAttrs(m_cfg, f, m_XAttrsFields);

    // Gather metadata from external commands as configured.
    reapMetaCmds(m_cfg, f, m_cmdFields);

    m_mimetype = l_mime;

    // Look for appropriate handler (might still return empty)
    RecollFilter *df = getMimeHandler(l_mime, m_cfg, !m_forPreview, f);

    if (!df || df->is_unknown()) {
        // No real handler for this type, for now :( 
        LOGDEB("FileInterner:: unprocessed mime: [" << l_mime << "] [" << f << "]\n");
        if (!df)
            return;
    }
    df->set_property(Dijon::Filter::OPERATING_MODE, m_forPreview ? "view" : "index");
    df->set_property(Dijon::Filter::DJF_UDI, udi);

    df->set_docsize(docsize);
    // Don't process init errors here: doing it later allows indexing
    // the file name of even a totally unparsable file
    (void)df->set_document_file(l_mime, m_fn);
    m_handlers.push_back(df);
    LOGDEB("FileInterner:: init ok " << l_mime << " [" << m_fn << "]\n");
    m_ok = true;
}

// Setup from memory data (ie: out of the web cache). imime needs to be set.
FileInterner::FileInterner(const string &data, RclConfig *cnf, 
                           int flags, const string& imime)
{
    LOGDEB0("FileInterner::FileInterner(data)\n");
    initcommon(cnf, flags);
    init(data, cnf, flags, imime);
}

void FileInterner::init(const string &data, RclConfig *, int, const string& imime)
{
    if (imime.empty()) {
        LOGERR("FileInterner: inmemory constructor needs input mime type\n");
        return;
    }
    m_mimetype = imime;

    // Look for appropriate handler (might still return empty)
    RecollFilter *df = getMimeHandler(m_mimetype, m_cfg, !m_forPreview, m_fn);

    if (!df) {
        // No handler for this type, for now :( if indexallfilenames
        // is set in the config, this normally wont happen (we get mh_unknown)
        LOGDEB("FileInterner:: unprocessed mime [" << m_mimetype << "]\n");
        return;
    }
    df->set_property(Dijon::Filter::OPERATING_MODE, m_forPreview ? "view" : "index");

    df->set_docsize(data.length());
    if (df->is_data_input_ok(Dijon::Filter::DOCUMENT_STRING)) {
        (void)df->set_document_string(m_mimetype, data);
    } else if (df->is_data_input_ok(Dijon::Filter::DOCUMENT_DATA)) {
        (void)df->set_document_data(m_mimetype, data.c_str(), data.length());
    } else if (df->is_data_input_ok(Dijon::Filter::DOCUMENT_FILE_NAME)) {
        TempFile temp = dataToTempFile(data, m_mimetype);
        if (temp.ok()) {
            (void)df->set_document_file(m_mimetype, temp.filename());
            m_tmpflgs[m_handlers.size()] = true;
            m_tempfiles.push_back(temp);
        }
    }
    // Don't process init errors here: doing it later allows indexing
    // the file name of even a totally unparsable file
    m_handlers.push_back(df);
    m_ok = true;
}

void FileInterner::initcommon(RclConfig *cnf, int flags)
{
    m_cfg = cnf;
    m_forPreview = ((flags & FIF_forPreview) != 0);
    m_uncomp = new Uncomp(m_forPreview);
    // Initialize handler stack.
    m_handlers.reserve(MAXHANDLERS);
    for (unsigned int i = 0; i < MAXHANDLERS; i++)
        m_tmpflgs[i] = false;
    m_targetMType = cstr_textplain;
    m_noxattrs = false;
    m_cfg->getConfParam("noxattrfields", &m_noxattrs);
    m_direct = false;
}

FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, int flags)
{
    LOGDEB0("FileInterner::FileInterner(idoc)\n");
    initcommon(cnf, flags);

    std::unique_ptr fetcher(docFetcherMake(cnf, idoc));
    if (!fetcher) {
        LOGERR("FileInterner:: no backend\n");
        return;
    }
    DocFetcher::RawDoc rawdoc;
    if (!fetcher->fetch(cnf, idoc, rawdoc)) {
        LOGERR("FileInterner:: fetcher failed\n");
        return;
    }
    switch (rawdoc.kind) {
    case DocFetcher::RawDoc::RDK_FILENAME:
        init(rawdoc.data, rawdoc.st, cnf, flags, &idoc.mimetype);
        break;
    case DocFetcher::RawDoc::RDK_DATA:
        init(rawdoc.data, cnf, flags, idoc.mimetype);
        break;
    case DocFetcher::RawDoc::RDK_DATADIRECT:
        // Note: only used for demo with the sample python external
        // mbox indexer at this point. The external program is
        // responsible for all the extraction process.
        init(rawdoc.data, cnf, flags, idoc.mimetype);
        m_direct = true;
        break;
    default:
        LOGERR("FileInterner::FileInterner(idoc): bad rawdoc kind ??\n");
    }
    return;
}

FileInterner::ErrorPossibleCause FileInterner::tryGetReason(RclConfig *cnf,
                                                            const Rcl::Doc& idoc)
{
    LOGDEB0("FileInterner::tryGetReason(idoc)\n");

    std::unique_ptr fetcher(docFetcherMake(cnf, idoc));
    if (!fetcher) {
        LOGERR("FileInterner:: no backend\n");
        return FileInterner::FetchNoBackend;
    }
    DocFetcher::Reason fetchreason = fetcher->testAccess(cnf, idoc);
    switch (fetchreason) {
    case DocFetcher::FetchNotExist: return FileInterner::FetchMissing;
    case DocFetcher::FetchNoPerm: return FileInterner::FetchPerm;
    default: return FileInterner::InternfileOther;
    }
}

bool FileInterner::makesig(RclConfig *cnf, const Rcl::Doc& idoc, string& sig)
{
    std::unique_ptr fetcher(docFetcherMake(cnf, idoc));
    if (!fetcher) {
        LOGERR("FileInterner::makesig no backend for doc\n");
        return false;
    }

    bool ret = fetcher->makesig(cnf, idoc, sig);
    return ret;
}

FileInterner::~FileInterner()
{
    for (auto& entry: m_handlers) {
        returnMimeHandler(entry);
    }
    delete m_uncomp;
    // m_tempfiles will take care of itself
}

// Create a temporary file for a block of data (ie: attachment) found
// while walking the internal document tree, with a type for which the
// handler needs an actual file (ie : external script).
TempFile FileInterner::dataToTempFile(const string& dt, const string& mt)
{
    // Create temp file with appropriate suffix for mime type
    TempFile temp(m_cfg->getSuffixFromMimeType(mt));
    if (!temp.ok()) {
        LOGERR("FileInterner::dataToTempFile: cant create tempfile: " <<
               temp.getreason() << "\n");
        return TempFile();
    }
    string reason;
    if (!stringtofile(dt, temp.filename(), reason)) {
        LOGERR("FileInterner::dataToTempFile: stringtofile: " < verr;
        stringToStrings(msg, verr);
        if (verr.size() > 2) {
            vector::iterator it = verr.begin();
            it++;
            if (*it == "HELPERNOTFOUND") {
                it++;
                for (; it != verr.end(); it++) {
                    m_missingdatap->addMissing(*it, mt);
                }
            }
        }                   
    }
}

void FIMissingStore::getMissingExternal(string& out) 
{
    for (map >::const_iterator it = 
             m_typesForMissing.begin(); it != m_typesForMissing.end(); it++) {
        out += string(" ") + it->first;
    }
    trimstring(out);
}

void FIMissingStore::getMissingDescription(string& out)
{
    out.erase();

    for (map >::const_iterator it = 
             m_typesForMissing.begin(); it != m_typesForMissing.end(); it++) {
        out += it->first + " (";
        set::const_iterator it3;
        for (it3 = it->second.begin(); 
             it3 != it->second.end(); it3++) {
            out += *it3 + " ";
        }
        trimstring(out);
        out += ")";
        out += "\n";
    }
}

FIMissingStore::FIMissingStore(const string& in)
{
    // The "missing" file is text. Each line defines a missing filter
    // and the list of mime types actually encountered that needed it
    // (see method getMissingDescription())

    vector lines;
    stringToTokens(in, lines, "\n");

    for (vector::const_iterator it = lines.begin();
         it != lines.end(); it++) {
        // Lines from the file are like: 
        //
        // filter name string (mime1 mime2) 
        //
        // We can't be too sure that there will never be a parenthesis
        // inside the filter string as this comes from the filter
        // itself. The list part is safer, so we start from the end.
        const string& line = *it;
        string::size_type lastopen = line.find_last_of("(");
        if (lastopen == string::npos)
            continue;
        string::size_type lastclose = line.find_last_of(")");
        if (lastclose == string::npos || lastclose <= lastopen + 1)
            continue;
        string smtypes = line.substr(lastopen+1, lastclose - lastopen - 1);
        vector mtypes;
        stringToTokens(smtypes, mtypes);
        string filter = line.substr(0, lastopen);
        trimstring(filter);
        if (filter.empty())
            continue;

        for (vector::const_iterator itt = mtypes.begin(); 
             itt != mtypes.end(); itt++) {
            m_typesForMissing[filter].insert(*itt);
        }
    }
}

// Helper for extracting a value from a map.
static inline bool getKeyValue(const map& docdata, 
                               const string& key, string& value)
{
    auto it = docdata.find(key);
    if (it != docdata.end()) {
        value = it->second;
        LOGDEB2("getKeyValue: [" << key << "]->[" << value << "]\n");
        return true;
    }
    LOGDEB2("getKeyValue: no value for [" << key << "]\n");
    return false;
}

// Copy most metadata fields from the top filter to the recoll
// doc. Some fields need special processing, because they go into
// struct fields instead of metadata entry, or because we don't want
// to copy them.
bool FileInterner::dijontorcl(Rcl::Doc& doc)
{
    RecollFilter *df = m_handlers.back();
    if (df == 0) {
        //??
        LOGERR("FileInterner::dijontorcl: null top handler ??\n");
        return false;
    }
    for (const auto& ent :  df->get_meta_data()) {
        if (ent.first == cstr_dj_keycontent) {
            doc.text = ent.second;
            if (doc.fbytes.empty()) {
                // It's normally set by walking the filter stack, in
                // collectIpathAndMt, which was called before us.  It
                // can happen that the doc size is still empty at this
                // point if the last container filter is directly
                // returning text/plain content, so that there is no
                // ipath-less filter at the top
                lltodecstr(doc.text.length(), doc.fbytes);
                LOGDEB("FileInterner::dijontorcl: fbytes->" << doc.fbytes << "\n");
            }
        } else if (ent.first == cstr_dj_keymd) {
            doc.dmtime = ent.second;
        } else if (ent.first == cstr_dj_keyanc) {
            doc.haschildren = true;
        } else if (ent.first == cstr_dj_keyorigcharset) {
            doc.origcharset = ent.second;
        } else if (ent.first == cstr_dj_keyfn) {
            // Only if not set during the stack walk
            const string *fnp = 0;
            if (!doc.peekmeta(Rcl::Doc::keyfn, &fnp) || fnp->empty())
                doc.meta[Rcl::Doc::keyfn] = ent.second;
        } else if (ent.first == cstr_dj_keymd5) {
            // Only if not set during the stack walk: we want the md5
            // from the actual document, not from further conversions,
            // as computed, e.g. by the html to text handler
            const string *val = 0;
            if (!doc.peekmeta(Rcl::Doc::keymd5, &val) || val->empty())
                doc.meta[Rcl::Doc::keymd5] = ent.second;
        } else if (ent.first == cstr_dj_keymt || ent.first == cstr_dj_keycharset) {
            // don't need/want these.
        } else {
            LOGDEB2("dijontorcl: " << m_cfg->fieldCanon(ent.first) << " -> " << ent.second << "\n");
            if (!ent.second.empty()) {
                doc.meta[m_cfg->fieldCanon(ent.first)] = ent.second;
            }
        }
    }
    if (doc.meta[Rcl::Doc::keyabs].empty() && 
        !doc.meta[cstr_dj_keyds].empty()) {
        doc.meta[Rcl::Doc::keyabs] = doc.meta[cstr_dj_keyds];
        doc.meta.erase(cstr_dj_keyds);
    }
    return true;
}

const set nocopyfields{cstr_dj_keycontent, cstr_dj_keymd,
        cstr_dj_keyanc, cstr_dj_keyorigcharset, cstr_dj_keyfn,
        cstr_dj_keymt, cstr_dj_keycharset, cstr_dj_keyds};

static void copymeta(const RclConfig *cfg, Rcl::Doc& doc, const RecollFilter* hp)
{
    for (const auto& entry : hp->get_meta_data()) {
        if (nocopyfields.find(entry.first) == nocopyfields.end()) {
            doc.addmeta(cfg->fieldCanon(entry.first), entry.second);
        }
    }
}


// Collect the ipath from the filter stack.
// While we're at it, we also set the mimetype and filename,
// which are special properties: we want to get them from the topmost
// doc with an ipath, not the last one which is usually text/plain We
// also set the author and modification time from the last doc which
// has them.
// 
// The stack can contain objects with an ipath element (corresponding
// to actual embedded documents), and, towards the top, elements
// without an ipath element, for format translations of the last doc.
//
// The docsize is fetched from the first element without an ipath
// (first non container). If the last element directly returns
// text/plain so that there is no ipath-less element, the value will
// be set in dijontorcl(). 
// 
// The whole thing is a bit messy but it's not obvious how it should
// be cleaned up as the "inheritance" rules inside the stack are
// actually complicated.
void FileInterner::collectIpathAndMT(Rcl::Doc& doc) const
{
    LOGDEB2("FileInterner::collectIpathAndMT\n");

    // Set to true if any element in the stack sets an ipath. (at least one of
    // the docs is a compound).
    bool hasipath = false;

    if (!m_noxattrs) {
        docFieldsFromXattrs(m_cfg, m_XAttrsFields, doc);
    }

    docFieldsFromMetaCmds(m_cfg, m_cmdFields, doc);

    // If there is no ipath stack, the mimetype is the one from the
    // file, else we'll change it further down.
    doc.mimetype = m_mimetype;

    string pathelprev;
    for (unsigned int i = 0; i < m_handlers.size(); i++) {
        const map& docdata = m_handlers[i]->get_meta_data();
        string ipathel;
        getKeyValue(docdata, cstr_dj_keyipath, ipathel);
        if (!ipathel.empty()) {
            // Non-empty ipath. This stack element is for an
            // actual embedded document, not a format translation.
            hasipath = true;
            doc.ipath += colon_hide(ipathel) + cstr_isep;
            getKeyValue(docdata, cstr_dj_keymt, doc.mimetype);
            getKeyValue(docdata, cstr_dj_keyfn, doc.meta[Rcl::Doc::keyfn]);
        } else {
            // We copy all the metadata from the topmost actual
            // document: either the first if it has no ipath, or the
            // last one with an ipath (before pure format
            // translations). This would allow, for example mh_execm
            // handlers to use setfield() instead of embedding
            // metadata in the HTML meta tags.
            if (i == 0 || !pathelprev.empty()) {
                copymeta(m_cfg, doc, m_handlers[i == 0 ? 0 : i-1]);
            }
            if (doc.fbytes.empty()) {
                lltodecstr(m_handlers[i]->get_docsize(), doc.fbytes);
                LOGDEB("collectIpath..: fbytes->" << doc.fbytes << endl);
            }
        }
        // We set the author field from the innermost doc which has
        // one: allows finding, e.g. an image attachment having no
        // metadata by a search on the sender name. Only do this for
        // actually embedded documents (avoid replacing values from
        // metacmds for the topmost one). For a topmost doc, author
        // will be merged by dijontorcl() later on. About same for
        // dmtime, but an external value will be replaced, not
        // augmented if dijontorcl() finds an internal value.
        if (hasipath) {
            getKeyValue(docdata, cstr_dj_keyauthor, doc.meta[Rcl::Doc::keyau]);
            getKeyValue(docdata, cstr_dj_keymd, doc.dmtime);
        }
        pathelprev = ipathel;
    }

    if (hasipath) {
        // Trim ending ipath separator
        LOGDEB2("IPATH [" << doc.ipath << "]\n");
        if (doc.ipath.back() ==  cstr_isep[0]) {
            doc.ipath.erase(doc.ipath.end()-1);
        }
    }
}

// Remove handler from stack. Clean up temp file if needed.
void FileInterner::popHandler()
{
    if (m_handlers.empty())
        return;
    size_t i = m_handlers.size() - 1;
    if (m_tmpflgs[i]) {
        m_tempfiles.pop_back();
        m_tmpflgs[i] = false;
    }
    returnMimeHandler(m_handlers.back());
    m_handlers.pop_back();
}

enum addResols {ADD_OK, ADD_CONTINUE, ADD_BREAK, ADD_ERROR};

// Just got document from current top handler. See what type it is,
// and possibly add a filter/handler to the stack
int FileInterner::addHandler()
{
    const map& docdata = m_handlers.back()->get_meta_data();
    string charset, mimetype;
    getKeyValue(docdata, cstr_dj_keycharset, charset);
    getKeyValue(docdata, cstr_dj_keymt, mimetype);

    LOGDEB("FileInterner::addHandler: back()  is " << mimetype <<
           " target [" << m_targetMType << "]\n");

    // A handler may send a path pointing to the content in a file instead of returning the data
    bool isdatapath{false};
    {std::string sdp;
        if (getKeyValue(docdata, cstr_dj_content_is_datapath, sdp)) {
            isdatapath = stringToBool(sdp);
        }
    }

    // If we find a document of the target type (text/plain in
    // general), we're done decoding. If we hit text/plain, we're done
    // in any case
    if (!stringicmp(mimetype, m_targetMType) || 
        !stringicmp(mimetype, cstr_textplain)) {
        m_reachedMType = mimetype;
        LOGDEB1("FileInterner::addHandler: target reached\n");
        return ADD_BREAK;
    }

    // We need to stack another handler. Check stack size
    if (m_handlers.size() >= MAXHANDLERS) {
        // Stack too big. Skip this and go on to check if there is
        // something else in the current back()
        LOGERR("FileInterner::addHandler: stack too high\n");
        return ADD_CONTINUE;
    }

    // We must not filter out HTML when it is an intermediate
    // conversion format. We discriminate between e.g. an HTML email
    // attachment (needs filtering) and a result of pdf conversion
    // (must process) by looking at the last ipath element: a
    // conversion will have an empty one (same test as in
    // collectIpathAndMT).
    string ipathel;
    getKeyValue(docdata, cstr_dj_keyipath, ipathel);
    bool dofilter = !m_forPreview &&
        (mimetype.compare(cstr_texthtml) || !ipathel.empty());
    RecollFilter *newflt = getMimeHandler(mimetype, m_cfg, dofilter, m_fn);
    if (!newflt) {
        // If we can't find a handler, this doc can't be handled
        // but there can be other ones so we go on
        LOGINFO("FileInterner::addHandler: no filter for [" << mimetype << "]\n");
        return ADD_CONTINUE;
    }
    newflt->set_property(Dijon::Filter::OPERATING_MODE, m_forPreview ? "view" : "index");
    if (!charset.empty())
        newflt->set_property(Dijon::Filter::DEFAULT_CHARSET, charset);

    // Get current content: we don't use getkeyvalue() here to avoid
    // copying the text, which may be big.
    string ns;
    const string *txt = &ns;
    {
        auto it = docdata.find(cstr_dj_keycontent);
        if (it != docdata.end())
            txt = &it->second;
    }
    bool setres = false;
    newflt->set_docsize(txt->length());
    if (!isdatapath && newflt->is_data_input_ok(Dijon::Filter::DOCUMENT_STRING)) {
        setres = newflt->set_document_string(mimetype, *txt);
    } else if (!isdatapath && newflt->is_data_input_ok(Dijon::Filter::DOCUMENT_DATA)) {
        setres = newflt->set_document_data(mimetype,txt->c_str(), txt->length());
    } else if (newflt->is_data_input_ok(Dijon::Filter::DOCUMENT_FILE_NAME)) {
        if (isdatapath && !txt->empty()) {
            setres = newflt->set_document_file(mimetype, *txt);
        } else {
            TempFile temp = dataToTempFile(*txt, mimetype);
            if (temp.ok() && 
                (setres = newflt->set_document_file(mimetype, temp.filename()))) {
                m_tmpflgs[m_handlers.size()] = true;
                m_tempfiles.push_back(temp);
                // Hack here, but really helps perfs: if we happen to
                // create a temp file for, ie, an image attachment, keep
                // it around for preview to use it through get_imgtmp()
                if (!mimetype.compare(0, 6, "image/")) {
                    m_imgtmp = m_tempfiles.back();
                }
            }
        }
    }
    if (!setres) {
        LOGINFO("FileInterner::addHandler: set_doc failed inside [" << m_fn <<
                "]  for mtype " << mimetype << "\n");
    }
    // Add handler and go on, maybe this one will give us text...
    m_handlers.push_back(newflt);
    LOGDEB1("FileInterner::addHandler: added\n");
    return setres ? ADD_OK : ADD_BREAK;
}

// Information and debug after a next_document error
void FileInterner::processNextDocError(Rcl::Doc &doc)
{
    collectIpathAndMT(doc);
    m_reason = m_handlers.back()->get_error();
    checkExternalMissing(m_reason, doc.mimetype);
    LOGERR("FileInterner::internfile: next_document error [" << m_fn <<
           (doc.ipath.empty() ? "" : "|") << doc.ipath << "] " <<
           doc.mimetype << " " << m_reason << "\n");
}

FileInterner::Status FileInterner::internfile(Rcl::Doc& doc,const string& ipath)
{
    LOGDEB("FileInterner::internfile. ipath [" << ipath << "]\n");

    // Get rid of possible image tempfile from older call
    m_imgtmp = TempFile();

    if (m_handlers.size() < 1) {
        // Just means the constructor failed
        LOGDEB("FileInterner::internfile: no handler: constructor failed\n");
        return FIError;
    }

    // Input Ipath vector when retrieving a given subdoc for previewing
    vector vipath;
    if (!ipath.empty() && !m_direct) {
        stringToTokens(ipath, vipath, cstr_isep, true);
        for (auto& entry: vipath) {
            entry = colon_restore(entry);
        }
        if (!m_handlers.back()->skip_to_document(vipath[m_handlers.size()-1])){
            LOGERR("FileInterner::internfile: can't skip\n");
            return FIError;
        }
    }

    // Try to get doc from the topmost handler
    // Security counter: looping happens when we stack one other 
    // handler or when walking the file document tree without finding
    // something to index (typical exemple: email with multiple image
    // attachments and no image filter installed). So we need to be
    // quite generous here, especially because there is another
    // security in the form of a maximum handler stack size.
    int loop = 0;
    while (!m_handlers.empty()) {
        CancelCheck::instance().checkCancel();
        if (loop++ > 1000) {
            LOGERR("FileInterner:: looping!\n");
            return FIError;
        }
        // If there are no more docs at the current top level we pop and
        // see if there is something at the previous one
        if (!m_handlers.back()->has_documents()) {
            // If looking for a specific doc, this is an error. Happens if
            // the index is stale, and the ipath points to the wrong message
            // for exemple (one with less attachments)
            if (m_forPreview) {
                m_reason += "Requested document does not exist. ";
                m_reason += m_handlers.back()->get_error();
                LOGERR("FileInterner: requested document does not exist\n");
                return FIError;
            }
            popHandler();
            continue;
        }

        // While indexing, don't stop on next_document() error. There
        // might be ie an error while decoding an attachment, but we
        // still want to process the rest of the mbox! For preview: fatal.
        if (!m_handlers.back()->next_document()) {
            // Using a temp doc here because else we'd need to pop the
            // last ipath element when we do the pophandler (else the
            // ipath continues to grow in the current doc with each
            // consecutive error). It would be better to have
            // something like ipath.pop(). We do need the MIME type
            Rcl::Doc doc1 = doc;
            processNextDocError(doc1);
            doc.mimetype = doc1.mimetype;
            if (m_forPreview) {
                m_reason += "Requested document does not exist. ";
                m_reason += m_handlers.back()->get_error();
                LOGERR("FileInterner: requested document does not exist\n");
                return FIError;
            }
            popHandler();
            continue;
        }

        // Look at the type for the next document and possibly add
        // handler to stack.
        switch (addHandler()) {
        case ADD_OK: // Just go through: handler has been stacked, use it
            LOGDEB2("addHandler returned OK\n");
            break; 
        case ADD_CONTINUE: 
            // forget this doc and retrieve next from current handler
            // (ipath stays same)
            LOGDEB2("addHandler returned CONTINUE\n");
            continue;
        case ADD_BREAK: 
            // Stop looping: doc type ok, need complete its processing
            // and return it
            LOGDEB2("addHandler returned BREAK\n");
            goto breakloop; // when you have to you have to
        case ADD_ERROR: 
            LOGDEB2("addHandler returned ERROR\n");
            return FIError;
        }

        // If we have an ipath, meaning that we are seeking a specific
        // document (ie: previewing a search result), we may have to
        // seek to the correct entry of a compound doc (ie: archive or
        // mail). When we are out of ipath entries, we stop seeking,
        // the handlers stack may still grow for translation (ie: if
        // the target doc is msword, we'll still stack the
        // word-to-text translator).
        if (!ipath.empty()) {
            if (m_handlers.size() <= vipath.size() &&
                !m_handlers.back()->skip_to_document(vipath[m_handlers.size()-1])) {
                LOGERR("FileInterner::internfile: can't skip\n");
                return FIError;
            }
        }
    }
breakloop:
    if (m_handlers.empty()) {
        LOGDEB("FileInterner::internfile: conversion ended with no doc\n");
        return FIError;
    }

    // Compute ipath and significant mimetype.  ipath is returned
    // through doc.ipath. We also retrieve some metadata fields from
    // the ancesters (like date or author). This is useful for email
    // attachments. The values will be replaced by those internal to
    // the document (by dijontorcl()) if any, so the order of calls is
    // important. We used to only do this when indexing, but the aux
    // fields like filename and author may be interesting when
    // previewing too
    collectIpathAndMT(doc);
    if (m_forPreview) {
        doc.mimetype = m_reachedMType;
    }
    // Keep this AFTER collectIpathAndMT
    dijontorcl(doc);
    // Fix the bogus mtype used to force mh_text processing of text subdocs
    if (doc.mimetype == "text/plain1") {
        doc.mimetype = "text/plain";
    }
    // Possibly destack so that we can test for FIDone. While doing this
    // possibly set aside an ancestor html text (for the GUI preview)
    while (!m_handlers.empty() && !m_handlers.back()->has_documents()) {
        if (m_forPreview) {
            MimeHandlerHtml *hth = 
                dynamic_cast(m_handlers.back());
            if (hth) {
                m_html = hth->get_html();
            }
        }
        popHandler();
    }
    if (m_handlers.empty())
        return FIDone;
    else 
        return FIAgain;
}

bool FileInterner::tempFileForMT(TempFile& otemp, RclConfig* cnf, 
                                 const string& mimetype)
{
    TempFile temp(cnf->getSuffixFromMimeType(mimetype));
    if (!temp.ok()) {
        LOGERR("FileInterner::tempFileForMT: can't create temp file\n");
        return false;
    }
    otemp = temp;
    return true;
}

// Static method, creates a FileInterner object to do the job.
bool FileInterner::idocToFile(
    TempFile& otemp, const string& tofile, RclConfig *cnf,
    const Rcl::Doc& idoc, bool uncompress)
{
    LOGDEB("FileInterner::idocToFile\n");

    if (idoc.ipath.empty()) {
        // Because of the mandatory first conversion in the
        // FileInterner constructor, need to use a specific method.
        return topdocToFile(otemp, tofile, cnf, idoc, uncompress);
    }

    // We set FIF_forPreview for consistency with the previous version
    // which determined this by looking at mtype!=null. Probably
    // doesn't change anything in this case.
    FileInterner interner(idoc, cnf, FIF_forPreview);
    interner.setTargetMType(idoc.mimetype);
    return interner.interntofile(otemp, tofile, idoc.ipath, idoc.mimetype);
}

// This is only needed because the FileInterner constructor always performs
// the first conversion, so that we need another approach for accessing the
// original document (targetmtype won't do).
bool FileInterner::topdocToFile(
    TempFile& otemp, const string& tofile,
    RclConfig *cnf, const Rcl::Doc& idoc, bool uncompress)
{
    std::unique_ptr fetcher(docFetcherMake(cnf, idoc));
    if (!fetcher) {
        LOGERR("FileInterner::topdocToFile no backend\n");
        return false;
    }
    DocFetcher::RawDoc rawdoc;
    if (!fetcher->fetch(cnf, idoc, rawdoc)) {
        LOGERR("FileInterner::topdocToFile fetcher failed\n");
        return false;
    }
    const char *filename = "";
    TempFile temp;
    if (tofile.empty()) {
        if (!tempFileForMT(temp, cnf, idoc.mimetype)) {
            return false;
        }
        filename = temp.filename();
    } else {
        filename = tofile.c_str();
    }
    string reason;
    switch (rawdoc.kind) {
    case DocFetcher::RawDoc::RDK_FILENAME: {
        string fn(rawdoc.data);
        TempFile temp;
        if (uncompress && isCompressed(fn, cnf)) {
            if (!maybeUncompressToTemp(temp, fn, cnf, idoc)) {
                LOGERR("FileInterner::idocToFile: uncompress failed\n");
                return false;
            }
        }
        fn = temp.ok() ? temp.filename() : rawdoc.data;
        if (!copyfile(fn.c_str(), filename, reason)) {
            LOGERR("FileInterner::idocToFile: copyfile: " << reason << "\n");
            return false;
        }
    }
        break;
    case DocFetcher::RawDoc::RDK_DATA:
    case DocFetcher::RawDoc::RDK_DATADIRECT:
        if (!stringtofile(rawdoc.data, filename, reason)) {
            LOGERR("FileInterner::idocToFile: stringtofile: " << reason <<"\n");
            return false;
        }
        break;
    default:
        LOGERR("FileInterner::FileInterner(idoc): bad rawdoc kind ??\n");
    }

    if (tofile.empty())
        otemp = temp;
    return true;
}

bool FileInterner::interntofile(TempFile& otemp, const string& tofile,
                                const string& ipath, const string& mimetype)
{
    if (!ok()) {
        LOGERR("FileInterner::interntofile: constructor failed\n");
        return false;
    }
    Rcl::Doc doc;
    Status ret = internfile(doc, ipath);
    if (ret == FileInterner::FIError) {
        LOGERR("FileInterner::interntofile: internfile() failed\n");
        return false;
    }

    // Specialcase text/html. This is to work around a bug that will
    // get fixed some day: the internfile constructor always loads the
    // first handler so that at least one conversion is always
    // performed (and the access to the original data may be lost). A
    // common case is an "Open" on an HTML file (we end up
    // with text/plain content). As the HTML version is saved in this
    // case, use it.
    if (!stringlowercmp(cstr_texthtml, mimetype) && !get_html().empty()) {
        doc.text = get_html();
        doc.mimetype = cstr_texthtml;
    }

    const char *filename;
    TempFile temp;
    if (tofile.empty()) {
        if (!tempFileForMT(temp, m_cfg, mimetype)) {
            return false;
        }
        filename = temp.filename();
    } else {
        filename = tofile.c_str();
    }
    string reason;
    if (!stringtofile(doc.text, filename, reason)) {
        LOGERR("FileInterner::interntofile: stringtofile : " << reason << "\n");
        return false;
    }

    if (tofile.empty())
        otemp = temp;
    return true;
}

bool FileInterner::isCompressed(const string& fn, RclConfig *cnf)
{
    LOGDEB("FileInterner::isCompressed: [" << fn << "]\n");
    struct PathStat st;
    if (path_fileprops(fn, &st) < 0) {
        LOGERR("FileInterner::isCompressed: can't stat [" << fn << "]\n");
        return false;
    }
    string l_mime = mimetype(fn, cnf, true, st);
    if (l_mime.empty()) {
        LOGERR("FileInterner::isUncompressed: can't get mime for [" << fn <<
               "]\n");
        return false;
    }

    vector ucmd;
    if (cnf->getUncompressor(l_mime, ucmd)) {
        return true;
    }
    return false;
}

// Static.
bool FileInterner::maybeUncompressToTemp(TempFile& temp, const string& fn, 
                                         RclConfig *cnf, const Rcl::Doc& doc)
{
    LOGDEB("FileInterner::maybeUncompressToTemp: [" << fn << "]\n");
    struct PathStat st;
    if (path_fileprops(fn.c_str(), &st) < 0) {
        LOGERR("FileInterner::maybeUncompressToTemp: can't stat [" <ucmd;
    if (!cnf->getUncompressor(l_mime, ucmd)) {
        return true;
    }
    // Check for compressed size limit
    int maxkbs = -1;
    if (cnf->getConfParam("compressedfilemaxkbs", &maxkbs) &&
        maxkbs >= 0 && int(st.pst_size / 1024) > maxkbs) {
        LOGINFO("FileInterner:: " << fn << " over size limit " << maxkbs <<
                " kbs\n");
        return false;
    }
    temp = TempFile(cnf->getSuffixFromMimeType(doc.mimetype));
    if (!temp.ok()) {
        LOGERR("FileInterner: cant create temporary file\n");
        return false;
    }

    Uncomp uncomp;
    string uncomped;
    if (!uncomp.uncompressfile(fn, ucmd, uncomped)) {
        return false;
    }

    // uncompressfile choses the output file name, there is good
    // reason for this, but it's not nice here. Have to move, the
    // uncompressed file, hopefully staying on the same dev.
    string reason;
    if (!renameormove(uncomped.c_str(), temp.filename(), reason)) {
        LOGERR("FileInterner::maybeUncompress: move [" << uncomped <<
               "] -> [" << temp.filename() << "] failed: " << reason << "\n");
        return false;
    }
    return true;
}
recoll-1.36.1/internfile/htmlparse.cpp0000644000175000017500000003217214427373216014663 00000000000000/* This file was copied/updated from xapian-omega-1.0.1 to 1.2.6 and modified */

/* htmlparse.cc: simple HTML parser for omega indexer
 *
 * Copyright 1999,2000,2001 BrightStation PLC
 * Copyright 2001 Ananova Ltd
 * Copyright 2002,2006,2007,2008,2009,2010,2011 Olly Betts
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */

#include 
#include "htmlparse.h"
#include 
#include 
#include 

using std::find;
using std::find_if;
using std::string;
using std::map;

inline void
lowercase_string(string &str)
{
    for (string::iterator i = str.begin(); i != str.end(); ++i) {
        *i = tolower(static_cast(*i));
    }
}

map HtmlParser::named_ents;

inline static bool
p_notdigit(char c)
{
    return !isdigit(static_cast(c));
}

inline static bool
p_notxdigit(char c)
{
    return !isxdigit(static_cast(c));
}

inline static bool
p_notalnum(char c)
{
    return !isalnum(static_cast(c));
}

inline static bool
p_notwhitespace(char c)
{
    return !isspace(static_cast(c));
}

inline static bool
p_nottag(char c)
{
    return !isalnum(static_cast(c)) &&
        c != '.' && c != '-' && c != ':'; // ':' for XML namespaces.
}

inline static bool
p_whitespacegt(char c)
{
    return isspace(static_cast(c)) || c == '>';
}

inline static bool
p_whitespaceeqgt(char c)
{
    return isspace(static_cast(c)) || c == '=' || c == '>';
}

bool
HtmlParser::get_parameter(const string & param, string & value) const
{
    map::const_iterator i = parameters.find(param);
    if (i == parameters.end()) return false;
    value = i->second;
    return true;
}

HtmlParser::HtmlParser()
{
    // RECOLL: no need to initialize these entities, we use those from
    // myhtmlparse
#if 0
    static const struct ent { const char *n; unsigned int v; } ents[] = {
#include "namedentities.h"
        { NULL, 0 }
    };
    if (named_ents.empty()) {
        const struct ent *i = ents;
        while (i->n) {
            named_ents[string(i->n)] = i->v;
            ++i;
        }
    }
#endif
}

void
HtmlParser::decode_entities(string &)
{
    // Not used for recoll. Kept here to minimize the amount of
    // diffs. Almost the same code in myhtmlparse except that the
    // entity table directly holds the utf-8 strings instead of the
    // unicode positions (one less conversion).
#if 0
    // We need a const_iterator version of s.end() - otherwise the
    // find() and find_if() templates don't work...
    string::const_iterator amp = s.begin(), s_end = s.end();
    while ((amp = find(amp, s_end, '&')) != s_end) {
        unsigned int val = 0;
        string::const_iterator end, p = amp + 1;
        if (p != s_end && *p == '#') {
            p++;
            if (p != s_end && (*p == 'x' || *p == 'X')) {
                // hex
                p++;
                end = find_if(p, s_end, p_notxdigit);
                sscanf(s.substr(p - s.begin(), end - p).c_str(), "%x", &val);
            } else {
                // number
                end = find_if(p, s_end, p_notdigit);
                val = atoi(s.substr(p - s.begin(), end - p).c_str());
            }
        } else {
            end = find_if(p, s_end, p_notalnum);
            string code = s.substr(p - s.begin(), end - p);
            map::const_iterator i;
            i = named_ents.find(code);
            if (i != named_ents.end()) val = i->second;
        }
        if (end < s_end && *end == ';') end++;
        if (val) {
            string::size_type amp_pos = amp - s.begin();
            if (val < 0x80) {
                s.replace(amp_pos, end - amp, 1u, char(val));
            } else {
                // Convert unicode value val to UTF-8.
                char seq[4];
                unsigned len = Xapian::Unicode::nonascii_to_utf8(val, seq);
                s.replace(amp_pos, end - amp, seq, len);
            }
            s_end = s.end();
            // We've modified the string, so the iterators are no longer
            // valid...
            amp = s.begin() + amp_pos + 1;
        } else {
            amp = end;
        }
    }
#endif
}

void
HtmlParser::parse_html(const string &body)
{
    in_script = false;

    parameters.clear();
    string::const_iterator start = body.begin();

    while (true) {
        // Skip through until we find an HTML tag, a comment, or the end of
        // document.  Ignore isolated occurrences of `<' which don't start
        // a tag or comment.    
        string::const_iterator p = start;
        while (true) {
            p = find(p, body.end(), '<');
            if (p == body.end()) break;
            unsigned char ch = *(p + 1);

            // Tag, closing tag, or comment (or SGML declaration).
            if ((!in_script && isalpha(ch)) || ch == '/' || ch == '!') break;

            if (ch == '?') {
                // PHP code or XML declaration.
                // XML declaration is only valid at the start of the first line.
                // FIXME: need to deal with BOMs...
                if (p != body.begin() || body.size() < 20) break;

                // XML declaration looks something like this:
                // 
                if (p[2] != 'x' || p[3] != 'm' || p[4] != 'l') break;
                if (strchr(" \t\r\n", p[5]) == NULL) break;

                string::const_iterator decl_end = find(p + 6, body.end(), '?');
                if (decl_end == body.end()) break;

                // Default charset for XML is UTF-8.
                charset = "utf-8";

                string decl(p + 6, decl_end);
                size_t enc = decl.find("encoding");
                if (enc == string::npos) break;

                enc = decl.find_first_not_of(" \t\r\n", enc + 8);
                if (enc == string::npos || enc == decl.size()) break;

                if (decl[enc] != '=') break;
        
                enc = decl.find_first_not_of(" \t\r\n", enc + 1);
                if (enc == string::npos || enc == decl.size()) break;

                if (decl[enc] != '"' && decl[enc] != '\'') break;

                char quote = decl[enc++];
                size_t enc_end = decl.find(quote, enc);

                if (enc != string::npos)
                    charset = decl.substr(enc, enc_end - enc);

                break;
            }
            p++;
        }

        // Process text up to start of tag.
        if (p > start || p == body.end()) {
            string text = body.substr(start - body.begin(), p - start);
            decode_entities(text);
            process_text(text);
        }

        if (p == body.end()) {
            do_eof();
            break;
        }

        start = p + 1;
   
        if (start == body.end()) break;

        if (*start == '!') {
            if (++start == body.end()) break;
            if (++start == body.end()) break;
            // comment or SGML declaration
            if (*(start - 1) == '-' && *start == '-') {
                ++start;
                string::const_iterator close = find(start, body.end(), '>');
                // An unterminated comment swallows rest of document
                // (like Netscape, but unlike MSIE IIRC)
                if (close == body.end()) break;

                p = close;
                // look for -->
                while (p != body.end() && (*(p - 1) != '-' || *(p - 2) != '-'))
                    p = find(p + 1, body.end(), '>');

                if (p != body.end()) {
                    // Check for htdig's "ignore this bit" comments.
                    if (p - start == 15 && string(start, p - 2) == "htdig_noindex") {
                        string::size_type i;
                        i = body.find("", p + 1 - body.begin());
                        if (i == string::npos) break;
                        start = body.begin() + i + 21;
                        continue;
                    }
                    // If we found --> skip to there.
                    start = p;
                } else {
                    // Otherwise skip to the first > we found (as Netscape does).
                    start = close;
                }
            } else {
                // just an SGML declaration, perhaps giving the DTD - ignore it
                start = find(start - 1, body.end(), '>');
                if (start == body.end()) break;
            }
            ++start;
        } else if (*start == '?') {
            if (++start == body.end()) break;
            // PHP - swallow until ?> or EOF
            start = find(start + 1, body.end(), '>');

            // look for ?>
            while (start != body.end() && *(start - 1) != '?')
                start = find(start + 1, body.end(), '>');

            // unterminated PHP swallows rest of document (rather arbitrarily
            // but it avoids polluting the database when things go wrong)
            if (start != body.end()) ++start;
        } else {
            // opening or closing tag
            int closing = 0;

            if (*start == '/') {
                closing = 1;
                start = find_if(start + 1, body.end(), p_notwhitespace);
            }
          
            p = start;
            start = find_if(start, body.end(), p_nottag);
            string tag = body.substr(p - body.begin(), start - p);
            // convert tagname to lowercase
            lowercase_string(tag);

            if (closing) {
                if (!closing_tag(tag))
                    return;
                if (in_script && tag == "script") in_script = false;

                /* ignore any bogus parameters on closing tags */
                p = find(start, body.end(), '>');
                if (p == body.end()) break;
                start = p + 1;
            } else {
                bool empty_element = false;
                // FIXME: parse parameters lazily.
                while (start < body.end() && *start != '>') {
                    string name, value;

                    p = find_if(start, body.end(), p_whitespaceeqgt);

                    size_t name_len = p - start;
                    if (name_len == 1) {
                        if (*start == '/' && p < body.end() && *p == '>') {
                            // E.g. 
                            start = p;
                            empty_element = true;
                            break;
                        }
                    }

                    name.assign(body, start - body.begin(), name_len);

                    p = find_if(p, body.end(), p_notwhitespace);

                    start = p;
                    if (start != body.end() && *start == '=') {
                        start = find_if(start + 1, body.end(), p_notwhitespace);

                        p = body.end();

                        int quote = *start;
                        if (quote == '"' || quote == '\'') {
                            start++;
                            p = find(start, body.end(), quote);
                        }

                        if (p == body.end()) {
                            // unquoted or no closing quote
                            p = find_if(start, body.end(), p_whitespacegt);
                        }
                        value.assign(body, start - body.begin(), p - start);
                        start = find_if(p, body.end(), p_notwhitespace);

                        if (!name.empty()) {
                            // convert parameter name to lowercase
                            lowercase_string(name);
                            // in case of multiple entries, use the first
                            // (as Netscape does)
                            parameters.insert(make_pair(name, value));
                        }
                    }
                }
#if 0
                cout << "<" << tag;
                map::const_iterator x;
                for (x = parameters.begin(); x != parameters.end(); x++) {
                    cout << " " << x->first << "=\"" << x->second << "\"";
                }
                cout << ">\n";
#endif
                if (!opening_tag(tag))
                    return;
                parameters.clear();

                if (empty_element) {
                    if (!closing_tag(tag))
                        return;
                }

                // In 

  

  

    

A Recoll-searchable HTML page

This is a text sample in which links have been inserted for words, such as system installation, which can be searched for in the whole document set by using recoll

Also a little bit of javascript magic can make all words searchable (try double-clicking any word).

recoll-1.36.1/kde/kioslave/kio_recoll-kde4/data/help.html0000644000175000017500000000752714410615043020072 00000000000000 Recoll Kio Slave Recoll search

Recoll kio slave

Use this module to perform Recoll searches from any program with a KIO interface.

The module can work in two modes:

  • Html interface, close to a simplified QT Recoll interface.
  • File manager interface, Only with KDE 4.1 and newer, which presents results as directory entries

The module is still in its infancy. You will undoubtedly obtain strange effects from time to time. If you have any remarks or ideas about improving kio_recoll, or observe an interesting and reproducible sequence, please report it.

kio_recoll is primarily designed and tested with konqueror, and you will undoubtedly get even more surprising effects with other tools.

The Html interface is currently much more usable. The directory interface is extremely quirky.

The module is particularly unhelpful with search hits inside email folders, which Konqueror has no way to access.

HTML interface

This works more or less like the Recoll QT GUI, much simplified. The Recoll manual describes the queries that can be performed.

Most pages in the interface should quite self-explanatory.

You normally enter this interface by entering "recoll:" or "recoll:/" in the Konqueror URL entry, and following the "search" link. You can also directly enter "recoll:/search.html".
In most circumstances, entering a link like recoll:/john smith will also yield an HTML result list.

Compared to QT Recoll, the nice point is that you can click or drag/drop the icons to access the results in the standard desktop way.

File manager interface

The path part of the URI is taken as a Recoll query language string and executed. The results are displayed as directory entries.

There are several ways to enter this interface:

  • Using "recollf" as protocol name instead of "recoll". This is probably the easiest option inside open dialogs.
  • Using an URL ending with a '/', ie:
    recoll:/red apples ext:html/
  • Users who will want to use the file manager view most of the time can set the RECOLL_KIO_ALWAYS_DIR environment variable or the kio_always_dir recoll.conf variable to 1. The HTML interface will then only be accessible through the search link in the top "recoll:" view.

No search result details (samples, relevance etc.) are available, but this interface allows multiple selections and copies, usage inside any KDE open dialog, etc.

To avoid swamping the interface with thousands of results, the result count is limited to 100 by default. You can change this value by setting the kio_max_direntries parameter in your recoll configuration file (typically ~/.recoll/recoll.conf)

Because of limitations in the current KIO slave usage, the actual entry names are not those displayed but synthetic ones like "recollResultxxx". This has unfortunate side-effects when dragging/dropping the entries to some other application, or when using an open dialog (the opened file doesn't have the correct path to the original file).

Recoll Search

recoll-1.36.1/kde/kioslave/kio_recoll-kde4/kio_recoll.cpp0000644000175000017500000002745514427373216020206 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "kio_recoll.h" #include "rclconfig.h" #include "rclinit.h" #include "docseqdb.h" #include "wasatorcl.h" #include "rcldb.h" #include "rclquery.h" #include "searchdata.h" #include "pathut.h" #include "smallut.h" inline QString u8s2qs(const std::string& us) { return QString::fromUtf8(us.c_str(), us.size()); } using namespace std; using namespace KIO; RclConfig *RecollProtocol::o_rclconfig; RecollProtocol::RecollProtocol(const QByteArray &pool, const QByteArray &app) : SlaveBase("recoll", pool, app), m_initok(false), m_alwaysdir(false) { kDebug() << endl; if (o_rclconfig == 0) { o_rclconfig = recollinit(0, 0, 0, m_reason); if (!o_rclconfig || !o_rclconfig->ok()) { m_reason = string("Configuration problem: ") + m_reason; return; } } if (o_rclconfig->getDbDir().empty()) { // Note: this will have to be replaced by a call to a // configuration building dialog for initial configuration? Or // do we assume that the QT GUO is always used for this ? m_reason = "No db directory in configuration ??"; return; } m_rcldb = std::shared_ptr(new Rcl::Db(o_rclconfig)); if (!m_rcldb) { m_reason = "Could not build database object. (out of memory ?)"; return; } // Decide if we allow switching between html and file manager // presentation by using an end slash or not. Can also be done dynamically // by switching proto names. const char *cp = getenv("RECOLL_KIO_ALWAYS_DIR"); if (cp) { m_alwaysdir = stringToBool(cp); } else { o_rclconfig->getConfParam("kio_always_dir", &m_alwaysdir); } cp = getenv("RECOLL_KIO_STEMLANG"); if (cp) { m_stemlang = cp; } else { m_stemlang = "english"; } m_pager = std::unique_ptr(new RecollKioPager(o_rclconfig)); m_pager->setParent(this); m_initok = true; return; } // There should be an object counter somewhere to delete the config when done. // Doesn't seem needed in the kio context. RecollProtocol::~RecollProtocol() { kDebug(); } bool RecollProtocol::maybeOpenDb(string &reason) { if (!m_rcldb) { reason = "Internal error: initialization error"; return false; } if (!m_rcldb->isopen() && !m_rcldb->open(Rcl::Db::DbRO)) { reason = "Could not open database in " + o_rclconfig->getDbDir(); return false; } return true; } // This is never called afaik void RecollProtocol::mimetype(const KUrl &url) { kDebug() << url << endl; mimeType("text/html"); finished(); } UrlIngester::UrlIngester(RecollProtocol *p, const KUrl& url) : m_parent(p), m_slashend(false), m_alwaysdir(false), m_retType(UIRET_NONE), m_resnum(0), m_type(UIMT_NONE) { kDebug() << "Url" << url; m_alwaysdir = !url.protocol().compare("recollf"); QString path = url.path(); if (url.host().isEmpty()) { if (path.isEmpty() || !path.compare("/")) { m_type = UIMT_ROOTENTRY; m_retType = UIRET_ROOT; return; } else if (!path.compare("/help.html")) { m_type = UIMT_ROOTENTRY; m_retType = UIRET_HELP; return; } else if (!path.compare("/search.html")) { m_type = UIMT_ROOTENTRY; m_retType = UIRET_SEARCH; // Retrieve the query value for preloading the form m_query.query = url.queryItem("q"); return; } else if (m_parent->isRecollResult(url, &m_resnum, &m_query.query)) { m_type = UIMT_QUERYRESULT; m_query.opt = "l"; m_query.page = 0; } else { // Have to think this is some search string m_type = UIMT_QUERY; m_query.query = url.path(); m_query.opt = "l"; m_query.page = 0; } } else { // Non empty host, url must be something like : // //search/query?q=query¶m=value... kDebug() << "host" << url.host() << "path" << url.path(); if (url.host().compare("search") || url.path().compare("/query")) { return; } m_type = UIMT_QUERY; // Decode the forms' arguments m_query.query = url.queryItem("q"); m_query.opt = url.queryItem("qtp"); if (m_query.opt.isEmpty()) { m_query.opt = "l"; } QString p = url.queryItem("p"); if (p.isEmpty()) { m_query.page = 0; } else { sscanf(p.toAscii(), "%d", &m_query.page); } p = url.queryItem("det"); m_query.isDetReq = !p.isEmpty(); p = url.queryItem("cmd"); if (!p.isEmpty() && !p.compare("pv")) { p = url.queryItem("dn"); if (!p.isEmpty()) { // Preview and no docnum ?? m_resnum = atoi((const char *)p.toUtf8()); // Result in page is 1+ m_resnum--; m_type = UIMT_PREVIEW; } } } if (m_query.query.startsWith("/")) m_query.query.remove(0,1); if (m_query.query.endsWith("/")) { kDebug() << "Ends with /"; m_slashend = true; m_query.query.chop(1); } else { m_slashend = false; } return; } bool RecollProtocol::syncSearch(const QueryDesc &qd) { kDebug(); if (!m_initok || !maybeOpenDb(m_reason)) { string reason = "RecollProtocol::listDir: Init error:" + m_reason; error(KIO::ERR_SLAVE_DEFINED, reason.c_str()); return false; } if (qd.sameQuery(m_query)) { return true; } // doSearch() calls error() if appropriate. return doSearch(qd); } // This is used by the html interface, but also by the directory one // when doing file copies for example. This is the central dispatcher // for requests, it has to know a little about both models. void RecollProtocol::get(const KUrl& url) { kDebug() << url << endl; if (!m_initok || !maybeOpenDb(m_reason)) { string reason = "Recoll: init error: " + m_reason; error(KIO::ERR_SLAVE_DEFINED, reason.c_str()); return; } UrlIngester ingest(this, url); UrlIngester::RootEntryType rettp; QueryDesc qd; int resnum; if (ingest.isRootEntry(&rettp)) { switch(rettp) { case UrlIngester::UIRET_HELP: { QString location = KStandardDirs::locate("data", "kio_recoll/help.html"); redirection(location); } goto out; default: searchPage(); goto out; } } else if (ingest.isResult(&qd, &resnum)) { // Url matched one generated by konqueror/Dolphin out of a // search directory listing: ie: // recoll:/some search string/recollResultxx // // This happens when the user drags/drop the result to another // app, or with the "open-with" right-click. Does not happen // if the entry itself is clicked (the UDS_URL is apparently // used in this case // // Redirect to the result document URL if (!syncSearch(qd)) { return; } Rcl::Doc doc; if (resnum >= 0 && m_source && m_source->getDoc(resnum, doc)) { mimeType(doc.mimetype.c_str()); redirection(KUrl::fromLocalFile((const char *)(doc.url.c_str()+7))); goto out; } } else if (ingest.isPreview(&qd, &resnum)) { if (!syncSearch(qd)) { return; } Rcl::Doc doc; if (resnum >= 0 && m_source && m_source->getDoc(resnum, doc)) { showPreview(doc); goto out; } } else if (ingest.isQuery(&qd)) { #if 0 // Do we need this ? if (host.isEmpty()) { char cpage[20];sprintf(cpage, "%d", page); QString nurl = QString::fromAscii("recoll://search/query?q=") + query + "&qtp=" + opt + "&p=" + cpage; redirection(KUrl(nurl)); goto out; } #endif // htmlDoSearch does the search syncing (needs to know about changes). htmlDoSearch(qd); goto out; } error(KIO::ERR_SLAVE_DEFINED, "Unrecognized URL or internal error"); out: finished(); } // Execute Recoll search, and set the docsource bool RecollProtocol::doSearch(const QueryDesc& qd) { kDebug() << "query" << qd.query << "opt" << qd.opt; m_query = qd; char opt = qd.opt.isEmpty() ? 'l' : qd.opt.toUtf8().at(0); string qs = (const char *)qd.query.toUtf8(); std::shared_ptr sdata; if (opt != 'l') { Rcl::SearchDataClause *clp = 0; if (opt == 'f') { clp = new Rcl::SearchDataClauseFilename(qs); } else { clp = new Rcl::SearchDataClauseSimple(opt == 'o' ? Rcl::SCLT_OR : Rcl::SCLT_AND, qs); } sdata = std::make_shared(Rcl::SCLT_OR, m_stemlang); if (sdata && clp) sdata->addClause(clp); } else { sdata = wasaStringToRcl(o_rclconfig, m_stemlang, qs, m_reason); } if (!sdata) { m_reason = "Internal Error: cant build search"; error(KIO::ERR_SLAVE_DEFINED, m_reason.c_str()); return false; } std::shared_ptrquery(new Rcl::Query(m_rcldb.get())); bool collapsedups; o_rclconfig->getConfParam("kiocollapseduplicates", &collapsedups); query->setCollapseDuplicates(collapsedups); if (!query->setQuery(sdata)) { m_reason = "Query execute failed. Invalid query or syntax error?"; error(KIO::ERR_SLAVE_DEFINED, m_reason.c_str()); return false; } DocSequenceDb *src = new DocSequenceDb(m_rcldb, std::shared_ptr(query), "Query results", sdata); if (src == 0) { error(KIO::ERR_SLAVE_DEFINED, "Can't build result sequence"); return false; } m_source = std::shared_ptr(src); // Reset pager in all cases. Costs nothing, stays at page -1 initially // htmldosearch will fetch the first page if needed. m_pager->setDocSource(m_source); return true; } // Note: KDE_EXPORT is actually needed on Unix when building with // cmake. Says something like __attribute__(visibility(defautl)) // (cmake apparently sets all symbols to not exported) extern "C" {KDE_EXPORT int kdemain(int argc, char **argv);} int kdemain(int argc, char **argv) { #ifdef KDE_VERSION_3 KInstance instance("kio_recoll"); #else KComponentData instance("kio_recoll"); #endif kDebug() << "*** starting kio_recoll " << endl; if (argc != 4) { kDebug() << "Usage: kio_recoll proto dom-socket1 dom-socket2\n" << endl; exit(-1); } RecollProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); kDebug() << "kio_recoll Done" << endl; return 0; } recoll-1.36.1/kde/kioslave/kio_recoll-kde4/htmlif.cpp0000644000175000017500000002103114427373216017327 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include "plaintorich.h" #include "rclconfig.h" #include "rclinit.h" #include "internfile.h" #include "docseqdb.h" #include "wasatorcl.h" #include "rcldb.h" #include "rclquery.h" #include "searchdata.h" #include "hldata.h" #include "pathut.h" #include "readfile.h" #include "smallut.h" #include "kio_recoll.h" using namespace std; using namespace KIO; bool RecollKioPager::append(const string& data) { if (!m_parent) return false; m_parent->data(QByteArray(data.c_str())); return true; } #include string RecollProtocol::makeQueryUrl(int page, bool isdet) { ostringstream str; str << "recoll://search/query?q=" << url_encode((const char*)m_query.query.toUtf8()) << "&qtp=" << (const char*)m_query.opt.toUtf8(); if (page >= 0) str << "&p=" << page; if (isdet) str << "&det=1"; return str.str(); } string RecollKioPager::detailsLink() { string chunk = string("makeQueryUrl(m_parent->m_pager->pageNumber(), true) + "\">" + "(show query)" + ""; return chunk; } static string parformat; const string& RecollKioPager::parFormat() { // Need to escape the % inside the query url string qurl = m_parent->makeQueryUrl(-1, false), escurl; for (string::size_type pos = 0; pos < qurl.length(); pos++) { switch(qurl.at(pos)) { case '%': escurl += "%%"; break; default: escurl += qurl.at(pos); } } ostringstream str; str << "" "%R %S " "Preview  " << "Open " << "%T
" "%M %D   %U  %i
" "%A %K"; return parformat = str.str(); } string RecollKioPager::pageTop() { string pt = "

m_query.query.toUtf8())); pt += "\">New Search"; return pt; // Would be nice to have but doesnt work because the query may be executed // by another kio instance which has no idea of the current page o #if 0 && KDE_IS_VERSION(4,1,0) "    m_query.query.toUtf8())) + "/\">Directory view (you may need to reload the page)" #endif } string RecollKioPager::nextUrl() { int pagenum = pageNumber(); if (pagenum < 0) pagenum = 0; else pagenum++; return m_parent->makeQueryUrl(pagenum); } string RecollKioPager::prevUrl() { int pagenum = pageNumber(); if (pagenum <= 0) pagenum = 0; else pagenum--; return m_parent->makeQueryUrl(pagenum); } static string welcomedata; void RecollProtocol::searchPage() { mimeType("text/html"); if (welcomedata.empty()) { QString location = KStandardDirs::locate("data", "kio_recoll/welcome.html"); string reason; if (location.isEmpty() || !file_to_string((const char *)location.toUtf8(), welcomedata, &reason)) { welcomedata = "Recoll Error" "

Could not locate Recoll welcome.html file: "; welcomedata += reason; welcomedata += "

"; } } string catgq; #if 0 // Catg filtering. A bit complicated to do because of the // stateless thing (one more thing to compare to check if same // query) right now. Would be easy by adding to the query // language, but not too useful in this case, so scrap it for now. list cats; if (o_rclconfig->getMimeCategories(cats) && !cats.empty()) { catgq = "

Filter on types: " "All"; for (list::iterator it = cats.begin(); it != cats.end();it++) { catgq += "\n" + *it ; } } #endif string tmp; map subs; subs['Q'] = (const char *)m_query.query.toUtf8(); subs['C'] = catgq; subs['S'] = ""; pcSubst(welcomedata, tmp, subs); data(tmp.c_str()); } void RecollProtocol::queryDetails() { mimeType("text/html"); QByteArray array; QTextStream os(&array, QIODevice::WriteOnly); os << "" << endl; os << "" << endl; os << "" << "Recoll query details" << "\n" << endl; os << "" << endl; os << "

Query details:

" << endl; os << "

" << m_pager->queryDescription().c_str() <<"

"<< endl; os << "

pageNumber()).c_str() << "\">Return to results" << endl; os << "" << endl; data(array); } class PlainToRichKio : public PlainToRich { public: PlainToRichKio(const string& nm) : m_name(nm) { } virtual string header() { if (m_inputhtml) { return std::string(); } else { return string("" ""). append(m_name). append("

");
        }
    }

    virtual string startMatch(unsigned int)
        {
            return string("");
        }

    virtual string endMatch() 
        {
            return string("");
        }

    const string &m_name;
};

void RecollProtocol::showPreview(const Rcl::Doc& idoc)
{
    FileInterner interner(idoc, o_rclconfig, FileInterner::FIF_forPreview);
    Rcl::Doc fdoc;
    string ipath = idoc.ipath;
    if (!interner.internfile(fdoc, ipath)) {
        error(KIO::ERR_SLAVE_DEFINED, "Cannot convert file to internal format");
        return;
    }
    if (!interner.get_html().empty()) {
        fdoc.text = interner.get_html();
        fdoc.mimetype = "text/html";
    }

    mimeType("text/html");

    string fname =  path_getsimple(fdoc.url).c_str();
    PlainToRichKio ptr(fname);
    ptr.set_inputhtml(!fdoc.mimetype.compare("text/html"));
    list otextlist;
    HighlightData hdata;
    if (m_source)
        m_source->getTerms(hdata);
    ptr.plaintorich(fdoc.text, otextlist, hdata);

    QByteArray array;
    QTextStream os(&array, QIODevice::WriteOnly);
    for (list::iterator it = otextlist.begin(); 
         it != otextlist.end(); it++) {
        os << (*it).c_str();
    }
    os << "" << endl;
    data(array);
}

void RecollProtocol::htmlDoSearch(const QueryDesc& qd)
{
    kDebug() << "q" << qd.query << "option" << qd.opt << "page" << qd.page <<
        "isdet" << qd.isDetReq << endl;
 
    mimeType("text/html");

    if (!syncSearch(qd))
        return;
    // syncSearch/doSearch do the setDocSource when needed
    if (m_pager->pageNumber() < 0) {
        m_pager->resultPageNext();
    }
    if (qd.isDetReq) {
        queryDetails();
        return;
    }

    // Check / adjust page number
    if (qd.page > m_pager->pageNumber()) {
        int npages = qd.page - m_pager->pageNumber();
        for (int i = 0; i < npages; i++)
            m_pager->resultPageNext();
    } else if (qd.page < m_pager->pageNumber()) {
        int npages = m_pager->pageNumber() - qd.page;
        for (int i = 0; i < npages; i++) 
            m_pager->resultPageBack();
    }
    // Display
    m_pager->displayPage(o_rclconfig);
}
recoll-1.36.1/kde/kioslave/kio_recoll-kde4/00README.txt0000644000175000017500000000656414410615043017201 00000000000000Recoll KIO slave
================

An experiment with a recoll KIO slave.

Caveat: I am only currently testing this with a production, but very
recent, version of KDE 4.1, and I don't intend to really support
older versions. The most usable aspects work under KDE 4.0 though. As
a reference, my test system is an up to date (2009-01) Kubuntu 8.10.

Usage
=====

Depending on the protocol name used, the search results will be
returned either as HTML pages (looking quite like a normal Recoll
result list), or as directory entries.

The HTML mode only works with Konqueror, not Dolphin. The directory
mode is available with both browsers, and also application open dialog
(ie Kate).

The HTML mode is much more usable than the directory mode at this point

More detailed help/explanations can be found a document accessible
from the slave:

To try things out, after building and installing, enter "recoll:/" in
a Konqueror URL entry. Depending on the KDE version, this will bring
you either to an HTML search form, or to a directory listing, where
you should READ THE HELP FILE.

Building and installing:
=======================

Only tested with KDE 4.1 and later.

The main Recoll installation shares its prefix with the KIO slave,
which needs to use the KDE one. This means that, if KDE lives in /usr,
Recoll must be configured with --prefix=/usr, not /usr/local. Else
you'll have run-time problems, the slave will not be able to find the
Recoll configuration.

!!*Notice: You cannot share a build directory between recoll and kio_recoll
because they use different configure options for the main lib, but build it
in the same place. The main lib "configure" is run at "cmake" time for
kio_recoll, the build is done at "make" time.


Recipe:
 - Make sure the KDE4 core devel packages and cmake are installed.

 - Extract the Recoll source.

 - IF Recoll is not installed yet: configure recoll with 
   --prefix=/usr (or wherever KDE lives), build and install 
   Recoll.

 - In the Recoll source, go to kde/kioslave/recoll, then build and
   install the kio slave:

mkdir builddir
cd builddir
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DQT_QMAKE_EXECUTABLE=/usr/bin/qmake-qt4
make
sudo make install

 - You should have a look at where "make install" copies things,
   because misconfigured distribution, generating wrong targets, are
   frequent. Especially, you should check that kio_recoll.so is copied
   to the right place, meaning among the output of "kde4-config --path
   module". As an additional check, there should be many other
   kio_[xxx].so in there. Same for the protocol file, check that it's
   not alone in its directory (really, this sounds strange, but, to
   this point, I've seen more systems with broken cmake/KDE configs
   than correct ones).

You need to build/update the index with recollindex, the KIO slave
doesn't deal with indexing for now.


Misc build problems:
===================

KUBUNTU 8.10 (updated to 2008-27-11)
------------------------------------
cmake generates a bad dependency on
      /build/buildd/kde4libs-4.1.2/obj-i486-linux-gnu/lib/libkdecore.so 
inside CMakeFiles/kio_recoll.dir/build.make 

Found no way to fix this. You need to edit the line and replace the
/build/[...]/lib with /usr/lib. This manifests itself with the
following error message:

   make[2]: *** No rule to make target `/build/buildd/kde4libs-4.1.2/obj-i486-linux-gnu/lib/libkdecore.so', needed by `lib/kio_recoll.so'.  Stop.
recoll-1.36.1/kde/kioslave/kio_recoll-kde4/dirif.cpp0000644000175000017500000002522714427373216017154 00000000000000/* Copyright (C) 2008 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/*
 * A lot of code in this file was copied from kio_beagle 0.4.0,
 * which is a GPL program. The authors listed are:
 * Debajyoti Bera 
 * 
 * KDE4 port:
 * Stephan Binner 
 */

#include 

#include 

#if KDE_IS_VERSION(4,1,0)
// Couldn't get listDir() to work with kde 4.0, konqueror keeps
// crashing because of kdirmodel, couldn't find a workaround (not
// saying it's impossible)...

#include 
#include 

#include 
#include 
#include 
#include 

#include "kio_recoll.h"

#include "pathut.h"
#include "docseq.h"
#include "rclconfig.h"

using namespace KIO;

static const QString resultBaseName("recollResult");

// Check if the input URL is of the form that konqueror builds by
// appending one of our result file names to the directory name (which
// is the search string). If it is, extract return the result document
// number. Possibly restart the search if the search string does not
// match the current one
bool RecollProtocol::isRecollResult(const KUrl &url, int *num, QString *q)
{
    *num = -1;
    kDebug() << "url" << url;

    // Basic checks
    if (!url.host().isEmpty() || url.path().isEmpty() || 
        (url.protocol().compare("recoll") && url.protocol().compare("recollf")))
        return false;
    QString path = url.path();
    if (!path.startsWith("/")) 
        return false;

    // Look for the last '/' and check if it is followed by
    // resultBaseName (riiiight...)
    int slashpos = path.lastIndexOf("/");
    if (slashpos == -1 || slashpos == 0 || slashpos == path.length() -1)
        return false;
    slashpos++;
    //kDebug() << "Comparing " << path.mid(slashpos, resultBaseName.length()) <<
    //  "and " << resultBaseName;
    if (path.mid(slashpos, resultBaseName.length()).compare(resultBaseName))
        return false;

    // Extract the result number
    QString snum = path.mid(slashpos + resultBaseName.length());
    sscanf(snum.toAscii(), "%d", num);
    if (*num == -1)
        return false;

    //kDebug() << "URL analysis ok, num:" << *num;

    // We do have something that ressembles a recoll result locator. Check if
    // this matches the current search, else have to run the requested one
    *q = path.mid(1, slashpos-2);
    return true;
}

// Translate rcldoc result into directory entry
static const UDSEntry resultToUDSEntry(const Rcl::Doc& doc, int num)
{
    UDSEntry entry;
    
    KUrl url(doc.url.c_str());
//    kDebug() << doc.url.c_str();

    entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, url.fileName());
    char cnum[30];sprintf(cnum, "%04d", num);
    entry.insert(KIO::UDSEntry::UDS_NAME, resultBaseName + cnum);

    if (!doc.mimetype.compare("application/x-fsdirectory") || 
        !doc.mimetype.compare("inode/directory")) {
        entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory");
        entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
    } else {
        entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, doc.mimetype.c_str());
        entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
    }
    entry.insert(KIO::UDSEntry::UDS_LOCAL_PATH, url.path());
    // For local files, supply the usual file stat information
    struct stat info;
    if (lstat(url.path().toAscii(), &info) >= 0) {
        entry.insert( KIO::UDSEntry::UDS_SIZE, info.st_size);
        entry.insert( KIO::UDSEntry::UDS_ACCESS, info.st_mode);
        entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, info.st_mtime);
        entry.insert( KIO::UDSEntry::UDS_ACCESS_TIME, info.st_atime);
        entry.insert( KIO::UDSEntry::UDS_CREATION_TIME, info.st_ctime);
    }
    entry.insert(KIO::UDSEntry::UDS_TARGET_URL, doc.url.c_str());

    return entry;
}


// From kio_beagle
static void createRootEntry(KIO::UDSEntry& entry)
{
    entry.clear();
    entry.insert( KIO::UDSEntry::UDS_NAME, ".");
    entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
    entry.insert( KIO::UDSEntry::UDS_ACCESS, 0700);
    entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory");
}

// Points to html query screen
static void createGoHomeEntry(KIO::UDSEntry& entry)
{
    entry.clear();
    entry.insert(KIO::UDSEntry::UDS_NAME, "search.html");
    entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, "Recoll search (click me)");
    entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
    entry.insert(KIO::UDSEntry::UDS_TARGET_URL, "recoll:///search.html");
    entry.insert(KIO::UDSEntry::UDS_ACCESS, 0500);
    entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "text/html");
    entry.insert(KIO::UDSEntry::UDS_ICON_NAME, "recoll");
}

// Points to help file
static void createGoHelpEntry(KIO::UDSEntry& entry)
{
    QString location = 
        KStandardDirs::locate("data", "kio_recoll/help.html");
    entry.clear();
    entry.insert(KIO::UDSEntry::UDS_NAME, "help");
    entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, "Recoll help (click me first)");
    entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
    entry.insert(KIO::UDSEntry::UDS_TARGET_URL, QString("file://") +
                 location);
    entry.insert(KIO::UDSEntry::UDS_ACCESS, 0500);
    entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "text/html");
    entry.insert(KIO::UDSEntry::UDS_ICON_NAME, "help");
}

void RecollProtocol::stat(const KUrl & url)
{
    kDebug() << url << endl ;

    UrlIngester ingest(this, url);

    KIO::UDSEntry entry;
    entry.insert(KIO::UDSEntry::UDS_TARGET_URL, url.url());
    UrlIngester::RootEntryType rettp;
    QueryDesc qd;
    int num;
    if (ingest.isRootEntry(&rettp)) {
        switch(rettp) {
        case UrlIngester::UIRET_ROOT:
            createRootEntry(entry);
            break;
        case UrlIngester::UIRET_HELP: 
            createGoHelpEntry(entry);
            break;
        case UrlIngester::UIRET_SEARCH:
            createGoHomeEntry(entry);
            break;
        default: 
            error(ERR_DOES_NOT_EXIST, "");
            break;
        }
    } else if (ingest.isResult(&qd, &num)) {
        if (syncSearch(qd)) {
            Rcl::Doc doc;
            if (num >= 0 && m_source && 
                m_source->getDoc(num, doc)) {
                entry = resultToUDSEntry(doc, num);
            } else {
                error(ERR_DOES_NOT_EXIST, "");
            }
        } else {
            // hopefully syncSearch() set the error?
        }
    } else if (ingest.isQuery(&qd)) {
        // ie "recoll:/some string" or "recoll:/some string/" 
        //
        // We have a problem here. We'd like to let the user enter
        // either form and get an html or a dir contents result,
        // depending on the ending /. Otoh this makes the name space
        // inconsistent, because /toto can't be a file (the html
        // result page) while /toto/ would be a directory ? or can it
        //
        // Another approach would be to use different protocol names
        // to avoid any possibility of mixups
        if (m_alwaysdir || ingest.alwaysDir() || ingest.endSlashQuery()) {
            kDebug() << "Directory type";
            entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
            entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700);
            entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory");
            entry.insert(KIO::UDSEntry::UDS_NAME, qd.query);
            entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, time(0));
            entry.insert( KIO::UDSEntry::UDS_CREATION_TIME, time(0));
        }
    }
    statEntry(entry);
    finished();
}

void RecollProtocol::listDir(const KUrl& url)
{
    kDebug() << url << endl;

    UrlIngester ingest(this, url);
    UrlIngester::RootEntryType rettp;
    QueryDesc qd;

    if (ingest.isRootEntry(&rettp)) {
        switch(rettp) {
        case UrlIngester::UIRET_ROOT:
            {
                kDebug() << "list /" << endl;
                UDSEntryList entries;
                KIO::UDSEntry entry;
                createRootEntry(entry);
                entries.append(entry);
                createGoHomeEntry(entry);
                entries.append(entry);
                createGoHelpEntry(entry);
                entries.append(entry);
                listEntries(entries);
                finished();
            }
            return;
        default:
            error(ERR_CANNOT_ENTER_DIRECTORY, "");
            return;
        }
    } else if (ingest.isQuery(&qd)) {
        // At this point, it seems that when the request is from
        // konqueror autocompletion it comes with a / at the end,
        // which offers an opportunity to not perform it.
        if (ingest.endSlashQuery()) {
            kDebug() << "Ends With /" << endl;
            error(ERR_SLAVE_DEFINED, "Autocompletion search aborted");
            return;
        }
        if (!syncSearch(qd)) {
            // syncSearch did the error thing
            return;
        }
        // Fallthrough to actually listing the directory
    } else {
        kDebug() << "Cant grok input url";
        error(ERR_CANNOT_ENTER_DIRECTORY, "");
        return;
    }

    static int maxentries = -1;
    if (maxentries == -1) {
        if (o_rclconfig)
            o_rclconfig->getConfParam("kio_max_direntries", &maxentries);
        if (maxentries == -1)
            maxentries = 10000;
    }
    static const int pagesize = 200;
    int pagebase = 0;
    while (pagebase < maxentries) {
        std::vector page;
        int pagelen = m_source->getSeqSlice(pagebase, pagesize, page);
        UDSEntry entry;
        if (pagelen < 0) {
            error(ERR_SLAVE_DEFINED, "Internal error");
            listEntry(entry, true);
            break;
        }
        for (int i = 0; i < pagelen; i++) {
            listEntry(resultToUDSEntry(page[i].doc, i), false);
        }
        if (pagelen != pagesize) {
            listEntry(entry, true);
            break;
        }
        pagebase += pagelen;
    }
    finished();
}

#else // <--- KDE 4.1+ 

#include 
#include "kio_recoll.h"
bool RecollProtocol::isRecollResult(const KUrl &, int *, QString *)
{
    return false;
}
#endif 
recoll-1.36.1/kde/kioslave/kio_recoll-kde4/recollf.protocol0000644000175000017500000000025714410615043020545 00000000000000[Protocol]
exec=kio_recoll
protocol=recollf
input=none
output=filesystem
listing=Name,Type, URL
reading=true
defaultMimeType=text/html
Icon=recoll
Class=:local
URIMode=rawuri
recoll-1.36.1/kde/kioslave/kio_recoll-kde4/kio_recoll.h0000644000175000017500000001451614427373216017645 00000000000000#ifndef _RECOLL_H
#define _RECOLL_H
/* Copyright (C) 2005 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include 
using std::string;
#include 

#include 
#include 

#include 
#include 
#include 
#include 

#include "reslistpager.h"

class RecollProtocol;
class RclConfig;
class DocSeq;
namespace Rcl {
class Db;
}

/** Specialize the recoll html pager for the kind of links we use etc. */
class RecollKioPager : public ResListPager {
public:
    RecollKioPager(RclConfig *cnf)
        : ResListPager(cnf), m_parent(0) {}
    void setParent(RecollProtocol *proto) {m_parent = proto;}

    virtual bool append(const string& data);
    virtual bool append(const string& data, int, const Rcl::Doc&)
        {return append(data);}
    virtual string detailsLink();
    virtual const string &parFormat();
    virtual string nextUrl();
    virtual string prevUrl();
    virtual string pageTop();

private:
    RecollProtocol *m_parent;
};

class QueryDesc {
public:
    QueryDesc() : opt("l"), page(0), isDetReq(false) {}
    QString query;
    QString opt;
    int page;
    bool isDetReq;
    bool sameQuery(const QueryDesc& o) const {
        return !opt.compare(o.opt) && !query.compare(o.query);
    }
};

// Our virtual tree is a bit complicated. We need a class to analyse an URL
// and tell what we should do with it
class UrlIngester {
public:
    UrlIngester(RecollProtocol *p, const KUrl& url);
    enum RootEntryType {UIRET_NONE, UIRET_ROOT, UIRET_HELP, UIRET_SEARCH};
    bool isRootEntry(RootEntryType *tp) {
        if (m_type != UIMT_ROOTENTRY) return false;
        *tp = m_retType;
        return true;
    }
    bool isQuery(QueryDesc *q) {
        if (m_type != UIMT_QUERY) return false;
        *q = m_query;
        return true;
    }
    bool isResult(QueryDesc *q, int *num) {
        if (m_type != UIMT_QUERYRESULT) return false;
        *q = m_query;
        *num = m_resnum;
        return true;
    }
    bool isPreview(QueryDesc *q, int *num) {
        if (m_type != UIMT_PREVIEW) return false;
        *q = m_query;
        *num = m_resnum;
        return true;
    }
    bool endSlashQuery() {return m_slashend;}
    bool alwaysDir() {return m_alwaysdir;}

private:
    RecollProtocol *m_parent;
    QueryDesc       m_query;
    bool            m_slashend;
    bool            m_alwaysdir;
    RootEntryType   m_retType;
    int             m_resnum;
    enum MyType {UIMT_NONE, UIMT_ROOTENTRY, UIMT_QUERY, UIMT_QUERYRESULT,
        UIMT_PREVIEW};
    MyType           m_type;
};


/**
 * A KIO slave to execute and display Recoll searches.
 *
 * Things are made a little complicated because KIO slaves can't hope
 * that their internal state will remain consistent with their user
 * application state: slaves die, are restarted, reused, at random
 * between requests. 
 * In our case, this means that any request has to be processed
 * without reference to the last operation performed. Ie, if the
 * search parameters are not those from the last request, the search
 * must be restarted anew. This happens for example with different
 * searches in 2 konqueror screens: typically only one kio_slave will
 * be used.
 * The fact that we check if the search is the same as the last one,
 * to avoid restarting is an optimization, not the base mechanism
 * (contrary to what was initially assumed, and may have left a few
 * crumbs around).
 *
 * We have two modes of operation, one based on html forms and result
 * pages, which can potentially be developped to the full Recoll
 * functionality, and one based on a directory listing model, which
 * will always be more limited, but may be useful in some cases to
 * allow easy copying of files etc. Which one is in use is decided by
 * the form of the URL. 
 */
class RecollProtocol : public KIO::SlaveBase {
public:
    RecollProtocol(const QByteArray &pool, const QByteArray &app );
    virtual ~RecollProtocol();
    virtual void mimetype(const KUrl& url);
    virtual void get(const KUrl& url);
    // The directory mode is not available with KDE 4.0, I could find
    // no way to avoid crashing kdirmodel
#if KDE_IS_VERSION(4,1,0)
    virtual void stat(const KUrl & url);
    virtual void listDir(const KUrl& url);
#endif

    static RclConfig  *o_rclconfig;

    friend class RecollKioPager;
    friend class UrlIngester;

private:
    bool maybeOpenDb(string& reason);
    bool URLToQuery(const KUrl &url, QString& q, QString& opt, int *page=0);
    bool doSearch(const QueryDesc& qd);

    void searchPage();
    void queryDetails();
    string makeQueryUrl(int page, bool isdet = false);
    bool syncSearch(const QueryDesc& qd);
    void htmlDoSearch(const QueryDesc& qd);
    void showPreview(const Rcl::Doc& doc);
    bool isRecollResult(const KUrl &url, int *num, QString* q);

    bool        m_initok;
    std::shared_ptr m_rcldb;
    string      m_reason;
    bool        m_alwaysdir;
    string      m_stemlang; // english by default else env[RECOLL_KIO_STEMLANG]

    // Search state: because of how the KIO slaves are used / reused,
    // we can't be sure that the next request will be for the same
    // search, and we need to check and restart one if the data
    // changes. This is very wasteful but hopefully won't happen too
    // much in actual use. One possible workaround for some scenarios
    // (one slave several konqueror windows) would be to have a small
    // cache of recent searches kept open.
    std::unique_ptr m_pager;
    std::shared_ptr m_source;
    // Note: page here is not used, current page always comes from m_pager.
    QueryDesc      m_query;
};

extern "C" {int kdemain(int, char**);}

#endif // _RECOLL_H
recoll-1.36.1/kde/kioslave/kio_recoll-kde4/CMakeLists.txt0000644000175000017500000000570214463116321020077 00000000000000cmake_minimum_required(VERSION 2.6)

project(kio_recoll)

find_package(KDE4 REQUIRED)

add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=7130
    -DRECOLL_DATADIR=\\"${CMAKE_INSTALL_PREFIX}/share/recoll\\"
    -DLIBDIR=\\"${CMAKE_INSTALL_PREFIX}/lib\\"
    -DHAVE_CONFIG_H
)
set(CMAKE_CXX_FLAGS "-fPIC ${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") 

set(rcltop ${CMAKE_CURRENT_SOURCE_DIR}/../../../)

if (NOT RECOLL_PUBLIC_LIB)
# Execute recoll configuration to create autoconfig.h and version.h and
# generate a PIC lib
execute_process(COMMAND ${rcltop}/configure --disable-static --disable-qtgui --disable-x11mon --disable-python-chm --disable-python-module --prefix=${CMAKE_INSTALL_PREFIX} --mandir=${CMAKE_INSTALL_PREFIX}/share/man
		WORKING_DIRECTORY ${rcltop}
)
add_custom_target(rcllib
                 COMMAND make PicStatic
                 WORKING_DIRECTORY ${rcltop}
)

link_directories(${rcltop}/.libs ${CMAKE_INSTALL_PREFIX}/lib)
endif()

include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES}
  ${rcltop}/common 
  ${rcltop}/internfile 
  ${rcltop}/query 
  ${rcltop}/rcldb 
  ${rcltop}/utils 
)

if (RECOLL_PUBLIC_LIB)
  include_directories (
    ${CMAKE_INSTALL_PREFIX}/include/recoll
  )
  # Check if running inside a build tree, then use the not yet installed lib
  # Check if running inside a build tree, then use the not yet installed lib
  if (EXISTS ${CMAKE_BINARY_DIR}/../../librecoll.la)
    link_directories(${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_BINARY_DIR}/../../.libs)
  elseif(EXISTS ${CMAKE_BINARY_DIR}/../../../librecoll.la)
    link_directories(${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_BINARY_DIR}/../../../.libs)
  elseif(EXISTS ${CMAKE_BINARY_DIR}/../../../../librecoll.la)
    link_directories(${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_BINARY_DIR}/../../../../.libs)
  endif()
endif()

set(kio_recoll_SRCS  kio_recoll.cpp htmlif.cpp dirif.cpp ${rcltop}/qtgui/guiutils.cpp)

CHECK_LIBRARY_EXISTS(dl dlopen "" DLOPEN_IN_LIBDL)
IF(DLOPEN_IN_LIBDL)
	LIST(APPEND EXTRA_LIBS dl)
ENDIF(DLOPEN_IN_LIBDL)
CHECK_LIBRARY_EXISTS(pthread pthread_sigmask "" PTHREAD_IN_LIBPTHREAD)
IF(PTHREAD_IN_LIBPTHREAD)
	LIST(APPEND EXTRA_LIBS pthread)
ENDIF(PTHREAD_IN_LIBPTHREAD)

kde4_add_plugin(kio_recoll ${kio_recoll_SRCS})

if (NOT RECOLL_PUBLIC_LIB)
  add_dependencies(kio_recoll rcllib)
endif()

target_link_libraries(kio_recoll recoll xapian xslt xml2 z ${EXTRA_LIBS} ${KDE4_KIO_LIBS} X11)

install(TARGETS kio_recoll DESTINATION ${PLUGIN_INSTALL_DIR})

IF ("${KDE_VERSION_MAJOR}.${KDE_VERSION_MINOR}" GREATER 4.0)
     install(FILES recoll.protocol recollf.protocol DESTINATION ${SERVICES_INSTALL_DIR})
ELSE ("${KDE_VERSION_MAJOR}.${KDE_VERSION_MINOR}" GREATER 4.0)
   install(FILES recollnolist.protocol DESTINATION ${SERVICES_INSTALL_DIR}
   		 RENAME recoll.protocol)
ENDIF ("${KDE_VERSION_MAJOR}.${KDE_VERSION_MINOR}" GREATER 4.0)

install(FILES data/welcome.html	data/help.html
	      DESTINATION  ${DATA_INSTALL_DIR}/kio_recoll)
recoll-1.36.1/kde/kioslave/kio_recoll/0000755000175000017500000000000014521161751014570 500000000000000recoll-1.36.1/kde/kioslave/kio_recoll/recoll.json0000644000175000017500000000176214427373216016677 00000000000000{
    "KDE-KIO-Protocols": {
        "recoll": {
            "Class": ":local",
            "Icon": "recoll",
            "deleting": true,
            "input": "none",
            "linking": false,
            "listing": [
                "Name",
                "Type",
                "Size",
		"Date"
            ],
            "makedir":false,
            "moving": false,
            "output": "filesystem",
            "protocol": "recoll",
            "reading": true,
            "source": false,
            "writing": false
        },
        "hrecoll": {
            "Class": ":local", 
            "Icon": "recoll",
            "defaultMimetype": "text/html", 
            "deleting": true, 
            "determineMimetypeFromExtension": false, 
            "input": "none", 
            "maxInstances": 20, 
            "maxInstancesPerHost": 5, 
            "output": "filesystem", 
            "protocol": "hrecoll",
            "reading": true, 
            "writing": false
        }
    }
}
recoll-1.36.1/kde/kioslave/kio_recoll/data/0000755000175000017500000000000014521161751015501 500000000000000recoll-1.36.1/kde/kioslave/kio_recoll/data/welcome.html0000644000175000017500000000145614427373216017756 00000000000000

  
  Recoll Search



  

Recoll search

Query type:
Query language
All terms
Any term
File name
Enter search string:

recoll-1.36.1/kde/kioslave/kio_recoll/data/searchable.html0000644000175000017500000000145014410615043020373 00000000000000 Recoll searchable HTML

A Recoll-searchable HTML page

This is a text sample in which links have been inserted for words, such as system installation, which can be searched for in the whole document set by using recoll

Also a little bit of javascript magic can make all words searchable (try double-clicking any word).

recoll-1.36.1/kde/kioslave/kio_recoll/data/help.html0000644000175000017500000000512214427373216017245 00000000000000 Recoll Kio Slave

Recoll KDE KIO worker

Use this module to perform Recoll searches from any program with a KIO interface.

The module used to work in two modes:

  • File manager interface, which presents results as directory entries
  • HTML interface, close to a simplified QT Recoll interface, only usable in Konqueror.

The choice of interface is mostly determined by the protocol name. recoll for the file interface, hrecoll for the HTML one..

You will undoubtedly obtain strange effects from time to time. If you have any remarks or ideas about improving kio_recoll, or observe an interesting and reproducible sequence, please report it.

File manager interface

The path part of an URI with a recoll: scheme (e.g. recoll: some query) is taken as a Recoll query language string and executed. The results are displayed as directory entries.

No search result details (samples, relevance etc.) are available, but this interface allows multiple selections and copies, usage inside any KDE open dialog, etc.

To avoid swamping the interface with too many results, the result count is limited to 10000 by default. You can change this value by setting the kio_max_direntries parameter in your recoll configuration file (typically ~/.recoll/recoll.conf)

HTML interface

Konqueror only.

This works more or less like the Recoll QT GUI, much simplified. The Recoll manual describes the queries that can be performed.

Most pages in the interface should quite self-explanatory.

You normally enter this interface by entering "hrecoll:" or "hrecoll:/" in the Konqueror URL entry, and following the "search" link. You can also directly enter "hrecoll:/search.html".

In most circumstances, entering a link like recoll:/john smith will also yield an HTML result list.

Compared to QT Recoll, the nice point is that you can click or drag/drop the icons to access the results in the standard desktop way.

recoll-1.36.1/kde/kioslave/kio_recoll/kio_recoll.cpp0000644000175000017500000002772114427373216017355 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include "kio_recoll.h" #include "rclconfig.h" #include "rcldb.h" #include "rclinit.h" #include "pathut.h" #include "searchdata.h" #include "rclquery.h" #include "wasatorcl.h" #include "docseqdb.h" #include "smallut.h" using namespace KIO; using namespace std; static inline QString u8s2qs(const std::string& us) { return QString::fromUtf8(us.c_str(), us.size()); } class KIOPluginForMetaData : public QObject { Q_OBJECT Q_PLUGIN_METADATA(IID "org.recoll.kio.slave.recoll" FILE "recoll.json") }; RclConfig *RecollProtocol::o_rclconfig; RecollProtocol::RecollProtocol(const QByteArray& pool, const QByteArray& app) : SlaveBase("recoll", pool, app), m_initok(false), m_alwaysdir(true) { qDebug() << "RecollProtocol::RecollProtocol()"; if (o_rclconfig == 0) { o_rclconfig = recollinit(0, 0, 0, m_reason); if (!o_rclconfig || !o_rclconfig->ok()) { m_reason = string("Configuration problem: ") + m_reason; return; } } if (o_rclconfig->getDbDir().empty()) { // Note: this will have to be replaced by a call to a // configuration building dialog for initial configuration ? // For now we assume that the QT GUI is always used for this. m_reason = "No db directory in configuration ??"; return; } o_rclconfig->getConfParam("kioshowsubdocs", &m_showSubdocs); m_rcldb = std::shared_ptr(new Rcl::Db(o_rclconfig)); if (!m_rcldb) { m_reason = "Could not build database object. (out of memory ?)"; return; } m_pager = std::unique_ptr(new RecollKioPager(o_rclconfig)); m_pager->setParent(this); // Decide if we allow switching between html and file manager // presentation by using an end slash or not. Can also be done dynamically // by switching proto names. const char *cp = getenv("RECOLL_KIO_ALWAYS_DIR"); if (cp) { m_alwaysdir = stringToBool(cp); } else { bool cfval; if (o_rclconfig->getConfParam("kio_always_dir", &cfval)) m_alwaysdir = cfval; } cp = getenv("RECOLL_KIO_STEMLANG"); if (cp) { m_stemlang = cp; } else { m_stemlang = "english"; } m_initok = true; return; } // There should be an object counter somewhere to delete the config when done. // Doesn't seem needed in the kio context. RecollProtocol::~RecollProtocol() { qDebug() << "RecollProtocol::~RecollProtocol()"; } bool RecollProtocol::maybeOpenDb(string& reason) { if (!m_rcldb) { reason = "Internal error: initialization error"; return false; } if (!m_rcldb->isopen() && !m_rcldb->open(Rcl::Db::DbRO)) { reason = "Could not open database in " + o_rclconfig->getDbDir(); return false; } return true; } // This is never called afaik void RecollProtocol::mimetype(const QUrl& url) { qDebug() << "RecollProtocol::mimetype: url: " << url; mimeType("text/html"); finished(); } UrlIngester::UrlIngester(RecollProtocol *p, const QUrl& url) : m_parent(p), m_slashend(false), m_alwaysdir(!url.scheme().compare("recollf")), m_retType(UIRET_NONE), m_resnum(0), m_type(UIMT_NONE) { qDebug() << "UrlIngester::UrlIngester: Url: " << url; QString path = url.path(); if (url.host().isEmpty()) { if (path.isEmpty() || !path.compare("/")) { m_type = UIMT_ROOTENTRY; m_retType = UIRET_ROOT; return; } else if (!path.compare("/help.html")) { m_type = UIMT_ROOTENTRY; m_retType = UIRET_HELP; return; } else if (!path.compare("/search.html")) { m_type = UIMT_ROOTENTRY; m_retType = UIRET_SEARCH; QUrlQuery q(url); // Retrieve the query value for preloading the form m_query.query = q.queryItemValue("q"); return; } else if (m_parent->isRecollResult(url, &m_resnum, &m_query.query)) { m_type = UIMT_QUERYRESULT; m_query.opt = "l"; m_query.page = 0; } else { // Have to think this is some search string m_type = UIMT_QUERY; m_query.query = url.path(); m_query.opt = "l"; m_query.page = 0; } } else { // Non empty host, url must be something like : // //search/query?q=query¶m=value... qDebug() << "UrlIngester::UrlIngester: host " << url.host() << " path " << url.path(); if (url.host().compare("search") || url.path().compare("/query")) { return; } m_type = UIMT_QUERY; // Decode the forms' arguments // Retrieve the query value for preloading the form QUrlQuery q(url); m_query.query = q.queryItemValue("q"); m_query.opt = q.queryItemValue("qtp"); if (m_query.opt.isEmpty()) { m_query.opt = "l"; } QString p = q.queryItemValue("p"); if (p.isEmpty()) { m_query.page = 0; } else { sscanf(p.toUtf8(), "%d", &m_query.page); } p = q.queryItemValue("det"); m_query.isDetReq = !p.isEmpty(); p = q.queryItemValue("cmd"); if (!p.isEmpty() && !p.compare("pv")) { p = q.queryItemValue("dn"); if (!p.isEmpty()) { // Preview and no docnum ?? m_resnum = atoi((const char *)p.toUtf8()); // Result in page is 1+ m_resnum--; m_type = UIMT_PREVIEW; } } } if (m_query.query.startsWith("/")) { m_query.query.remove(0, 1); } if (m_query.query.endsWith("/")) { qDebug() << "UrlIngester::UrlIngester: query Ends with /"; m_slashend = true; m_query.query.chop(1); } else { m_slashend = false; } return; } bool RecollProtocol::syncSearch(const QueryDesc& qd) { qDebug() << "RecollProtocol::syncSearch"; if (!m_initok || !maybeOpenDb(m_reason)) { string reason = "RecollProtocol::listDir: Init error:" + m_reason; error(KIO::ERR_SLAVE_DEFINED, u8s2qs(reason)); return false; } if (qd.sameQuery(m_query)) { return true; } // doSearch() calls error() if appropriate. return doSearch(qd); } // This is used by the html interface, but also by the directory one // when doing file copies for example. This is the central dispatcher // for requests, it has to know a little about both models. void RecollProtocol::get(const QUrl& url) { qDebug() << "RecollProtocol::get: " << url; if (!m_initok || !maybeOpenDb(m_reason)) { string reason = "Recoll: init error: " + m_reason; error(KIO::ERR_SLAVE_DEFINED, u8s2qs(reason)); return; } UrlIngester ingest(this, url); UrlIngester::RootEntryType rettp; QueryDesc qd; int resnum; if (ingest.isRootEntry(&rettp)) { switch (rettp) { case UrlIngester::UIRET_HELP: { QString location = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kio_recoll/help.html"); redirection(QUrl::fromLocalFile(location)); } goto out; default: searchPage(); goto out; } } else if (ingest.isResult(&qd, &resnum)) { // Url matched one generated by konqueror/Dolphin out of a // search directory listing: ie: // recoll:/some search string/recollResultxx // // This happens when the user drags/drop the result to another // app, or with the "open-with" right-click. Does not happen // if the entry itself is clicked (the UDS_URL is apparently // used in this case // // Redirect to the result document URL if (!syncSearch(qd)) { return; } Rcl::Doc doc; if (resnum >= 0 && m_source && m_source->getDoc(resnum, doc)) { mimeType(doc.mimetype.c_str()); redirection(QUrl::fromLocalFile((const char *)(doc.url.c_str() + 7))); goto out; } } else if (ingest.isPreview(&qd, &resnum)) { if (!syncSearch(qd)) { return; } Rcl::Doc doc; if (resnum >= 0 && m_source && m_source->getDoc(resnum, doc)) { showPreview(doc); goto out; } } else if (ingest.isQuery(&qd)) { // htmlDoSearch does the search syncing (needs to know about changes). htmlDoSearch(qd); goto out; } error(KIO::ERR_SLAVE_DEFINED, u8s2qs("Unrecognized URL or internal error")); out: finished(); } // Execute Recoll search, and set the docsource bool RecollProtocol::doSearch(const QueryDesc& qd) { qDebug() << "RecollProtocol::doSearch:query" << qd.query << "opt" << qd.opt; m_query = qd; char opt = qd.opt.isEmpty() ? 'l' : qd.opt.toUtf8().at(0); string qs = (const char *)qd.query.toUtf8(); std::shared_ptr sdata; if (opt != 'l') { Rcl::SearchDataClause *clp = 0; if (opt == 'f') { clp = new Rcl::SearchDataClauseFilename(qs); } else { clp = new Rcl::SearchDataClauseSimple(opt == 'o' ? Rcl::SCLT_OR : Rcl::SCLT_AND, qs); } sdata = std::make_shared(Rcl::SCLT_OR, m_stemlang); if (sdata && clp) { sdata->addClause(clp); } } else { sdata = wasaStringToRcl(o_rclconfig, m_stemlang, qs, m_reason); } if (!sdata) { m_reason = "Internal Error: cant build search"; error(KIO::ERR_SLAVE_DEFINED, u8s2qs(m_reason)); return false; } sdata->setSubSpec(m_showSubdocs ? Rcl::SearchData::SUBDOC_ANY: Rcl::SearchData::SUBDOC_NO); std::shared_ptrquery(new Rcl::Query(m_rcldb.get())); bool collapsedups; o_rclconfig->getConfParam("kiocollapseduplicates", &collapsedups); query->setCollapseDuplicates(collapsedups); if (!query->setQuery(sdata)) { m_reason = "Query execute failed. Invalid query or syntax error?"; error(KIO::ERR_SLAVE_DEFINED, u8s2qs(m_reason)); return false; } DocSequenceDb *src = new DocSequenceDb(m_rcldb, std::shared_ptr(query), "Query results", sdata); if (src == 0) { error(KIO::ERR_SLAVE_DEFINED, u8s2qs("Can't build result sequence")); return false; } m_source = std::shared_ptr(src); // Reset pager in all cases. Costs nothing, stays at page -1 initially // htmldosearch will fetch the first page if needed. m_pager->setDocSource(m_source); return true; } extern "C" { Q_DECL_EXPORT int kdemain(int argc, char **argv) { QCoreApplication app(argc, argv); app.setApplicationName("kio_recoll"); qDebug() << "*** starting kio_recoll "; if (argc != 4) { qDebug() << "Usage: kio_recoll proto dom-socket1 dom-socket2\n"; exit(-1); } RecollProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); qDebug() << "kio_recoll Done"; return 0; } } #include "kio_recoll.moc" recoll-1.36.1/kde/kioslave/kio_recoll/htmlif.cpp0000644000175000017500000002113414427373216016506 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include "plaintorich.h" #include "rclconfig.h" #include "rclinit.h" #include "internfile.h" #include "docseqdb.h" #include "wasatorcl.h" #include "rcldb.h" #include "rclquery.h" #include "searchdata.h" #include "hldata.h" #include "pathut.h" #include "readfile.h" #include "smallut.h" #include "kio_recoll.h" using namespace std; using namespace KIO; bool RecollKioPager::append(const string& data) { if (!m_parent) { return false; } m_parent->data(QByteArray(data.c_str())); return true; } #include string RecollProtocol::makeQueryUrl(int page, bool isdet) { ostringstream str; str << "hrecoll://search/query?q=" << url_encode((const char*)m_query.query.toUtf8()) << "&qtp=" << (const char*)m_query.opt.toUtf8(); if (page >= 0) { str << "&p=" << page; } if (isdet) { str << "&det=1"; } return str.str(); } string RecollKioPager::detailsLink() { string chunk = string("makeQueryUrl(m_parent->m_pager->pageNumber(), true) + "\">" + "(show query)" + ""; return chunk; } static string parformat; const string& RecollKioPager::parFormat() { // Need to escape the % inside the query url string qurl = m_parent->makeQueryUrl(-1, false), escurl; for (string::size_type pos = 0; pos < qurl.length(); pos++) { switch (qurl.at(pos)) { case '%': escurl += "%%"; break; default: escurl += qurl.at(pos); } } ostringstream str; str << "" "%R %S " "Preview  " << "Open " << "%T
" "%M %D   %U  %i
" "%A %K"; return parformat = str.str(); } string RecollKioPager::pageTop() { string pt = "

m_query.query.toUtf8())); pt += "\">New Search"; return pt; // Would be nice to have but doesnt work because the query may be executed // by another kio instance which has no idea of the current page o #if 0 "    m_query.query.toUtf8())) + "/\">Directory view (you may need to reload the page)" #endif } string RecollKioPager::nextUrl() { int pagenum = pageNumber(); if (pagenum < 0) { pagenum = 0; } else { pagenum++; } return m_parent->makeQueryUrl(pagenum); } string RecollKioPager::prevUrl() { int pagenum = pageNumber(); if (pagenum <= 0) { pagenum = 0; } else { pagenum--; } return m_parent->makeQueryUrl(pagenum); } static string welcomedata; void RecollProtocol::searchPage() { mimeType("text/html"); if (welcomedata.empty()) { QString location = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kio_recoll/welcome.html"); string reason; if (location.isEmpty() || !file_to_string((const char *)location.toUtf8(), welcomedata, &reason)) { welcomedata = "Recoll Error" "

Could not locate Recoll welcome.html file: "; welcomedata += reason; welcomedata += "

"; } } string catgq; #if 0 // Catg filtering. A bit complicated to do because of the // stateless thing (one more thing to compare to check if same // query) right now. Would be easy by adding to the query // language, but not too useful in this case, so scrap it for now. list cats; if (o_rclconfig->getMimeCategories(cats) && !cats.empty()) { catgq = "

Filter on types: " "All"; for (list::iterator it = cats.begin(); it != cats.end(); it++) { catgq += "\n" + *it ; } } #endif string tmp; map subs; subs['Q'] = (const char *)m_query.query.toUtf8(); subs['C'] = catgq; subs['S'] = ""; pcSubst(welcomedata, tmp, subs); data(tmp.c_str()); } void RecollProtocol::queryDetails() { mimeType("text/html"); QByteArray array; QTextStream os(&array, QIODevice::WriteOnly); os << "" << "\n"; os << "" << "\n"; os << "" << "Recoll query details" << "\n" << "\n"; os << "" << "\n"; os << "

Query details:

" << "\n"; os << "

" << m_pager->queryDescription().c_str() << "

" << "\n"; os << "

pageNumber()).c_str() << "\">Return to results" << "\n"; os << "" << "\n"; data(array); } class PlainToRichKio : public PlainToRich { public: PlainToRichKio(const string& nm) : m_name(nm) { } virtual string header() override { if (m_inputhtml) { return std::string(); } else { return string("" ""). append(m_name). append("

");
        }
    }

    virtual string startMatch(unsigned int) override {
        return string("");
    }

    virtual string endMatch() override {
        return string("");
    }

    const string& m_name;
};

void RecollProtocol::showPreview(const Rcl::Doc& idoc)
{
    FileInterner interner(idoc, o_rclconfig, FileInterner::FIF_forPreview);
    Rcl::Doc fdoc;
    string ipath = idoc.ipath;
    if (!interner.internfile(fdoc, ipath)) {
        error(KIO::ERR_SLAVE_DEFINED,
              QString::fromUtf8("Cannot convert file to internal format"));
        return;
    }
    if (!interner.get_html().empty()) {
        fdoc.text = interner.get_html();
        fdoc.mimetype = "text/html";
    }

    mimeType("text/html");

    string fname =  path_getsimple(fdoc.url).c_str();
    PlainToRichKio ptr(fname);
    ptr.set_inputhtml(!fdoc.mimetype.compare("text/html"));
    list otextlist;
    HighlightData hdata;
    if (m_source) {
        m_source->getTerms(hdata);
    }
    ptr.plaintorich(fdoc.text, otextlist, hdata);
    //qDebug() << "PREVIEW: got: " << QString::fromUtf8(fdoc.text.c_str());
    QByteArray out;
    for (const auto& chunk : otextlist) {
        out.append(chunk.c_str(), chunk.size());
    }
    out.append("
"); data(out); } void RecollProtocol::htmlDoSearch(const QueryDesc& qd) { qDebug() << "q" << qd.query << "option" << qd.opt << "page" << qd.page << "isdet" << qd.isDetReq << "\n"; mimeType("text/html"); if (!syncSearch(qd)) { return; } // syncSearch/doSearch do the setDocSource when needed if (m_pager->pageNumber() < 0) { m_pager->resultPageNext(); } if (qd.isDetReq) { queryDetails(); return; } // Check / adjust page number if (qd.page > m_pager->pageNumber()) { int npages = qd.page - m_pager->pageNumber(); for (int i = 0; i < npages; i++) { m_pager->resultPageNext(); } } else if (qd.page < m_pager->pageNumber()) { int npages = m_pager->pageNumber() - qd.page; for (int i = 0; i < npages; i++) { m_pager->resultPageBack(); } } // Display m_pager->displayPage(o_rclconfig); } recoll-1.36.1/kde/kioslave/kio_recoll/00README.txt0000644000175000017500000000304014427373216016351 00000000000000Recoll KIO slave ================ Usage ===== Depending on the protocol name used, the search results will be returned either as HTML pages (looking quite like a normal Recoll result list), or as directory entries. The HTML mode only works with Konqueror, not Dolphin. The directory mode is available with both browsers, and also application open dialog (ie Kate). To try things out, after building and installing, enter "recoll:/" in a Konqueror URL entry. Depending on the KDE version, this will bring you either to an HTML search form, or to a directory listing, where you should READ THE HELP FILE. You need to build/update the index with recollindex, the KIO slave doesn't deal with indexing for now. Building and installing: ======================= The main Recoll installation shares its prefix with the KIO slave, which needs to use the KDE one. This means that, if KDE lives in /usr, Recoll must be configured with --prefix=/usr, not /usr/local. Else you'll have run-time problems, the slave will not be able to find the Recoll configuration. Recipe: - Make sure the KF5 core and KIO devel packages and cmake are installed. You probably need the kio-devel and extra-cmake-modules packages. - Extract the Recoll source. - IF Recoll is not installed yet: configure recoll with --prefix=/usr (or wherever KDE lives), build and install Recoll. - In the Recoll source, go to kde/kioslave/kio_recoll, then build and install the kio slave: mkdir builddir cd builddir cmake -DCMAKE_INSTALL_PREFIX=/usr .. make sudo make install recoll-1.36.1/kde/kioslave/kio_recoll/dirif.cpp0000644000175000017500000003135714427373216016330 00000000000000/* Copyright (C) 2008 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * A lot of code in this file was copied from kio_beagle 0.4.0, * which is a GPL program. The authors listed are: * Debajyoti Bera * * KDE4 port: * Stephan Binner */ // Couldn't get listDir() to work with kde 4.0, konqueror keeps // crashing because of kdirmodel, couldn't find a workaround (not // saying it's impossible)... #include #include #include #include #include #include #include "kio_recoll.h" #include "pathut.h" #include "rclconfig.h" using namespace KIO; #if KIO_VERSION < ((5<<16)|(49<<8)|(0)) #define fastInsert insert #endif static const QString resultBaseName("recollResult"); // Check if the input URL is of the form that konqueror builds by // appending one of our result file names to the directory name (which // is the search string). If it is, extract and return the result // document number. Possibly restart the search if the search string // does not match the current one bool RecollProtocol::isRecollResult(const QUrl& url, int *num, QString *q) { *num = -1; qDebug() << "RecollProtocol::isRecollResult: url: " << url; // Basic checks if (!url.host().isEmpty() || url.path().isEmpty() || (url.scheme().compare("recoll") && url.scheme().compare("recollf"))) { qDebug() << "RecollProtocol::isRecollResult: no: url.host " << url.host() << " path " << url.path() << " scheme " << url.scheme(); return false; } QString path = url.path(); qDebug() << "RecollProtocol::isRecollResult: path: " << path; if (!path.startsWith("/")) { return false; } // Look for the last '/' and check if it is followed by // resultBaseName (riiiight...) int slashpos = path.lastIndexOf("/"); if (slashpos == -1 || slashpos == 0 || slashpos == path.length() - 1) { return false; } slashpos++; //qDebug() << "Comparing " << path.mid(slashpos, resultBaseName.length()) << // "and " << resultBaseName; if (path.mid(slashpos, resultBaseName.length()).compare(resultBaseName)) { return false; } // Extract the result number QString snum = path.mid(slashpos + resultBaseName.length()); sscanf(snum.toUtf8(), "%d", num); if (*num == -1) { return false; } //qDebug() << "URL analysis ok, num:" << *num; // We do have something that ressembles a recoll result locator. Check if // this matches the current search, else have to run the requested one *q = path.mid(1, slashpos - 2); return true; } // Translate rcldoc result into directory entry static const UDSEntry resultToUDSEntry(const Rcl::Doc& doc, int num) { UDSEntry entry; QUrl url(doc.url.c_str()); //qDebug() << doc.url.c_str(); /// Filename - as displayed in directory listings etc. /// "." has the usual special meaning of "current directory" /// UDS_NAME must always be set and never be empty, neither contain '/'. /// /// Note that KIO will append the UDS_NAME to the url of their /// parent directory, so all kioslaves must use that naming scheme /// ("url_of_parent/filename" will be the full url of that file). /// To customize the appearance of files without changing the url /// of the items, use UDS_DISPLAY_NAME. // // Use the result number to designate the file in case we are // asked to access it char cnum[30]; sprintf(cnum, "%04d", num); entry.fastInsert(KIO::UDSEntry::UDS_NAME, resultBaseName + cnum); // Display the real file name entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, url.fileName()); /// A local file path if the ioslave display files sitting on the /// local filesystem (but in another hierarchy, e.g. settings:/ or /// remote:/) entry.fastInsert(KIO::UDSEntry::UDS_LOCAL_PATH, url.path()); /// This file is a shortcut or mount, pointing to an /// URL in a different hierarchy /// @since 4.1 // We should probably set this only if the scheme is not 'file' (e.g. // from the web cache). entry.fastInsert(KIO::UDSEntry::UDS_TARGET_URL, doc.url.c_str()); if (!doc.mimetype.compare("application/x-fsdirectory") || !doc.mimetype.compare("inode/directory")) { entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory"); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); } else { entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, doc.mimetype.c_str()); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); } // For local files, supply the usual file stat information struct stat info; if (lstat(url.path().toUtf8(), &info) >= 0) { entry.fastInsert(KIO::UDSEntry::UDS_SIZE, info.st_size); entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, info.st_mode); entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, info.st_mtime); entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, info.st_atime); entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, info.st_ctime); } return entry; } // From kio_beagle static void createRootEntry(KIO::UDSEntry& entry) { entry.clear(); entry.fastInsert(KIO::UDSEntry::UDS_NAME, "."); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0700); entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory"); } // Points to html query screen static void createGoHomeEntry(KIO::UDSEntry& entry) { entry.clear(); entry.fastInsert(KIO::UDSEntry::UDS_NAME, "search.html"); entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, "Recoll search (click me)"); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); entry.fastInsert(KIO::UDSEntry::UDS_TARGET_URL, "recoll:///search.html"); entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0500); entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, "text/html"); entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, "recoll"); } // Points to help file static void createGoHelpEntry(KIO::UDSEntry& entry) { QString location = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kio_recoll/help.html"); entry.clear(); entry.fastInsert(KIO::UDSEntry::UDS_NAME, "help"); entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, "Recoll help (click me first)"); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); entry.fastInsert(KIO::UDSEntry::UDS_TARGET_URL, QString("file://") + location); entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0500); entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, "text/html"); entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, "help"); } // As far as I can see we only ever get this on '/' so why all the code? void RecollProtocol::stat(const QUrl& url) { qDebug() << "RecollProtocol::stat:" << url; UrlIngester ingest(this, url); KIO::UDSEntry entry; // entry.fastInsert(KIO::UDSEntry::UDS_TARGET_URL, url.url()); // entry.fastInsert(KIO::UDSEntry::UDS_URL, url.url()); UrlIngester::RootEntryType rettp; QueryDesc qd; int num; if (ingest.isRootEntry(&rettp)) { qDebug() << "RecollProtocol::stat: root entry"; switch (rettp) { case UrlIngester::UIRET_ROOT: qDebug() << "RecollProtocol::stat: root"; createRootEntry(entry); break; case UrlIngester::UIRET_HELP: qDebug() << "RecollProtocol::stat: root help"; createGoHelpEntry(entry); break; case UrlIngester::UIRET_SEARCH: qDebug() << "RecollProtocol::stat: root search"; createGoHomeEntry(entry); break; default: qDebug() << "RecollProtocol::stat: ??"; error(ERR_DOES_NOT_EXIST, QString()); break; } } else if (ingest.isResult(&qd, &num)) { qDebug() << "RecollProtocol::stat: isresult"; if (syncSearch(qd)) { Rcl::Doc doc; if (num >= 0 && m_source && m_source->getDoc(num, doc)) { entry = resultToUDSEntry(doc, num); } else { error(ERR_DOES_NOT_EXIST, QString()); } } else { // hopefully syncSearch() set the error? } } else if (ingest.isQuery(&qd)) { qDebug() << "RecollProtocol::stat: isquery"; // ie "recoll:/some string" or "recoll:/some string/" // // We have a problem here. We'd like to let the user enter // either form and get an html or a dir contents result, // depending on the ending /. Otoh this makes the name space // inconsistent, because /toto can't be a file (the html // result page) while /toto/ would be a directory ? or can it // // Another approach would be to use different protocol names // to avoid any possibility of mixups if (m_alwaysdir || ingest.alwaysDir() || ingest.endSlashQuery()) { qDebug() << "RecollProtocol::stat: Directory type:"; // Need to check no / in there entry.fastInsert(KIO::UDSEntry::UDS_NAME, qd.query); entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0700); entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time(0)); entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, time(0)); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory"); } } else { qDebug() << "RecollProtocol::stat: none of the above ??"; } statEntry(entry); finished(); } void RecollProtocol::listDir(const QUrl& url) { qDebug() << "RecollProtocol::listDir: url: " << url; UrlIngester ingest(this, url); UrlIngester::RootEntryType rettp; QueryDesc qd; if (ingest.isRootEntry(&rettp)) { switch (rettp) { case UrlIngester::UIRET_ROOT: { qDebug() << "RecollProtocol::listDir:list /"; UDSEntryList entries; KIO::UDSEntry entry; createRootEntry(entry); entries.append(entry); if (!m_alwaysdir) { createGoHomeEntry(entry); entries.append(entry); } createGoHelpEntry(entry); entries.append(entry); listEntries(entries); finished(); } return; default: error(ERR_CANNOT_ENTER_DIRECTORY, QString()); return; } } else if (ingest.isQuery(&qd)) { // At this point, it seems that when the request is from // konqueror autocompletion it comes with a / at the end, // which offers an opportunity to not perform it. if (ingest.endSlashQuery()) { qDebug() << "RecollProtocol::listDir: Ends With /"; error(ERR_SLAVE_DEFINED, QString::fromUtf8("Autocompletion search aborted")); return; } if (!syncSearch(qd)) { // syncSearch did the error thing return; } // Fallthrough to actually listing the directory } else { qDebug() << "RecollProtocol::listDir: Cant grok input url"; error(ERR_CANNOT_ENTER_DIRECTORY, QString()); return; } static int maxentries = -1; if (maxentries == -1) { if (o_rclconfig) { o_rclconfig->getConfParam("kio_max_direntries", &maxentries); } if (maxentries == -1) { maxentries = 10000; } } static const int pagesize = 200; int pagebase = 0; while (pagebase < maxentries) { std::vector page; int pagelen = m_source->getSeqSlice(pagebase, pagesize, page); UDSEntry entry; if (pagelen < 0) { error(ERR_SLAVE_DEFINED, QString::fromUtf8("Internal error")); break; } UDSEntryList entries; for (int i = 0; i < pagelen; i++) { entries.push_back(resultToUDSEntry(page[i].doc, i)); } listEntries(entries); if (pagelen != pagesize) { break; } pagebase += pagelen; } finished(); } recoll-1.36.1/kde/kioslave/kio_recoll/kio_recoll.h0000644000175000017500000001542214427373216017015 00000000000000#ifndef _RECOLL_H #define _RECOLL_H /* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include "reslistpager.h" class RecollProtocol; class RclConfig; class DocSeq; namespace Rcl { class Db; } /** Specialize the recoll html pager for the kind of links we use etc. */ class RecollKioPager : public ResListPager { public: RecollKioPager(RclConfig *cnf) : ResListPager(cnf), m_parent(0) {} void setParent(RecollProtocol *proto) { m_parent = proto; } virtual bool append(const std::string& data) override; virtual bool append(const std::string& data, int, const Rcl::Doc&) override { return append(data); } virtual std::string detailsLink() override; virtual const std::string& parFormat() override; virtual std::string nextUrl() override; virtual std::string prevUrl() override; virtual std::string pageTop() override; private: RecollProtocol *m_parent; }; class QueryDesc { public: QueryDesc() : opt("l"), page(0), isDetReq(false) {} QString query; QString opt; int page; bool isDetReq; bool sameQuery(const QueryDesc& o) const { return !opt.compare(o.opt) && !query.compare(o.query); } }; // Our virtual tree is a bit complicated. We need a class to analyse an URL // and tell what we should do with it class UrlIngester { public: UrlIngester(RecollProtocol *p, const QUrl& url); enum RootEntryType {UIRET_NONE, UIRET_ROOT, UIRET_HELP, UIRET_SEARCH}; bool isRootEntry(RootEntryType *tp) { if (m_type != UIMT_ROOTENTRY) { return false; } *tp = m_retType; return true; } bool isQuery(QueryDesc *q) { if (m_type != UIMT_QUERY) { return false; } *q = m_query; return true; } bool isResult(QueryDesc *q, int *num) { if (m_type != UIMT_QUERYRESULT) { return false; } *q = m_query; *num = m_resnum; return true; } bool isPreview(QueryDesc *q, int *num) { if (m_type != UIMT_PREVIEW) { return false; } *q = m_query; *num = m_resnum; return true; } bool endSlashQuery() { return m_slashend; } bool alwaysDir() { return m_alwaysdir; } private: RecollProtocol *m_parent; QueryDesc m_query; bool m_slashend; bool m_alwaysdir; RootEntryType m_retType; int m_resnum; enum MyType {UIMT_NONE, UIMT_ROOTENTRY, UIMT_QUERY, UIMT_QUERYRESULT, UIMT_PREVIEW }; MyType m_type; }; /** * A KIO slave to execute and display Recoll searches. * * Things are made a little complicated because KIO slaves can't hope * that their internal state will remain consistent with their user * application state: slaves die, are restarted, reused, at random * between requests. * In our case, this means that any request has to be processed * without reference to the last operation performed. Ie, if the * search parameters are not those from the last request, the search * must be restarted anew. This happens for example with different * searches in 2 konqueror screens: typically only one kio_slave will * be used. * The fact that we check if the search is the same as the last one, * to avoid restarting is an optimization, not the base mechanism * (contrary to what was initially assumed, and may have left a few * crumbs around). * * We have two modes of operation, one based on html forms and result * pages, which can potentially be developped to the full Recoll * functionality, and one based on a directory listing model, which * will always be more limited, but may be useful in some cases to * allow easy copying of files etc. Which one is in use is decided by * the form of the URL. */ class RecollProtocol : public KIO::SlaveBase { public: RecollProtocol(const QByteArray& pool, const QByteArray& app); virtual ~RecollProtocol(); virtual void mimetype(const QUrl& url) override; virtual void get(const QUrl& url) override; // The directory mode is not available with KDE 4.0, I could find // no way to avoid crashing kdirmodel virtual void stat(const QUrl& url) override; virtual void listDir(const QUrl& url) override; static RclConfig *o_rclconfig; friend class RecollKioPager; friend class UrlIngester; private: bool maybeOpenDb(std::string& reason); bool URLToQuery(const QUrl& url, QString& q, QString& opt, int *page = 0); bool doSearch(const QueryDesc& qd); void searchPage(); void queryDetails(); std::string makeQueryUrl(int page, bool isdet = false); bool syncSearch(const QueryDesc& qd); void htmlDoSearch(const QueryDesc& qd); void showPreview(const Rcl::Doc& doc); bool isRecollResult(const QUrl& url, int *num, QString* q); bool m_initok; std::shared_ptr m_rcldb; std::string m_reason; bool m_alwaysdir; // english by default else env[RECOLL_KIO_STEMLANG] std::string m_stemlang; // Search state: because of how the KIO slaves are used / reused, // we can't be sure that the next request will be for the same // search, and we need to check and restart one if the data // changes. This is very wasteful but hopefully won't happen too // much in actual use. One possible workaround for some scenarios // (one slave several konqueror windows) would be to have a small // cache of recent searches kept open. std::unique_ptr m_pager; std::shared_ptr m_source; // Note: page here is not used, current page always comes from m_pager. QueryDesc m_query; // Display results from embedded documents. The KIO does not know what to do // with them. This can be disabled from recoll.conf with kio_showsubdocs = 0 bool m_showSubdocs{true}; }; extern "C" { __attribute__((visibility("default"))) int kdemain(int argc, char **argv); } #endif // _RECOLL_H recoll-1.36.1/kde/kioslave/kio_recoll/CMakeLists.txt0000644000175000017500000000527114427373216017263 00000000000000cmake_minimum_required(VERSION 3.16.0) project(kio_recoll) include(FeatureSummary) set(QT_MIN_VERSION 5.2.0) set(KF5_MIN_VERSION 5.0.0) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Network Widgets) find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) # CoreAddons? find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS KIO) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) ## Recoll stuff set(rcltop ${CMAKE_CURRENT_SOURCE_DIR}/../../../) if (NOT RECOLL_PUBLIC_LIB) # Execute recoll configuration to create autoconfig.h and version.h and # generate a PIC lib execute_process(COMMAND ${rcltop}/configure --disable-static --disable-qtgui --disable-x11mon --disable-python-chm --disable-python-module --prefix=${CMAKE_INSTALL_PREFIX} --mandir=${CMAKE_INSTALL_PREFIX}/share/man WORKING_DIRECTORY ${rcltop} ) add_custom_target(rcllib COMMAND make -j 3 PicStatic WORKING_DIRECTORY ${rcltop} ) link_directories(${rcltop}/.libs ${CMAKE_INSTALL_PREFIX}/lib) endif() include_directories ( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${rcltop}/common ${rcltop}/internfile ${rcltop}/query ${rcltop}/rcldb ${rcltop}/utils ) if (RECOLL_PUBLIC_LIB) include_directories ( ${CMAKE_INSTALL_PREFIX}/include/recoll ) # Check if running inside a build tree, then use the not yet installed lib if (EXISTS ${CMAKE_BINARY_DIR}/../../librecoll.la) link_directories(${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_BINARY_DIR}/../../.libs) elseif(EXISTS ${CMAKE_BINARY_DIR}/../../../librecoll.la) link_directories(${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_BINARY_DIR}/../../../.libs) elseif(EXISTS ${CMAKE_BINARY_DIR}/../../../../librecoll.la) link_directories(${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_BINARY_DIR}/../../../../.libs) endif() endif() add_definitions( -DRECOLL_DATADIR="${CMAKE_INSTALL_PREFIX}/share/recoll" -DLIBDIR="${CMAKE_INSTALL_PREFIX}/lib" -DHAVE_CONFIG_H ) set(kio_recoll_SRCS kio_recoll.cpp htmlif.cpp dirif.cpp ${rcltop}/qtgui/guiutils.cpp) add_library(kio_recoll MODULE ${kio_recoll_SRCS}) if (NOT RECOLL_PUBLIC_LIB) add_dependencies(kio_recoll rcllib) endif() target_link_libraries(kio_recoll recoll xapian xslt xml2 KF5::KIOCore X11 dl z pthread ) install(FILES data/welcome.html data/help.html DESTINATION ${DATA_INSTALL_DIR}/kio_recoll) set_target_properties(kio_recoll PROPERTIES OUTPUT_NAME "kio_recoll") # This is not set on ubuntu focal set(QT_MAJOR_VERSION 5) install(TARGETS kio_recoll DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf${QT_MAJOR_VERSION}/kio) recoll-1.36.1/COPYING0000644000175000017500000004312114410615043011035 00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. recoll-1.36.1/common/0000755000175000017500000000000014521161750011355 500000000000000recoll-1.36.1/common/plaintorich.h0000644000175000017500000000666114427373216014002 00000000000000/* Copyright (C) 2004-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _PLAINTORICH_H_INCLUDED_ #define _PLAINTORICH_H_INCLUDED_ #include #include struct HighlightData; /** * A class for highlighting search results. Overridable methods allow * for different styles. We can handle plain text or html input. In the latter * case, we may fail to highligt term groups if they are mixed with HTML * tags (ex: firstterm 2ndterm). */ class PlainToRich { public: PlainToRich() {} virtual ~PlainToRich() {} PlainToRich(const PlainToRich&) = delete; PlainToRich& operator=(const PlainToRich&) = delete; void set_inputhtml(bool v) { m_inputhtml = v; } void set_activatelinks(bool v) { m_activatelinks = v; } /** * Transform plain text for highlighting search terms, ie in the * preview window or result list entries. * * The actual tags used for highlighting and anchoring are * determined by deriving from this class which handles the searching for * terms and groups, but there is an assumption that the output will be * html-like: we escape characters like < or & * * Finding the search terms is relatively complicated because of * phrase/near searches, which need group highlights. As a matter * of simplification, we handle "phrase" as "near", not filtering * on word order. * * @param in raw text out of internfile. * @param out rich text output, divided in chunks (to help our caller * avoid inserting half tags into textedit which doesnt like it) * @param in hdata terms and groups to be highlighted. See utils/hldata.h * @param chunksize max size of chunks in output list */ virtual bool plaintorich(const std::string &in, std::list &out, const HighlightData& hdata, int chunksize = 50000 ); /* Overridable output methods for headers, highlighting and marking tags */ virtual std::string header() { return std::string(); } /** Return match prefix (e.g.:
). @param groupidx the index into hdata.groups */ virtual std::string startMatch(unsigned int) { return std::string(); } /** Return data for end of match area (e.g.:
). */ virtual std::string endMatch() { return std::string(); } virtual std::string startChunk() { return std::string(); } protected: bool m_inputhtml{false}; // Use
to break plain text lines (else caller has used a
 tag)
    bool m_eolbr{false}; 
    const HighlightData *m_hdata{0};
    bool m_activatelinks{false};
};

#endif /* _PLAINTORICH_H_INCLUDED_ */
recoll-1.36.1/common/rclconfig.cpp0000644000175000017500000021120114516704066013753 00000000000000/* Copyright (C) 2004-2022 J.F.Dockes 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "autoconfig.h"

#include 
#include 
#ifndef _WIN32
#include 
#include 
#else
#include 
#include "wincodepages.h"
#include "safeunistd.h"
#endif
#include 
#ifdef __FreeBSD__
#include 
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "cstr.h"
#include "pathut.h"
#include "rclutil.h"
#include "rclconfig.h"
#include "conftree.h"
#include "log.h"
#include "smallut.h"
#include "readfile.h"
#include "fstreewalk.h"
#include "cpuconf.h"
#include "execmd.h"
#include "md5.h"
#include "idxdiags.h"
#include "unacpp.h"

using namespace std;

// Naming the directory for platform-specific default config files, overriding the top-level ones
// E.g. /usr/share/recoll/examples/windows
#ifdef _WIN32
static const string confsysdir{"windows"};
#elif defined(__APPLE__)
static const string confsysdir{"macos"};
#else
static const string confsysdir;
#endif

// This is only used for testing the lower-casing code on linux
//#define NOCASE_PTRANS
#ifdef NOCASE_PTRANS
#warning NOCASE_PTRANS IS SET
#endif

#if defined(_WIN32) || defined(NOCASE_PTRANS)
// The confsimple copies can't print themselves out: no conflines. Need to work around this one day.
// For now: use a ConfSimple::sortwalk() callback. While we're at it, lowercase the values.
ConfSimple::WalkerCode varprintlower(void *vstr, const std::string& nm, const std::string& val)
{
    std::string *str = (std::string *)vstr;
    if (nm.empty()) {
        *str += std::string("[") + unactolower(val) + "]\n";
    } else {
        *str += unactolower(nm) + " = " + val + "\n";
    }
    return ConfSimple::WALK_CONTINUE;
}
#endif // _WIN32

// Things for suffix comparison. We define a string class and string 
// comparison with suffix-only sensitivity
class SfString {
public:
    SfString(const string& s) : m_str(s) {}
    bool operator==(const SfString& s2) const {
        string::const_reverse_iterator r1 = m_str.rbegin(), re1 = m_str.rend(),
            r2 = s2.m_str.rbegin(), re2 = s2.m_str.rend();
        while (r1 != re1 && r2 != re2) {
            if (*r1 != *r2) {
                return 0;
            }
            ++r1; ++r2;
        }
        return 1;
    }
    string m_str;
};

class SuffCmp {
public:
    int operator()(const SfString& s1, const SfString& s2) const {
        //cout << "Comparing " << s1.m_str << " and " << s2.m_str << "\n";
        string::const_reverse_iterator 
            r1 = s1.m_str.rbegin(), re1 = s1.m_str.rend(),
            r2 = s2.m_str.rbegin(), re2 = s2.m_str.rend();
        while (r1 != re1 && r2 != re2) {
            if (*r1 != *r2) {
                return *r1 < *r2 ? 1 : 0;
            }
            ++r1; ++r2;
        }
        return 0;
    }
};

typedef multiset SuffixStore;

// Static, logically const, RclConfig members or module static
// variables are initialized once from the first object build during
// process initialization.

// We default to a case- and diacritics-less index for now
bool o_index_stripchars = true;
// Default to storing the text contents for generating snippets. This
// is only an approximate 10% bigger index and produces nicer
// snippets.
bool o_index_storedoctext = true;

bool o_uptodate_test_use_mtime = false;

bool o_expand_phrases = false;
// We build this once. Used to ensure that the suffix used for a temp
// file of a given MIME type is the FIRST one from the mimemap config
// file. Previously it was the first in alphabetic (map) order, with
// sometimes strange results.
static unordered_map mime_suffixes;

// Cache parameter string values for params which need computation and which can change with the
// keydir. Minimize work by using the keydirgen and a saved string to avoid unneeded recomputations:
// keydirgen is incremented in RclConfig with each setKeyDir(). We compare our saved value with the
// current one. If it did not change no get() is needed. If it did change, but the resulting param
// get() string value is identical, no recomputation is needed.
class ParamStale {
public:
    ParamStale() {}
    ParamStale(RclConfig *rconf, const std::string& nm)
        : parent(rconf), paramnames(std::vector(1, nm)), savedvalues(1) {
    }
    ParamStale(RclConfig *rconf, const std::vector& nms)
        : parent(rconf), paramnames(nms), savedvalues(nms.size()) {
    }

    void init(ConfNull *cnf) {
        conffile = cnf;
        active = false;
        if (conffile) {
            for (auto& nm : paramnames) {
                if (conffile->hasNameAnywhere(nm)) {
                    active = true;
                    break;
                }
            }
        }
        savedkeydirgen = -1;
    }

    const string& getvalue(unsigned int i = 0) const {
        if (i < savedvalues.size()) {
            return savedvalues[i];
        } else {
            static std::string nll;
            return nll;
        }
    }
    // definition needs Internal class: later
    bool needrecompute();

private:
    // The config we belong to. 
    RclConfig *parent{0};
    // The configuration file we search for values. This is a borrowed
    // pointer belonging to the parent, we do not manage it.
    ConfNull  *conffile{0};
    std::vector    paramnames;
    std::vector    savedvalues;
    // Check at init if the configuration defines our vars at all. No
    // further processing is needed if it does not.
    bool      active{false}; 
    int       savedkeydirgen{-1};
};

class RclConfig::Internal {
public:
    Internal(RclConfig *p)
        : m_parent(p),
          m_oldstpsuffstate(p, "recoll_noindex"),
          m_stpsuffstate(p, {"noContentSuffixes", "noContentSuffixes+", "noContentSuffixes-"}),
          m_skpnstate(p, {"skippedNames", "skippedNames+", "skippedNames-"}),
          m_onlnstate(p, "onlyNames"),
          m_rmtstate(p, "indexedmimetypes"),
          m_xmtstate(p, "excludedmimetypes"),
          m_mdrstate(p, "metadatacmds") {}

    RclConfig *m_parent;
    int m_ok;
    // Original current working directory. Set once at init before we do any
    // chdir'ing and used for converting user args to absolute paths.
    static std::string o_origcwd;
    std::string m_reason;    // Explanation for bad state
    std::string m_confdir;   // User directory where the customized files are stored
    // Normally same as confdir. Set to store all bulk data elsewhere.
    // Provides defaults top location for dbdir, webcachedir,
    // mboxcachedir, aspellDictDir, which can still be used to
    // override.
    std::string m_cachedir;  
    std::string m_datadir;   // Example: /usr/local/share/recoll
    std::string m_keydir;    // Current directory used for parameter fetches.
    int    m_keydirgen; // To help with knowing when to update computed data.

    std::vector m_cdirs; // directory stack for the confstacks

    std::map  m_fldtotraits; // Field to field params
    std::map  m_aliastocanon;
    std::map  m_aliastoqcanon;
    std::set m_storedFields;
    std::map  m_xattrtofld;

    unsigned int m_maxsufflen;

    ParamStale   m_oldstpsuffstate; // Values from user mimemap, now obsolete
    ParamStale   m_stpsuffstate;
    std::vector m_stopsuffvec;
    // skippedNames state 
    ParamStale   m_skpnstate;
    std::vector m_skpnlist;
    // onlyNames state 
    ParamStale   m_onlnstate;
    std::vector m_onlnlist;

    // Parameters auto-fetched on setkeydir
    std::string m_defcharset;
    static std::string o_localecharset;
    // Limiting set of mime types to be processed. Normally empty.
    ParamStale    m_rmtstate;
    std::unordered_set   m_restrictMTypes; 
    // Exclusion set of mime types. Normally empty
    ParamStale    m_xmtstate;
    std::unordered_set   m_excludeMTypes; 
    // Metadata-gathering external commands (e.g. used to reap tagging info: "tmsu tags %f")
    ParamStale    m_mdrstate;
    std::vector m_mdreapers;

    std::vector > m_thrConf;

    //////////////////
    // Members needing explicit processing when copying 
    std::unique_ptr> m_conf;   // Parsed configuration files
    std::unique_ptr> mimemap;  // The files don't change with keydir, 
    std::unique_ptr> mimeconf; // but their content may depend on it.
    std::unique_ptr> mimeview; // 
    std::unique_ptr> m_fields;
    std::unique_ptr            m_ptrans; // Paths translations
#if defined(_WIN32) || defined(NOCASE_PTRANS)
    std::unique_ptr m_lptrans; // lowercased paths translations
    bool m_lptrans_stale{true};
#endif
    std::unique_ptr m_stopsuffixes;
    ///////////////////

    /** Create initial user configuration */
    bool initUserConfig();
    /** Init all ParamStale members */
    void initParamStale(ConfNull *cnf, ConfNull *mimemap);
    /** Copy from other */
    void initFrom(const RclConfig& r);
    /** Init pointers to 0 */
    void zeroMe();
    bool readFieldsConfig(const std::string& errloc);
};

string RclConfig::Internal::o_localecharset; 
string RclConfig::Internal::o_origcwd; 

bool ParamStale::needrecompute()
{
    LOGDEB1("ParamStale:: needrecompute. parent gen " << parent->m->m_keydirgen <<
            " mine " << savedkeydirgen << "\n");

    if (!conffile) {
        LOGDEB("ParamStale::needrecompute: conffile not set\n");
        return false;
    }

    bool needrecomp = false;
    if (active && parent->m->m_keydirgen != savedkeydirgen) {
        savedkeydirgen = parent->m->m_keydirgen;
        for (unsigned int i = 0; i < paramnames.size(); i++) {
            string newvalue;
            conffile->get(paramnames[i], newvalue, parent->m->m_keydir);
            LOGDEB1("ParamStale::needrecompute: " << paramnames[i] << " -> " <<
                    newvalue << " keydir " << parent->m->m_keydir << "\n");
            if (newvalue.compare(savedvalues[i])) {
                savedvalues[i] = newvalue;
                needrecomp = true;
            }
        }
    }
    return needrecomp;
}


static const char blurb0[] = 
    "# The system-wide configuration files for recoll are located in:\n"
    "#   ";
static const char blurb1[] = 
    "\n"
    "# The default configuration files are commented, you should take a look\n"
    "# at them for an explanation of what can be set (you could also take a look\n"
    "# at the manual instead).\n"
    "# Values set in this file will override the system-wide values for the file\n"
    "# with the same name in the central directory. The syntax for setting\n"
    "# values is identical.\n"
    ;

// Use uni2ascii -a K to generate these from the utf-8 strings
// Swedish and Danish. 
static const char swedish_ex[] = "unac_except_trans = \303\244\303\244 \303\204\303\244 \303\266\303\266 \303\226\303\266 \303\274\303\274 \303\234\303\274 \303\237ss \305\223oe \305\222oe \303\246ae \303\206ae \357\254\201fi \357\254\202fl \303\245\303\245 \303\205\303\245";
// German:
static const char german_ex[] = "unac_except_trans = \303\244\303\244 \303\204\303\244 \303\266\303\266 \303\226\303\266 \303\274\303\274 \303\234\303\274 \303\237ss \305\223oe \305\222oe \303\246ae \303\206ae \357\254\201fi \357\254\202fl";

static std::array configfiles{
    "recoll.conf", "mimemap", "mimeconf", "mimeview", "fields"};

// Create initial user config by creating commented empty files
bool RclConfig::Internal::initUserConfig()
{
    // Explanatory text
    std::string blurb = blurb0 + path_cat(m_datadir, "examples") + blurb1;

    // Use protective 700 mode to create the top configuration
    // directory: documents can be reconstructed from index data.
    if (!path_exists(m_confdir) && !path_makepath(m_confdir, 0700)) {
        m_reason += string("mkdir(") + m_confdir + ") failed: "+ strerror(errno);
        return false;
    }
    string lang = localelang();
    for (const auto& cnff : configfiles) {
        string dst = path_cat(m_confdir, string(cnff));
        if (!path_exists(dst)) {
            fstream output;
            if (path_streamopen(dst, ios::out, output)) {
                output << blurb << "\n";
                if (!strcmp(cnff, "recoll.conf")) {
                    // Add improved unac_except_trans for some languages
                    if (lang == "se" || lang == "dk" || lang == "no" || lang == "fi") {
                        output << swedish_ex << "\n";
                    } else if (lang == "de") {
                        output << german_ex << "\n";
                    }
                }
            } else {
                m_reason += string("open ") + dst + ": " + strerror(errno);
                return false;
            }
        }
    }
    return true;
}

void RclConfig::Internal::zeroMe() {
    m_ok = false; 
    m_keydirgen = 0;
    m_maxsufflen = 0;
    initParamStale(0, 0);
}

void RclConfig::Internal::initFrom(const RclConfig& r)
{
    zeroMe();

    // Copyable fields
    m_ok = r.m->m_ok;
    if (!m_ok)
        return;
    m_reason = r.m->m_reason;
    m_confdir = r.m->m_confdir;
    m_cachedir = r.m->m_cachedir;
    m_datadir = r.m->m_datadir;
    m_keydir = r.m->m_keydir;
    m_keydirgen = r.m->m_keydirgen;
    m_cdirs = r.m->m_cdirs;
    m_fldtotraits = r.m->m_fldtotraits;
    m_aliastocanon = r.m->m_aliastocanon;
    m_aliastoqcanon = r.m->m_aliastoqcanon;
    m_storedFields = r.m->m_storedFields;
    m_xattrtofld = r.m->m_xattrtofld;
    m_maxsufflen = r.m->m_maxsufflen;
    m_stopsuffvec = r.m->m_stopsuffvec;
    m_skpnlist = r.m->m_skpnlist;
    m_onlnlist = r.m->m_onlnlist;
    m_defcharset = r.m->m_defcharset;
    m_restrictMTypes  = r.m->m_restrictMTypes;
    m_excludeMTypes = r.m->m_excludeMTypes;
    m_mdreapers = r.m->m_mdreapers;
    m_thrConf = r.m->m_thrConf;

    // Special treatment
    if (r.m->m_conf)
        m_conf = std::make_unique>(*(r.m->m_conf));
    if (r.m->mimemap)
        mimemap = std::make_unique>(*(r.m->mimemap));
    if (r.m->mimeconf)
        mimeconf = std::make_unique>(*(r.m->mimeconf));
    if (r.m->mimeview)
        mimeview = std::make_unique>(*(r.m->mimeview));
    if (r.m->m_fields)
        m_fields = std::make_unique>(*(r.m->m_fields));
    if (r.m->m_ptrans)
        m_ptrans = std::make_unique(*(r.m->m_ptrans));
#if defined(_WIN32) || defined(NOCASE_PTRANS)
    m_lptrans_stale = true;
#endif
    if (r.m->m_stopsuffixes)
        m_stopsuffixes = std::make_unique(*(r.m->m_stopsuffixes));
    initParamStale(m_conf.get(), mimemap.get());
}

void RclConfig::Internal::initParamStale(ConfNull *cnf, ConfNull *mimemap)
{
    m_oldstpsuffstate.init(mimemap);
    m_stpsuffstate.init(cnf);
    m_skpnstate.init(cnf);
    m_onlnstate.init(cnf);
    m_rmtstate.init(cnf);
    m_xmtstate.init(cnf);
    m_mdrstate.init(cnf);
}


RclConfig::RclConfig(const string *argcnf)
{
    m = std::make_unique(this);
    m->zeroMe();

    if (m->o_origcwd.empty()) {
        char buf[MAXPATHLEN];
        if (getcwd(buf, MAXPATHLEN)) {
            m->o_origcwd = string(buf);
        } else {
            std::cerr << "recollxx: can't retrieve current working "
                "directory: relative path translations will fail\n";
        }
    }

    // Compute our data dir name, typically /usr/local/share/recoll
    m->m_datadir = path_pkgdatadir();
    // We only do the automatic configuration creation thing for the default
    // config dir, not if it was specified through -c or RECOLL_CONFDIR
    bool autoconfdir = false;

    // Command line config name overrides environment
    if (argcnf && !argcnf->empty()) {
        m->m_confdir = path_absolute(*argcnf);
        if (m->m_confdir.empty()) {
            m->m_reason = string("Cant turn [") + *argcnf + "] into absolute path";
            return;
        }
    } else {
        const char *cp = getenv("RECOLL_CONFDIR");
        if (cp) {
            m->m_confdir = path_canon(cp);
        } else {
            autoconfdir = true;
            m->m_confdir=path_cat(path_homedata(), path_defaultrecollconfsubdir());
        }
    }

    // Note: autoconfdir and isDefaultConfig() are normally the same. We just 
    // want to avoid the imperfect test in isDefaultConfig() if we actually know
    // this is the default conf
    if (!autoconfdir && !isDefaultConfig()) {
        if (!path_exists(m->m_confdir)) {
            m->m_reason = std::string("Explicitly specified configuration [") + m->m_confdir +
                "] directory must exist (won't be automatically created). Use mkdir first";
            return;
        }
    }

    if (!path_exists(m->m_confdir)) {
        if (!m->initUserConfig()) 
            return;
    }

    // This can't change once computed inside a process. It would be
    // nicer to move this to a static class initializer to avoid
    // possible threading issues but this doesn't work (tried) as
    // things would not be ready. In practise we make sure that this
    // is called from the main thread at once, by constructing a config
    // from recollinit
    if (m->o_localecharset.empty()) {
#ifdef _WIN32
        m->o_localecharset = winACPName();
#elif defined(__APPLE__)
        m->o_localecharset = "UTF-8";
#else
        const char *cp;
        cp = nl_langinfo(CODESET);
        // We don't keep US-ASCII. It's better to use a superset
        // Ie: me have a C locale and some french file names, and I
        // can't imagine a version of iconv that couldn't translate
        // from iso8859?
        // The 646 thing is for solaris. 
        if (cp && *cp && strcmp(cp, "US-ASCII") 
#ifdef sun
            && strcmp(cp, "646")
#endif
            ) {
            m->o_localecharset = string(cp);
        } else {
            // Use cp1252 instead of iso-8859-1, it's a superset.
            m->o_localecharset = string(cstr_cp1252);
        }
#endif
        LOGDEB1("RclConfig::getDefCharset: localecharset ["  << m->o_localecharset << "]\n");
    }

    const char *cp;

    // Additional config directory, values override user ones
    if ((cp = getenv("RECOLL_CONFTOP"))) {
        m->m_cdirs.push_back(cp);
    } 

    // User config
    m->m_cdirs.push_back(m->m_confdir);

    // Additional config directory, overrides system's, overridden by user's
    if ((cp = getenv("RECOLL_CONFMID"))) {
        m->m_cdirs.push_back(cp);
    } 

    // Base/installation config, and its platform-specific overrides
    std::string defaultsdir = path_cat(m->m_datadir, "examples");
    if (!confsysdir.empty()) {
        std::string sdir = path_cat(defaultsdir, confsysdir);
        if (path_isdir(sdir)) {
            m->m_cdirs.push_back(sdir);
        }
    }
    m->m_cdirs.push_back(defaultsdir);

    string cnferrloc;
    for (const auto& dir : m->m_cdirs) {
        cnferrloc += "[" + dir + "] or ";
    }
    if (cnferrloc.size() > 4) {
        cnferrloc.erase(cnferrloc.size()-4);
    }

    // Read and process "recoll.conf"
    if (!updateMainConfig()) {
        m->m_reason = string("No/bad main configuration file in: ") + cnferrloc;
        return;
    }

    // Other files

    // Note: no need to make mimemap case-insensitive, the caller code lowercases the suffixes, 
    // which is more efficient.
    m->mimemap = std::make_unique>(ConfSimple::CFSF_RO, "mimemap", m->m_cdirs);
    if (!m->mimemap->ok()) {
        m->m_reason = string("No or bad mimemap file in: ") + cnferrloc;
        return;
    }

    // Maybe create the MIME to suffix association reverse map. Do it
    // in file order so that we can control what suffix is used when
    // there are several. This only uses the distributed file, not any
    // local customization (too complicated).
    if (mime_suffixes.empty()) {
        ConfSimple mm(path_cat(path_cat(m->m_datadir, "examples"), "mimemap").c_str());
        vector order = mm.getlines();
        for (const auto& entry: order) {
            if (entry.m_kind == ConfLine::CFL_VAR) {
                LOGDEB1("CONFIG: " << entry.m_data << " -> " << entry.m_value << "\n");
                // Remember: insert() only does anything for new keys,
                // so we only have the first value in the map
                mime_suffixes.insert(pair(entry.m_value, entry.m_data));
            }
        }
    }

    m->mimeconf = std::make_unique>(
        ConfSimple::CFSF_RO, "mimeconf", m->m_cdirs);
    if (!m->mimeconf->ok()) {
        m->m_reason = string("No/bad mimeconf in: ") + cnferrloc;
        return;
    }
    m->mimeview = std::make_unique>(
        ConfSimple::CFSF_NONE, "mimeview", m->m_cdirs);
    if (!m->mimeview->ok())
        m->mimeview = std::make_unique>(
            ConfSimple::CFSF_RO, "mimeview", m->m_cdirs);
    if (!m->mimeview->ok()) {
        m->m_reason = string("No/bad mimeview in: ") + cnferrloc;
        return;
    }
    if (!m->readFieldsConfig(cnferrloc))
        return;

    // Default is no threading
    m->m_thrConf = {{-1, 0}, {-1, 0}, {-1, 0}};

    m->m_ptrans = std::make_unique(
        ConfSimple::CFSF_NONE, path_cat(m->m_confdir, "ptrans"));

    m->m_ok = true;
    setKeyDir(cstr_null);

    m->initParamStale(m->m_conf.get(), m->mimemap.get());

    return;
}

RclConfig::RclConfig(const RclConfig &r) 
{
    m = std::make_unique(this);
    m->initFrom(r);
}

RclConfig::~RclConfig()
{
}

RclConfig& RclConfig::operator=(const RclConfig &r)
{
    if (this != &r) {
        m->zeroMe();
        m->initFrom(r);
    }
    return *this;
}


bool RclConfig::isDefaultConfig() const
{
    string defaultconf = path_cat(path_homedata(), path_defaultrecollconfsubdir());
    path_catslash(defaultconf);
    string specifiedconf = path_canon(m->m_confdir);
    path_catslash(specifiedconf);
    return !defaultconf.compare(specifiedconf);
}

bool RclConfig::updateMainConfig()
{
    auto newconf = std::make_unique>(
        ConfSimple::CFSF_RO|ConfSimple::CFSF_KEYNOCASE, "recoll.conf", m->m_cdirs);

    if (!newconf->ok()) {
        std::cerr << "updateMainConfig: NEW CONFIGURATION READ FAILED. dirs: " <<
            stringsToString(m->m_cdirs) << "\n";
        if (m->m_conf && m->m_conf->ok())
            return false;
        m->m_ok = false;
        m->initParamStale(0, 0);
        return false;
    }

    m->m_conf.swap(newconf);
    m->initParamStale(m->m_conf.get(), m->mimemap.get());
    
    setKeyDir(cstr_null);

    bool bvalue = true;
    if (getConfParam("skippedPathsFnmPathname", &bvalue) && bvalue == false) {
        FsTreeWalker::setNoFnmPathname();
    }
    string nowalkfn;
    getConfParam("nowalkfn", nowalkfn);
    if (!nowalkfn.empty()) {
        FsTreeWalker::setNoWalkFn(nowalkfn);
    }
    
    static int m_index_stripchars_init = 0;
    if (!m_index_stripchars_init) {
        getConfParam("indexStripChars", &o_index_stripchars);
        getConfParam("indexStoreDocText", &o_index_storedoctext);
        getConfParam("testmodifusemtime", &o_uptodate_test_use_mtime);
        getConfParam("stemexpandphrases", &o_expand_phrases);
        m_index_stripchars_init = 1;
    }

    if (getConfParam("cachedir", m->m_cachedir)) {
        m->m_cachedir = path_canon(path_tildexpand(m->m_cachedir));
    }

    return true;
}

bool RclConfig::ok() const
{
    return m->m_ok;
}

const std::string& RclConfig::getReason() const
{
    return m->m_reason;
}

std::string RclConfig::getConfDir() const
{
    return m->m_confdir;
}

const std::string& RclConfig::getDatadir() const
{
    return m->m_datadir;
}

std::string RclConfig::getKeyDir() const
{
    return m->m_keydir;
}

bool RclConfig::getConfParam(const std::string& name, std::string& value, bool shallow) const
{
    if (!m->m_conf->ok()) {
        return false;
    }
    return m->m_conf->get(name, value, m->m_keydir, shallow);
}

std::vector RclConfig::getConfNames(const char *pattern) const
{
    return m->m_conf->getNames(m->m_keydir, pattern);
}

ConfSimple *RclConfig::getPTrans()
{
#if defined(_WIN32) || defined(NOCASE_PTRANS)
    m->m_lptrans_stale = true;
#endif
    return m->m_ptrans.get();
}

const std::set& RclConfig::getStoredFields() const
{
    return m->m_storedFields;
}

const std::map& RclConfig::getXattrToField() const
{
    return m->m_xattrtofld;
}

bool RclConfig::hasNameAnywhere(const std::string& nm) const
{
    return m->m_conf? m->m_conf->hasNameAnywhere(nm) : false;
}

ConfNull *RclConfig::cloneMainConfig()
{
    ConfNull *conf = new ConfStack(ConfSimple::CFSF_KEYNOCASE, "recoll.conf", m->m_cdirs);
    if (!conf->ok()) {
        m->m_reason = string("Can't read config");
        return 0;
    }
    return conf;
}

// Remember what directory we're under and prefetch a few common values.
void RclConfig::setKeyDir(const string &dir) 
{
    if (!dir.compare(m->m_keydir))
        return;

    m->m_keydirgen++;
    m->m_keydir = dir;
    if (!m->m_conf->ok())
        return;

    if (!m->m_conf->get("defaultcharset", m->m_defcharset, m->m_keydir))
        m->m_defcharset.erase();
}

bool RclConfig::getConfParam(const string &name, int *ivp, bool shallow) const
{
    string value;
    if (!ivp || !getConfParam(name, value, shallow))
        return false;
    errno = 0;
    long lval = strtol(value.c_str(), nullptr, 0);
    if (lval == 0 && errno)
        return false;
    *ivp = int(lval);
    return true;
}

bool RclConfig::getConfParam(const string &name, double *dvp, bool shallow) const
{
    string value;
    if (!dvp || !getConfParam(name, value, shallow))
        return false;
    errno = 0;
    double dval = strtod(value.c_str(), nullptr);
    if (errno)
        return false;
    *dvp = int(dval);
    return true;
}

bool RclConfig::getConfParam(const string &name, bool *bvp, bool shallow) const
{
    string s;
    if (!bvp || !getConfParam(name, s, shallow))
        return false;
    *bvp = stringToBool(s);
    return true;
}

bool RclConfig::getConfParam(const string &name, vector *svvp, bool shallow) const
{
    string s;
    if (!svvp ||!getConfParam(name, s, shallow))
        return false;
    svvp->clear();
    return stringToStrings(s, *svvp);
}

bool RclConfig::getConfParam(const string &name, unordered_set *out, bool shallow) const
{
    vector v;
    if (!out || !getConfParam(name, &v, shallow)) {
        return false;
    }
    out->clear();
    out->insert(v.begin(), v.end());
    return true;
}

bool RclConfig::getConfParam(const string &name, vector *vip, bool shallow) const
{
    if (!vip) 
        return false;
    vip->clear();
    vector vs;
    if (!getConfParam(name, &vs, shallow))
        return false;
    vip->reserve(vs.size());
    for (unsigned int i = 0; i < vs.size(); i++) {
        char *ep;
        vip->push_back(strtol(vs[i].c_str(), &ep, 0));
        if (ep == vs[i].c_str()) {
            LOGDEB("RclConfig::getConfParam: bad int value in [" << name << "]\n");
            return false;
        }
    }
    return true;
}

void RclConfig::initThrConf()
{
    // Default is no threading
    m->m_thrConf = {{-1, 0}, {-1, 0}, {-1, 0}};

    vector vt;
    vector vq;
    if (!getConfParam("thrQSizes", &vq)) {
        LOGINFO("RclConfig::initThrConf: no thread info (queues)\n");
        goto out;
    }

    // If the first queue size is 0, autoconf is requested.
    if (vq.size() > 0 && vq[0] == 0) {
        CpuConf cpus;
        if (!getCpuConf(cpus) || cpus.ncpus < 1) {
            LOGERR("RclConfig::initThrConf: could not retrieve cpu conf\n");
            cpus.ncpus = 1;
        }
        if (cpus.ncpus != 1) {
            LOGDEB("RclConfig::initThrConf: autoconf requested. " <<
                   cpus.ncpus << " concurrent threads available.\n");
        }

        // Arbitrarily set threads config based on number of CPUS. This also
        // depends on the IO setup actually, so we're bound to be wrong...
        if (cpus.ncpus == 1) {
            // Somewhat counter-intuitively (because of possible IO//)
            // it seems that the best config here is no threading
        } else if (cpus.ncpus < 4) {
            // Untested so let's guess...
            m->m_thrConf = {{2, 2}, {2, 2}, {2, 1}};
        } else if (cpus.ncpus < 6) {
            m->m_thrConf = {{2, 4}, {2, 2}, {2, 1}};
        } else {
            m->m_thrConf = {{2, 5}, {2, 3}, {2, 1}};
        }
        goto out;
    } else if (vq.size() > 0 && vq[0] < 0) {
        // threads disabled by config
        goto out;
    }

    if (!getConfParam("thrTCounts", &vt) ) {
        LOGINFO("RclConfig::initThrConf: no thread info (threads)\n");
        goto out;
    }

    if (vq.size() != 3 || vt.size() != 3) {
        LOGINFO("RclConfig::initThrConf: bad thread info vector sizes\n");
        goto out;
    }

    // Normal case: record info from config
    m->m_thrConf.clear();
    for (unsigned int i = 0; i < 3; i++) {
        m->m_thrConf.push_back({vq[i], vt[i]});
    }

out:
    ostringstream sconf;
    for (unsigned int i = 0; i < 3; i++) {
        sconf << "(" << m->m_thrConf[i].first << ", " << m->m_thrConf[i].second << ") ";
    }

    LOGDEB("RclConfig::initThrConf: chosen config (ql,nt): " << sconf.str() << "\n");
}

pair RclConfig::getThrConf(ThrStage who) const
{
    if (m->m_thrConf.size() != 3) {
        LOGERR("RclConfig::getThrConf: bad data in rclconfig\n");
        return pair(-1,-1);
    }
    return m->m_thrConf[who];
}

vector RclConfig::getTopdirs(bool formonitor) const
{
    vector tdl;
    if (formonitor) {
        if (!getConfParam("monitordirs", &tdl)) {
            getConfParam("topdirs", &tdl);
        }
    } else {
        getConfParam("topdirs", &tdl);
    }
    if (tdl.empty()) {
        LOGERR("RclConfig::getTopdirs: nothing to index:  topdirs/monitordirs "
               " are not set or have a bad list format\n");
        return tdl;
    }

    for (auto& dir : tdl) {
        dir = path_canon(path_tildexpand(dir));
    }
    return tdl;
}

const string& RclConfig::getLocaleCharset()
{
    return Internal::o_localecharset;
}

// Get charset to be used for transcoding to utf-8 if unspecified by doc
// For document contents:
//  If defcharset was set (from the config or a previous call, this
//   is done in setKeydir), use it.
//  Else, try to guess it from the locale
//  Use cp1252 (as a superset of iso8859-1) as ultimate default
//
// For filenames, same thing except that we do not use the config file value
// (only the locale).
const string& RclConfig::getDefCharset(bool filename) const
{
    if (filename) {
        return m->o_localecharset;
    } else {
        return m->m_defcharset.empty() ? m->o_localecharset : m->m_defcharset;
    }
}

// Get all known document mime values. We get them from the mimeconf
// 'index' submap.
// It's quite possible that there are other mime types in the index
// (defined in mimemap and not mimeconf, or output by "file -i"). We
// just ignore them, because there may be myriads, and their contents
// are not indexed. 
//
// This unfortunately means that searches by file names and mime type
// filtering don't work well together.
vector RclConfig::getAllMimeTypes() const
{
    return m->mimeconf ? m->mimeconf->getNames("index") : vector();
}

// Compute the difference of 1st to 2nd sets and return as plus/minus
// sets. Some args are std::set and some others stringToString()
// strings for convenience
void RclConfig::setPlusMinus(const string& sbase, const set& upd,
                             string& splus, string& sminus)
{
    set base;
    stringToStrings(sbase, base);

    vector diff;
    auto it = set_difference(base.begin(), base.end(), upd.begin(), upd.end(),
                             std::inserter(diff, diff.begin()));
    sminus = stringsToString(diff);

    diff.clear();
    it = set_difference(upd.begin(), upd.end(), base.begin(), base.end(),
                        std::inserter(diff, diff.begin()));
    splus = stringsToString(diff);
}

/* Compute result of substracting strminus and adding strplus to base string.
   All string represent sets of values to be computed with stringToStrings() */
static void computeBasePlusMinus(set& res, const string& strbase,
                                 const string& strplus, const string& strminus)
{
    set plus, minus;
    res.clear();
    stringToStrings(strbase, res);
    stringToStrings(strplus, plus);
    stringToStrings(strminus, minus);
    for (auto& it : minus) {
        auto it1 = res.find(it);
        if (it1 != res.end()) {
            res.erase(it1);
        }
    }
    for (auto& it : plus) {
        res.insert(it);
    }
}

vector& RclConfig::getStopSuffixes()
{
    bool needrecompute = m->m_stpsuffstate.needrecompute();
    needrecompute = m->m_oldstpsuffstate.needrecompute() || needrecompute;
    if (needrecompute || !m->m_stopsuffixes) {
        // Need to initialize the suffixes

        // Let the old customisation have priority: if recoll_noindex from
        // mimemap is set, it the user's (the default value is gone). Else
        // use the new variable
        if (!m->m_oldstpsuffstate.getvalue(0).empty()) {
            stringToStrings(m->m_oldstpsuffstate.getvalue(0), m->m_stopsuffvec);
        } else {
            std::set ss;
            computeBasePlusMinus(ss, m->m_stpsuffstate.getvalue(0), 
                                 m->m_stpsuffstate.getvalue(1), 
                                 m->m_stpsuffstate.getvalue(2));
            m->m_stopsuffvec = vector(ss.begin(), ss.end());
        }

        // Compute the special suffixes store
        m->m_stopsuffixes = std::make_unique();
        m->m_maxsufflen = 0;
        for (const auto& entry : m->m_stopsuffvec) {
            m->m_stopsuffixes->insert(SfString(stringtolower(entry)));
            if (m->m_maxsufflen < entry.length())
                m->m_maxsufflen = int(entry.length());
        }
    }
    LOGDEB1("RclConfig::getStopSuffixes: ->" << stringsToString(m->m_stopsuffvec) << "\n");
    return m->m_stopsuffvec;
}

bool RclConfig::inStopSuffixes(const string& fni)
{
    LOGDEB2("RclConfig::inStopSuffixes(" << fni << ")\n");

    // Call getStopSuffixes() to possibly update state, ignore result
    getStopSuffixes();

    // Only need a tail as long as the longest suffix.
    int pos = MAX(0, int(fni.length() - m->m_maxsufflen));
    string fn(fni, pos);

    stringtolower(fn);
    SuffixStore::const_iterator it = m->m_stopsuffixes->find(fn);
    if (it != m->m_stopsuffixes->end()) {
        LOGDEB2("RclConfig::inStopSuffixes: Found (" << fni << ") ["  <<
                ((*it).m->m_str) << "]\n");
        IdxDiags::theDiags().record(IdxDiags::NoContentSuffix, fni);
        return true;
    } else {
        LOGDEB2("RclConfig::inStopSuffixes: not found [" << fni << "]\n");
        return false;
    }
}

string RclConfig::getMimeTypeFromSuffix(const string& suff) const
{
    string mtype;
    m->mimemap->get(suff, mtype, m->m_keydir);
    return mtype;
}

string RclConfig::getSuffixFromMimeType(const string &mt) const
{
    // First try from standard data, ensuring that we can control the value
    // from the order in the configuration file.
    auto rclsuff = mime_suffixes.find(mt);
    if (rclsuff != mime_suffixes.end()) {
        return rclsuff->second;
    }
    // Try again from local data. The map is in the wrong direction,
    // have to walk it.
    vector sfs = m->mimemap->getNames(cstr_null);
    for (const auto& suff : sfs) {
        string mt1;
        if (m->mimemap->get(suff, mt1, cstr_null) && !stringicmp(mt, mt1)) {
            return suff;
        }
    }
    return cstr_null;
}

/** Get list of file categories from mimeconf */
bool RclConfig::getMimeCategories(vector& cats) const
{
    if (!m->mimeconf)
        return false;
    cats = m->mimeconf->getNames("categories");
    return true;
}

bool RclConfig::isMimeCategory(const string& cat) const
{
    vectorcats;
    getMimeCategories(cats);
    for (vector::iterator it = cats.begin(); it != cats.end(); it++) {
        if (!stringicmp(*it,cat))
            return true;
    }
    return false;
}

/** Get list of mime types for category from mimeconf */
bool RclConfig::getMimeCatTypes(const string& cat, vector& tps) const
{
    tps.clear();
    if (!m->mimeconf)
        return false;
    string slist;
    if (!m->mimeconf->get(cat, slist, "categories"))
        return false;

    stringToStrings(slist, tps);
    return true;
}

string RclConfig::getMimeHandlerDef(const string &mtype, bool filtertypes, const std::string& fn)
{
    string hs;

    if (filtertypes) {
        if(m->m_rmtstate.needrecompute()) {
            m->m_restrictMTypes.clear();
            stringToStrings(stringtolower((const string&)m->m_rmtstate.getvalue()),
                            m->m_restrictMTypes);
        }
        if (m->m_xmtstate.needrecompute()) {
            m->m_excludeMTypes.clear();
            stringToStrings(stringtolower((const string&)m->m_xmtstate.getvalue()),
                            m->m_excludeMTypes);
        }
        if (!m->m_restrictMTypes.empty() && !m->m_restrictMTypes.count(stringtolower(mtype))) {
            IdxDiags::theDiags().record(IdxDiags::NotIncludedMime, fn, mtype);
            LOGDEB1("RclConfig::getMimeHandlerDef: " << mtype << " not in mime type list\n");
            return hs;
        }
        if (!m->m_excludeMTypes.empty() && m->m_excludeMTypes.count(stringtolower(mtype))) {
            IdxDiags::theDiags().record(IdxDiags::ExcludedMime, fn, mtype);
            LOGDEB1("RclConfig::getMimeHandlerDef: " << mtype << " in excluded mime list (fn " <<
                    fn << ")\n");
            return hs;
        }
    }

    if (!m->mimeconf->get(mtype, hs, "index")) {
        if (mtype.find("text/") == 0) {
            bool alltext{false};
            getConfParam("textunknownasplain", &alltext);
            if (alltext && m->mimeconf->get("text/plain", hs, "index")) {
                return hs;
            }
        }
        if (mtype != "inode/directory") {
            IdxDiags::theDiags().record(IdxDiags::NoHandler, fn, mtype);
            LOGDEB1("getMimeHandlerDef: no handler for '" << mtype << "' (fn " << fn << ")\n");
        }
    }
    return hs;
}

const vector& RclConfig::getMDReapers()
{
    string hs;
    if (m->m_mdrstate.needrecompute()) {
        m->m_mdreapers.clear();
        // New value now stored in m->m_mdrstate.getvalue(0)
        const string& sreapers = m->m_mdrstate.getvalue(0);
        if (sreapers.empty())
            return m->m_mdreapers;
        string value;
        ConfSimple attrs;
        valueSplitAttributes(sreapers, value, attrs);
        vector nmlst = attrs.getNames(cstr_null);
        for (const auto& nm : nmlst) {
            MDReaper reaper;
            reaper.fieldname = fieldCanon(nm);
            string s;
            attrs.get(nm, s);
            stringToStrings(s, reaper.cmdv);
            m->m_mdreapers.push_back(reaper);
        }
    }
    return m->m_mdreapers;
}

bool RclConfig::getGuiFilterNames(vector& cats) const
{
    if (!m->mimeconf)
        return false;
    cats = m->mimeconf->getNamesShallow("guifilters");
    return true;
}

bool RclConfig::getGuiFilter(const string& catfiltername, string& frag) const
{
    frag.clear();
    if (!m->mimeconf)
        return false;
    if (!m->mimeconf->get(catfiltername, frag, "guifilters"))
        return false;
    return true;
}

bool RclConfig::valueSplitAttributes(const string& whole, string& value, ConfSimple& attrs)
{
    bool inquote{false};
    string::size_type semicol0;    
    for (semicol0 = 0; semicol0 < whole.size(); semicol0++) {
        if (whole[semicol0] == '"') {
            inquote = !inquote;
        } else if (whole[semicol0] == ';' && !inquote) {
            break;
        }
    }
    value = whole.substr(0, semicol0);
    trimstring(value);
    string attrstr;
    if (semicol0 != string::npos && semicol0 < whole.size() - 1) {
        attrstr = whole.substr(semicol0+1);
    }

    // Handle additional attributes. We substitute the semi-colons
    // with newlines and use a ConfSimple
    if (!attrstr.empty()) {
        for (string::size_type i = 0; i < attrstr.size(); i++) {
            if (attrstr[i] == ';')
                attrstr[i] = '\n';
        }
        attrs.reparse(attrstr);
    } else {
        attrs.clear();
    }
    
    return true;
}

bool RclConfig::getMissingHelperDesc(string& out) const
{
    string fmiss = path_cat(getConfDir(), "missing");
    out.clear();
    if (!file_to_string(fmiss, out))
        return false;
    return true;
}

void RclConfig::storeMissingHelperDesc(const string &s)
{
    string fmiss = path_cat(getCacheDir(), "missing");
    fstream fp;
    if (path_streamopen(fmiss, ios::trunc | ios::out, fp)) {
        fp << s;
    }
}

// Read definitions for field prefixes, aliases, and hierarchy and arrange 
// things for speed (theses are used a lot during indexing)
bool RclConfig::Internal::readFieldsConfig(const string& cnferrloc)
{
    LOGDEB2("RclConfig::readFieldsConfig\n");
    m_fields = std::make_unique>("fields", m_cdirs, true);
    if (!m_fields->ok()) {
        m_reason = string("No/bad fields file in: ") + cnferrloc;
        return false;
    }

    // Build a direct map avoiding all indirections for field to
    // prefix translation
    // Add direct prefixes from the [prefixes] section
    vector tps = m_fields->getNames("prefixes");
    for (const auto& fieldname : tps) {
        string val;
        m_fields->get(fieldname, val, "prefixes");
        ConfSimple attrs;
        FieldTraits ft;
        // fieldname = prefix ; attr1=val;attr2=val...
        if (!valueSplitAttributes(val, ft.pfx, attrs)) {
            LOGERR("readFieldsConfig: bad config line for ["  << fieldname <<
                   "]: [" << val << "]\n");
            return 0;
        }
        ft.wdfinc = (int)attrs.getInt("wdfinc", 1);
        ft.boost = attrs.getFloat("boost", 1.0);
        ft.pfxonly = attrs.getBool("pfxonly", false);
        ft.noterms = attrs.getBool("noterms", false);
        m_fldtotraits[stringtolower(fieldname)] = ft;
        LOGDEB2("readFieldsConfig: ["  << stringtolower(fieldname) << "] -> ["  << ft.pfx <<
                "] " << ft.wdfinc << " " << ft.boost << "\n");
    }

    // Values section
    tps = m_fields->getNames("values");
    for (const auto& fieldname : tps) {
        string canonic = stringtolower(fieldname); // canonic name
        string val;
        m_fields->get(fieldname, val, "values");
        ConfSimple attrs;
        string svslot;
        // fieldname = valueslot ; attr1=val;attr2=val...
        if (!valueSplitAttributes(val, svslot, attrs)) {
            LOGERR("readFieldsConfig: bad value line for ["  << fieldname <<
                   "]: [" << val << "]\n");
            return 0;
        }
        uint32_t valueslot = uint32_t(atoi(svslot.c_str()));
        if (valueslot == 0) {
            LOGERR("readFieldsConfig: found 0 value slot for [" << fieldname <<
                   "]: [" << val << "]\n");
            continue;
        }

        string tval;
        FieldTraits::ValueType valuetype{FieldTraits::STR};
        if (attrs.get("type", tval)) {
            if (tval == "string") {
                valuetype = FieldTraits::STR;
            } else if (tval == "int") {
                valuetype = FieldTraits::INT;
            } else {
                LOGERR("readFieldsConfig: bad type for value for " <<
                       fieldname << " : " << tval << "\n");
                return 0;
            }
        }
        int valuelen = (int)attrs.getInt("len", 0);
        // Find or insert traits entry
        const auto pit =
            m_fldtotraits.insert(pair(canonic, FieldTraits())).first;
        pit->second.valueslot = valueslot;
        pit->second.valuetype = valuetype;
        pit->second.valuelen = valuelen;
    }
    
    // Add prefixes for aliases and build alias-to-canonic map while
    // we're at it. Having the aliases in the prefix map avoids an
    // additional indirection at index time.
    tps = m_fields->getNames("aliases");
    for (const auto& fieldname : tps) {
        string canonic = stringtolower(fieldname); // canonic name
        FieldTraits ft;
        const auto pit = m_fldtotraits.find(canonic);
        if (pit != m_fldtotraits.end()) {
            ft = pit->second;
        }
        string aliases;
        m_fields->get(fieldname, aliases, "aliases");
        LOGDEB2("readFieldsConfig: " << canonic << " -> " << aliases << "\n");
        vector l;
        stringToStrings(aliases, l);
        for (const auto& alias : l) {
            auto canonicalias = stringtolower(alias);
            if (pit != m_fldtotraits.end())
                m_fldtotraits[canonicalias] = ft;
            m_aliastocanon[canonicalias] = canonic;
        }
    }

    // Query aliases map
    tps = m_fields->getNames("queryaliases");
    for (const auto& entry: tps) {
        string canonic = stringtolower(entry); // canonic name
        string aliases;
        m_fields->get(entry, aliases, "queryaliases");
        vector l;
        stringToStrings(aliases, l);
        for (const auto& alias : l) {
            m_aliastoqcanon[stringtolower(alias)] = canonic;
        }
    }

#if 0
    for (map::const_iterator it = m_fldtotraits.begin();
         it != m_fldtotraits.end(); it++) {
        LOGDEB("readFieldsConfig: ["  << entry << "] -> ["  << it->second.pfx <<
               "] " << it->second.wdfinc << " " << it->second.boost << "\n");
    }
#endif

    vector sl = m_fields->getNames("stored");
    for (const auto& fieldname : sl) {
        m_storedFields.insert(m_parent->fieldCanon(stringtolower(fieldname)));
    }

    // Extended file attribute to field translations
    vectorxattrs = m_fields->getNames("xattrtofields");
    for (const auto& xattr : xattrs) {
        string val;
        m_fields->get(xattr, val, "xattrtofields");
        m_xattrtofld[xattr] = val;
    }

    return true;
}

// Return specifics for field name:
bool RclConfig::getFieldTraits(const string& _fld, const FieldTraits **ftpp,
                               bool isquery) const
{
    string fld = isquery ? fieldQCanon(_fld) : fieldCanon(_fld);
    map::const_iterator pit = m->m_fldtotraits.find(fld);
    if (pit != m->m_fldtotraits.end()) {
        *ftpp = &pit->second;
        LOGDEB1("RclConfig::getFieldTraits: [" << _fld << "]->["  <<
                pit->second.pfx << "]\n");
        return true;
    } else {
        LOGDEB1("RclConfig::getFieldTraits: no prefix for field [" << fld << "]\n");
        *ftpp = 0;
        return false;
    }
}

set RclConfig::getIndexedFields() const
{
    set flds;
    if (!m->m_fields->ok())
        return flds;

    vector sl = m->m_fields->getNames("prefixes");
    flds.insert(sl.begin(), sl.end());
    return flds;
}

string RclConfig::fieldCanon(const string& f) const
{
    string fld = stringtolower(f);
    const auto it = m->m_aliastocanon.find(fld);
    if (it != m->m_aliastocanon.end()) {
        LOGDEB1("RclConfig::fieldCanon: [" << f << "] -> [" << it->second << "]\n");
        return it->second;
    }
    LOGDEB1("RclConfig::fieldCanon: [" << f << "] -> [" << fld << "]\n");
    return fld;
}

string RclConfig::fieldQCanon(const string& f) const
{
    const auto it = m->m_aliastoqcanon.find(stringtolower(f));
    if (it != m->m_aliastoqcanon.end()) {
        LOGDEB1("RclConfig::fieldQCanon: [" << f << "] -> ["  << it->second << "]\n");
        return it->second;
    }
    return fieldCanon(f);
}

vector RclConfig::getFieldSectNames(const string &sk, const char* patrn) const
{
    if (!m->m_fields->ok())
        return vector();
    return m->m_fields->getNames(sk, patrn);
}

bool RclConfig::getFieldConfParam(const string &name, const string &sk, 
                                  string &value) const
{
    if (!m->m_fields->ok())
        return false;
    return m->m_fields->get(name, value, sk);
}

set RclConfig::getMimeViewerAllEx() const
{
    set res;
    if (!m->mimeview->ok())
        return res;

    string base, plus, minus;
    m->mimeview->get("xallexcepts", base, "");
    LOGDEB1("RclConfig::getMimeViewerAllEx(): base: " << base << "\n");
    m->mimeview->get("xallexcepts+", plus, "");
    LOGDEB1("RclConfig::getMimeViewerAllEx(): plus: " << plus << "\n");
    m->mimeview->get("xallexcepts-", minus, "");
    LOGDEB1("RclConfig::getMimeViewerAllEx(): minus: " << minus << "\n");

    computeBasePlusMinus(res, base, plus, minus);
    LOGDEB1("RclConfig::getMimeViewerAllEx(): res: " << stringsToString(res) << "\n");
    return res;
}

bool RclConfig::setMimeViewerAllEx(const set& allex)
{
    if (!m->mimeview->ok())
        return false;

    string sbase;
    m->mimeview->get("xallexcepts", sbase, "");

    string splus, sminus;
    setPlusMinus(sbase, allex, splus, sminus);

    if (!m->mimeview->set("xallexcepts-", sminus, "")) {
        m->m_reason = string("RclConfig:: cant set value. Readonly?");
        return false;
    }
    if (!m->mimeview->set("xallexcepts+", splus, "")) {
        m->m_reason = string("RclConfig:: cant set value. Readonly?");
        return false;
    }

    return true;
}

string RclConfig::getMimeViewerDef(const string &mtype, const string& apptag, bool useall) const
{
    LOGDEB2("RclConfig::getMimeViewerDef: mtype [" << mtype << "] apptag [" << apptag << "]\n");
    string hs;
    if (!m->mimeview->ok())
        return hs;

    if (useall) {
        // Check for exception
        set allex = getMimeViewerAllEx();
        bool isexcept = false;
        for (auto& it : allex) {
            vector mita;
            stringToTokens(it, mita, "|");
            if ((mita.size() == 1 && apptag.empty() && mita[0] == mtype) ||
                (mita.size() == 2 && mita[1] == apptag && mita[0] == mtype)) {
                // Exception to x-all
                isexcept = true;
                break;
            }
        }

        if (isexcept == false) {
            m->mimeview->get("application/x-all", hs, "view");
            return hs;
        }
        // Fallthrough to normal case.
    }

    if (apptag.empty() || !m->mimeview->get(mtype + string("|") + apptag, hs, "view"))
        m->mimeview->get(mtype, hs, "view");

    // Last try for text/xxx if alltext is set
    if (hs.empty() && mtype.find("text/") == 0 && mtype != "text/plain") {
        bool alltext{false};
        getConfParam("textunknownasplain", &alltext);
        if (alltext) {
            return getMimeViewerDef("text/plain", apptag, useall);
        }
    }
        
    return hs;
}

bool RclConfig::getMimeViewerDefs(vector >& defs) const
{
    if (!m->mimeview->ok())
        return false;
    vectortps = m->mimeview->getNames("view");
    for (const auto& tp : tps) {
        defs.push_back(pair(tp, getMimeViewerDef(tp, "", 0)));
    }
    return true;
}

bool RclConfig::setMimeViewerDef(const string& mt, const string& def)
{
    if (!m->mimeview->ok())
        return false;
    bool status;
    if (!def.empty()) 
        status = m->mimeview->set(mt, def, "view");
    else 
        status = m->mimeview->erase(mt, "view");

    if (!status) {
        m->m_reason = string("RclConfig:: cant set value. Readonly?");
        return false;
    }
    return true;
}

bool RclConfig::mimeViewerNeedsUncomp(const string &mimetype) const
{
    string s;
    vector v;
    if (m->mimeview != 0 && m->mimeview->get("nouncompforviewmts", s, "") && 
        stringToStrings(s, v) && 
        find_if(v.begin(), v.end(), StringIcmpPred(mimetype)) != v.end()) 
        return false;
    return true;
}

string RclConfig::getMimeIconPath(const string &mtype, const string &apptag)
    const
{
    string iconname;
    if (!apptag.empty())
        m->mimeconf->get(mtype + string("|") + apptag, iconname, "icons");
    if (iconname.empty())
        m->mimeconf->get(mtype, iconname, "icons");
    if (iconname.empty())
        iconname = "document";

    string iconpath;
#if defined (__FreeBSD__) && __FreeBSD_version < 500000
    // gcc 2.95 dies if we call getConfParam here ??
    if (m->m_conf->ok())
        m->m_conf->get(string("iconsdir"), iconpath, m->m_keydir);
#else
    getConfParam("iconsdir", iconpath);
#endif

    if (iconpath.empty()) {
        iconpath = path_cat(m->m_datadir, "images");
    } else {
        iconpath = path_tildexpand(iconpath);
    }
    return path_cat(iconpath, iconname) + ".png";
}

// Return path defined by varname. May be absolute or relative to
// confdir, with default in confdir
string RclConfig::getConfdirPath(const char *varname, const char *dflt) const
{
    string result;
    if (!getConfParam(varname, result)) {
        result = path_cat(getConfDir(), dflt);
    } else {
        result = path_tildexpand(result);
        // If not an absolute path, compute relative to config dir
        if (!path_isabsolute(result)) {
            result = path_cat(getConfDir(), result);
        }
    }
    return path_canon(result);
}

string RclConfig::getCacheDir() const
{
    return m->m_cachedir.empty() ? getConfDir() : m->m_cachedir;
}

// Return path defined by varname. May be absolute or relative to
// confdir, with default in confdir
string RclConfig::getCachedirPath(const char *varname, const char *dflt) const
{
    string result;
    if (!getConfParam(varname, result)) {
        result = path_cat(getCacheDir(), dflt);
    } else {
        result = path_tildexpand(result);
        // If not an absolute path, compute relative to cache dir
        if (!path_isabsolute(result)) {
            result = path_cat(getCacheDir(), result);
        }
    }
    return path_canon(result);
}

// Windows: try to translate a possibly non-ASCII path into the shortpath alias. We first create the
// target, as this only works if it exists.
//
// This is mainly useful/necessary for paths based on the configuration directory, when the user has
// a non ASCII name, and for use with code which can't handle properly an Unicode path.
//
// For example, we execute an aspell command to create the spelling dictionary, and the parameter is
// a path to a dictionary file located inside the configuration directory.
// 
// This code is now mostly (only?) necessary for aspell as we use a patched Xapian, modified to
// properly handle Unicode paths, so that the shortpath getDbDir() is probably redundant.
//
// Note that it is not clear from the Windows doc that the shortpath call is going to work in all
// cases (file system types etc.). It returns the original path in case it fails, so it's important
// that the essential code (Xapian) can deal with Unicode paths.
static string maybeshortpath(const std::string& in)
{
#ifdef _WIN32
    path_makepath(in, 0700);
    return path_shortpath(in);
#else
    return in;
#endif
}

string RclConfig::getDbDir() const
{
    return getCachedirPath("dbdir", "xapiandb");
}

string RclConfig::getWebcacheDir() const
{
    return getCachedirPath("webcachedir", "webcache");
}
string RclConfig::getMboxcacheDir() const
{
    return getCachedirPath("mboxcachedir", "mboxcache");
}
string RclConfig::getAspellcacheDir() const
{
    return maybeshortpath(getCachedirPath("aspellDicDir", ""));
}

string RclConfig::getStopfile() const
{
    return getConfdirPath("stoplistfile", "stoplist.txt");
}

string RclConfig::getIdxSynGroupsFile() const
{
    return getConfdirPath("idxsynonyms", "thereisnodefaultidxsynonyms");
}

// The index status file is fast changing, so it's possible to put it outside
// of the config directory (for ssds, not sure this is really useful).
// To enable being quite xdg-correct we should add a getRundirPath()
string RclConfig::getIdxStatusFile() const
{
    return getCachedirPath("idxstatusfile", "idxstatus.txt");
}

const std::string& RclConfig::getOrigCwd() const
{
    return m->o_origcwd;
}

// The pid file is opened r/w every second by the GUI to check if the
// indexer is running. This is a problem for systems which spin down
// their disks. Locate it in XDG_RUNTIME_DIR if this is set.
// Thanks to user Madhu for this fix.
string RclConfig::getPidfile() const
{
    static string fn;
    if (fn.empty()) {
#ifndef _WIN32
        const char *p = getenv("XDG_RUNTIME_DIR");
        string rundir;
        if (nullptr == p) {
            // Problem is, we may have been launched outside the desktop, maybe by cron. Basing
            // everything on XDG_RUNTIME_DIR was a mistake, sometimes resulting in different pidfiles
            // being used by recollindex instances. So explicitely test for /run/user/$uid, still
            // leaving open the remote possibility that XDG_RUNTIME_DIR would be set to something
            // else...
            rundir = path_cat("/run/user", lltodecstr(getuid()));
            if (path_isdir(rundir)) {
                p = rundir.c_str();
            }
        }
        if (p) {
            string base = path_canon(p);
            string digest, hex;
            string cfdir = path_canon(getConfDir());
            path_catslash(cfdir);
            MD5String(cfdir, digest);
            MD5HexPrint(digest, hex);
            fn =  path_cat(base, "recoll-" + hex + "-index.pid");
            goto out;
        }
#endif // ! _WIN32
    
        fn = path_cat(getCacheDir(), "index.pid");
    out:
        LOGINF("RclConfig: pid/lock file: " << fn << "\n");
    }
    return fn;
}


string RclConfig::getIdxStopFile() const
{
    return path_cat(getCacheDir(), "index.stop");
}

/* Eliminate the common leaf part of file paths p1 and p2. Example: 
 * /mnt1/common/part /mnt2/common/part -> /mnt1 /mnt2. This is used
 * for computing translations for paths when the dataset has been
 * moved. Of course this could be done more efficiently than by splitting 
 * into vectors, but we don't care.*/
static string path_diffstems(const string& p1, const string& p2,
                             string& r1, string& r2)
{
    string reason;
    r1.clear();
    r2.clear();
    vector v1, v2;
    stringToTokens(p1, v1, "/");
    stringToTokens(p2, v2, "/");
    unsigned int l1 = v1.size();
    unsigned int l2 = v2.size();
        
    // Search for common leaf part
    unsigned int cl = 0;
    for (; cl < MIN(l1, l2); cl++) {
        if (v1[l1-cl-1] != v2[l2-cl-1]) {
            break;
        }
    }
    //cerr << "Common length = " << cl << "\n";
    if (cl == 0) {
        reason = "Input paths are empty or have no common part";
        return reason;
    }
    for (unsigned i = 0; i < l1 - cl; i++) {
        r1 += "/" + v1[i];
    }
    for (unsigned i = 0; i < l2 - cl; i++) {
        r2 += "/" + v2[i];
    }
        
    return reason;
}

void RclConfig::urlrewrite(const string& _dbdir, string& url) const
{
    LOGDEB1("RclConfig::urlrewrite: dbdir [" << _dbdir << "] url [" << url << "]\n");

#if defined(_WIN32) || defined(NOCASE_PTRANS)
    // Under Windows we want case-insensitive comparisons. Create an all-lowercase version
    // of m_ptrans (if not already done).
    if (m->m_lptrans_stale) {
        std::string str;
        m->m_ptrans->sortwalk(varprintlower, &str);
        m->m_lptrans = std::make_unique(ConfSimple::CFSF_FROMSTRING, str);
        m->m_lptrans_stale = false;
    }
    std::string dbdir{unactolower(_dbdir)};
    ConfSimple *ptrans = m->m_lptrans.get();
#else
    std::string dbdir{_dbdir};
    ConfSimple *ptrans = m->m_ptrans.get();
#endif

    // If orgidxconfdir is set, we assume that this index is for a
    // movable dataset, with the configuration directory stored inside
    // the dataset tree. This allows computing automatic path
    // translations if the dataset has been moved.
    string orig_confdir;
    string cur_confdir;
    string confstemorg, confstemrep;
    if (m->m_conf->get("orgidxconfdir", orig_confdir, "")) {
        if (!m->m_conf->get("curidxconfdir", cur_confdir, "")) {
            cur_confdir = m->m_confdir;
        }
        LOGDEB1("RclConfig::urlrewrite: orgidxconfdir: " << orig_confdir <<
                " cur_confdir " << cur_confdir << "\n");
        string reason = path_diffstems(orig_confdir, cur_confdir, confstemorg, confstemrep);
        if (!reason.empty()) {
            LOGERR("urlrewrite: path_diffstems failed: " << reason << " : orig_confdir [" <<
                   orig_confdir << "] cur_confdir [" << cur_confdir << "\n");
            confstemorg = confstemrep = "";
        }
    }
    
    // Do path translations exist for this index ?
    bool needptrans = true;
    if (!ptrans->ok() || !ptrans->hasSubKey(dbdir)) {
        LOGDEB2("RclConfig::urlrewrite: no paths translations for [" << _dbdir << "]\n");
        needptrans = false;
    }

    if (!needptrans && confstemorg.empty()) {
        return;
    }
    bool computeurl = false;
    
    string path = fileurltolocalpath(url);
    if (path.empty()) {
        LOGDEB2("RclConfig::urlrewrite: not file url\n");
        return;
    }
    
    // Do the movable volume thing.
    if (!confstemorg.empty() && confstemorg.size() <= path.size() &&
        !path.compare(0, confstemorg.size(), confstemorg)) {
        path = path.replace(0, confstemorg.size(), confstemrep);
        computeurl = true;
    }

    if (needptrans) {
#if defined(_WIN32) || defined(NOCASE_PTRANS)
        path = unactolower(path);
#endif // _WIN32
        
        // For each translation check if the prefix matches the input path,
        // replace and return the result if it does.
        vector opaths = ptrans->getNames(dbdir);
        for (const auto& opath: opaths) {
            if (opath.size() <= path.size() && !path.compare(0, opath.size(), opath)) {
                string npath;
                // Key comes from getNames()=> call must succeed
                if (ptrans->get(opath, npath, dbdir)) { 
                    path = path_canon(path.replace(0, opath.size(), npath));
                    computeurl = true;
                }
                break;
            }
        }
    }
    if (computeurl) {
        url = path_pathtofileurl(path);
    }
}

bool RclConfig::sourceChanged() const
{
    if (m->m_conf->ok() && m->m_conf->sourceChanged())
        return true;
    if (m->mimemap->ok() && m->mimemap->sourceChanged())
        return true;
    if (m->mimeconf->ok() && m->mimeconf->sourceChanged())
        return true;
    if (m->mimeview->ok() && m->mimeview->sourceChanged())
        return true;
    if (m->m_fields->ok() && m->m_fields->sourceChanged())
        return true;
    if (m->m_ptrans->ok() && m->m_ptrans->sourceChanged())
        return true;
    return false;
}

string RclConfig::getWebQueueDir() const
{
    string webqueuedir;
    if (!getConfParam("webqueuedir", webqueuedir)) {
#ifdef _WIN32
        webqueuedir = "~/AppData/Local/RecollWebQueue";
#else
        webqueuedir = "~/.recollweb/ToIndex/";
#endif
    }
    webqueuedir = path_tildexpand(webqueuedir);
    return webqueuedir;
}

vector& RclConfig::getSkippedNames()
{
    if (m->m_skpnstate.needrecompute()) {
        set ss;
        computeBasePlusMinus(ss, m->m_skpnstate.getvalue(0),
                             m->m_skpnstate.getvalue(1), m->m_skpnstate.getvalue(2));
        m->m_skpnlist = vector(ss.begin(), ss.end());
    }
    return m->m_skpnlist;
}

vector& RclConfig::getOnlyNames()
{
    if (m->m_onlnstate.needrecompute()) {
        stringToStrings(m->m_onlnstate.getvalue(), m->m_onlnlist);
    }
    return m->m_onlnlist;
}

vector RclConfig::getSkippedPaths() const
{
    vector skpl;
    getConfParam("skippedPaths", &skpl);

    // Always add the dbdir and confdir to the skipped paths. This is 
    // especially important for the rt monitor which will go into a loop if we
    // don't do this.
    skpl.push_back(getDbDir());
    skpl.push_back(getConfDir());
#ifdef _WIN32
    skpl.push_back(TempFile::rcltmpdir());
#endif
    if (getCacheDir().compare(getConfDir())) {
        skpl.push_back(getCacheDir());
    }
    // And the web queue dir
    skpl.push_back(getWebQueueDir());
    for (vector::iterator it = skpl.begin(); it != skpl.end(); it++) {
        *it = path_tildexpand(*it);
        *it = path_canon(*it);
    }
    sort(skpl.begin(), skpl.end());
    vector::iterator uit = unique(skpl.begin(), skpl.end());
    skpl.resize(uit - skpl.begin());
    return skpl;
}

vector RclConfig::getDaemSkippedPaths() const
{
    vector dskpl;
    getConfParam("daemSkippedPaths", &dskpl);

    for (vector::iterator it = dskpl.begin(); it != dskpl.end(); it++) {
        *it = path_tildexpand(*it);
        *it = path_canon(*it);
    }

    vector skpl1 = getSkippedPaths();
    vector skpl;
    if (dskpl.empty()) {
        skpl = skpl1;
    } else {
        sort(dskpl.begin(), dskpl.end());
        merge(dskpl.begin(), dskpl.end(), skpl1.begin(), skpl1.end(), 
              skpl.begin());
        vector::iterator uit = unique(skpl.begin(), skpl.end());
        skpl.resize(uit - skpl.begin());
    }
    return skpl;
}


// Look up an executable filter.  We add $RECOLL_FILTERSDIR,
// and filtersdir from the config file to the PATH, then use execmd::which()
string RclConfig::findFilter(const string &icmd) const
{
    LOGDEB2("findFilter: " << icmd << "\n");
    // If the path is absolute, this is it
    if (path_isabsolute(icmd))
        return icmd;

    const char *cp = getenv("PATH");
    if (!cp) //??
        cp = "";
    string PATH(cp);

    // For historical reasons: check in personal config directory
    PATH = getConfDir() + path_PATHsep() + PATH;

    string temp;
    // Prepend $datadir/filters
    temp = path_cat(m->m_datadir, "filters");
    PATH = temp + path_PATHsep() + PATH;
#ifdef _WIN32
    // Windows only: use the bundled Python
    temp = path_cat(m->m_datadir, "filters");
    temp = path_cat(temp, "python");
    PATH = temp + path_PATHsep() + PATH;
#endif
    // Prepend possible configuration parameter?
    if (getConfParam(string("filtersdir"), temp)) {
        temp = path_tildexpand(temp);
        PATH = temp + path_PATHsep() + PATH;
    }

    // Prepend possible environment variable
    if ((cp = getenv("RECOLL_FILTERSDIR"))) {
        PATH = string(cp) + path_PATHsep() + PATH;
    } 

    string cmd;
    if (ExecCmd::which(icmd, cmd, PATH.c_str())) {
        return cmd;
    } else {
        // Let the shell try to find it...
        return icmd;
    }
}

bool RclConfig::processFilterCmd(std::vector& cmd) const
{
    LOGDEB0("processFilterCmd: in: " << stringsToString(cmd) << "\n");
    auto it = cmd.begin();

#ifdef _WIN32
    // Special-case interpreters on windows: we used to have an additional 1st argument "python" in
    // mimeconf, but we now rely on the .py extension for better sharing of mimeconf.
    std::string ext = path_suffix(*it);
    if ("py" == ext) {
        it = cmd.insert(it, findFilter("python"));
        it++;
    } else if ("pl" == ext) {
        it = cmd.insert(it, findFilter("perl"));
        it++;
    }
#endif
    
    // Note that, if the cmd vector size is 1, post-incrementing the
    // iterator in the following statement, which works on x86, leads
    // to a crash on ARM with gcc 6 and 8 (at least), which does not
    // seem right (it should just become cmd.end() ?) but
    // whatever... We do it later then.
    *it = findFilter(*it);

    LOGDEB0("processFilterCmd: out: " << stringsToString(cmd) << "\n");
    return true;
}

// This now does nothing more than processFilterCmd (after we changed to relying on the py extension)
bool RclConfig::pythonCmd(const std::string& scriptname, std::vector& cmd) const
{
#ifdef _WIN32
    cmd = {scriptname};
#else
    cmd = {scriptname};
#endif
    return processFilterCmd(cmd);
}

/** 
 * Return decompression command line for given mime type
 */
bool RclConfig::getUncompressor(const string &mtype, vector& cmd) const
{
    string hs;

    m->mimeconf->get(mtype, hs, cstr_null);
    if (hs.empty())
        return false;
    vector tokens;
    stringToStrings(hs, tokens);
    if (tokens.empty()) {
        LOGERR("getUncompressor: empty spec for mtype " << mtype << "\n");
        return false;
    }
    auto it = tokens.begin();
    if (tokens.size() < 2)
        return false;
    if (stringlowercmp("uncompress", *it++)) 
        return false;
    cmd.clear();
    cmd.insert(cmd.end(), it, tokens.end());
    return processFilterCmd(cmd);
}
recoll-1.36.1/common/textsplit.cpp0000644000175000017500000012007714520460621014046 00000000000000/* Copyright (C) 2004-2019 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "autoconfig.h"

#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include "textsplit.h"
#include "log.h"
//#define UTF8ITER_CHECK
#include "utf8iter.h"
#include "uproplist.h"
#include "smallut.h"
#include "rclconfig.h"

using namespace std;
/**
 * Splitting a text into words. The code in this file works with utf-8
 * in a semi-clean way (see uproplist.h). Ascii still gets special
 * treatment in the sense that many special characters can only be
 * ascii (e.g. @, _,...). However, this compromise works quite well
 * while being much more light-weight than a full-blown Unicode
 * approach (ICU...)
 */


// Decide if we treat katakana as western scripts, splitting into
// words instead of n-grams. This is not absurd (katakana is a kind of
// alphabet, albeit phonetic and syllabic and is mostly used to
// transcribe western words), but it does not work well because
// japanese uses separator-less compound katakana words, and because
// the plural terminaisons are irregular and would need a specialized
// stemmer. So we for now process katakana as the rest of cjk, using
// ngrams
#undef KATAKANA_AS_WORDS

// Same for Korean syllabic, and same problem. However we have a
// runtime option to use an external text analyser for hangul, so this
// is defined at compile time.
#define HANGUL_AS_WORDS


// Ascii character classes: we have three main groups, and then some chars
// are their own class because they want special handling.
// 
// We have an array with 256 slots where we keep the character types. 
// The array could be fully static, but we use a small function to fill it 
// once.
// The array is actually a remnant of the original version which did no utf8.
// Only the lower 127 slots are  now used, but keep it at 256
// because it makes some tests in the code simpler.
const unsigned int charclasses_size = 256;
enum CharClass {LETTER=256, SPACE=257, DIGIT=258, WILD=259, 
                A_ULETTER=260, A_LLETTER=261, SKIP=262};
static int charclasses[charclasses_size];


bool          TextSplit::o_processCJK{true};
unsigned int  TextSplit::o_CJKNgramLen{2};
bool          TextSplit::o_noNumbers{false};
bool          TextSplit::o_deHyphenate{false};
int           TextSplit::o_maxWordLength{40};
int           TextSplit::o_maxWordsInSpan{6};

static const int o_CJKMaxNgramLen{5};
bool o_exthangultagger{false};

// This is changed to 0 if _ is processed as a letter
static char underscoreatend = '_';

void TextSplit::staticConfInit(RclConfig *config)
{
    config->getConfParam("maxtermlength", &o_maxWordLength);
    config->getConfParam("maxwordsinspan", &o_maxWordsInSpan);

    bool bvalue{false};
    if (config->getConfParam("nocjk", &bvalue) && bvalue == true) {
        o_processCJK = false;
    } else {
        o_processCJK = true;
        int ngramlen;
        if (config->getConfParam("cjkngramlen", &ngramlen)) {
            o_CJKNgramLen = (unsigned int)(ngramlen <= o_CJKMaxNgramLen ?
                                           ngramlen : o_CJKMaxNgramLen);
        }
    }

    bvalue = false;
    if (config->getConfParam("nonumbers", &bvalue)) {
        o_noNumbers = bvalue;
    }

    bvalue = false;
    if (config->getConfParam("dehyphenate", &bvalue)) {
        o_deHyphenate = bvalue;
    }

    bvalue = false;
    if (config->getConfParam("backslashasletter", &bvalue)) {
        if (bvalue) {
        } else {
            charclasses[int('\\')] = SPACE;
        }
    }

    bvalue = false;
    if (config->getConfParam("underscoreasletter", &bvalue)) {
        if (bvalue) {
            charclasses[int('_')] = A_LLETTER;
            underscoreatend = 0;
        }
    }

    string kotagger;
    config->getConfParam("hangultagger", kotagger);
    if (!kotagger.empty()) {
        o_exthangultagger = true;
        koStaticConfInit(config, kotagger);
    }
}


// Non-ascii UTF-8 characters are handled with sets holding all
// characters with interesting properties. This is far from full-blown
// management of Unicode properties, but seems to do the job well
// enough in most common cases
static vector vpuncblocks;
static std::unordered_set spunc;
static std::unordered_set visiblewhite;
static std::unordered_set sskip;

class CharClassInit {
public:
    CharClassInit() {
        unsigned int i;

        // Set default value for all: SPACE
        for (i = 0 ; i < 256 ; i ++)
            charclasses[i] = SPACE;

        char digits[] = "0123456789";
        for (i = 0; i  < strlen(digits); i++)
            charclasses[int(digits[i])] = DIGIT;

        char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        for (i = 0; i  < strlen(upper); i++)
            charclasses[int(upper[i])] = A_ULETTER;

        char lower[] = "abcdefghijklmnopqrstuvwxyz";
        for (i = 0; i  < strlen(lower); i++)
            charclasses[int(lower[i])] = A_LLETTER;

        char wild[] = "*?[]";
        for (i = 0; i  < strlen(wild); i++)
            charclasses[int(wild[i])] = WILD;

        // Characters with special treatment:
        //
        // The first ones are mostly span-constructing "glue"
        // characters, for example those typically allowing us to
        // search for an email address as a whole (bob@isp.org instead
        // of as a phrase "bob isp org"
        //
        // The case of the minus sign is a complicated one. It went
        // from glue to non-glue to glue along Recoll versions. 
        // See minus-hyphen-dash.txt in doc/notes
        char special[] = ".@+-#'_\n\r\f";
        for (i = 0; i  < strlen(special); i++)
            charclasses[int(special[i])] = special[i];

        for (i = 0; i < sizeof(unipunc) / sizeof(int); i++) {
            spunc.insert(unipunc[i]);
        }
        spunc.insert((unsigned int)-1);

        for (i = 0; i < sizeof(unipuncblocks) / sizeof(int); i++) {
            vpuncblocks.push_back(unipuncblocks[i]);
        }
        assert((vpuncblocks.size() % 2) == 0);

        for (i = 0; i < sizeof(avsbwht) / sizeof(int); i++) {
            visiblewhite.insert(avsbwht[i]);
        }
        for (i = 0; i < sizeof(uniskip) / sizeof(int); i++) {
            sskip.insert(uniskip[i]);
        }
    }
};
static const CharClassInit charClassInitInstance;

static inline bool isvisiblewhite(int c)
{
    return visiblewhite.find(c) != visiblewhite.end();
}

static inline int whatcc(unsigned int c)
{
    if (c <= 127) {
        return charclasses[c]; 
    } else {
        if (c == 0x2010 || c == 0x2019 || c == 0x275c || c == 0x02bc) {
            return c;
        } else if (sskip.find(c) != sskip.end()) {
            return SKIP;
        } else if (spunc.find(c) != spunc.end()) {
            return SPACE;
        } else {
            auto it = lower_bound(vpuncblocks.begin(), vpuncblocks.end(), c);
            if (it == vpuncblocks.end())
                return LETTER;
            if (c == *it)
                return SPACE;
            if ((it - vpuncblocks.begin()) % 2 == 1) {
                return SPACE;
            } else {
                return LETTER;
            }
        } 
    }
}

// testing whatcc...
#if 0
unsigned int testvalues[] = {'a', '0', 0x80, 0xbf, 0xc0, 0x05c3, 0x1000, 
                             0x2000, 0x2001, 0x206e, 0x206f, 0x20d0, 0x2399, 
                             0x2400, 0x2401, 0x243f, 0x2440, 0xff65};
int ntest = sizeof(testvalues) / sizeof(int);
for (int i = 0; i < ntest; i++) {
    int ret = whatcc(testvalues[i]);
    printf("Tested value 0x%x, returned value %d %s\n",
           testvalues[i], ret, ret == LETTER ? "LETTER" : 
           ret == SPACE ? "SPACE" : "OTHER");
}
#endif

// CJK Unicode character detection. CJK text is indexed using an n-gram
// method, we do not try to extract words. There have been tentative
// exceptions for katakana and hangul, not successful because, even if
// these are closer to european text, they are still too different for
// the normal word splitter to work well on them. katakana and hangul
// are processed by the n-gram splitter at the moment.
//
// 1100..11FF; Hangul Jamo (optional: see UNICODE_IS_HANGUL)
// 2E80..2EFF; CJK Radicals Supplement
// 3000..303F; CJK Symbols and Punctuation
// 3040..309F; Hiragana
// 30A0..30FF; Katakana
// 3100..312F; Bopomofo
// 3130..318F; Hangul Compatibility Jamo (optional: see UNICODE_IS_HANGUL)
// 3190..319F; Kanbun
// 31A0..31BF; Bopomofo Extended
// 31C0..31EF; CJK Strokes
// 31F0..31FF; Katakana Phonetic Extensions
// 3200..32FF; Enclosed CJK Letters and Months
// 3300..33FF; CJK Compatibility
// 3400..4DBF; CJK Unified Ideographs Extension A
// 4DC0..4DFF; Yijing Hexagram Symbols
// 4E00..9FFF; CJK Unified Ideographs
// A700..A71F; Modifier Tone Letters
// AC00..D7AF; Hangul Syllables (optional: see UNICODE_IS_HANGUL)
// F900..FAFF; CJK Compatibility Ideographs
// FE30..FE4F; CJK Compatibility Forms
// FF00..FFEF; Halfwidth and Fullwidth Forms
// 20000..2A6DF; CJK Unified Ideographs Extension B
// 2F800..2FA1F; CJK Compatibility Ideographs Supplement
#define UNICODE_IS_CJK(p)                       \
    (((p) >= 0x1100 && (p) <= 0x11FF) ||        \
     ((p) >= 0x2E80 && (p) <= 0x2EFF) ||        \
     ((p) >= 0x3000 && (p) <= 0x9FFF) ||        \
     ((p) >= 0xA700 && (p) <= 0xA71F) ||        \
     ((p) >= 0xAC00 && (p) <= 0xD7AF) ||        \
     ((p) >= 0xF900 && (p) <= 0xFAFF) ||        \
     ((p) >= 0xFE30 && (p) <= 0xFE4F) ||        \
     ((p) >= 0xFF00 && (p) <= 0xFFEF) ||        \
     ((p) >= 0x20000 && (p) <= 0x2A6DF) ||      \
     ((p) >= 0x2F800 && (p) <= 0x2FA1F))

// We should probably map 'fullwidth ascii variants' and 'halfwidth
// katakana variants' to something else.  Look up "Kuromoji" Lucene
// filter, KuromojiNormalizeFilter.java
// 309F is Hiragana.
#ifdef KATAKANA_AS_WORDS
#define UNICODE_IS_KATAKANA(p)                  \
    ((p) != 0x309F &&                           \
     (((p) >= 0x3099 && (p) <= 0x30FF) ||       \
      ((p) >= 0x31F0 && (p) <= 0x31FF)))
#else
#define UNICODE_IS_KATAKANA(p) false
#endif

#ifdef HANGUL_AS_WORDS
// If no external tagger is configured, we process HANGUL as generic
// cjk (n-grams)
#define UNICODE_IS_HANGUL(p) (                  \
        o_exthangultagger &&                    \
        (((p) >= 0x1100 && (p) <= 0x11FF) ||    \
         ((p) >= 0x3130 && (p) <= 0x318F) ||    \
         ((p) >= 0x3200 && (p) <= 0x321e) ||    \
         ((p) >= 0x3248 && (p) <= 0x327F) ||    \
         ((p) >= 0x3281 && (p) <= 0x32BF) ||    \
         ((p) >= 0xAC00 && (p) <= 0xD7AF))      \
        )
#else
#define UNICODE_IS_HANGUL(p) false
#endif

bool TextSplit::isCJK(int c)
{
    PRETEND_USE(c);
    return UNICODE_IS_CJK(c);
}
bool TextSplit::isKATAKANA(int c)
{
    PRETEND_USE(c);
    return UNICODE_IS_KATAKANA(c);
}
bool TextSplit::isHANGUL(int c)
{
    PRETEND_USE(c);
    return UNICODE_IS_HANGUL(c);
}
bool TextSplit::isNGRAMMED(int c)
{
    PRETEND_USE(c);
    return UNICODE_IS_CJK(c) && !UNICODE_IS_KATAKANA(c) &&
        !UNICODE_IS_HANGUL(c);
}


// This is used to detect katakana/other transitions, which must trigger a word split (there is not
// always a separator, and katakana is otherwise treated like other, in the same routine, unless cjk
// which has its span reader causing a word break)
enum CharSpanClass {CSC_HANGUL, CSC_CJK, CSC_KATAKANA, CSC_OTHER};
std::vector csc_names {CHARFLAGENTRY(CSC_HANGUL), CHARFLAGENTRY(CSC_CJK),
                                  CHARFLAGENTRY(CSC_KATAKANA), CHARFLAGENTRY(CSC_OTHER)};

// Final term checkpoint: do some checking (the kind which is simpler to do here than in the main
// loop), then send term to our client.
inline bool TextSplit::emitterm(bool isspan, string &w, int pos, size_t btstart, size_t btend)
{
    LOGDEB2("TextSplit::emitterm: [" << w << "] pos " << pos << "\n");

    int l = int(w.length());

#ifdef TEXTSPLIT_STATS
    // Update word length statistics. Do this before we filter out
    // long words because stats are used to detect bad text
    if (!isspan || m_wordLen == m_span.length())
        m_stats.newsamp(m_wordChars);
#else
    PRETEND_USE(isspan);
#endif

    if (l == 0 || l > o_maxWordLength) {
        return true;
    }
    if (l == 1) {
        // 1 byte word: we index single ascii letters and digits, but nothing else. We might want to
        // turn this into a test for a single utf8 character instead ?
        unsigned int c = ((unsigned int)w[0]) & 0xff;
        if (charclasses[c] != A_ULETTER && charclasses[c] != A_LLETTER && 
            charclasses[c] != DIGIT &&
            (!(m_flags & TXTS_KEEPWILD) || charclasses[c] != WILD)
            ) {
            //cerr << "ERASING single letter term " << c << endl;
            return true;
        }
    }
    if (pos != m_prevpos || l != m_prevlen) {
        bool ret = takeword(w, pos, int(btstart), int(btend));
        m_prevpos = pos;
        m_prevlen = int(w.length());
        return ret;
    }
    LOGDEB2("TextSplit::emitterm:dup: [" << w << "] pos " << pos << "\n");
    return true;
}

// Check for an acronym/abbreviation ie I.B.M. This only works with ascii (we do not detect
// non-ascii utf-8 acronyms)
bool TextSplit::span_is_acronym(string *acronym)
{
    bool acron = false;

    if (m_wordLen != m_span.length() && m_span.length() > 2 && m_span.length() <= 20) {
        acron = true;
        // Check odd chars are '.'
        for (unsigned int i = 1 ; i < m_span.length(); i += 2) {
            if (m_span[i] != '.') {
                acron = false;
                break;
            }
        }
        if (acron) {
            // Check that even chars are letters
            for (unsigned int i = 0 ; i < m_span.length(); i += 2) {
                int c = m_span[i];
                if (!((c >= 'a' && c <= 'z')||(c >= 'A' && c <= 'Z'))) {
                    acron = false;
                    break;
                }
            }
        }
    }
    if (acron) {
        for (unsigned int i = 0; i < m_span.length(); i += 2) {
            *acronym += m_span[i];
        }
    }
    return acron;
}


// Generate terms from span. Have to take into account the flags: ONLYSPANS, NOSPANS, noNumbers
bool TextSplit::words_from_span(size_t bp)
{
#if 0
    cerr << "Span: [" << m_span << "] " << " bp " << bp <<
        " w_i_s size: " << m_words_in_span.size() <<  " : ";
    for (unsigned int i = 0; i < m_words_in_span.size(); i++) {
        cerr << " [" << m_words_in_span[i].first << " " << m_words_in_span[i].second << "] ";
                
    }
    cerr << endl;
#endif
    int spanwords = int(m_words_in_span.size());
    // It seems that something like: tv_combo-sample_util.Po@am_quote can get the splitter to call
    // doemit with a span of '@' and words_in_span==0, which then causes a crash when accessing
    // words_in_span[0] if the stl assertions are active (e.g. Fedora RPM build). Not too sure what
    // the right fix would be, but for now, just defend against it
    if (spanwords == 0) {
        return true;
    }
    int pos = m_spanpos;
    // Byte position of the span start
    size_t spboffs = bp - m_span.size();

    if (o_deHyphenate && spanwords == 2 && m_span[m_words_in_span[0].second] == '-') {
        unsigned int s0 = m_words_in_span[0].first;
        unsigned int l0 = m_words_in_span[0].second - m_words_in_span[0].first;
        unsigned int s1 = m_words_in_span[1].first;
        unsigned int l1 = m_words_in_span[1].second - m_words_in_span[1].first;
        string word = m_span.substr(s0, l0) + m_span.substr(s1, l1);
        if (l0 && l1) 
            emitterm(false, word, m_spanpos, spboffs, spboffs + m_words_in_span[1].second);
    }

    for (int i = 0; i < ((m_flags&TXTS_ONLYSPANS) ? 1 : spanwords); i++) {

        int deb = m_words_in_span[i].first;
        bool noposinc = m_words_in_span[i].second == deb;
        for (int j = ((m_flags&TXTS_ONLYSPANS) ? spanwords-1 : i);
             j < ((m_flags&TXTS_NOSPANS) ? i+1 : spanwords);
             j++) {

            int fin = m_words_in_span[j].second;
            //cerr << "i " << i << " j " << j << " deb " << deb << " fin " << fin << endl;
            if (fin - deb > int(m_span.size()))
                break;
            string word(m_span.substr(deb, fin-deb));
            if (!emitterm(j != i+1, word, pos, spboffs+deb, spboffs+fin))
                return false;
        }
        if (!noposinc)
            ++pos;
    }
    return true;
}

/**
 * A method called at word boundaries (different places in
 * text_to_words()), to adjust the current state of the parser, and
 * possibly generate term(s). While inside a span (words linked by
 * glue characters), we just keep track of the word boundaries. Once
 * actual white-space is reached, we get called with spanerase set to
 * true, and we process the span, calling the emitterm() routine for
 * each generated term.
 * 
 * The object flags can modify our behaviour, deciding if we only emit
 * single words (bill, recoll, org), only spans (bill@recoll.org), or
 * words and spans (bill@recoll.org, recoll.org, jf, recoll...)
 * 
 * @return true if ok, false for error. Splitting should stop in this case.
 * @param spanerase Set if the current span is at its end. Process it.
 * @param bp        The current BYTE position in the stream (it's beyond the current span data).
 */
inline bool TextSplit::doemit(bool spanerase, size_t _bp)
{
    int bp = int(_bp);
    LOGDEB2("TextSplit::doemit: sper " << spanerase << " bp " << bp <<
            " spp " << m_spanpos << " spanwords " << m_words_in_span.size() <<
            " wS " << m_wordStart << " wL " << m_wordLen << " inn " <<
            m_inNumber << " span [" << m_span << "]\n");

    if (m_wordLen) {
        // We have a current word. Remember it

        if (int(m_words_in_span.size()) >= o_maxWordsInSpan) {
            // Limit max span word count
            spanerase = true;
        }
        
        if (!(o_noNumbers && m_inNumber)) {
            m_words_in_span.push_back({m_wordStart, m_wordStart + m_wordLen});
            m_wordpos++;
        }
        m_wordLen = m_wordChars = 0;
    }

    if (!spanerase) {
        // Not done with this span. Just update relative word start offset.
        m_wordStart = int(m_span.length());
        return true;
    }

    // Span is done (too long or span-terminating character). Produce terms and reset it.
    string acronym;
    if (span_is_acronym(&acronym)) {
        if (!emitterm(false, acronym, m_spanpos, bp - m_span.length(), bp))
            return false;
    }

    if (!words_from_span(bp)) {
        return false;
    }
    discardspan();
    return true;
}

void TextSplit::discardspan()
{
    m_span.clear();
    m_words_in_span.clear();
    m_spanpos = m_wordpos;
    m_wordStart = 0;
    m_wordLen = m_wordChars = 0;
}

static inline bool isalphanum(int what, unsigned int flgs)
{
    return what == A_LLETTER || what == A_ULETTER ||
        what == DIGIT || what == LETTER ||
        ((flgs & TextSplit::TXTS_KEEPWILD) && what == WILD);
}
static inline bool isdigit(int what, unsigned int flgs)
{
    return what == DIGIT || ((flgs & TextSplit::TXTS_KEEPWILD) && what == WILD);
}

#ifdef TEXTSPLIT_STATS
#define STATS_INC_WORDCHARS ++m_wordChars
#else
#define STATS_INC_WORDCHARS
#endif

vector splitFlags{
    {TextSplit::TXTS_NOSPANS, "nospans"},
    {TextSplit::TXTS_ONLYSPANS, "onlyspans"},
    {TextSplit::TXTS_KEEPWILD, "keepwild"}
};

/** 
 * Splitting a text into terms to be indexed.
 * We basically emit a word every time we see a separator, but some chars are
 * handled specially so that special cases, ie, c++ and jfd@recoll.com etc, 
 * are handled properly,
 */
bool TextSplit::text_to_words(const string &in)
{
    LOGDEB1("TextSplit::text_to_words: docjk " << o_processCJK << "(" <<
            o_CJKNgramLen <<  ") " << flagsToString(splitFlags, m_flags) <<
            " [" << in.substr(0,50) << "]\n");

    if (in.empty())
        return true;

    // Reset the data members relative to splitting state
    clearsplitstate();
    
    bool pagepending = false;
    bool nlpending = false;
    bool softhyphenpending = false;

    // Running count of non-alphanum chars. Reset when we see one;
    int nonalnumcnt = 0;

    Utf8Iter it(in);
#if defined(KATAKANA_AS_WORDS) || defined(HANGUL_AS_WORDS)
    int prev_csc = -1;
#endif
    for (; !it.eof() && !it.error(); it++) {
        unsigned int c = *it;
        nonalnumcnt++;

        if (c == (unsigned int)-1) {
            LOGERR("Textsplit: error occurred while scanning UTF-8 string\n");
            return false;
        }

        CharSpanClass csc;
        if (UNICODE_IS_KATAKANA(c)) {
            csc = CSC_KATAKANA;
        } else if (UNICODE_IS_HANGUL(c)) {
            csc = CSC_HANGUL;
        } else if (UNICODE_IS_CJK(c)) {
            csc = CSC_CJK;
        } else {
            csc = CSC_OTHER;
        }

        if (o_processCJK && (csc == CSC_CJK || csc == CSC_HANGUL)) {
            // CJK character hit. Hangul processing may be special.

            // Do like at EOF with the current non-cjk data.
            if (m_wordLen || m_span.length()) {
                if (!doemit(true, it.getBpos()))
                    return false;
            }
            // Hand off situation to the appropriate routine.
            if (csc == CSC_HANGUL) {
                if (!ko_to_words(&it, &c)) {
                    LOGERR("Textsplit: scan error in korean handler\n");
                    return false;
                }
            } else {
                if (!cjk_to_words(it, &c)) {
                    LOGERR("Textsplit: scan error in cjk handler\n");
                    return false;
                }
            }
            // Check for eof, else c contains the first non-cjk
            // character after the cjk sequence, just go on.
            if (it.eof() || it.error())
                break;
        }

#if defined(KATAKANA_AS_WORDS) || defined(HANGUL_AS_WORDS)
        // Only needed if we have script transitions inside this
        // routine, else the call to cjk_to_words does the job (so do
        // nothing right after a CJK section). Because
        // katakana-western transitions sometimes have no whitespace
        // (and maybe hangul too, but probably not).
        if (prev_csc != CSC_CJK && prev_csc != CSC_HANGUL &&
            csc != prev_csc && (m_wordLen || m_span.length())) {
            LOGDEB2("csc " << valToString(csc_names, csc) << " prev_csc " <<
                    valToString(csc_names, prev_csc) << " wl " <<
                    m_wordLen << " spl " << m_span.length() << endl);
            if (!doemit(true, it.getBpos())) {
                return false;
            }
        }
        prev_csc = csc;
#endif

        int cc = whatcc(c);

        switch (cc) {
        case SKIP:
            // Special-case soft-hyphen. To work, this depends on the
            // fact that only SKIP calls "continue" inside the
            // switch. All the others will do the softhyphenpending
            // reset after the switch
            if (c == 0xad) {
                softhyphenpending = true;
            } else {
                softhyphenpending = false;
            }
            // Skips the softhyphenpending reset
            continue;

        case DIGIT:
            nonalnumcnt = 0;
            if (m_wordLen == 0)
                m_inNumber = true;
            m_wordLen += it.appendchartostring(m_span);
            STATS_INC_WORDCHARS;
            break;

        case SPACE:
            nonalnumcnt = 0;
        SPACE:
            if (m_wordLen || m_span.length()) {
                if (!doemit(true, it.getBpos()))
                    return false;
                m_inNumber = false;
            }
            if (pagepending) {
                pagepending = false;
                newpage(m_wordpos);
            }
            if (nlpending) {
                nlpending = false;
                newline(m_wordpos);
            }
            break;

        case WILD:
            if (m_flags & TXTS_KEEPWILD)
                goto NORMALCHAR;
            else
                goto SPACE;
            break;

        case '-':
        case '+':
            if (m_wordLen == 0) {
                // + or - don't start a term except if this looks like
                // it's going to be to be a number
                if (isdigit(whatcc(it[it.getCpos()+1]), m_flags)) {
                    // -10
                    m_inNumber = true;
                    m_wordLen += it.appendchartostring(m_span);
                    STATS_INC_WORDCHARS;
                    break;
                } 
            } else if (m_inNumber) {
                if ((m_span[m_span.length() - 1] == 'e' ||
                     m_span[m_span.length() - 1] == 'E')) {
                    if (isdigit(whatcc(it[it.getCpos()+1]), m_flags)) {
                        m_wordLen += it.appendchartostring(m_span);
                        STATS_INC_WORDCHARS;
                        break;
                    }
                }
            } else {
                int nextc = it[it.getCpos()+1];
                if (cc == '+') {
                    if (nextc == '+' || nextc == -1 || isvisiblewhite(nextc)) {
                        // someword++[+...] !
                        m_wordLen += it.appendchartostring(m_span);
                        STATS_INC_WORDCHARS;
                        break;
                    }
                } else {
                    // Note about dangling hyphens: we always strip '-' found before whitespace,
                    // even before a newline, then generate two terms, before and after the line
                    // break. We have no way to know if '-' is there because a word was broken by
                    // justification or if it was part of an actual compound word (would need a
                    // dictionary to check). As soft-hyphen *should* be used if the '-' is not part
                    // of the text.
                    if (nextc == -1 || isvisiblewhite(nextc)) {
                        goto SPACE;
                    }
                    if (!doemit(false, it.getBpos()))
                        return false;
                    m_inNumber = false;
                    m_wordStart += it.appendchartostring(m_span);
                    break;
                }
            }
            goto SPACE;

        case '.':
        {
            // Need a little lookahead here. At worse this gets the end null
            int nextc = it[it.getCpos()+1];
            int nextwhat = whatcc(nextc);
            if (m_inNumber) {
                if (!isdigit(nextwhat, m_flags))
                    goto SPACE;
                m_wordLen += it.appendchartostring(m_span);
                STATS_INC_WORDCHARS;
                break;
            } else {
                // Found '.' while not in number

                // Only letters and digits make sense after
                if (!isalphanum(nextwhat, m_flags))
                    goto SPACE;

                // Keep an initial '.' for catching .net, and .34 (aka
                // 0.34) but this adds quite a few spurious terms !
                if (m_span.length() == 0) {
                    // Check for number like .1
                    if (isdigit(nextwhat, m_flags)) {
                        m_inNumber = true;
                        m_wordLen += it.appendchartostring(m_span);
                    } else {
                        m_words_in_span.push_back(pair(m_wordStart, m_wordStart));
                        m_wordStart += it.appendchartostring(m_span);
                    }
                    STATS_INC_WORDCHARS;
                    break;
                }

                // '.' between words: span glue
                if (m_wordLen) {
                    if (!doemit(false, it.getBpos()))
                        return false;
                    m_wordStart += it.appendchartostring(m_span);
                }
            }
        }
        break;

        case 0x2010: // hyphen
        case 0x2019: // variations on single quote
        case 0x275c:
        case 0x02bc:
        case '@':
        case '_': // If underscoreasletter is set, we'll never get this
        case '\'':
        {
            // If in word, potential span: o'brien, jf@dockes.org,
            // else just ignore
            int nextc = it[it.getCpos()+1];
            if (nextc == -1 || isvisiblewhite(nextc)) {
                goto SPACE;
            }
            if (m_wordLen) {
                if (!doemit(false, it.getBpos()))
                    return false;
                m_inNumber = false;
                m_wordStart += it.appendchartostring(m_span);
            }
        }
        break;

        case '#':  {
            int w = whatcc(it[it.getCpos()+1]);
            // Keep it only at the beginning of a word (hashtag), 
            if (m_wordLen == 0 && isalphanum(w, m_flags)) {
                m_wordLen += it.appendchartostring(m_span);
                STATS_INC_WORDCHARS;
                break;
            }
            // or at the end (special case for c# ...)
            if (m_wordLen > 0) {
                if (w == SPACE || w == '\n' || w == '\r') {
                    m_wordLen += it.appendchartostring(m_span);
                    STATS_INC_WORDCHARS;
                    break;
                }
            }
            goto SPACE;
        }
            break;

        case '\n':
            nlpending = true;
            /* FALLTHROUGH */
        case '\r':
            if (softhyphenpending) {
                // Don't reset soft-hyphen
                continue;
            } else {
                // Normal case: EOL is white space
                goto SPACE;
            }
            break;

        case '\f':
            pagepending = true;
            goto SPACE;
            break;

#ifdef RCL_SPLIT_CAMELCASE
            // Camelcase handling. 
            // If we get uppercase ascii after lowercase ascii, emit word.
            // This emits "camel" when hitting the 'C' of camelCase
            // Not enabled by defaults as this makes phrase searches quite 
            // confusing. 
            // ie "MySQL manual" is matched by "MySQL manual" and 
            // "my sql manual" but not "mysql manual"

            // A possibility would be to emit both my and sql at the
            // same position. All non-phrase searches would work, and
            // both "MySQL manual" and "mysql manual" phrases would
            // match too. "my sql manual" would not match, but this is
            // not an issue.
        case A_ULETTER:
            if (m_span.length() && 
                charclasses[(unsigned char)m_span[m_span.length() - 1]] == 
                A_LLETTER) {
                if (m_wordLen) {
                    if (!doemit(false, it.getBpos()))
                        return false;
                }
            }
            goto NORMALCHAR;

            // CamelCase handling.
            // If we get lowercase after uppercase and the current
            // word length is bigger than one, it means we had a
            // string of several upper-case letters:  an
            // acronym (readHTML) or a single letter article (ALittleHelp).
            // Emit the uppercase word before proceeding
        case A_LLETTER:
            if (m_span.length() && 
                charclasses[(unsigned char)m_span[m_span.length() - 1]] == 
                A_ULETTER && m_wordLen > 1) {
                // Multiple upper-case letters. Single letter word
                // or acronym which we want to emit now
                m_wordLen--;
                if (!doemit(false, it.getBpos()))
                    return false;
                // m_wordstart could be 0 here if the span was reset
                // for excessive length
                if (m_wordStart)
                    m_wordStart--;
                m_wordLen++;
            }
            goto NORMALCHAR;
#endif /* CAMELCASE */

        default:
        NORMALCHAR:
            nonalnumcnt = 0;
            if (m_inNumber && c != 'e' && c != 'E') {
                m_inNumber = false;
            }
            m_wordLen += it.appendchartostring(m_span);
            STATS_INC_WORDCHARS;
            break;
        }
        softhyphenpending = false;
    }
    if (m_wordLen || m_span.length()) {
        if (!doemit(true, it.getBpos()))
            return false;
    }
    return true;
}

// We output ngrams for exemple for char input a b c and ngramlen== 2, 
// we generate: a ab b bc c as words
//
// This is very different from the normal behaviour, so we don't use
// the doemit() and emitterm() routines
//
// The routine is sort of a mess and goes to show that we'd probably
// be better off converting the whole buffer to utf32 on entry...
bool TextSplit::cjk_to_words(Utf8Iter& it, unsigned int *cp)
{
    LOGDEB1("cjk_to_words: m_wordpos " << m_wordpos << "\n");

    // We use an offset buffer to remember the starts of the utf-8
    // characters which we still need to use.
    assert(o_CJKNgramLen < o_CJKMaxNgramLen);
    string::size_type boffs[o_CJKMaxNgramLen+1];
    string mybuf;
    string::size_type myboffs[o_CJKMaxNgramLen+1];
    
    // Current number of valid offsets;
    unsigned int nchars = 0;
    unsigned int c = 0;
    bool spacebefore{false};
    for (; !it.eof() && !it.error(); it++) {
        c = *it;
        // We had a version which ignored whitespace for some time,
        // but this was a bad idea. Only break on a non-cjk
        // *alphabetic* character, except if following punctuation, in
        // which case we return for any non-cjk. This allows compound
        // cjk+numeric spans, or punctuated cjk spans to be
        // continually indexed as cjk. The best approach is a matter
        // of appreciation...
        if (!UNICODE_IS_CJK(c) &&
            (spacebefore || (c > 255 || isalpha(c)))) {
            // Return to normal handler
            break;
        }
        if (whatcc(c) == SPACE) {
            // Flush the ngram buffer and go on
            nchars = 0;
            mybuf.clear();
            spacebefore = true;
            continue;
        } else {
            spacebefore = false;
        }
        if (nchars == o_CJKNgramLen) {
            // Offset buffer full, shift it. Might be more efficient
            // to have a circular one, but things are complicated
            // enough already...
            for (unsigned int i = 0; i < nchars-1; i++) {
                boffs[i] = boffs[i+1];
            }
            for (unsigned int i = 0; i < nchars-1; i++) {
                myboffs[i] = myboffs[i+1];
            }
        }  else {
            nchars++;
        }

        // Copy to local buffer, and note local offset
        myboffs[nchars-1] = mybuf.size();
        it.appendchartostring(mybuf);
        // Take note of document byte offset for this character.
        boffs[nchars-1] = it.getBpos();

        // Output all new ngrams: they begin at each existing position
        // and end after the new character. onlyspans->only output
        // maximum words, nospans=> single chars
        if (!(m_flags & TXTS_ONLYSPANS) || nchars == o_CJKNgramLen) {
            int btend = it.getBpos() + it.getBlen();
            int loopbeg = (m_flags & TXTS_NOSPANS) ? nchars-1 : 0;
            int loopend = (m_flags & TXTS_ONLYSPANS) ? 1 : nchars;
            for (int i = loopbeg; i < loopend; i++) {
                // Because of the whitespace handling above there may be whitespace in the
                // buffer. Strip it from the output words. This means that the offs/size will be
                // slightly off (->highlights), to be fixed one day.
                auto word = mybuf.substr(myboffs[i], mybuf.size() - myboffs[i]);
                if (!takeword(trimstring(word, "\r\n\f \t"), 
                              m_wordpos - (nchars - i - 1), boffs[i], btend)) {
                    return false;
                }
            }

            if ((m_flags & TXTS_ONLYSPANS)) {
                // Only spans: don't overlap: flush buffer
                nchars = 0;
                mybuf.clear();
            }
        }
        // Increase word position by one, other words are at an
        // existing position. This could be subject to discussion...
        m_wordpos++;
    }

    // If onlyspans is set, there may be things to flush in the buffer
    // first
    if ((m_flags & TXTS_ONLYSPANS) && nchars > 0 && nchars != o_CJKNgramLen)  {
        int btend = int(it.getBpos()); // Current char is out
        // See comment before takeword above.
        auto word = mybuf.substr(myboffs[0], mybuf.size() - myboffs[0]);
        if (!takeword(trimstring(word, "\r\n\f \t"), m_wordpos - nchars, boffs[0], btend)) {
            return false;
        }
    }

    // Reset state, saving term position, and return the found non-cjk
    // unicode character value. The current input byte offset is kept
    // in the utf8Iter
    int pos = m_wordpos;
    clearsplitstate();
    m_spanpos = m_wordpos = pos;
    *cp = c;
    return true;
}

// Specialization for countWords 
class TextSplitCW : public TextSplit {
public:
    int wcnt;
    TextSplitCW(Flags flags) : TextSplit(flags), wcnt(0) {}
    bool takeword(const string &, int, int, int) {
        wcnt++;
        return true;
    }
};

int TextSplit::countWords(const string& s, TextSplit::Flags flgs)
{
    TextSplitCW splitter(flgs);
    splitter.text_to_words(s);
    return splitter.wcnt;
}

bool TextSplit::hasVisibleWhite(const string &in)
{
    Utf8Iter it(in);
    for (; !it.eof() && !it.error(); it++) {
        unsigned int c = (unsigned char)*it;
        if (c == (unsigned int)-1) {
            LOGERR("hasVisibleWhite: error while scanning UTF-8 string\n");
            return false;
        }
        if (isvisiblewhite(c))
            return true;
    }
    return false;
}

template  bool u8stringToStrings(const string &s, T &tokens)
{
    Utf8Iter it(s);

    string current;
    tokens.clear();
    enum states {SPACE, TOKEN, INQUOTE, ESCAPE};
    states state = SPACE;
    for (; !it.eof() && !it.error(); it++) {
        unsigned int c = *it;
        if (visiblewhite.find(c) != visiblewhite.end()) 
            c = ' ';
        if (c == (unsigned int)-1) {
            LOGERR("TextSplit::stringToStrings: error while scanning UTF-8 "
                   "string\n");
            return false;
        }

        switch (c) {
        case '"': 
            switch(state) {
            case SPACE: state = INQUOTE; continue;
            case TOKEN: goto push_char;
            case ESCAPE: state = INQUOTE; goto push_char;
            case INQUOTE: tokens.push_back(current);current.clear();
                state = SPACE; continue;
            }
            break;
        case '\\': 
            switch(state) {
            case SPACE: 
            case TOKEN: state=TOKEN; goto push_char;
            case INQUOTE: state = ESCAPE; continue;
            case ESCAPE: state = INQUOTE; goto push_char;
            }
            break;

        case ' ': 
        case '\t': 
        case '\n': 
        case '\r': 
            switch(state) {
            case SPACE: continue;
            case TOKEN: tokens.push_back(current); current.clear();
                state = SPACE; continue; 
            case INQUOTE: 
            case ESCAPE: goto push_char;
            }
            break;

        default:
            switch(state) {
            case ESCAPE: state = INQUOTE; break;
            case SPACE:  state = TOKEN;  break;
            case TOKEN: 
            case INQUOTE: break;
            }
        push_char:
            it.appendchartostring(current);
        }
    }

    // End of string. Process residue, and possible error (unfinished quote)
    switch(state) {
    case SPACE: break;
    case TOKEN: tokens.push_back(current); break;
    case INQUOTE: 
    case ESCAPE: return false;
    }
    return true;
}

bool TextSplit::stringToStrings(const string &s, vector &tokens)
{
    return u8stringToStrings >(s, tokens);
}
recoll-1.36.1/common/conf_post.h0000644000175000017500000000357714427373216013463 00000000000000#ifndef _AUTOCONF_CONF_POST_H_INCLUDED
#define _AUTOCONF_CONF_POST_H_INCLUDED

#ifdef _WIN32


#ifdef _MSC_VER
#include "safewindows.h"
// gmtime is supposedly thread-safe on windows
#define gmtime_r(A, B) gmtime(A)
#define localtime_r(A,B) localtime_s(B,A)
typedef int mode_t;
#define fseeko _fseeki64
#define ftello (off_t)_ftelli64
#define ftruncate _chsize_s
#define PATH_MAX MAX_PATH
#define RCL_ICONV_INBUF_CONST 1
#define HAVE_STRUCT_TIMESPEC
#define strdup _strdup
#define timegm _mkgmtime

#else // End _MSC_VER -> Gminw

// Allow use of features specific to Windows 7 or later.
#define WINVER 0x0601
#define _WIN32_WINNT 0x0601
#define LOGFONTW void
#include "safewindows.h"

#undef RCL_ICONV_INBUF_CONST
#define timegm portable_timegm

#endif // GMinw only

typedef int pid_t;
inline int readlink(const char *a, void *b, int c)
{
    a = a; b = b; c = c;
    return -1;
}

#define MAXPATHLEN PATH_MAX
typedef DWORD32 u_int32_t;
typedef DWORD64 u_int64_t;
typedef unsigned __int8 u_int8_t;
typedef int ssize_t;
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#define chdir _chdir

#ifndef R_OK
#define R_OK 4
#endif
#ifndef W_OK
#define W_OK 2
#endif
#ifndef X_OK
#define X_OK 4
#endif

#define S_ISLNK(X) false
#define lstat stat

#endif // _WIN32

#ifndef PRETEND_USE
#  define PRETEND_USE(expr) ((void)(expr))
#endif /* PRETEND_USE */

// It's complicated to really detect gnu gcc because other compilers define __GNUC__
// See stackoverflow questions/38499462/how-to-tell-clang-to-stop-pretending-to-be-other-compilers
#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
#define REAL_GCC   __GNUC__ // probably
#endif

#if defined(REAL_GCC) && defined(HAVE_FEATURES_H)
// Older gcc versions pretended to supply std::regex, but the resulting programs mostly crashed.
#include 
#if ! __GNUC_PREREQ(6,0)
#define NO_STD_REGEX 1
#endif
#endif

#endif /* INCLUDED */
recoll-1.36.1/common/rclconfig.h0000644000175000017500000003616114444307651013431 00000000000000/* Copyright (C) 2004-2023 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _RCLCONFIG_H_INCLUDED_
#define _RCLCONFIG_H_INCLUDED_

#include 
#include 
#include 
#include 
#include 
#include 
#include 

class ParamStale;
class ConfSimple;
class ConfNull;

// Hold the description for an external metadata-gathering command
struct MDReaper {
    std::string fieldname;
    std::vector cmdv;
};

// Data associated to a indexed field name: 
struct FieldTraits {
    std::string pfx; // indexing prefix,
    uint32_t valueslot{0};
    enum ValueType {STR, INT};
    ValueType valuetype{STR};
    // INT type fields are stored as Xapian values, and padded so that they can be compared as
    // strings. valuelen is the padding width.
    int    valuelen{0};
    int    wdfinc{1}; // Index time term frequency increment (default 1)
    double boost{1.0}; // Query time boost (default 1.0)
    bool   pfxonly{false}; // Suppress prefix-less indexing
    bool   noterms{false}; // Don't add term to highlight data (e.g.: rclbes)
};

class RclConfig {
public:

    /** Constructor: we will look for the configuration location, except if this was specified on the
     * command line and passed through argcnf */
    RclConfig(const std::string *argcnf = nullptr);
    RclConfig(const RclConfig &r);

    ~RclConfig();

    RclConfig& operator=(const RclConfig &r);

    /** Return a writable clone of the main config. This belongs to the caller (must delete it when
     * done) */
    ConfNull *cloneMainConfig();

    /** (re)Read recoll.conf */
    bool updateMainConfig();

    bool ok() const;
    const std::string &getReason() const;

    /** Return the directory where this configuration is stored. This was possibly silently created
     *  by the rclconfig constructor it it is the default one (~/.recoll) and it did not exist
     *  yet. */
    std::string getConfDir() const;
    std::string getCacheDir() const;

    /** Check if the config files were modified since we read them */
    bool sourceChanged() const;

    /** Returns true if this is ~/.recoll */
    bool isDefaultConfig() const;
    /** Get the local value for /usr/local/share/recoll/ */
    const std::string& getDatadir() const;

    /** Set current directory reference, and fetch automatic parameters. */
    void setKeyDir(const std::string &dir);
    std::string getKeyDir() const;

    /** Get generic configuration parameter according to current keydir */
    bool getConfParam(const std::string& name, std::string& value, bool shallow=false) const;
    /** Variant with autoconversion to int */
    bool getConfParam(const std::string &name, int *value, bool shallow=false) const;
    /** Variant with autoconversion to double */
    bool getConfParam(const std::string &name, double *value, bool shallow=false) const;
    /** Variant with autoconversion to bool */
    bool getConfParam(const std::string &name, bool *value, bool shallow=false) const;
    /** Variant with conversion to vector
     *  (stringToStrings). Can fail if the string is malformed. */
    bool getConfParam(const std::string& name, std::vector *value,
                      bool shallow=false) const;
    /** Variant with conversion to unordered_set
     *  (stringToStrings). Can fail if the string is malformed. */
    bool getConfParam(const std::string &name, std::unordered_set *v, 
                      bool shallow=false) const;
    /** Variant with conversion to vector */
    bool getConfParam(const std::string &name, std::vector *value, bool shallow=false) const;

    enum ThrStage {ThrIntern=0, ThrSplit=1, ThrDbWrite=2};
    std::pair getThrConf(ThrStage who) const;

    /** 
     * Get list of config names under current sk, with possible wildcard filtering 
     */
    std::vector getConfNames(const char *pattern = nullptr) const;

    /** Check if name exists anywhere in config */
    bool hasNameAnywhere(const std::string& nm) const;

    /** Get default charset for current keydir (was set during setKeydir) 
     * filenames are handled differently */
    const std::string &getDefCharset(bool filename = false) const;

    /** Get list of top directories. This is needed from a number of places
     * and needs some cleaning-up code. An empty list is always an error, no
     * need for other status 
     * @param formonitor if set retrieve the list for real time monitoring 
     *         (if the monitor list does not exist we return the normal one).
     */
    std::vector getTopdirs(bool formonitor = false) const;

    std::string getConfdirPath(const char *varname, const char *dflt) const;
    std::string getCachedirPath(const char *varname, const char *dflt) const;
    /** Get database and other directories */
    std::string getDbDir() const;
    std::string getWebcacheDir() const;
    std::string getMboxcacheDir() const;
    std::string getAspellcacheDir() const;
    /** Get stoplist file name */
    std::string getStopfile() const;
    /** Get synonym groups file name */
    std::string getIdxSynGroupsFile() const;
    /** Get indexing pid file name */
    std::string getPidfile() const;
    /** Get indexing status file name */
    std::string getIdxStatusFile() const;
    std::string getIdxStopFile() const;
    /** Do path translation according to the ptrans table */
    void urlrewrite(const std::string& dbdir, std::string& url) const;
    ConfSimple *getPTrans();
    /** Get Web Queue directory name */
    std::string getWebQueueDir() const;

    /** Get list of skipped file names for current keydir */
    std::vector& getSkippedNames();
    /** Get list of file name filters for current keydir (only those names indexed) */
    std::vector& getOnlyNames();

    /** Get list of skipped paths patterns. Doesn't depend on the keydir */
    std::vector getSkippedPaths() const;
    /** Get list of skipped paths patterns, daemon version (may add some)
        Doesn't depend on the keydir */
    std::vector getDaemSkippedPaths() const;

    /** Return list of no content suffixes. Used by confgui, indexing uses
        inStopSuffixes() for testing suffixes */
    std::vector& getStopSuffixes();

    /** 
     * mimemap: Check if file name should be ignored because of suffix
     *
     * The list of ignored suffixes is initialized on first call, and
     * not changed for subsequent setKeydirs.
     */
    bool inStopSuffixes(const std::string& fn);

    /** 
     * Check in mimeconf if input mime type is a compressed one, and
     * return command to uncompress if it is.
     *
     * The returned command has substitutable places for input file name 
     * and temp dir name, and will return output name
     */
    bool getUncompressor(const std::string &mtpe, std::vector& cmd) const;

    /** mimemap: compute mimetype */
    std::string getMimeTypeFromSuffix(const std::string &suffix) const;
    /** mimemap: get a list of all indexable mime types defined */
    std::vector getAllMimeTypes() const;
    /** mimemap: Get appropriate suffix for mime type. This is inefficient */
    std::string getSuffixFromMimeType(const std::string &mt) const;

    /** mimeconf: get input filter for mimetype */
    std::string getMimeHandlerDef(const std::string &mimetype, bool filtertypes=false,
                             const std::string& fn = std::string());

    /** For lines like: [name = some value; attr1 = value1; attr2 = val2]
     * Separate the value and store the attributes in a ConfSimple 
     *
     * In the value part, semi-colons inside double quotes are ignored, and double quotes are
     * conserved. In the common case where the string is then processed by stringToStrings() to
     * build a command line, this allows having semi-colons inside arguments. However, no backslash
     * escaping is possible, so that, for example "bla\"1;2\"" would not work (the value part
     * would stop at the semi-colon).
     *
     * @param whole the raw value.
     */
    static bool valueSplitAttributes(const std::string& whole, std::string& value,
                                     ConfSimple& attrs) ;

    /** Compute difference between 'base' and 'changed', as elements to be
     * added and substracted from base. Input and output strings are in
     * stringToStrings() format. */
    static void setPlusMinus(const std::string& base, const std::set& changed,
                             std::string& plus, std::string& minus);

    /** Return the locale's character set */
    static const std::string& getLocaleCharset();
    
    /** Return icon path for mime type and tag */
    std::string getMimeIconPath(const std::string &mt, const std::string& apptag) const;

    /** mimeconf: get list of file categories */
    bool getMimeCategories(std::vector&) const;
    /** mimeconf: is parameter one of the categories ? */
    bool isMimeCategory(const std::string&) const;
    /** mimeconf: get list of mime types for category */
    bool getMimeCatTypes(const std::string& cat, std::vector&) const;

    /** mimeconf: get list of gui filters (doc cats by default */
    bool getGuiFilterNames(std::vector&) const;
    /** mimeconf: get query lang frag for named filter */
    bool getGuiFilter(const std::string& filtername, std::string& frag) const;

    /** fields: get field prefix from field name. Use additional query
        aliases if isquery is set */
    bool getFieldTraits(const std::string& fldname, const FieldTraits **,
                        bool isquery = false) const;

    const std::set& getStoredFields() const;

    std::set getIndexedFields() const;

    /** Get canonic name for possible alias */
    std::string fieldCanon(const std::string& fld) const;

    /** Get canonic name for possible alias, including query-only aliases */
    std::string fieldQCanon(const std::string& fld) const;

    /** Get xattr name to field names translations */
    const std::map& getXattrToField() const;

    /** Get value of a parameter inside the "fields" file. Only some filters 
     * use this (ie: mh_mail). The information specific to a given filter
     * is typically stored in a separate section(ie: [mail]) 
     */
    std::vector getFieldSectNames(const std::string &sk, const char* = nullptr) const;
    bool getFieldConfParam(const std::string &name, const std::string &sk, std::string &value)
        const;

    /** mimeview: get/set external viewer exec string(s) for mimetype(s) */
    std::string getMimeViewerDef(const std::string &mimetype, const std::string& apptag, 
                            bool useall) const;
    std::set getMimeViewerAllEx() const;
    bool setMimeViewerAllEx(const std::set& allex);
    bool getMimeViewerDefs(std::vector >&) const;
    bool setMimeViewerDef(const std::string& mimetype, const std::string& cmd);
    /** Check if mime type is designated as needing no uncompress before view
     * (if a file of this type is found compressed). Default is true,
     *  exceptions are found in the nouncompforviewmts mimeview list */
    bool mimeViewerNeedsUncomp(const std::string &mimetype) const;

    /** Retrieve extra metadata-gathering commands */
    const std::vector& getMDReapers();

    /** Store/retrieve missing helpers description string */
    bool getMissingHelperDesc(std::string&) const;
    void storeMissingHelperDesc(const std::string &s);

    /** Replace simple command name(s) inside vector with full
     * paths. May have to replace two if the first entry is an
     * interpreter name */
    bool processFilterCmd(std::vector& cmd) const;

    /** Build command vector for python script, possibly prepending 
        interpreter on Windows */
    bool pythonCmd(
        const std::string& script,  std::vector& cmd) const;
    
    /** Find exec file for external filter. 
     *
     * If the input is an absolute path, we just return it. Else We
     * look in $RECOLL_FILTERSDIR, "filtersdir" from the config file,
     * $RECOLL_CONFDIR/. If nothing is found, we return the input with
     * the assumption that this will be used with a PATH-searching
     * exec.
     *
     * @param cmd is normally the command name from the command string 
     *    returned by getMimeHandlerDef(), but this could be used for any 
     *    command. If cmd begins with a /, we return cmd without
     *    further processing.
     */
    std::string findFilter(const std::string& cmd) const;

    /** Thread config init is not done automatically because not all
        programs need it and it uses the debug log so that it's better to
        call it after primary init */
    void initThrConf();

    const std::string& getOrigCwd() const;

    class Internal;
    friend class ParamStale;
private:
    std::unique_ptr m;
};

// This global variable defines if we are running with an index
// stripped of accents and case or a raw one. Ideally, it should be
// constant, but it needs to be initialized from the configuration, so
// there is no way to do this. It never changes after initialization
// of course. Changing the value on a given index imposes a
// reset. When using multiple indexes, all must have the same value
extern bool o_index_stripchars;

// Store document text in index. Allows extracting snippets from text
// instead of building them from index position data. Has become
// necessary for versions of Xapian 1.6, which have dropped support
// for the chert index format, and adopted a setup which renders our
// use of positions list unacceptably slow in cases. The text just
// translated from its original format to UTF-8 plain text, and is not
// stripped of upper-case, diacritics, or punctuation signs. Defaults to true.
extern bool o_index_storedoctext;

// This global variable defines if we use mtime instead of ctime for
// up-to-date tests. This is mostly incompatible with xattr indexing,
// in addition to other issues. See recoll.conf comments. 
extern bool o_uptodate_test_use_mtime;

// Up to version 1.33.x recoll never stem-expanded terms inside phrase searches. There was actually
// no obvious reasons for this. The default was not changed, but a modifier was added (x) to allow
// for on-request phrase expansion. Setting the following to true will change the default behaviour
// (use 'l' to disable for a specific instance)
extern bool o_expand_phrases;

#endif /* _RCLCONFIG_H_INCLUDED_ */
recoll-1.36.1/common/unacpp.h0000644000175000017500000000326014516377154012750 00000000000000/* Copyright (C) 2004-2023 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _UNACPP_H_INCLUDED_
#define _UNACPP_H_INCLUDED_

#include 

// A small stringified wrapper for unac.c
enum UnacOp {UNACOP_UNAC = 1, UNACOP_FOLD = 2, UNACOP_UNACFOLD = 3};
extern bool unacmaybefold(const std::string& in, std::string& out, 
              const char *encoding, UnacOp what);

// Utility function to determine if string begins with capital
extern bool unaciscapital(const std::string& in);
// Utility function to determine if string has upper-case anywhere
extern bool unachasuppercase(const std::string& in);
// Utility function to determine if any character is accented. This
// approprialey ignores the characters from unac_except_chars which
// are really separate letters
extern bool unachasaccents(const std::string& in);

// Simplified interface to unacmaybefold for cases where we just mimic tolower()
extern std::string unactolower(const std::string& in);

#endif /* _UNACPP_H_INCLUDED_ */
recoll-1.36.1/common/webstore.cpp0000644000175000017500000000520214427373216013641 00000000000000/* Copyright (C) 2011 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "autoconfig.h"

#include "webstore.h"

#include 

#include "cstr.h"
#include "circache.h"
#include "log.h"
#include "rclconfig.h"
#include "pathut.h"
#include "rcldoc.h"
#include "conftree.h"

using std::string;

const string cstr_bgc_mimetype("mimetype");

WebStore::WebStore(RclConfig *cnf) 
{
    string ccdir = cnf->getWebcacheDir();

    int maxmbs = 40;
    cnf->getConfParam("webcachemaxmbs", &maxmbs);
    if ((m_cache = new CirCache(ccdir)) == nullptr) {
        LOGERR("WebStore: cant create CirCache object\n" );
        return;
    }
    if (!m_cache->create(int64_t(maxmbs)*1000*1024, CirCache::CC_CRUNIQUE)) {
        LOGERR("WebStore: cache file creation failed: " <<
               m_cache->getReason() << "\n");
        delete m_cache;
        m_cache = nullptr;
        return;
    }
}

WebStore::~WebStore()
{
    delete m_cache;
}

// Read  document from cache. Return the metadata as an Rcl::Doc
// @param htt Web Hit Type 
bool WebStore::getFromCache(const string& udi, Rcl::Doc &dotdoc, 
                            string& data, string *htt)
{
    string dict;

    if (nullptr == m_cache) {
        LOGERR("WebStore::getFromCache: cache is null\n");
        return false;
    }
    if (!m_cache->get(udi, dict, &data)) {
        LOGDEB("WebStore::getFromCache: get failed\n");
        return false;
    }

    ConfSimple cf(dict, 1);
    
    if (htt)
        cf.get(Rcl::Doc::keybght, *htt, cstr_null);

    // Build a doc from saved metadata 
    cf.get(cstr_url, dotdoc.url, cstr_null);
    cf.get(cstr_bgc_mimetype, dotdoc.mimetype, cstr_null);
    cf.get(cstr_fmtime, dotdoc.fmtime, cstr_null);
    cf.get(cstr_fbytes, dotdoc.pcbytes, cstr_null);
    dotdoc.sig.clear();
    auto names = cf.getNames(cstr_null);
    for (const auto& nm : names) {
        cf.get(nm, dotdoc.meta[nm], cstr_null);
    }
    dotdoc.meta[Rcl::Doc::keyudi] = udi;
    return true;
}
recoll-1.36.1/common/syngroups.h0000644000175000017500000000322014410615043013510 00000000000000/* Copyright (C) 2015-2021 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifndef _SYNGROUPS_H_INCLUDED_
#define _SYNGROUPS_H_INCLUDED_

#include 
#include 
#include 

// Manage synonym groups. This is very different from stemming and
// case/diac expansion because there is no reference form: all terms
// in a group are equivalent.
class SynGroups {
public:
    SynGroups();
    ~SynGroups();
    SynGroups(const SynGroups&) = delete;
    SynGroups& operator=(const SynGroups&) = delete;
    SynGroups(const SynGroups&&) = delete;
    SynGroups& operator=(const SynGroups&&) = delete;

    bool setfile(const std::string& fname);
    std::vector getgroup(const std::string& term) const;
    const std::set& getmultiwords() const;
    size_t getmultiwordsmaxlength() const;
    const std::string& getpath() const;
    bool ok() const;
private:
    class Internal;
    Internal *m;
};

#endif /* _SYNGROUPS_H_INCLUDED_ */
recoll-1.36.1/common/uproplist.h0000644000175000017500000001670414410615043013513 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _PROPLIST_H_INCLUDED_
#define _PROPLIST_H_INCLUDED_


/** 
 * A subset of Unicode chars that we consider word breaks when we
 * split text in words. 
 *
 * This is used as a quick fix to the ascii-based code, and is not correct.
 * the correct way would be to do what http://www.unicode.org/reports/tr29/ 
 * says. 
*/

// Punctuation chararacters blocks array.  Each block is defined by a
// starting and ending code point (both included). MUST BE SORTED.
static const unsigned unipuncblocks[] = {
    // Start of latin-1 supplement block, up to capital A grave
    0x0080, 0x00BF,
    // General punctuation
    0x2000, 0x206F,
    // Superscripts and subscripts
    0x2070, 0x209F,
    // Currency symbols
    0x20A0, 0x20CF,
    // Letterlike symbols
    0x2100, 0x214f,
    // Number forms
    0x2150, 0x218F,
    // Arrows
    0x2190, 0x21FF,
    // Mathematical Operators
    0x2200, 0x22FF,
    // Miscellaneous Technical
    0x2300, 0x23FF,
    // Control Pictures
    0x2400, 0x243F,
    // Optical Character Recognition
    0x2440, 0x245F,
    // Enclosed Alphanumerics
    0x2460, 0x24FF,
    // Box Drawing
    0x2500, 0x257F,
    // Block Elements
    0x2580, 0x259F,
    // Geometric Shapes
    0x25A0, 0x25FF,
    // Miscellaneous Symbols
    0x2600, 0x26FF,
    // Dingbats
    0x2700, 0x27BF,
    // Miscellaneous Mathematical Symbols-A     
    0x27C0, 0x27EF,
    // Supplemental Arrows-A
    0x27F0, 0x27FF,
    // Supplemental Arrows-B
    0x2900, 0x297F,
    // Miscellaneous Mathematical Symbols-B     
    0x2980,  0x29FF,
    // Supplemental Mathematical Operators
    0x2A00, 0x2AFF,
    // Miscellaneous Symbols and Arrows
    0x2B00, 0x2BFF,
};

// Other punctuation characters list. Not all punctuation is in a
// separate block some is found in the middle of alphanumeric codes.
static const unsigned int unipunc[] = {
    0x00D7, /* MULTIPLICATION SIGN */
    0x00F7, /* DIVISION SIGN */
    0x037E, /* GREEK QUESTION MARK */
    0x0387, /* GREEK ANO TELEIA */
    0x055C, /* ARMENIAN EXCLAMATION MARK */
    0x055E, /* ARMENIAN QUESTION MARK */
    0x0589, /* ARMENIAN FULL STOP */
    0x058A, /* ARMENIAN HYPHEN */
    0x05C3, /* HEBREW PUNCTUATION SOF PASUQ */
    0x060C, /* ARABIC COMMA */
    0x061B, /* ARABIC SEMICOLON */
    0x061F, /* ARABIC QUESTION MARK */
    0x06D4, /* ARABIC FULL STOP */
    0x0964, /* DEVANAGARI DANDA */
    0x0965, /* DEVANAGARI DOUBLE DANDA */
    0x166E, /* CANADIAN SYLLABICS FULL STOP */
    0x1680, /* OGHAM SPACE MARK */
    0x16EB, /* RUNIC SINGLE PUNCTUATION */
    0x16EC, /* RUNIC MULTIPLE PUNCTUATION */
    0x16ED, /* RUNIC CROSS PUNCTUATION */
    0x1803, /* MONGOLIAN FULL STOP */
    0x1806, /* MONGOLIAN TODO SOFT HYPHEN */
    0x1809, /* MONGOLIAN MANCHU FULL STOP */
    0x180E, /* MONGOLIAN VOWEL SEPARATOR */
    0x2E2E, /* REVERSED QUESTION MARK;Po;0;ON;;;;;N;;;;; */
    0x3000, /* IDEOGRAPHIC SPACE*/
    0x3002, /* IDEOGRAPHIC FULL STOP*/
    0x300C, /* LEFT CORNER BRACKET*/
    0x300D, /* RIGHT CORNER BRACKET*/
    0x300E, /* LEFT WHITE CORNER BRACKET*/
    0x300F, /* RIGHT WHITE CORNER BRACKET*/
    0x301C, /* WAVE DASH*/
    0x301D, /* REVERSED DOUBLE PRIME QUOTATION MARK*/
    0x301E, /* LOW DOUBLE PRIME QUOTATION MARK*/
    0x3030, /* WAVY DASH*/
    0x30FB, /* KATAKANA MIDDLE DOT*/
    0xC2B6, /* PILCROW SIGN;So;0;ON;;;;;N;PARAGRAPH SIGN;;;; */
    0xC3B7, /* DIVISION SIGN;Sm;0;ON;;;;;N;;;;; */
    0xFE31, /* PRESENTATION FORM FOR VERTICAL EM DASH*/
    0xFE32, /* PRESENTATION FORM FOR VERTICAL EN DASH*/
    0xFE41, /* PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET*/
    0xFE42, /* PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET*/
    0xFE43, /* PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET*/
    0xFE44, /* PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET*/
    0xFE50, /* [3] SMALL COMMA..SMALL FULL STOP*/
    0xFE51, /* [3] SMALL COMMA..SMALL FULL STOP*/
    0xFE52, /* STOP*/
    0xFE52, /* [3] SMALL COMMA..SMALL FULL STOP*/
    0xFE54, /* [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK*/
    0xFE55, /* [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK*/
    0xFE56, /* [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK*/
    0xFE57, /* [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK*/
    0xFE58, /* SMALL EM DASH */
    0xFE63, /* SMALL HYPHEN-MINUS */
    0xFF01, /* FULLWIDTH EXCLAMATION MARK */
    0xFF02, /* FULLWIDTH QUOTATION MARK */
    0xFF03, /* FULLWIDTH NUMBER SIGN */
    0xFF04, /* FULLWIDTH DOLLAR SIGN */
    0xFF05, /* FULLWIDTH PERCENT SIGN */
    0xFF06, /* FULLWIDTH AMPERSAND */
    0xFF07, /* FULLWIDTH APOSTROPHE */
    0xFF08, /* FULLWIDTH LEFT PARENTHESIS */
    0xFF09, /* FULLWIDTH RIGHT PARENTHESIS */
    0xFF0A, /* FULLWIDTH ASTERISK */
    0xFF0B, /* FULLWIDTH PLUS SIGN */
    0xFF0C, /* FULLWIDTH COMMA */
    0xFF0D, /* FULLWIDTH HYPHEN-MINUS */
    0xFF0E, /* FULLWIDTH FULL STOP */
    0xFF0F, /* FULLWIDTH SOLIDUS  */
    0xFF1A, /* [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON*/
    0xFF1B, /* [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON*/
    0xFF1F, /* FULLWIDTH QUESTION MARK*/
    0xFF61, /* HALFWIDTH IDEOGRAPHIC FULL STOP*/
    0xFF62, /* HALFWIDTH LEFT CORNER BRACKET*/
    0xFF63, /* HALFWIDTH RIGHT CORNER BRACKET*/
    0xFF64, /* HALFWIDTH IDEOGRAPHIC COMMA*/
    0xFF65, /* HALFWIDTH KATAKANA MIDDLE DOT*/
};

// Characters that should just be discarded. Some of these are in the
// above blocks, but this array is tested first, so it's not worth
// breaking the blocks
static const unsigned int uniskip[] = {
    0x00AD, /* SOFT HYPHEN */
    0x034F, /* COMBINING GRAPHEME JOINER */
    0x2027, /* HYPHENATION POINT */
    0x200C, /* ZERO WIDTH NON-JOINER */
    0x200D, /* ZERO WIDTH JOINER */
    0x2060, /* WORD JOINER . Actually this should not be ignored but used to 
         * prevent a word break... */
};

/* Things that would visibly break a block of text, rendering obvious the need
 * of quotation if a phrase search is wanted */
static const unsigned int avsbwht[] = {
    0x0009, /* CHARACTER TABULATION */
    0x000A, /* LINE FEED */
    0x000D, /* CARRIAGE RETURN */
    0x0020, /* SPACE;Zs;0;WS */
    0x00A0, /* NO-BREAK SPACE;Zs;0;CS */
    0x1680, /* OGHAM SPACE MARK;Zs;0;WS */
    0x180E, /* MONGOLIAN VOWEL SEPARATOR;Zs;0;WS */
    0x2000, /* EN QUAD;Zs;0;WS */
    0x2001, /* EM QUAD;Zs;0;WS */
    0x2002, /* EN SPACE;Zs;0;WS */
    0x2003, /* EM SPACE;Zs;0;WS */
    0x2004, /* THREE-PER-EM SPACE;Zs;0;WS */
    0x2005, /* FOUR-PER-EM SPACE;Zs;0;WS */
    0x2006, /* SIX-PER-EM SPACE;Zs;0;WS */
    0x2007, /* FIGURE SPACE;Zs;0;WS */
    0x2008, /* PUNCTUATION SPACE;Zs;0;WS */
    0x2009, /* THIN SPACE;Zs;0;WS */
    0x200A, /* HAIR SPACE;Zs;0;WS */
    0x202F, /* NARROW NO-BREAK SPACE;Zs;0;CS */
    0x205F, /* MEDIUM MATHEMATICAL SPACE;Zs;0;WS */
    0x3000, /* IDEOGRAPHIC SPACE;Zs;0;WS */
};

#endif // _PROPLIST_H_INCLUDED_
recoll-1.36.1/common/rclinit.h0000644000175000017500000000556214426500174013123 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _RCLINIT_H_INCLUDED_
#define _RCLINIT_H_INCLUDED_

#include 

class RclConfig;
/**
 * Initialize by reading configuration, opening log file, etc.
 *
 * This must be called from the main thread before starting any others. It sets
 * up the global signal handling. other threads must call recoll_threadinit()
 * when starting.
 *
 * @param flags   misc modifiers. These are currently only used to customize
 *      the log file and verbosity.
 * @param cleanup function to call before exiting (atexit)
 * @param sigcleanup function to call on terminal signal (INT/HUP...) This
 *       should typically set a flag which tells the program (recoll,
 *       recollindex etc.. to exit as soon as possible (after closing the db,
 *       etc.). cleanup will then be called by exit().
 * @param reason in case of error: output string explaining things
 * @param argcnf Configuration directory name from the command line (overriding
 *               default and environment
 * @return the parsed configuration.
 */
enum RclInitFlags {RCLINIT_NONE = 0, RCLINIT_DAEMON = 1, RCLINIT_IDX = 2,
                   RCLINIT_PYTHON = 4};
// Kinds of termination requests, in addition to the normal signal
// values. Passed as type int to sigcleanup() when it is not invoked
// directly as a sig handler. Note that because of the existence of
// sigset_t, we are pretty sure that no signals can have a high value
enum RclSigKind {
    // System resume from sleep
    RCLSIG_RESUME = 1002};
                 
extern RclConfig *recollinit(int flags,
                             void (*cleanup)(void), void (*sigcleanup)(int),
                             std::string& reason, const std::string *argcnf = nullptr);

// Threads need to call this to block signals.
// The main thread handles all signals.
extern void recoll_threadinit();

// Check if main thread
extern bool recoll_ismainthread();

// Should be called while exiting asap when critical cleanup (db
// close) has been performed. Only useful for the indexer (writes to
// the db), and only actually does something on Windows.
extern void recoll_exitready();

#endif /* _RCLINIT_H_INCLUDED_ */
recoll-1.36.1/common/syngroups.cpp0000644000175000017500000001617214410615043014055 00000000000000/* Copyright (C) 2014-2019 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "autoconfig.h"

#include "syngroups.h"

#include "log.h"
#include "smallut.h"
#include "pathut.h"

#include 
#include 
#include 
#include 
#include 
#include "safesysstat.h"

using namespace std;

// Note that we are storing each term twice. I don't think that the
// size could possibly be a serious issue, but if it was, we could
// reduce the storage a bit by storing (short hash)-> vector
// correspondances in the direct map, and then checking all the
// resulting groups for the input word.
//
// As it is, a word can only index one group (the last it is found
// in). It can be part of several groups though (appear in
// expansions). I really don't know what we should do with multiple
// groups anyway
class SynGroups::Internal {
public:
    Internal() {}
    void setpath(const string& fn) {
        path = path_canon(fn);
        stat(path.c_str(), &st);
    }
    bool samefile(const string& fn) {
        string p1 = path_canon(fn);
        if (path != p1) {
            return false;
        }
        struct stat st1;
        if (stat(p1.c_str(), &st1) != 0) {
            return false;
        }
        return st.st_mtime == st1.st_mtime && st.st_size == st1.st_size;
    }

    void clear() {
        ok = false;
        terms.clear();
        groups.clear();
        multiwords.clear();
        multiwords_maxlen = 0;
        path.clear();
        st.st_mtime = 0;
        st.st_size = 0;
    }
    
    bool ok{false};
    // Term to group num 
    std::unordered_map terms;
    // Group num to group
    vector > groups;

    // Aux: set of multiword synonyms used for generating multiword
    // terms while indexing
    std::set multiwords;
    size_t multiwords_maxlen{0};
    
    std::string path;
    struct stat st;
};

bool SynGroups::ok() const
{
    return m && m->ok;
}

SynGroups::~SynGroups()
{
    delete m;
}

SynGroups::SynGroups()
    : m(new Internal)
{
}

bool SynGroups::setfile(const string& fn)
{
    LOGDEB("SynGroups::setfile(" << fn << ")\n");
    if (!m) {
        return false;
    }

    if (fn.empty()) {
        m->clear();
        return true;
    }

    if (m->samefile(fn)) {
        LOGDEB("SynGroups::setfile: unchanged: " << fn << endl);
        return true;
    }
    LOGDEB("SynGroups::setfile: parsing file " << fn << endl);
    
    ifstream input;
    input.open(fn.c_str(), ios::in);
    if (!input.is_open()) {
        LOGSYSERR("SynGroups:setfile", "open", fn);
        return false;
    }        

    string cline;
    bool appending = false;
    string line;
    bool eof = false;
    int lnum = 0;
    m->clear();
    for (;;) {
        cline.clear();
        getline(input, cline);
        if (!input.good()) {
            if (input.bad()) {
                LOGERR("Syngroup::setfile(" << fn << "):Parse: input.bad()\n");
                return false;
            }
            // Must be eof ? But maybe we have a partial line which
            // must be processed. This happens if the last line before
            // eof ends with a backslash, or there is no final \n
            eof = true;
        }
        lnum++;

        {
            string::size_type pos = cline.find_last_not_of("\n\r");
            if (pos == string::npos) {
                cline.clear();
            } else if (pos != cline.length()-1) {
                cline.erase(pos+1);
            }
        }

        if (appending)
            line += cline;
        else
            line = cline;

        // Note that we trim whitespace before checking for backslash-eol
        // This avoids invisible whitespace problems.
        trimstring(line);
        if (line.empty() || line.at(0) == '#') {
            if (eof)
                break;
            continue;
        }
        if (line[line.length() - 1] == '\\') {
            line.erase(line.length() - 1);
            appending = true;
            continue;
        }
        appending = false;

        vector words;
        if (!stringToStrings(line, words)) {
            LOGERR("SynGroups:setfile: " << fn << ": bad line " << lnum <<
                   ": " << line << "\n");
            continue;
        }

        if (words.empty())
            continue;
        if (words.size() == 1) {
            LOGERR("Syngroup::setfile(" << fn << "):single term group at line "
                   << lnum << " ??\n");
            continue;
        }

        m->groups.push_back(words);
        for (const auto& word : words) {
            m->terms[word] = m->groups.size()-1;
        }
        LOGDEB1("SynGroups::setfile: group: [" <<
                stringsToString(m->groups.back()) << "]\n");
    }

    for (const auto& group : m->groups) {
        for (const auto& term : group) {
            std::vector words;
            stringToTokens(term, words);
            if (words.size() > 1) {
                std::string multiword;
                for (const auto& word : words) {
                    if (!multiword.empty()) {
                        multiword += " ";
                    }
                    multiword += word;
                }
                m->multiwords.insert(multiword);
                if (m->multiwords_maxlen < words.size()) {
                    m->multiwords_maxlen = words.size();
                }
            }
        }
    }
    LOGDEB("SynGroups::setfile: got " << m->groups.size() << " distinct terms. "
           "Multiwords: " << stringsToString(m->multiwords) <<"\n");
    m->ok = true;
    m->setpath(fn);
    return true;
}

vector SynGroups::getgroup(const string& term) const
{
    vector ret;
    if (!ok())
        return ret;

    const auto it1 = m->terms.find(term);
    if (it1 == m->terms.end()) {
        LOGDEB0("SynGroups::getgroup: [" << term << "] not found in map\n");
        return ret;
    }

    unsigned int idx = it1->second;
    if (idx >= m->groups.size()) {
        LOGERR("SynGroups::getgroup: line index higher than line count !\n");
        return ret;
    }
    LOGDEB0("SynGroups::getgroup: result: " << stringsToString(m->groups[idx])
            << endl);
    return m->groups[idx];
}

const std::set& SynGroups::getmultiwords() const
{
    return m->multiwords;
}

size_t SynGroups::getmultiwordsmaxlength() const
{
    return m->multiwords_maxlen;
}

const std::string& SynGroups::getpath() const
{
    static string empty;
    return m ? m->path : empty;
}
recoll-1.36.1/common/cstr.cpp0000644000175000017500000000013214410615043012744 00000000000000
#include "cstr.h"
#define RCLIN_CSTR_CPPFILE
#undef _CSTR_H_INCLUDED_
#include "cstr.h"

recoll-1.36.1/common/webstore.h0000644000175000017500000000312414426500174013301 00000000000000/* Copyright (C) 2009 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _webstore_h_included_
#define _webstore_h_included_

#include 

class RclConfig;
namespace Rcl {
class Doc;
}
class CirCache;

/**
 * Manage the CirCache for the Web Queue indexer. Separated from the main
 * indexer code because it's also used for querying (getting the data for a
 * preview 
 */
class WebStore {
public:
    WebStore(RclConfig *config);
    ~WebStore();
    WebStore(const WebStore&) = delete;
    WebStore& operator=(const WebStore&) = delete;

    bool getFromCache(const std::string& udi, Rcl::Doc &doc, std::string& data,
                      std::string *hittype = nullptr);
    // We could write proxies for all the circache ops, but why bother?
    CirCache *cc() {return m_cache;}

private:
    CirCache *m_cache;
};

extern const std::string cstr_bgc_mimetype;

#endif /* _webstore_h_included_ */
recoll-1.36.1/common/rclinit.cpp0000644000175000017500000003551314427373216013463 00000000000000/* Copyright (C) 2004 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 

#include 
#ifdef _WIN32
#include "safewindows.h"
#endif
#include 
#include 
#include 
#if !defined(PUTENV_ARG_CONST)
#include 
#endif

#include 

#include "log.h"
#include "rclconfig.h"
#include "rclinit.h"
#include "pathut.h"
#include "rclutil.h"
#include "unac.h"
#include "smallut.h"
#include "execmd.h"
#include "textsplit.h"
#include "rcldb.h"

using std::string;

std::thread::id mainthread_id;

// Signal etc. processing. We want to be able to properly close the
// index if we are currently writing to it.
//
// This is active if the sigcleanup parameter to recollinit is set,
// which only recollindex does. We arrange for the handler to be
// called when process termination is requested either by the system
// or a user keyboard intr.
//
// The recollindex handler just sets a global termination flag (plus
// the cancelcheck thing), which are tested in all timeout loops
// etc. When the flag is seen, the main thread processing returns, and
// recollindex calls exit().
//
// The other parameter, to recollinit(), cleanup, is set as an
// atexit() routine, it does the job of actually signalling the
// workers to stop and tidy up. It's automagically called by exit().

#ifndef _WIN32
static void siglogreopen(int)
{
    if (recoll_ismainthread())
        Logger::getTheLog("")->reopen("");
}

// We would like to block SIGCHLD globally, but we can't because
// QT uses it. Have to block it inside execmd.cpp
static const int catchedSigs[] = {SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2};
void initAsyncSigs(void (*sigcleanup)(int))
{
    // We ignore SIGPIPE always. All pieces of code which can write to a pipe
    // must check write() return values.
    signal(SIGPIPE, SIG_IGN);

    // Install app signal handler
    if (sigcleanup) {
        struct sigaction action;
        action.sa_handler = sigcleanup;
        action.sa_flags = 0;
        sigemptyset(&action.sa_mask);
        for (unsigned int i = 0; i < sizeof(catchedSigs) / sizeof(int); i++)
            if (signal(catchedSigs[i], SIG_IGN) != SIG_IGN) {
                if (sigaction(catchedSigs[i], &action, nullptr) < 0) {
                    perror("Sigaction failed");
                }
            }
    }

    // Install log rotate sig handler
    {
        struct sigaction action;
        action.sa_handler = siglogreopen;
        action.sa_flags = 0;
        sigemptyset(&action.sa_mask);
        if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
            if (sigaction(SIGHUP, &action, nullptr) < 0) {
                perror("Sigaction failed");
            }
        }
    }
}
void recoll_exitready()
{
}

#else // _WIN32 ->

// Windows signals etc.
//
// ^C can be caught by the signal() emulation, but not ^Break
// apparently, which is why we use the native approach too
//
// When a keyboard interrupt occurs, windows creates a thread inside
// the process and calls the handler. The process exits when the
// handler returns or after at most 10S
//
// This should also work, with different signals (CTRL_LOGOFF_EVENT,
// CTRL_SHUTDOWN_EVENT) when the user exits or the system shuts down).
//
// Unfortunately, this is not the end of the story. It seems that in
// recent Windows version "some kinds" of apps will not reliably
// receive the signals. "Some kind" is variably defined, for example a
// simple test program works when built with vs 2015, but not
// mingw. See the following discussion thread for tentative
// explanations, it seems that importing or not from user32.dll is the
// determining factor.
// https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/abf09824-4e4c-4f2c-ae1e-5981f06c9c6e/windows-7-console-application-has-no-way-of-trapping-logoffshutdown-event?forum=windowscompatibility
// In any case, it appears that the only reliable way to be advised of
// system shutdown or user exit is to create an "invisible window" and
// process window messages, which we now do.

static void (*l_sigcleanup)(int);
static HANDLE eWorkFinished = INVALID_HANDLE_VALUE;

static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
    LOGDEB("CtrlHandler\n" );
    if (l_sigcleanup == 0)
        return FALSE;

    switch(fdwCtrlType) { 
    case CTRL_C_EVENT: 
    case CTRL_CLOSE_EVENT: 
    case CTRL_BREAK_EVENT: 
    case CTRL_LOGOFF_EVENT: 
    case CTRL_SHUTDOWN_EVENT:
    {
        l_sigcleanup(SIGINT);
        LOGDEB0("CtrlHandler: waiting for exit ready\n" );
        DWORD res = WaitForSingleObject(eWorkFinished, INFINITE);
        if (res != WAIT_OBJECT_0) {
            LOGERR("CtrlHandler: exit ack wait failed\n" );
        }
        LOGDEB0("CtrlHandler: got exit ready event, exiting\n" );
        return TRUE;
    }
    default: 
        return FALSE; 
    } 
} 

LRESULT CALLBACK MainWndProc(HWND hwnd , UINT msg , WPARAM wParam,
                             LPARAM lParam)
{
    switch (msg) {
    case WM_POWERBROADCAST:
    {
        LOGDEB("MainWndProc: got powerbroadcast message\n");
        // This gets specific processing because we want to check the
        // state of topdirs on resuming indexing (in case a mounted
        // volume went away).
        if (l_sigcleanup) {
            if (wParam == PBT_APMRESUMEAUTOMATIC ||
                wParam == PBT_APMRESUMESUSPEND) {
                l_sigcleanup(RCLSIG_RESUME);
            }
        }
    }
    break;
    case WM_QUERYENDSESSION:
    case WM_ENDSESSION:
    case WM_DESTROY:
    case WM_CLOSE:
    {
        if (l_sigcleanup) {
            l_sigcleanup(SIGINT);
            LOGDEB("MainWndProc: got end message, waiting for work finished\n");
            DWORD res = WaitForSingleObject(eWorkFinished, INFINITE);
            if (res != WAIT_OBJECT_0) {
                LOGERR("MainWndProc: exit ack wait failed\n" );
            }
        }
        LOGDEB("MainWindowProc: got exit ready event, exiting\n" );
    }
    break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return TRUE;
}

bool CreateInvisibleWindow()
{
    HWND hwnd;
    WNDCLASS wc = {0,0,0,0,0,0,0,0,0,0};

    wc.lpfnWndProc = (WNDPROC)MainWndProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hIcon = LoadIcon(GetModuleHandle(NULL), L"TestWClass");
    wc.lpszClassName = L"TestWClass";
    RegisterClass(&wc);

    hwnd =
        CreateWindowEx(0, L"TestWClass", L"TestWClass", WS_OVERLAPPEDWINDOW,
                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                       CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL,
                       GetModuleHandle(NULL), (LPVOID) NULL);
    if (!hwnd) {
        return FALSE;
    }
    return TRUE;
}

DWORD WINAPI RunInvisibleWindowThread(LPVOID)
{
    MSG msg;
    CreateInvisibleWindow();
    while (GetMessage(&msg, (HWND) NULL , 0 , 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

static const int catchedSigs[] = {SIGINT, SIGTERM};
void initAsyncSigs(void (*sigcleanup)(int))
{
    DWORD tid;
    // Install app signal handler
    if (sigcleanup) {
        l_sigcleanup = sigcleanup;
        for (unsigned int i = 0; i < sizeof(catchedSigs) / sizeof(int); i++) {
            if (signal(catchedSigs[i], SIG_IGN) != SIG_IGN) {
                signal(catchedSigs[i], sigcleanup);
            }
        }
    }

    CreateThread(NULL, 0, RunInvisibleWindowThread, NULL, 0, &tid);
    SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
    eWorkFinished = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (eWorkFinished == INVALID_HANDLE_VALUE) {
        LOGERR("initAsyncSigs: error creating exitready event\n" );
    }
}
void recoll_exitready()
{
    LOGDEB("recoll_exitready()\n" );
    if (!SetEvent(eWorkFinished)) {
        LOGERR("recoll_exitready: SetEvent failed\n" );
    }
}

#endif

RclConfig *recollinit(int flags, 
                      void (*cleanup)(void), void (*sigcleanup)(int), 
                      string &reason, const string *argcnf)
{
    if (cleanup)
        atexit(cleanup);

    // Make sure the locale is set. This is only for converting file names 
    // to utf8 for indexing.
    setlocale(LC_CTYPE, "");

    // Initially log to stderr, at error level.
    Logger::getTheLog("")->setLogLevel(Logger::LLERR);

    initAsyncSigs(sigcleanup);
    
    RclConfig *config = new RclConfig(argcnf);
    if (!config || !config->ok()) {
        reason = "Configuration could not be built:\n";
        if (config)
            reason += config->getReason();
        else
            reason += "Out of memory ?";
        return nullptr;
    }

#ifdef __APPLE__
    // Setting the PATH for a desktop app
    //
    // Apple keeps changing the way to set the environment (PATH) for
    // a desktop app (started by launchd or whatever). Life is too
    // short.
    //
    // The MACPORTS and HOMEBREW flags are set by the resp. portfile
    // and recipee. The hard-coded values of paths added for MACPORTS
    // and HOMEBREW could be replaced with recollhelperpath use. Kept
    // to minimize disturbance.

    const char *cp = getenv("PATH");
    if (!cp) //??
        cp = "";
    string PATH(cp);

#if defined(MACPORTS)
    PATH = string("/opt/local/bin/") + ":" + PATH;
#elif defined(HOMEBREW)
    PATH = string("/opt/homebrew/bin:/usr/local/bin/") + ":" + PATH;
#else
    // Native qt build. Add our own directory to the path so that
    // recoll finds recollindex pkgdatadir:
    // /Applications/recoll.app/Contents/Resources
    //
    // NOTE: This does not work when running from a mounted dmg
    // because the location contains colons:/Volumes/:Users:dockes:Recoll:...
    // which messes with the PATH colon separators of course.
    // 
    // Also, as far as I can see launchd actually includes the
    // directory in the PATH, so this is redundant. Otoh, launchd
    // changes a lot...
    std::string exedir = path_cat(path_getfather(path_pkgdatadir()), "MacOS");
    PATH = exedir + ":" + PATH;
#endif

    std::string rhpp;
    if (config->getConfParam("recollhelperpath", rhpp) && !rhpp.empty()) {
      PATH = rhpp + ":" + PATH;
    }

    setenv("PATH", PATH.c_str(), 1);
    LOGDEB("Rclinit: PATH [" << getenv("PATH") << "]\n");
#endif /* __APPLE__ */

    TextSplit::staticConfInit(config);
    
    // Retrieve the log file name and level. Daemon and batch indexing
    // processes may use specific values, else fall back on common
    // ones.
    string logfilename, loglevel;
    if (flags & RCLINIT_DAEMON) {
        config->getConfParam(string("daemlogfilename"), logfilename);
        config->getConfParam(string("daemloglevel"), loglevel);
    }
    if (flags & RCLINIT_IDX) {
        if (logfilename.empty()) {
            config->getConfParam(string("idxlogfilename"), logfilename);
        }
        if (loglevel.empty()) {
            config->getConfParam(string("idxloglevel"), loglevel);
        }
    }
    if (flags & RCLINIT_PYTHON) {
        if (logfilename.empty()) {
            config->getConfParam(string("pylogfilename"), logfilename);
        }
        if (loglevel.empty()) {
            config->getConfParam(string("pyloglevel"), loglevel);
        }
    }

    if (logfilename.empty())
        config->getConfParam(string("logfilename"), logfilename);
    if (loglevel.empty())
        config->getConfParam(string("loglevel"), loglevel);

    // Initialize logging
    if (!logfilename.empty()) {
        logfilename = path_tildexpand(logfilename);
        // If not an absolute path or stderr, compute relative to config dir.
        if (!path_isabsolute(logfilename) &&
            logfilename.compare("stderr")) {
            logfilename = path_cat(config->getConfDir(), logfilename);
        }
        Logger::getTheLog("")->reopen(logfilename);
    }
    if (!loglevel.empty()) {
        int lev = atoi(loglevel.c_str());
        Logger::getTheLog("")->setLogLevel(Logger::LogLevel(lev));
    }
    LOGINF(Rcl::version_string() << " [" << config->getConfDir() << "]\n");

    // Make sure the locale charset is initialized (so that multiple
    // threads don't try to do it at once).
    config->getDefCharset();

    mainthread_id = std::this_thread::get_id();

    // Init smallut and pathut static values
    pathut_init_mt();
    smallut_init_mt();
    rclutil_init_mt();
    
    // Init execmd.h static PATH and PATHELT splitting
    {string bogus;
        ExecCmd::which("nosuchcmd", bogus);
    }
    
    // Init Unac translation exceptions
    string unacex;
    if (config->getConfParam("unac_except_trans", unacex) && !unacex.empty()) 
        unac_set_except_translations(unacex.c_str());

#ifndef IDX_THREADS
    ExecCmd::useVfork(true);
#else
    // Keep threads init behind log init, but make sure it's done before
    // we do the vfork choice ! The latter is not used any more actually, 
    // we always use vfork except if forbidden by config.
    if ((flags & RCLINIT_IDX)) {
        config->initThrConf();
    }

    bool novfork{false};
    config->getConfParam("novfork", &novfork);
    if (novfork) {
        LOGDEB0("rclinit: will use fork() for starting commands\n" );
        ExecCmd::useVfork(false);
    } else {
        LOGDEB0("rclinit: will use vfork() for starting commands\n" );
        ExecCmd::useVfork(true);
    }
#endif

    int flushmb;
    if (config->getConfParam("idxflushmb", &flushmb) && flushmb > 0) {
        LOGDEB1("rclinit: idxflushmb=" << flushmb <<
                ", set XAPIAN_FLUSH_THRESHOLD to 10E6\n");
        static const char *cp = "XAPIAN_FLUSH_THRESHOLD=1000000";
#ifdef PUTENV_ARG_CONST
        ::putenv(cp);
#else
        ::putenv(strdup(cp));
#endif
    }

    return config;
}

// Signals are handled by the main thread. All others should call this
// routine to block possible signals
void recoll_threadinit()
{
#ifndef _WIN32
    sigset_t sset;
    sigemptyset(&sset);

    for (unsigned int i = 0; i < sizeof(catchedSigs) / sizeof(int); i++)
        sigaddset(&sset, catchedSigs[i]);
    sigaddset(&sset, SIGHUP);
    pthread_sigmask(SIG_BLOCK, &sset, nullptr);
#else
    // Not sure that this is needed at all or correct under windows.
    for (unsigned int i = 0; i < sizeof(catchedSigs) / sizeof(int); i++) {
        if (signal(catchedSigs[i], SIG_IGN) != SIG_IGN) {
            signal(catchedSigs[i], SIG_IGN);
        }
    }
#endif
}

bool recoll_ismainthread()
{
    return std::this_thread::get_id() == mainthread_id;
}
recoll-1.36.1/common/plaintorich.cpp0000644000175000017500000003207014427373216014326 00000000000000/* Copyright (C) 2005-2021 J.F.Dockes
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include "autoconfig.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// #define LOGGER_LOCAL_LOGINC 3

#include "rcldb.h"
#include "rclconfig.h"
#include "log.h"
#include "textsplit.h"
#include "utf8iter.h"
#include "smallut.h"
#include "chrono.h"
#include "plaintorich.h"
#include "cancelcheck.h"
#include "unacpp.h"
#include "hldata.h"

using std::vector;
using std::list;
using std::pair;
using std::set;
using std::unordered_map;
using std::map;
using std::string;

// Text splitter used to take note of the position of query terms
// inside the result text. This is then used to insert highlight tags.
class TextSplitPTR : public TextSplit {
public:

    // Out: begin and end byte positions of query terms/groups in text
    vector m_tboffs;

    TextSplitPTR(const HighlightData& hdata)
        :  m_wcount(0), m_hdata(hdata) {
        // We separate single terms and groups and extract the group
        // terms for computing positions list before looking for group
        // matches. Single terms are stored with a reference to the
        // entry they come with.
        for (unsigned int i = 0; i < hdata.index_term_groups.size(); i++) {
            const HighlightData::TermGroup& tg(hdata.index_term_groups[i]);
            if (tg.kind == HighlightData::TermGroup::TGK_TERM) {
                m_terms[tg.term] = i;
            } else {
                for (const auto& group : tg.orgroups) {
                    for (const auto& term : group) {
                        m_gterms.insert(term);
                    }
                }
            }
        }
    }

    // Accept word and its position. If word is search term, add
    // highlight zone definition. If word is part of search group
    // (phrase or near), update positions list.
    virtual bool takeword(const std::string& term, int pos, int bts, int bte) {
        string dumb = term;
        if (o_index_stripchars) {
            if (!unacmaybefold(term, dumb, "UTF-8", UNACOP_UNACFOLD)) {
                LOGINFO("PlainToRich::takeword: unac failed for [" << term << "]\n");
                return true;
            }
        }

        LOGDEB2("Input dumbbed term: '" << dumb << "' " <<  pos << " " << bts << " " << bte << "\n");

        // If this word is a search term, remember its byte-offset span. 
        map::const_iterator it = m_terms.find(dumb);
        if (it != m_terms.end()) {
            LOGDEB1("search term [" << dumb << "] at bytepos(" << bts << ", " << bte << ")\n");
            m_tboffs.push_back(GroupMatchEntry(bts, bte, it->second));
        }
        
        // If word is part of a search group, update its positions list
        if (m_gterms.find(dumb) != m_gterms.end()) {
            // Term group (phrase/near) handling
            m_plists[dumb].push_back(pos);
            m_gpostobytes[pos] = pair(bts, bte);
            LOGDEB1("Group term [" << dumb << "] at pos " << pos <<
                    " bytepos(" << bts << ", " << bte << ")\n");
        }

        // Check for cancellation request
        if ((m_wcount++ & 0xfff) == 0)
            CancelCheck::instance().checkCancel();

        return true;
    }

    // Must be called after the split to find the phrase/near match positions
    virtual bool matchGroups();

private:
    // Word count. Used to call checkCancel from time to time.
    int m_wcount;

    // In: user query terms
    map    m_terms; 

    // m_gterms holds all the terms in m_groups, as a set for quick lookup
    set    m_gterms;

    const HighlightData& m_hdata;

    // group/near terms word positions.
    unordered_map > m_plists;
    unordered_map > m_gpostobytes;
};


// Look for matches to PHRASE and NEAR term groups and finalize the
// matched regions list (sort it by increasing start then decreasing
// length)
bool TextSplitPTR::matchGroups()
{
    for (unsigned int i = 0; i < m_hdata.index_term_groups.size(); i++) {
        if (m_hdata.index_term_groups[i].kind != HighlightData::TermGroup::TGK_TERM) {
            matchGroup(m_hdata, i, m_plists, m_gpostobytes, m_tboffs);
        }
    }

    // Sort regions by increasing start and decreasing width.  
    // The output process will skip overlapping entries.
    std::sort(m_tboffs.begin(), m_tboffs.end(),
              [](const GroupMatchEntry& a, const GroupMatchEntry& b) -> bool {
                  if (a.offs.first != b.offs.first)
                      return a.offs.first < b.offs.first;
                  return a.offs.second > b.offs.second;
              }
        );
    return true;
}

#ifndef NO_STD_REGEX
// Replace HTTP(s) urls in text/plain with proper HTML anchors so that
// they become clickable in the preview. We don't make a lot of effort
// for validating, or catching things which are probably urls but miss
// a scheme (e.g. www.xxx.com/index.html), because complicated.
static const string urlRE = "(https?://[[:alnum:]~_/.%?&=,#@]+)[[:space:]|]";
static const string urlRep{"$1"};
static std::regex url_re(urlRE);
static string activate_urls(const string& in)
{
    return std::regex_replace(in, url_re, urlRep);
}
#else
static string activate_urls(const string& in)
{
    return in;
}
#endif

// Enrich result text for display inside the gui text window.
//
// We call overridden functions to output header data, beginnings and ends of matches etc.
//
// If the input is text, we output the result in chunks, arranging not to cut in the middle of a
// tag, which would confuse qtextedit. If the input is html, the body is always a single output
// chunk.
bool PlainToRich::plaintorich(
    const string& in, list& out, const HighlightData& hdata, int chunksize)
{
    Chrono chron;
    bool ret = true;
    LOGDEB1("plaintorich: hdata: [" << hdata.toString() << "] in: [" << in << "]\n");

    m_hdata = &hdata;
    // Compute the positions for the query terms.  We use the text splitter to break the text into
    // words, and compare the words to the search terms,
    TextSplitPTR splitter(hdata);
    // Note: the splitter returns the term locations in byte, not character, offsets.
    splitter.text_to_words(in);
    LOGDEB2("plaintorich: split done " << chron.millis() << " mS\n");
    // Compute the positions for NEAR and PHRASE groups.
    splitter.matchGroups();
    LOGDEB2("plaintorich: group match done " << chron.millis() << " mS\n");

    out.clear();
    out.push_back("");
    auto olit = out.begin();

    // Rich text output
    *olit = header();
    
    // No term matches. Happens, for example on a snippet selected for
    // a term match when we are actually looking for a group match
    // (the snippet generator does this...).
    if (splitter.m_tboffs.empty()) {
        LOGDEB1("plaintorich: no term matches\n");
        ret = false;
    }

    // Iterator for the list of input term positions. We use it to
    // output highlight tags and to compute term positions in the
    // output text
    vector::iterator tPosIt = splitter.m_tboffs.begin();
    vector::iterator tPosEnd = splitter.m_tboffs.end();

#if 0
    for (const auto& region : splitter.m_tboffs) {
        auto st = region.offs.first;
        auto nd = region.offs.second;
        LOGDEB0("plaintorich: region: " << st << " " << nd << "\n");
    }
#endif

    // Input character iterator
    Utf8Iter chariter(in);

    // State variables used to limit the number of consecutive empty lines,
    // convert all eol to '\n', and preserve some indentation
    int eol = 0;
    int hadcr = 0;
    int inindent = 1;

    // HTML state
    bool intag = false, inparamvalue = false;
    // My tag state
    int inrcltag = 0;

    string::size_type headend = 0;
    if (m_inputhtml) {
        headend = in.find("");
        if (headend == string::npos)
            headend = in.find("");
        if (headend != string::npos)
            headend += 7;
    }

    for (string::size_type pos = 0; pos != string::npos; pos = chariter++) {
        // Check from time to time if we need to stop
        if ((pos & 0xfff) == 0) {
            CancelCheck::instance().checkCancel();
        }

        // If we still have terms positions, check (byte) position. If
        // we are at or after a term match, mark.
        if (tPosIt != tPosEnd) {
            int ibyteidx = int(chariter.getBpos());
            if (ibyteidx == tPosIt->offs.first) {
                if (!intag && ibyteidx >= (int)headend) {
                    *olit += startMatch((unsigned int)(tPosIt->grpidx));
                }
                inrcltag = 1;
            } else if (ibyteidx == tPosIt->offs.second) {
                // Output end of match region tags
                if (!intag && ibyteidx > (int)headend) {
                    *olit += endMatch();
                }
                // Skip all highlight areas that would overlap this one
                int crend = tPosIt->offs.second;
                while (tPosIt != splitter.m_tboffs.end() && tPosIt->offs.first < crend)
                    tPosIt++;
                inrcltag = 0;
            }
        }
        
        unsigned int car = *chariter;

        if (car == '\n') {
            if (!hadcr)
                eol++;
            hadcr = 0;
            continue;
        } else if (car == '\r') {
            hadcr++;
            eol++;
            continue;
        } else if (eol) {
            // Got non eol char in line break state. Do line break;
            inindent = 1;
            hadcr = 0;
            if (eol > 2)
                eol = 2;
            while (eol) {
                if (!m_inputhtml && m_eolbr)
                    *olit += "
"; *olit += "\n"; eol--; } // Maybe end this chunk, begin next. Don't do it on html // there is just no way to do it right (qtextedit cant grok // chunks cut in the middle of for example). if (!m_inputhtml && !inrcltag && olit->size() > (unsigned int)chunksize) { if (m_activatelinks) { *olit = activate_urls(*olit); } out.push_back(string(startChunk())); olit++; } } switch (car) { case '<': inindent = 0; if (m_inputhtml) { if (!inparamvalue) intag = true; chariter.appendchartostring(*olit); } else { *olit += "<"; } break; case '>': inindent = 0; if (m_inputhtml) { if (!inparamvalue) intag = false; } chariter.appendchartostring(*olit); break; case '&': inindent = 0; if (m_inputhtml) { chariter.appendchartostring(*olit); } else { *olit += "&"; } break; case '"': inindent = 0; if (m_inputhtml && intag) { inparamvalue = !inparamvalue; } chariter.appendchartostring(*olit); break; case ' ': if (m_eolbr && inindent) { *olit += " "; } else { chariter.appendchartostring(*olit); } break; case '\t': if (m_eolbr && inindent) { *olit += "    "; } else { chariter.appendchartostring(*olit); } break; default: inindent = 0; chariter.appendchartostring(*olit); } } // End chariter loop #if 0 { FILE *fp = fopen("/tmp/debugplaintorich", "a"); fprintf(fp, "BEGINOFPLAINTORICHOUTPUT\n"); for (list::iterator it = out.begin(); it != out.end(); it++) { fprintf(fp, "BEGINOFPLAINTORICHCHUNK\n"); fprintf(fp, "%s", it->c_str()); fprintf(fp, "ENDOFPLAINTORICHCHUNK\n"); } fprintf(fp, "ENDOFPLAINTORICHOUTPUT\n"); fclose(fp); } #endif LOGDEB2("plaintorich: done " << chron.millis() << " mS\n"); if (!m_inputhtml && m_activatelinks) { out.back() = activate_urls(out.back()); } return ret; } recoll-1.36.1/common/textsplit.h0000644000175000017500000001727614427373216013532 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _TEXTSPLIT_H_INCLUDED_ #define _TEXTSPLIT_H_INCLUDED_ #include #include #include class Utf8Iter; class RclConfig; /** * Split text into words. * See comments at top of .cpp for more explanations. * This uses a callback function. It could be done with an iterator instead, * but 'ts much simpler this way... */ class TextSplit { public: enum Flags { // Default: will return spans and words (a_b, a, b) TXTS_NONE = 0, // Only return maximum spans (a@b.com, not a, b, or com) TXTS_ONLYSPANS = 1, // Special: Only return atomic words (a, b, com). This is not // used for indexing, but for position computation during // abstract generation, TXTS_NOSPANS = 2, // Handle wildcards as letters. This is used with ONLYSPANS // for parsing a user query (never alone). TXTS_KEEPWILD = 4 }; TextSplit(Flags flags = Flags(TXTS_NONE)) : m_flags(flags) {} virtual ~TextSplit() {} TextSplit(const TextSplit&) = delete; TextSplit& operator=(const TextSplit&) = delete; /** Call at program initialization to read non default values from the configuration */ static void staticConfInit(RclConfig *config); static void koStaticConfInit(RclConfig *config, const std::string& tagger); /** Split text, emit words and positions. */ virtual bool text_to_words(const std::string &in); /** Process one output word: to be implemented by the actual user class */ virtual bool takeword(const std::string& term, int pos, // term pos int bts, // byte offset of first char in term int bte // byte offset of first char after term ) = 0; /** Called when we encounter formfeed \f 0x0c. Override to use the event. * Mostly or exclusively used with pdftoxx output. Other filters mostly * just don't know about pages. */ virtual void newpage(int /*pos*/) {} /** Called when we encounter newline \n 0x0a. Override to use the event. */ virtual void newline(int /*pos*/) {} // Static utility functions: /** Count words in string, as the splitter would generate them */ static int countWords(const std::string &in, Flags flgs = TXTS_ONLYSPANS); /** Check if this is visibly not a single block of text */ static bool hasVisibleWhite(const std::string &in); /** Split text span into strings, at white space, allowing for substrings * quoted with " . Escaping with \ works as usual inside the quoted areas. * This has to be kept separate from smallut.cpp's stringsToStrings, which * basically works only if whitespace is ascii, and which processes * non-utf-8 input (iso-8859 config files work ok). This hopefully * handles all Unicode whitespace, but needs correct utf-8 input */ static bool stringToStrings(const std::string &s, std::vector &tokens); /** Is char CJK ? */ static bool isCJK(int c); static bool isKATAKANA(int c); static bool isHANGUL(int c); /* Not split in words */ static bool isNGRAMMED(int c); /** Statistics about word length (average and dispersion) can * detect bad data like undecoded base64 or other mis-identified * pieces of data taken as text. In practise, this keeps some junk out * of the index, but does not decrease the index size much, and is * probably not worth the trouble in general. Code kept because it * probably can be useful in special cases. Base64 data does has * word separators in it (+/) and is characterised by high average * word length (>10, often close to 20) and high word length * dispersion (avg/sigma > 0.8). In my tests, most natural * language text has average word lengths around 5-8 and avg/sigma * < 0.7 */ #ifdef TEXTSPLIT_STATS class Stats { public: Stats() { reset(); } void reset() { count = 0; totlen = 0; sigma_acc = 0; } void newsamp(unsigned int len) { ++count; totlen += len; double avglen = double(totlen) / double(count); sigma_acc += (avglen - len) * (avglen - len); } struct Values { int count; double avglen; double sigma; }; Values get() { Values v; v.count = count; v.avglen = double(totlen) / double(count); v.sigma = std::sqrt(sigma_acc / count); return v; } private: int count; int totlen; double sigma_acc; }; Stats::Values getStats() { return m_stats.get(); } void resetStats() { m_stats.reset(); } #endif // TEXTSPLIT_STATS private: static bool o_processCJK; // true static bool o_noNumbers; // false static bool o_deHyphenate; // false static unsigned int o_CJKNgramLen; // 2 static int o_maxWordLength; // 40 static int o_maxWordsInSpan; // 6 Flags m_flags; // Current span. Might be jf.dockes@wanadoo.f std::string m_span; // Words in span: byte positions of start and end of words in m_span. For example: // 0 4 9 // bill@some.com -> (0,4) (5,9) (10,13) std::vector > m_words_in_span; // Current word: no punctuation at all in there. Byte offset // relative to the current span and byte length int m_wordStart; unsigned int m_wordLen; // Currently inside number bool m_inNumber; // Term position of current word and span int m_wordpos; int m_spanpos; // It may happen that our cleanup would result in emitting the // same term twice. We try to avoid this int m_prevpos{-1}; int m_prevlen; #ifdef TEXTSPLIT_STATS // Stats counters. These are processed in TextSplit rather than by a // TermProc so that we can take very long words (not emitted) into // account. Stats m_stats; #endif // Word length in characters. Declared but not updated if !TEXTSPLIT_STATS unsigned int m_wordChars; void clearsplitstate() { m_span.clear(); m_words_in_span.clear(); m_inNumber = false; m_wordStart = m_wordLen = m_wordpos = m_spanpos = m_prevpos = m_prevlen = m_wordChars = 0; } // This processes cjk text: bool cjk_to_words(Utf8Iter& it, unsigned int *cp); // Experimental Korean splitter. This uses an external Python tokenizer bool ko_to_words(Utf8Iter *it, unsigned int *cp); bool emitterm(bool isspan, std::string &term, int pos, size_t bs,size_t be); bool doemit(bool spanerase, size_t bp); void discardspan(); bool span_is_acronym(std::string *acronym); bool words_from_span(size_t bp); }; #endif /* _TEXTSPLIT_H_INCLUDED_ */ recoll-1.36.1/common/cstr.h0000644000175000017500000000672614426501047012435 00000000000000/* Copyright (C) 2011-2018 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _CSTR_H_INCLUDED_ #define _CSTR_H_INCLUDED_ // recoll mostly uses STL strings. In many places we had automatic // conversion from a C string to an STL one. This costs, and can // become significant if used often. // // This file and the associated .cpp file declares/defines constant // strings used in the program. Strings are candidates for a move here // when they are used in a fast loop or are shared. #include // The following slightly hacky preprocessing directives and the // companion code in the cpp file looks complicated, but it just // ensures that we only have to write the strings once to get the // extern declaration and the definition. #ifdef RCLIN_CSTR_CPPFILE #undef DEF_CSTR #define DEF_CSTR(NM, STR) const std::string cstr_##NM(STR) #else #define DEF_CSTR(NM, STR) extern const std::string cstr_##NM #endif DEF_CSTR(caption, "caption"); DEF_CSTR(colon, ":"); DEF_CSTR(dmtime, "dmtime"); DEF_CSTR(dquote, "\""); DEF_CSTR(fbytes, "fbytes"); DEF_CSTR(fileu, "file://"); DEF_CSTR(fmtime, "fmtime"); DEF_CSTR(iso_8859_1, "ISO-8859-1"); DEF_CSTR(utf8, "UTF-8"); DEF_CSTR(cp1252, "CP1252"); DEF_CSTR(minwilds, "*?["); DEF_CSTR(newline, "\n"); DEF_CSTR(null, ""); DEF_CSTR(plus, "+"); DEF_CSTR(textplain, "text/plain"); DEF_CSTR(texthtml, "text/html"); DEF_CSTR(url, "url"); // Marker for HTML format fields DEF_CSTR(fldhtm, "\007"); // Characters that can -begin- a wildcard or regexp expression. DEF_CSTR(wildSpecStChars, "*?["); DEF_CSTR(regSpecStChars, "(.[{"); // Values used as keys inside Dijon::Filter::metaData[]. // Protocol-specific flag indicating that the "content" field is not the text but a file path // pointing to the actual content. DEF_CSTR(dj_content_is_datapath, "rclisdatapath"); // The document data. DEF_CSTR(dj_keycontent, "content"); // These fields go from the topmost handler (text/plain) into the // Rcl::Doc::meta, possibly with some massaging. DEF_CSTR(dj_keyanc, "rclanc"); DEF_CSTR(dj_keyorigcharset, "origcharset"); DEF_CSTR(dj_keyds, "description"); DEF_CSTR(dj_keyabstract, "abstract"); // Built or inherited along the handler stack, then copied to doc DEF_CSTR(dj_keyipath, "ipath"); DEF_CSTR(dj_keyfn, "filename"); DEF_CSTR(dj_keyauthor, "author"); DEF_CSTR(dj_keymd, "modificationdate"); // charset and mimetype are explicitly blocked from going into the doc meta DEF_CSTR(dj_keycharset, "charset"); DEF_CSTR(dj_keymt, "mimetype"); // All other meta fields are directly copied from // Dijon::Filter::metaData to Rcl::Doc::meta. The defininitions which // follow are just for well-known names, with no particular processing // in internfile. DEF_CSTR(dj_keytitle, "title"); DEF_CSTR(dj_keyrecipient, "recipient"); DEF_CSTR(dj_keymd5, "md5"); #endif /* _CSTR_H_INCLUDED_ */ recoll-1.36.1/common/autoconfig.h.in0000644000175000017500000001063714515753353014231 00000000000000/* common/autoconfig.h.in. Generated from configure.ac by autoheader. */ /* Path to the aspell program */ #undef ASPELL_PROG /* No X11 session monitoring support */ #undef DISABLE_X11MON /* Path to the fam api include file */ #undef FAM_INCLUDE /* Path to the file program */ #undef FILE_PROG /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* dlopen function is available */ #undef HAVE_DLOPEN /* Define to 1 if you have the header file. */ #undef HAVE_FEATURES_H /* Define if you have the iconv() function and it works. */ #undef HAVE_ICONV /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `kqueue' function. */ #undef HAVE_KQUEUE /* Define to 1 if you have the `aspell' library (-laspell). */ #undef HAVE_LIBASPELL /* Define to 1 if you have the `chm' library (-lchm). */ #undef HAVE_LIBCHM /* Define to 1 if you have the `pthread' library (-lpthread). */ #undef HAVE_LIBPTHREAD /* Define to 1 if you have the `z' library (-lz). */ #undef HAVE_LIBZ /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_H /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_MALLOC_H /* Define to 1 if you have the `malloc_trim' function. */ #undef HAVE_MALLOC_TRIM /* Define to 1 if you have the `mkdtemp' function. */ #undef HAVE_MKDTEMP /* Define to 1 if you have the `posix_fadvise' function. */ #undef HAVE_POSIX_FADVISE /* Define to 1 if you have the `posix_spawn' function. */ #undef HAVE_POSIX_SPAWN /* Define to 1 if you have the `setrlimit' function. */ #undef HAVE_SETRLIMIT /* Define to 1 if you have the header file. */ #undef HAVE_SPAWN_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MOUNT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H_ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STATFS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STATVFS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_VFS_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `vsnprintf' function. */ #undef HAVE_VSNPRINTF /* Define as const if the declaration of iconv() needs const. */ #undef ICONV_CONST /* Use multiple threads for indexing */ #undef IDX_THREADS /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* putenv parameter is const */ #undef PUTENV_ARG_CONST /* Real time monitoring option */ #undef RCL_MONITOR /* Split camelCase words */ #undef RCL_SPLIT_CAMELCASE /* Compile the aspell interface */ #undef RCL_USE_ASPELL /* Compile the fam interface */ #undef RCL_USE_FAM /* Compile the inotify interface */ #undef RCL_USE_INOTIFY /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* Use posix_spawn() */ #undef USE_POSIX_SPAWN /* Define to 1 if the X Window System is missing or not being used. */ #undef X_DISPLAY_MISSING /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES #include "conf_post.h" recoll-1.36.1/common/unacpp.cpp0000644000175000017500000001107014516235357013277 00000000000000/* Copyright (C) 2004-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include "unacpp.h" #include "unac.h" #include "log.h" #include "utf8iter.h" using namespace std; bool unacmaybefold(const string &in, string &out, const char *encoding, UnacOp what) { char *cout = 0; size_t out_len; int status = -1; switch (what) { case UNACOP_UNAC: status = unac_string(encoding, in.c_str(), in.length(), &cout, &out_len); break; case UNACOP_UNACFOLD: status = unacfold_string(encoding, in.c_str(), in.length(), &cout, &out_len); break; case UNACOP_FOLD: status = fold_string(encoding, in.c_str(), in.length(), &cout, &out_len); break; } if (status < 0) { if (cout) free(cout); char cerrno[20]; sprintf(cerrno, "%d", errno); out = string("unac_string failed, errno : ") + cerrno; return false; } out.assign(cout, out_len); if (cout) free(cout); return true; } std::string unactolower(const std::string& in) { std::string out; unacmaybefold(in, out, "UTF-8", UNACOP_FOLD); return out; } // Functions to determine upper-case or accented status could be implemented // hugely more efficiently inside the unac c code, but there only used for // testing user-entered terms, so we don't really care. bool unaciscapital(const string& in) { LOGDEB2("unaciscapital: [" << in << "]\n"); if (in.empty()) return false; Utf8Iter it(in); string shorter; it.appendchartostring(shorter); string lower; if (!unacmaybefold(shorter, lower, "UTF-8", UNACOP_FOLD)) { LOGINFO("unaciscapital: unac/fold failed for [" << in << "]\n"); return false; } Utf8Iter it1(lower); if (*it != *it1) return true; else return false; } // Check if input contains upper case characters. We used to case-fold // the input and look for a difference, but lowercasing and // casefolding are actually not exactly the same, for example german // sharp s folds to ss but lowercases to itself, and greek final sigma // folds to sigma. So an input containing one of these characters // would wrongly detected as containing upper case. We now handle a // few special cases explicitly, by folding them before performing // the lowercasing. There are actually quite a few other cases of // lowercase being transformed by casefolding, check Unicode // CaseFolding.txt for occurrences of SMALL. One more step towards // ditching everything and using icu... bool unachasuppercase(const string& _in) { LOGDEB("unachasuppercase: in [" << _in << "]\n"); if (_in.empty()) return false; string in; Utf8Iter it(_in); for (; !it.eof(); it++) { if (*it == 0xdf) { // s sharp -> ss in += 's'; in += 's'; } else if (*it == 0x3c2) { // final sigma -> sigma in.append("\xcf\x83"); } else { it.appendchartostring(in); } } LOGDEB("unachasuppercase: folded: [" << in << "]\n"); string lower; if (!unacmaybefold(in, lower, "UTF-8", UNACOP_FOLD)) { LOGINFO("unachasuppercase: unac/fold failed for [" << in << "]\n"); return false; } LOGDEB("unachasuppercase: lower [" << lower << "]\n"); if (lower != in) return true; else return false; } bool unachasaccents(const string& in) { LOGDEB("unachasaccents: in [" << in << "]\n"); if (in.empty()) return false; string noac; if (!unacmaybefold(in, noac, "UTF-8", UNACOP_UNAC)) { LOGINFO("unachasaccents: unac/unac failed for [" << (in) << "]\n" ); return false; } LOGDEB("unachasaccents: noac [" << noac << "]\n"); if (noac != in) return true; else return false; } recoll-1.36.1/common/textsplitko.cpp0000644000175000017500000002672414427373216014415 00000000000000/* Copyright (C) 2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Specialized Korean text splitter using konlpy running in a Python // subprocess. konlpy can use several different backends. We support // Okt (Twitter) and Mecab at this point. Unfortunately the different // backends have different POS TAG names, so that things are not // completly transparent when using another (need to translate the tag // names in the Python program). #include "autoconfig.h" #include #include #include #include #include #include #include "textsplit.h" #include "log.h" //#define UTF8ITER_CHECK #include "utf8iter.h" #include "smallut.h" #include "rclconfig.h" #include "cmdtalk.h" using namespace std; // Separator char used in words and tags lists. static const string sepchars("\t"); static CmdTalk *o_talker; static bool o_starterror{false}; static string o_cmdpath; static vector o_cmdargs; std::mutex o_mutex; static string o_taggername{"Okt"}; static bool isKomoran{false}; // The Python/Java splitter is leaking memory. We restart it from time to time static uint64_t restartcount; static uint64_t restartthreshold = 5 * 1000 * 1000; static const string magicpage{"NEWPPPAGE"}; void TextSplit::koStaticConfInit(RclConfig *config, const string& tagger) { std::vector cmdvec; if (config->pythonCmd("kosplitter.py", cmdvec)) { auto it = cmdvec.begin(); o_cmdpath = *it++; o_cmdargs.clear(); o_cmdargs.insert(o_cmdargs.end(), it, cmdvec.end()); } if (tagger == "Okt" || tagger == "Mecab" || tagger == "Komoran") { o_taggername = tagger; if (tagger == "Komoran") isKomoran = true; } else { LOGERR("TextSplit::koStaticConfInit: unknown tagger [" << tagger << "], using Okt\n"); } } // Start the Python subprocess static bool initCmd() { if (o_starterror) { // No use retrying return false; } if (o_talker) { if (restartcount > restartthreshold) { delete o_talker; o_talker = nullptr; restartcount = 0; } else { return true; } } if (o_cmdpath.empty()) { return false; } if (nullptr == (o_talker = new CmdTalk(300))) { o_starterror = true; return false; } if (!o_talker->startCmd(o_cmdpath, o_cmdargs)) { delete o_talker; o_talker = nullptr; o_starterror = true; return false; } return true; } #define LOGKO LOGDEB1 #define STRSZT std::string::size_type #define ISASCIIPUNCTORCTL(c) (c <= 0x7f && \ !((c >= 'A' && c <= 'Z') || \ (c >= 'a' && c <= 'z') || \ (c >= '0' && c <= '9'))) bool TextSplit::ko_to_words(Utf8Iter *itp, unsigned int *cp) { LOGDEB1("ko_to_words\n"); std::unique_lock mylock(o_mutex); initCmd(); if (nullptr == o_talker) { return false; } LOGDEB1("k_to_words: m_wordpos " << m_wordpos << "\n"); Utf8Iter &it = *itp; unsigned int c = 0; unordered_map args; args.insert(pair{"data", string()}); string& inputdata(args.begin()->second); // We send the tagger name every time but it's only used the first // one: can't change it after init. We could avoid sending it // every time, but I don't think that the performance hit is // significant args.insert(pair{"tagger", o_taggername}); // Walk the Korean characters section, and accumulate tagger // input. // While doing this, we compute spans (space-less chunks), which // we will index in addition to the parts. // We also strip some useless chars, and prepare page number computations. STRSZT orgbytepos = it.getBpos(); bool wasspace{true}; STRSZT spanstart{0}; std::vector> spans; for (; !it.eof() && !it.error(); it++) { c = *it; if (!isHANGUL(c) && !ISASCIIPUNCTORCTL(c)) { // Non-Korean: we keep on if encountering space and other // ASCII punctuation. Allows sending longer pieces of text // to the splitter (perf). Else break, process this piece, // and return to the main splitter LOGKO("ko_to_words: broke on " << (std::string)it << endl); break; } else { if (c == '\f') { if (!wasspace) { // End of span spans.push_back({spanstart, inputdata.size()}); wasspace = true; } inputdata += magicpage + " "; } else { // Alpha was taken care of above. Keep only ascii // numbers, replace all punctuation with spaces. if (ISASCIIPUNCTORCTL(c)) { if (!wasspace) { // End of span spans.push_back({spanstart, inputdata.size()}); wasspace = true; } inputdata += ' '; } else { if (wasspace) { // Beginning of span spanstart = inputdata.size(); wasspace = false; } it.appendchartostring(inputdata); } } } } // Possible dangling span if (!wasspace && inputdata.size() != spanstart) { spans.push_back({spanstart, inputdata.size()}); } LOGKO("TextSplit::k_to_words: sending out " << inputdata.size() << " bytes " << inputdata << endl); // Overall data counter for slave restarts restartcount += inputdata.size(); // Have the slave analyse the data, check that we get a result, unordered_map result; if (!o_talker->talk(args, result)) { LOGERR("Python splitter for Korean failed for [" << inputdata << "]\n"); return false; } // Split the resulting words and tags strings into vectors. This // could be optimized (less data copying) by using positions // instead. auto resit = result.find("text"); if (resit == result.end()) { LOGERR("No text in Python splitter for Korean\n"); return false; } string& outtext = resit->second; vector words; stringToTokens(outtext, words, sepchars); #if 0 // Actually we don't use the tags (word kind) any more, so don't // compute them. KEEP the code around in case we want to show the // tagger output further below resit = result.find("tags"); if (resit == result.end()) { LOGERR("No tags in Python splitter for Korean\n"); return false; } string& outtags = resit->second; vector tags; stringToTokens(outtags, tags, sepchars); #endif // Process the words and their tags. Some versions selected on tag // type (did not index everything, only Nouns, Verbs etc, but we // just now process everything. // bytepos is the position in the local fragment, not in the whole // text which is orgbytepos + bytepos STRSZT bytepos{0}; // Adjustment for our page markers STRSZT pagefix{0}; // Current span string span; for (unsigned int i = 0; i < words.size(); i++) { // The POS tagger strips characters from the input (e.g. multiple // spaces, sometimes new lines, possibly other stuff). This // means that we can't easily reconstruct the byte position // from the concatenated terms. The output seems to be always // shorter than the input, so we try to look ahead for the // term. Can't be too sure that this works though, depending // on exactly what transformation may have been applied from // the original input to the term. string word = words[i]; trimstring(word); if (word == magicpage) { LOGDEB1("ko_to_words: NEWPAGE\n"); newpage(m_wordpos); bytepos += word.size() + 1; pagefix += word.size(); continue; } // Find the actual start position of the word in the section. STRSZT newpos = inputdata.find(word, bytepos); if (newpos != string::npos) { bytepos = newpos; } else { LOGINF("textsplitko: word [" << word << "] not found in text\n"); } STRSZT abspos = orgbytepos + bytepos - pagefix; // See if we are at a span start position, emit a span if we are. auto it = std::find_if(spans.begin(), spans.end(), [bytepos] (const std::pair& e){ return e.first == bytepos; }); if (it != spans.end()) { span = inputdata.substr(it->first, it->second-it->first); LOGKO("KO: SPAN: [" << span << "] pos " << m_wordpos << " bytepos " << bytepos << "\n"); // Xapian max term length is 245 bytes. textsplit default // max word is 40 bytes. Let's take into account the // longer utf-8 Korean chars (usually 3 bytes). if (int(span.size()) > 3 * o_maxWordLength) { LOGINF("kosplitter: dropping span too long: " << span); } else if (!takeword( span, m_wordpos, abspos, abspos + span.size())) { return false; } } // Possibly emit a part of span word. LOGKO("KO: WORD: [" << word << "] pos " << m_wordpos << " bytepos " << bytepos << "\n"); // Emit words only if not in onlyspans mode, and different // from span. Else, just increase the position if (!(m_flags & TXTS_ONLYSPANS) && (it == spans.end() || word != span)) { if (!takeword(word, m_wordpos, abspos, abspos + word.size())) { return false; } } else { LOGKO("KO: WORD: SKIP\n"); } m_wordpos++; bytepos += word.size(); } #if DO_CHECK_THINGS int sizediff = inputdata.size() - (bytepos - orgbytepos); if (sizediff < 0) sizediff = -sizediff; if (sizediff > 1) { LOGERR("ORIGINAL TEXT SIZE: " << inputdata.size() << " FINAL BYTE POS " << bytepos - orgbytepos << " TEXT [" << inputdata << "]\n"); } #endif // Reset state, saving term position, and return the found non-cjk // Unicode character value. The current input byte offset is kept // in the utf8Iter int pos = m_wordpos; clearsplitstate(); m_spanpos = m_wordpos = pos; *cp = c; LOGDEB1("ko_to_words: returning\n"); return true; } recoll-1.36.1/common/utf8fn.h0000644000175000017500000000057514410615043012663 00000000000000#ifndef _UTF8FN_H_ #define _UTF8FN_H_ #include class RclConfig; // Translate file name/path to utf8 for indexing. // // @param simple If true we extract and process only the simple file name // (ignore the path) std::string compute_utf8fn(const RclConfig *config, const std::string& ifn, bool simple); #endif // _UTF8FN_H_ recoll-1.36.1/common/utf8fn.cpp0000644000175000017500000000352414427373216013226 00000000000000/* Copyright (C) 2004-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include "utf8fn.h" #include "rclconfig.h" #include "transcode.h" #include "log.h" #include "pathut.h" using namespace std; string compute_utf8fn(const RclConfig *config, const string& ifn, bool simple) { string lfn(simple ? path_getsimple(ifn) : ifn); #ifdef _WIN32 PRETEND_USE(config); // On windows file names are read as UTF16 wchar_t and converted to UTF-8 // while scanning directories return lfn; #else string charset = config->getDefCharset(true); string utf8fn; int ercnt; if (!transcode(lfn, utf8fn, charset, "UTF-8", &ercnt)) { LOGERR("compute_utf8fn: fn transcode failure from [" << charset << "] to UTF-8 for: [" << lfn << "]\n"); } else if (ercnt) { LOGDEB("compute_utf8fn: " << ercnt << " transcode errors from [" << charset << "] to UTF-8 for: [" << lfn << "]\n"); } LOGDEB1("compute_utf8fn: transcoded from [" << lfn << "] to [" << utf8fn << "] (" << charset << "->" << "UTF-8)\n"); return utf8fn; #endif } recoll-1.36.1/doc/0000755000175000017500000000000014521161750010632 500000000000000recoll-1.36.1/doc/user/0000755000175000017500000000000014521161751011611 500000000000000recoll-1.36.1/doc/user/usermanual.xml0000644000175000017500000131073114516133171014434 00000000000000 Recoll"> http://www.recoll.org/pages/features.html"> Xapian"> Windows"> Unix-like systems"> ]> Recoll user manual Jean-Francois Dockes
jfd@recoll.org
2005-2023 Jean-Francois Dockes Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at the following location: GNU web site. This document introduces full text search notions and describes the installation and use of the &RCL; application. This version describes &RCL; &RCLVERSION;.
Introduction This document introduces full text search notions and describes the installation and use of the &RCL; application. It is updated for &RCL; &RCLVERSION;. &RCL; was for a long time dedicated to Unix-like systems. It was only lately (2015) ported to MS-Windows. Many references in this manual, especially file locations, are specific to Unix, and not valid on Windows, where some described features are also not available. The manual will be progressively updated. Until this happens, on Windows, most references to shared files can be translated by looking under the Recoll installation directory (Typically C:/Program Files (x86)/Recoll). Especially, anything referenced inside /usr/share in this document will be found in the Share subdirectory of the installation). The user configuration is stored by default under AppData/Local/Recoll inside the user directory, along with the index itself. Giving it a try If you do not like reading manuals (who does?) but wish to give &RCL; a try, just install the application and start the recoll graphical user interface (GUI), which will ask permission to index your home directory, allowing you to search immediately after indexing completes. Do not do this if your home directory contains a huge number of documents and you do not want to wait or are very short on disk space. In this case, you may first want to customize the configuration to restrict the indexed area. From the recoll GUI go to: Preferences Indexing configuration , then adjust the Top directories section, which defines the directories from which the filesystem exploration starts. On &LIN;, you may need to install the appropriate supporting applications for document types that need them (for example antiword for Microsoft Word files). The &WIN; package is self-contained and includes most useful auxiliary programs. Full text search &RCL; is a full text search application, which means that it finds your data by content rather than by external attributes (like the file name). You specify words (terms) which should or should not appear in the text you are looking for, and receive in return a list of matching documents, ordered so that the most relevant documents will appear first. You do not need to remember in what file or email message you stored a given piece of information. You just ask for related terms, and the tool will return a list of documents where these terms are prominent, in a similar way to Internet search engines. Full text search applications try to determine which documents are most relevant to the search terms you provide. Computer algorithms for determining relevance can be very complex, and in general are inferior to the power of the human mind to rapidly determine relevance. The quality of relevance guessing is probably the most important aspect when evaluating a search application. &RCL; relies on the &XAP; probabilistic information retrieval library to determine relevance. In many cases, you are looking for all the forms of a word, including plurals, different tenses for a verb, or terms derived from the same root or stem (example: floor, floors, floored, flooring...). Queries are usually automatically expanded to all such related terms (words that reduce to the same stem). This can be prevented for searching for a specific form. Stemming, by itself, does not accommodate for misspellings or phonetic searches. A full text search application may also support this form of approximation. For example, a search for aliterattion returning no result might propose alliteration, alteration, alterations, or altercation as possible replacement terms. &RCL; bases its suggestions on the actual index contents, so that suggestions may be made for words which would not appear in a standard dictionary. Recoll overview &RCL; uses the &XAP; information retrieval library as its storage and retrieval engine. &XAP; is a very mature package using a sophisticated probabilistic ranking model. The &XAP; library manages an index database which describes where terms appear in your document files. It efficiently processes the complex queries which are produced by the &RCL; query expansion mechanism, and is in charge of the all-important relevance computation task. &RCL; provides the mechanisms and interface to get data into and out of the index. This includes translating the many possible document formats into pure text, handling term variations (using &XAP; stemmers), and spelling approximations (using the aspell speller), interpreting user queries and presenting results. In a shorter way, &RCL; does the dirty footwork, &XAP; deals with the intelligent parts of the process. The &XAP; index can be big (roughly the size of the original document set), but it is not a document archive. &RCL; can only display documents that still exist at the place from which they were indexed. &RCL; stores all internal data in Unicode UTF-8 format, and it can index many types of files with different character sets, encodings, and languages into the same index. It can process documents embedded inside other documents (for example a PDF document stored inside a Zip archive sent as an email attachment...), down to an arbitrary depth. Stemming is the process by which &RCL; reduces words to their radicals so that searching does not depend, for example, on a word being singular or plural (floor, floors), or on a verb tense (flooring, floored). Because the mechanisms used for stemming depend on the specific grammatical rules for each language, there is a separate &XAP; stemmer module for most common languages where stemming makes sense. &RCL; stores the unstemmed versions of terms in the main index and uses auxiliary databases for term expansion (one for each stemming language), which means that you can switch stemming languages between searches, or add a language without needing a full reindex. Storing documents written in different languages in the same index is possible, and commonly done. In this situation, you can specify several stemming languages for the index. &RCL; currently makes no attempt at automatic language recognition, which means that the stemmer will sometimes be applied to terms from other languages with potentially strange results. In practise, even if this introduces possibilities of confusion, this approach has been proven quite useful, and it is much less cumbersome than separating your documents according to what language they are written in. By default, &RCL; strips most accents and diacritics from terms, and converts them to lower case before either storing them in the index or searching for them. As a consequence, it is impossible to search for a particular capitalization of a term (US / us), or to discriminate two terms based on diacritics (sake / saké, mate / maté). &RCL; can optionally store the raw terms, without accent stripping or case conversion. In this configuration, default searches will behave as before, but it is possible to perform searches sensitive to case and diacritics. This is described in more detail in the section about index case and diacritics sensitivity. &RCL; uses many parameters to define exactly what to index, and how to classify and decode the source documents. These are kept in configuration files. A default configuration is copied into a standard location (usually something like /usr/share/recoll/examples) during installation. The default values set by the configuration files in this directory may be overridden by values set inside your personal configuration. With the default configuration, &RCL; will index your home directory with generic parameters. Most common parameters can be set by using configuration menus in the recoll GUI. Some less common parameters can only be set by editing the text files. The indexing process is started automatically (after asking permission), the first time you execute the recoll GUI. Indexing can also be performed by executing the recollindex command. &RCL; indexing is multithreaded by default when appropriate hardware resources are available, and can perform multiple tasks in parallel for text extraction, segmentation and index updates. Searches are usually performed inside the recoll GUI, which has many options to help you find what you are looking for. However, there are other ways to query the index: A command line interface. A Recoll WebUI. A Gnome Shell Search Provider . A Python programming interface A KDE KIO slave module. A Ubuntu Unity Scope module. Indexing Introduction Indexing is the process by which the set of documents is analyzed and the data entered into the database. &RCL; indexing is normally incremental: documents will only be processed if they have been modified since the last run. On the first execution, all documents will need processing. A full index build can be forced later by specifying an option to the indexing command (recollindex or ). recollindex skips files which caused an error during a previous pass. This is a performance optimization, and the command line option can be set to retry failed files, for example after updating an input handler. The following sections give an overview of different aspects of the indexing processes and configuration, with links to detailed sections. Depending on your data, temporary files may be needed during indexing, some of them possibly quite big. You can use the RECOLL_TMPDIR or TMPDIR environment variables to determine where they are created (the default is to use /tmp). Using TMPDIR has the nice property that it may also be taken into account by auxiliary commands executed by recollindex. Indexing modes &RCL; indexing can be performed along two main modes: <link linkend="RCL.INDEXING.PERIODIC">Periodic (or batch) indexing</link> recollindex is executed at discrete times. On &LIN;, the typical usage is to have a nightly run programmed into your cron file. On &WIN;, the Task Scheduler can be used to run indexing. In both cases, the &RCL; GUI includes a simplified interface to configure the system scheduler. <link linkend="RCL.INDEXING.MONITOR">Real time indexing</link> recollindex runs permanently as a daemon and uses a file system alteration monitor (e.g. inotify on &LIN;) to detect file changes. New or updated files are indexed at once. Monitoring a big file system tree can consume significant system resources. Choosing an indexing mode The choice between the two methods is mostly a matter of preference, and they can be combined by setting up multiple indexes (e.g.: use periodic indexing on a big documentation directory, and real time indexing on a small home directory), or by configuring the index so that only a subset of the tree will be monitored. The choice of method and the parameters used can be configured from the recoll GUI: Preferences Indexing schedule dialog. Configurations, multiple indexes &RCL; supports defining multiple indexes, each defined by its own configuration directory. A configuration directory contains several files which describe what should be indexed and how. When recoll or recollindex is first executed, it creates a default configuration directory. This configuration is the one used for indexing and querying when no specific configuration is specified. It is located in $HOME/.recoll/ for &LIN; and %LOCALAPPDATA%/Recoll on &WIN; (typically C:/Users/[me]/Appdata/Local/Recoll). All configuration parameters have defaults, defined in system-wide files. Without further customisation, the default configuration will process your complete home directory, with a reasonable set of defaults. It can be adjusted to process a different area of the file system, select files in different ways, and many other things. In some cases, it may be useful to create additional configuration directories, for example, to separate personal and shared indexes, or to take advantage of the organization of your data to improve search precision. In order to do this, you would create an empty directory in a location of your choice, and then instruct recoll or recollindex to use it by setting either a command line option (-c /some/directory), or an environment variable (RECOLL_CONFDIR=/some/directory). Any modification performed by the commands (e.g. configuration customisation or searches by recoll or index creation by recollindex) would then apply to the new directory and not to the default one. Once multiple indexes are created, you can use each of them separately by setting the -c option or the RECOLL_CONFDIR environment variable when starting a command, to select the desired index. It is also possible to instruct one configuration to query one or several other indexes in addition to its own, by using the External index function in the recoll GUI, or some equivalent in the command line and programming tools. A plausible usage scenario for the multiple index feature would be for a system administrator to set up a central index for shared data, that you choose to search or not in addition to your personal data. Of course, there are other possibilities. for example, there are many cases where you know the subset of files that should be searched, and where narrowing the search can improve the results. You can achieve approximately the same effect by using a directory filter clause in a search, but multiple indexes may have better performance and may be worth the trouble in some cases. A more advanced use case would be to use multiple indexes to improve indexing performance, by updating several indexes in parallel (using multiple CPU cores and disks, or possibly several machines), and then merging them, or querying them in parallel. See the section about configuring multiple indexes for more detail Document types &RCL; knows about quite a few different document types. The parameters for document types recognition and processing are set in configuration files. Most file types, like HTML or word processing files, only hold one document. Some file types, like email folders or zip archives, can hold many individually indexed documents, which may themselves be compound ones. Such hierarchies can go quite deep, and &RCL; can process, for example, a LibreOffice document stored as an attachment to an email message inside an email folder archived in a zip file... recollindex processes plain text, HTML, OpenDocument (Open/LibreOffice), email formats, and a few others internally. Other file types (e.g.: postscript, pdf, ms-word, rtf ...) need external applications for preprocessing. The list is in the installation section. After every indexing operation, &RCL; updates a list of commands that would be needed for indexing existing files types. This list can be displayed by selecting the menu option File Show Missing Helpers in the recoll GUI. It is stored in the missing text file inside the configuration directory. After installing a missing handler, you may need to tell recollindex to retry the failed files, by adding option -k to the command line, or by using the GUI File Special indexing menu. This is because recollindex, in its default operation mode, will not retry files which caused an error during an earlier pass. In special cases, it may be useful to reset the data for a category of files before indexing. See the recollindex manual page. If your index is not too big, it may be simpler to just reset it. By default, &RCL; will try to index any file type that it has a way to read. This is sometimes not desirable, and there are ways to either exclude some types, or on the contrary define a positive list of types to be indexed. In the latter case, any type not in the list will be ignored. Excluding files by name can be done by adding wildcard name patterns to the skippedNames list, which can be done from the GUI Index configuration menu. Excluding by type can be done by setting the excludedmimetypes list in the configuration file. This can be redefined for subdirectories. You can also define an exclusive list of MIME types to be indexed (no others will be indexed), by setting the indexedmimetypes configuration variable. Example: indexedmimetypes = text/html application/pdf It is possible to redefine this parameter for subdirectories. Example: [/path/to/my/dir] indexedmimetypes = application/pdf (When using sections like this, don't forget that they remain in effect until the end of the file or another section indicator). excludedmimetypes or indexedmimetypes, can be set either by editing the configuration file (recoll.conf) for the index, or by using the GUI index configuration tool. Note about MIME types When editing the indexedmimetypes or excludedmimetypes lists, you should use the MIME values listed in the mimemap file or in Recoll result lists in preference to file -i output: there are a number of differences. The file -i output should only be used for files without extensions, or for which the extension is not listed in mimemap Indexing failures Indexing may fail for some documents, for a number of reasons: a helper program may be missing, the document may be corrupt, we may fail to uncompress a file because no file system space is available, etc. The &RCL; indexer in versions 1.21 and later does not retry failed files by default, because some indexing failures can be quite costly (for example failing to uncompress a big file because of insufficient disk space). Retrying will only occur if an explicit option () is set on the recollindex command line, or if a script executed when recollindex starts up says so. The script is defined by a configuration variable (checkneedretryindexscript), and makes a rather lame attempt at deciding if a helper command may have been installed, by checking if any of the common bin directories have changed. Recovery In the rare case where the index becomes corrupted (which can signal itself by weird search results or crashes), the index files need to be erased before restarting a clean indexing pass. Just delete the xapiandb directory (see next section), or, alternatively, start the next recollindex with the option, which will reset the database before indexing. The difference between the two methods is that the second will not change the current index format, which may be undesirable if a newer format is supported by the &XAP; version. Index storage The default location for the index data is the xapiandb subdirectory of the &RCL; configuration directory, typically $HOME/.recoll/xapiandb/ on &LIN; or C:/Users/[me]/Appdata/Local/Recoll/xapiandb on &WIN;. This can be changed via two different methods (with different purposes): For a given configuration directory, you can specify a non-default storage location for the index by setting the dbdir parameter in the configuration file (see the configuration section). This method would mainly be of use if you wanted to keep the configuration directory in its default location, but desired another location for the index, typically out of disk occupation or performance concerns. You can specify a different configuration directory by setting the RECOLL_CONFDIR environment variable, or using the option to the &RCL; commands. This method would typically be used to index different areas of the file system to different indexes. For example, if you were to issue the following command: recoll -c ~/.indexes-email Then &RCL; would use configuration files stored in ~/.indexes-email/ and, (unless specified otherwise in recoll.conf) would look for the index in ~/.indexes-email/xapiandb/. Using multiple configuration directories and configuration options allows you to tailor multiple configurations and indexes to handle whatever subset of the available data you wish to make searchable. There are quite a few more parameters which can be set in the configuration file itself for tailoring &RCL; data storage. They are described in a section of the configuration chapter. The size of the index is determined by the size of the set of documents, but the ratio can vary a lot. For a typical mixed set of documents, the index size will often be close to the data set size. In specific cases (a set of compressed mbox files for example), the index can become much bigger than the documents. It may also be much smaller if the documents contain a lot of images or other non-indexed data (an extreme example being a set of mp3 files where only the tags would be indexed). Of course, images, sound and video do not increase the index size, which means that in most cases, the space used by the index will be negligible compared to the total amount of data on the computer. The index data directory (xapiandb) only contains data that can be completely rebuilt by an index run (as long as the original documents exist), and it can always be destroyed safely. &XAP; index formats &XAP; versions usually support several formats for index storage. A given major &XAP; version will have a current format, used to create new indexes, and will also support the format from the previous major version. &XAP; will not convert automatically an existing index from the older format to the newer one. If you want to upgrade to the new format, or if a very old index needs to be converted because its format is not supported any more, you will have to explicitly delete the old index (typically ~/.recoll/xapiandb), then run a normal indexing command. Using recollindex option would not work in this situation. Security aspects The &RCL; index does not hold complete copies of the indexed documents (it almost does after version 1.24). But it does hold enough data to allow for an almost complete reconstruction. If confidential data is indexed, access to the database directory should be restricted. &RCL; will create the configuration directory with a mode of 0700 (access by owner only). As the index data directory is by default a sub-directory of the configuration directory, this should result in appropriate protection. If you use another setup, you should think of the kind of protection you need for your index, set the directory and files access modes appropriately, and also maybe adjust the umask used during index updates. Special considerations for big indexes This only needs concern you if your index is going to be bigger than around 5 GBytes. Beyond 10 GBytes, it becomes a serious issue. Most people have much smaller indexes. For reference, 5 GBytes would be around 2000 bibles, a lot of text. If you have a huge text dataset (remember: images don't count, the text content of PDFs is typically less than 5% of the file size), read on. The amount of writing performed by Xapian during index creation is not linear with the index size (it is somewhere between linear and quadratic). For big indexes this becomes a performance issue, and may even be an SSD disk wear issue. The problem can be mitigated by observing the following rules: Partition the data set and create several indexes of reasonable size rather than a huge one. These indexes can then be queried in parallel (using the &RCL; external indexes facility), or merged using xapian-compact. Have a lot of RAM available and set the idxflushmb &RCL; configuration parameter as high as you can without swapping (experimentation will be needed). 200 would be a minimum in this context. Use Xapian 1.4.10 or newer, as this version brought a significant improvement in the amount of writes. Index configuration Variables stored inside the &RCL; configuration files control which areas of the file system are indexed, and how files are processed. The values can be set by editing the text files. Most of the more commonly used ones can also be adjusted by using the dialogs in the recoll GUI. The first time you start recoll, you will be asked whether or not you would like it to build the index. If you want to adjust the configuration before indexing, just click Cancel at this point, which will get you into the configuration interface. If you exit at this point, recoll will have created a default configuration directory with empty configuration files, which you can then edit. The configuration is documented inside the installation chapter of this document, or in the recoll.conf5 manual page. Both documents are automatically generated from the comments inside the configuration file. The most immediately useful variable is probably topdirs, which lists the subtrees and files to be indexed. The applications needed to index file types other than text, HTML or email (e.g.: pdf, postscript, ms-word...) are described in the external packages section. There are two incompatible types of Recoll indexes, depending on the treatment of character case and diacritics. A further section describes the two types in more detail. The default type is appropriate in most cases. The index configuration GUI Most index configuration parameters can be set from the recoll GUI (set RECOLL_CONFDIR or use the option to affect a non-default index.) The interface is started from the Preferences Index Configuration menu entry. It is divided in four tabs, Global parameters, Local parameters, Web history (details) and Search parameters. The Global parameters tab allows setting global variables, like the lists of top/start directories, skipped paths, or stemming languages. The Local parameters tab allows setting variables that can be redefined for subdirectories. This second tab has an initially empty list of customisation directories, to which you can add. The variables are then set for the currently selected directory (or at the top level if the empty line is selected). The Search parameters section defines parameters which are used at query time, but are global to an index and affect all search tools, not only the GUI. The meaning for most entries in the interface is self-evident and documented by a ToolTip popup on the text label. For more detail, you may need to refer to the configuration section of this guide. The configuration tool normally respects the comments and most of the formatting inside the configuration file, so that it is quite possible to use it on hand-edited files, which you might nevertheless want to backup first... Multiple indexes Multiple &RCL; indexes can be created by using several configuration directories which would typically be set to index different areas of the file system. A specific configuration can be selected by setting the RECOLL_CONFDIR environment variable or giving the option to recoll and recollindex. The recollindex program, used for creating or updating indexes, always works on a single index. The different configurations are entirely independent (no parameters are ever shared between configurations when indexing). All the search interfaces (recoll, recollq, the Python API, etc.) operate with a main configuration, from which both configuration and index data are used, and can also query data from multiple additional indexes. Only the index data from additional indexes is used, their configuration parameters are ignored. This implies that some parameters should be consistent among index configurations which are to be used together. When searching, the current main index (defined by RECOLL_CONFDIR or ) is always active. If this is undesirable, you can set up your base configuration to index an empty directory. Index configuration parameters can be set either by using a text editor on the files, or, for most parameters, by using the recoll index configuration GUI. In the latter case, the configuration directory for which parameters are modified is the one which was selected by RECOLL_CONFDIR or the parameter, and there is no way to switch configurations within the GUI. See the configuration section for a detailed description of the parameters Some configuration parameters must be consistent among a set of multiple indexes used together for searches. Most importantly, all indexes to be queried concurrently must have the same option concerning character case and diacritics stripping, but there are other constraints. Most of the relevant parameters affect the term generation. Using multiple configurations implies a small level of command line or file manager usage. The user must explicitly create additional configuration directories, the GUI will not do it. This is to avoid mistakenly creating additional directories when an argument is mistyped. Also, the GUI or the indexer must be launched with a specific option or environment to work on the right configuration. Creating and using an additional index: example The following applies to &LIN; Initially creating the configuration and index: mkdir /path/to/my/new/config Configuring the new index can be done from the recoll GUI, launched from the command line to pass the -c option (you could create a desktop file to do it for you), and then using the GUI index configuration tool to set up the index. recoll -c /path/to/my/new/config Alternatively, you can just start a text editor on the main configuration file: someEditor /path/to/my/new/config/recoll.conf Creating and updating the index can be done from the command line: recollindex -c /path/to/my/new/config or from the File menu of a GUI launched with the same option (recoll, see above). The same GUI would also let you set up batch indexing for the new index. Real time indexing can only be set up from the GUI for the default index (the menu entry will be inactive if the GUI was started with a non-default -c option). The new index can be queried alone with: recoll -c /path/to/my/new/config Or, in parallel with the default index, by starting recoll without a -c option, and using the External Indexes tab in the preferences dialog, which can be reached either trough: Preferences GUI Configuration External Index Dialog or Query External index dialog . See the GUI external indexes section for more details. Index case and diacritics sensitivity As of &RCL; version 1.18 you have a choice of building an index with terms stripped of character case and diacritics, or one with raw terms. For a source term of Résumé, the former will store resume, the latter Résumé. Each type of index allows performing searches insensitive to case and diacritics: with a raw index, the user entry will be expanded to match all case and diacritics variations present in the index. With a stripped index, the search term will be stripped before searching. A raw index allows using case and diacritics to discriminate between terms, e.g., returning different results when searching for US and us or resume and résumé. Read the section about search case and diacritics sensitivity for more details. The type of index to be created is controlled by the indexStripChars configuration variable which can only be changed by editing the configuration file. Any change implies an index reset (not automated by &RCL;), and all indexes in a search must be set in the same way (again, not checked by &RCL;). &RCL; creates a stripped index by default if indexStripChars is not set. As a cost for added capability, a raw index will be slightly bigger than a stripped one (around 10%). Also, searches will be more complex, so probably slightly slower, and the feature is relatively little used, so that a certain amount of weirdness cannot be excluded. One of the most adverse consequence of using a raw index is that some phrase and proximity searches may become impossible: because each term needs to be expanded, and all combinations searched for, the multiplicative expansion may become unmanageable. Indexing threads configuration (&LIN;) Note: you don't probably don't need to read this. The default automatic configuration is fine is most cases. Only the part about disabling multithreading may be more commonly useful, so I'll prepend it here. In recoll.conf: thrQSizes = -1 -1 -1 The &RCL; indexing process recollindex can use multiple threads to speed up indexing on multiprocessor systems. The work done to index files is divided in several stages and some of the stages can be executed by multiple threads. The stages are: File system walking: this is always performed by the main thread. File conversion and data extraction. Text processing (splitting, stemming, etc.). &XAP; index update. You can also read a longer document about the transformation of &RCL; indexing to multithreading. The threads configuration is controlled by two configuration file parameters. thrQSizes This variable defines the job input queues configuration. There are three possible queues for stages 2, 3 and 4, and this parameter should give the queue depth for each stage (three integer values). If a value of -1 is used for a given stage, no queue is used, and the thread will go on performing the next stage. In practise, deep queues have not been shown to increase performance. A value of 0 for the first queue tells &RCL; to perform autoconfiguration (no need for anything else in this case, thrTCounts is not used) - this is the default configuration. thrTCounts This defines the number of threads used for each stage. If a value of -1 is used for one of the queue depths, the corresponding thread count is ignored. It makes no sense to use a value other than 1 for the last stage because updating the &XAP; index is necessarily single-threaded (and protected by a mutex). If the first value in thrQSizes is 0, thrTCounts is ignored. The following example would use three queues (of depth 2), and 4 threads for converting source documents, 2 for processing their text, and one to update the index. This was tested to be the best configuration on the test system (quadri-processor with multiple disks). thrQSizes = 2 2 2 thrTCounts = 4 2 1 The following example would use a single queue, and the complete processing for each document would be performed by a single thread (several documents will still be processed in parallel in most cases). The threads will use mutual exclusion when entering the index update stage. In practise the performance would be close to the precedent case in general, but worse in certain cases (e.g. a Zip archive would be performed purely sequentially), so the previous approach is preferred. YMMV... The 2 last values for thrTCounts are ignored. thrQSizes = 2 -1 -1 thrTCounts = 6 1 1 The following example would disable multithreading. Indexing will be performed by a single thread. thrQSizes = -1 -1 -1 Index update scheduling Periodic indexing Running the indexer The recollindex program performs index updates. You can start it either from the command line or from the File menu in the recoll GUI program. When started from the GUI, the indexing will run on the same configuration recoll was started on. When started from the command line, recollindex will use the RECOLL_CONFDIR variable or accept a confdir option to specify a non-default configuration directory. If the recoll program finds no index when it starts, it will automatically start indexing (except if canceled). The GUI File menu has entries to start or stop the current indexing operation. When indexing is not currently running, you have a choice between Update Index or Rebuild Index. The first choice only processes changed files, the second one erases the index before starting so that all files are processed. On Linux and Windows, the GUI can be used to manage the indexing operation. Stopping the indexer can be done from the recoll GUI File Stop Indexing menu entry. On Linux, the recollindex indexing process can be interrupted by sending an interrupt (Ctrl-C, SIGINT) or terminate (SIGTERM) signal. When stopped, some time may elapse before recollindex exits, because it needs to properly flush and close the index. After an interruption, the index will be somewhat inconsistent because some operations which are normally performed at the end of the indexing pass will have been skipped (for example, the stemming and spelling databases will be inexistent or out of date). You just need to restart indexing at a later time to restore consistency. The indexing will restart at the interruption point (the full file tree will be traversed, but files that were indexed up to the interruption and for which the index is still up to date will not need to be reindexed). recollindex command line recollindex has many options which are listed in its manual page. Only a few will be described here. Option will reset the index when starting. This is almost the same as destroying the index files (the nuance is that the &XAP; format version will not be changed). Option will force the update of all documents without resetting the index first. This will not have the "clean start" aspect of , but the advantage is that the index will remain available for querying while it is rebuilt, which can be a significant advantage if it is very big (some installations need days for a full index rebuild). Option will force retrying files which previously failed to be indexed, for example because of a missing helper program. Of special interest also, maybe, are the and options. allows indexing an explicit list of files (given as command line parameters or read on stdin). tells recollindex to ignore file selection parameters from the configuration. Together, these options allow building a custom file selection process for some area of the file system, by adding the top directory to the skippedPaths list and using an appropriate file selection method to build the file list to be fed to recollindex . Trivial example: find . -name indexable.txt -print | recollindex -if recollindex will not descend into subdirectories specified as parameters, but just add them as index entries. It is up to the external file selection method to build the complete file list. Linux: using <command>cron</command> to automate indexing The most common way to set up indexing is to have a cron task execute it every night. For example the following crontab entry would do it every day at 3:30AM (supposing recollindex is in your PATH): 30 3 * * * recollindex > /some/tmp/dir/recolltrace 2>&1 Or, using anacron: 1 15 su mylogin -c "recollindex recollindex > /tmp/rcltraceme 2>&1" The &RCL; GUI has dialogs to manage crontab entries for recollindex. You can reach them from the Preferences Indexing Schedule menu. They only work with the good old cron, and do not give access to all features of cron scheduling. Entries created via the tool are marked with a RCLCRON_RCLINDEX= marker so that the tool knows which entries belong to it. As a side effect, this sets an environment variable for the process, but it's not actually used, this is just a marker. The usual command to edit your crontab is crontab (which will usually start the vi editor to edit the file). You may have more sophisticated tools available on your system. Please be aware that there may be differences between your usual interactive command line environment and the one seen by crontab commands. Especially the PATH variable may be of concern. Please check the crontab manual pages about possible issues. Real time indexing Real time monitoring/indexing is performed by starting the recollindex command. With this option, recollindex will permanently monitor file changes and update the index. On &WIN; systems, the monitoring process is started from the recoll GUI File menu. On &LIN;, there are other possibilities, see the following sections. When this is in use, the recoll GUI File menu makes two operations available: Stop and Trigger incremental pass. Trigger incremental pass has the same effect as restarting the indexer, and will cause a complete walk of the indexed area, processing the changed files, then switch to monitoring. This is only marginally useful, maybe in cases where the indexer is configured to delay updates, or to force an immediate rebuild of the stemming and phonetic data, which are only processed at intervals by the real time indexer. While it is convenient that data is indexed in real time, repeated indexing can generate a significant load on the system when files such as email folders change. Also, monitoring large file trees by itself significantly taxes system resources. You probably do not want to enable it if your system is short on resources. Periodic indexing is adequate in most cases. As of &RCL; 1.24, you can set the monitordirs configuration variable to specify that only a subset of your indexed files will be monitored for instant indexing. In this situation, an incremental pass on the full tree can be triggered by either restarting the indexer, or just running recollindex, which will notify the running process. The recoll GUI also has a menu entry for this. &LIN;: automatic daemon start with systemd The installation contains two example files (in share/recoll/examples) for starting the indexing daemon with systemd. recollindex.service would be used for starting recollindex as a user service. The indexer will start when the user logs in and run while there is a session open for them. recollindex@.service is a template service which would be used for starting the indexer at boot time, running as a specific user. It can be useful when running the text search as a shared service (e.g. when users access it through the WEB UI). If configured to do so, the unit files should have been installed in your system's default systemd paths (usually /usr/lib/systemd/system/ and /usr/lib/systemd/user/). If not, you may need to copy the files there before starting the service. With the unit files installed in the proper location, the user unit can be started with the following commands: systemctl --user daemon-reload systemctl --user enable --now recollindex.service The system unit file can be enabled for a particular user by running, as root: systemctl daemon-reload systemctl enable --now recollindex@username.service (A valid user name should be substituted for username, of course.) &LIN;: automatic daemon start from the desktop session Under KDE, Gnome and some other desktop environments, the daemon can automatically started when you log in, by creating a desktop file inside the ~/.config/autostart directory. This can be done for you by the &RCL; GUI. Use the Preferences->Indexing Schedule menu. With older X11 setups, starting the daemon is normally performed as part of the user session script. The rclmon.sh script can be used to easily start and stop the daemon. It can be found in the examples directory (typically /usr/local/[share/]recoll/examples). For example, a good old xdm-based session could have a .xsession script with the following lines at the end: recollconf=$HOME/.recoll-home recolldata=/usr/local/share/recoll RECOLL_CONFDIR=$recollconf $recolldata/examples/rclmon.sh start fvwm The indexing daemon gets started, then the window manager, for which the session waits. By default the indexing daemon will monitor the state of the X11 session, and exit when it finishes, it is not necessary to kill it explicitly. (The X11 server monitoring can be disabled with option to recollindex). If you use the daemon completely out of an X11 session, you need to add option to disable X11 session monitoring (else the daemon will not start). Miscellaneous details Logging By default, the messages from the indexing daemon will be sent to the same file as those from the interactive commands (logfilename). You may want to change this by setting the daemlogfilename and daemloglevel configuration parameters. Also the log file will only be truncated when the daemon starts. If the daemon runs permanently, the log file may grow quite big, depending on the log level. &LIN;: increasing resources for inotify On Linux systems, monitoring a big tree may need increasing the resources available to inotify, which are normally defined in /etc/sysctl.conf. ### inotify # # cat /proc/sys/fs/inotify/max_queued_events - 16384 # cat /proc/sys/fs/inotify/max_user_instances - 128 # cat /proc/sys/fs/inotify/max_user_watches - 16384 # # -- Change to: # fs.inotify.max_queued_events=32768 fs.inotify.max_user_instances=256 fs.inotify.max_user_watches=32768 Especially, you will need to trim your tree or adjust the max_user_watches value if indexing exits with a message about errno ENOSPC (28) from inotify_add_watch. Slowing down the reindexing rate for fast changing files When using the real time monitor, it may happen that some files need to be indexed, but change so often that they impose an excessive load for the system. &RCL; provides a configuration option to specify the minimum time before which a file, specified by a wildcard pattern, cannot be reindexed. See the mondelaypatterns parameter in the configuration section. Miscellaneous indexing notes The PDF input handler The PDF format is very important for scientific and technical documentation, and document archival. It has extensive facilities for storing metadata along with the document, and these facilities are actually used in the real world. In consequence, the rclpdf.py PDF input handler has more complex capabilities than most others, and it is also more configurable. Specifically, rclpdf.py has the following features: It can be configured to extract specific metadata tags from an XMP packet. It can extract PDF attachments. It can automatically perform OCR if the document text is empty. This is done by executing an external program and is now described in a separate section, because the OCR framework can also be used with non-PDF image files. XMP fields extraction The rclpdf.py script in &RCL; version 1.23.2 and later can extract XMP metadata fields by executing the pdfinfo command (usually found with poppler-utils). This is controlled by the pdfextrameta configuration variable, which specifies which tags to extract and, possibly, how to rename them. The pdfextrametafix variable can be used to designate a file with Python code to edit the metadata fields (available for &RCL; 1.23.3 and later. 1.23.2 has equivalent code inside the handler script). Example: import sys import re class MetaFixer(object): def __init__(self): pass def metafix(self, nm, txt): if nm == 'bibtex:pages': txt = re.sub(r'--', '-', txt) elif nm == 'someothername': # do something else pass elif nm == 'stillanother': # etc. pass return txt def wrapup(self, metaheaders): pass If the 'metafix()' method is defined, it is called for each metadata field. A new MetaFixer object is created for each PDF document (so the object can keep state for, for example, eliminating duplicate values). If the 'wrapup()' method is defined, it is called at the end of XMP fields processing with the whole metadata as parameter, as an array of '(nm, val)' pairs, allowing an alternate approach for editing or adding/deleting fields. See this page for a more detailed discussion about indexing PDF XMP properties. PDF attachment indexing If pdftk is installed, and if the the pdfattach configuration variable is set, the PDF input handler will try to extract PDF attachments for indexing as sub-documents of the PDF file. This is disabled by default, because it slows down PDF indexing a bit even if not one attachment is ever found (PDF attachments are uncommon in my experience). Running OCR on image documents The &RCL; PDF handler has the ability to call an external OCR program if the processed file has no text content. The OCR data is stored in a cache of separate files, avoiding any modification of the originals. It must be noted that, if modifying the files (or a copy) is acceptable, then running something like OCRmyPDF to add a text layer to the PDF itself is a better solution (e.g. allowing &RCL; to position the PDF viewer on the search target when opening the document, and permitting secondary search in the native tool). To enable the &RCL; OCR feature, you need to install one of the supported OCR applications (tesseract or ABBYY), enable OCR in the PDF handler (by setting pdfocr to 1 in the index configuration file), and tell &RCL; how to run the OCR by setting configuration variables. All parameters can be localized in subdirectories through the usual main configuration mechanism (path sections). Example configuration fragment in recoll.conf: pdfocr = 1 ocrprogs = tesseract pdfocrlang = eng This facility got a major update in &RCL; 1.26.5. Older versions had a more limited, non-caching capability to execute an external OCR program in the PDF handler. The new function has the following features: The OCR output is cached, stored as separate files. The caching is ultimately based on a hash value of the original file contents, so that it is immune to file renames. A first path-based layer ensures fast operation for unchanged (unmoved files), and the data hash (which is still orders of magnitude faster than OCR) is only re-computed if the file has moved. OCR is only performed if the file was not previously processed or if it changed. The support for a specific program is implemented in a simple Python module. It should be straightforward to add support for any OCR engine with a capability to run from the command line. Modules initially exist for tesseract (Linux and Windows), and ABBYY FineReader (Linux, tested with version 11). ABBYY FineReader is a commercial closed source program, but it sometimes perform better than tesseract. The OCR is currently only called from the PDF handler, but there should be no problem using it for other image types. Running a speech to text program on audio files If the OpenAI Whisper program is available and the appropriate parameters set in the configuration files, the &RCL; audio file handler will run speech to text recognition on audio files and the resulting text will be indexed. See the the FAQ entry for more details. The results of the speech recognition will be cached in the same manner as the results of image OCR. Removable volumes &RCL; used to have no support for indexing removable volumes (portable disks, USB keys, etc.). Recent versions have improved the situation and support indexing removable volumes in two different ways: By indexing the volume in the main, fixed, index, and ensuring that the volume data is not purged if the indexing runs while the volume is mounted. (since &RCL; 1.25.2). By storing a volume index on the volume itself (since &RCL; 1.24). Indexing removable volumes in the main index As of version 1.25.2, &RCL; provides a simple way to ensure that the index data for an absent volume will not be purged. Two conditions must be met: The volume mount point must be a member of the topdirs list. The mount directory must be empty (when the volume is not mounted). If recollindex finds that one of the topdirs is empty when starting up, any existing data for the tree will be preserved by the indexing pass (no purge for this area). Self contained volumes As of &RCL; 1.24, it has become possible to build self-contained datasets including a &RCL; configuration directory and index together with the indexed documents, and to move such a dataset around (for example copying it to an USB drive), without having to adjust the configuration for querying the index. This is a query-time feature only. The index must only be updated in its original location. If an update is necessary in a different location, the index must be reset. The principle of operation is that the configuration stores the location of the original configuration directory, which must reside on the movable volume. If the volume is later mounted elsewhere, &RCL; adjusts the paths stored inside the index by the difference between the original and current locations of the configuration directory. To make a long story short, here follows a script to create a &RCL; configuration and index under a given directory (given as single parameter). The resulting data set (files + recoll directory) can later to be moved to a CDROM or thumb drive. Longer explanations come after the script. #!/bin/sh fatal() { echo $*;exit 1 } usage() { fatal "Usage: init-recoll-volume.sh <top-directory>" } test $# = 1 || usage topdir=$1 test -d "$topdir" || fatal $topdir should be a directory confdir="$topdir/recoll-config" test ! -d "$confdir" || fatal $confdir should not exist mkdir "$confdir" cd "$topdir" topdir=`pwd` cd "$confdir" confdir=`pwd` (echo topdirs = '"'$topdir'"'; \ echo orgidxconfdir = $topdir/recoll-config) > "$confdir/recoll.conf" recollindex -c "$confdir" The examples below will assume that you have a dataset under /home/me/mydata/, with the index configuration and data stored inside /home/me/mydata/recoll-confdir. In order to be able to run queries after the dataset has been moved, you must ensure the following: The main configuration file must define the orgidxconfdir variable to be the original location of the configuration directory (orgidxconfdir=/home/me/mydata/recoll-confdir must be set inside /home/me/mydata/recoll-confdir/recoll.conf in the example above). The configuration directory must exist with the documents, somewhere under the directory which will be moved. E.g. if you are moving /home/me/mydata around, the configuration directory must exist somewhere below this point, for example /home/me/mydata/recoll-confdir, or /home/me/mydata/sub/recoll-confdir. You should keep the default locations for the index elements which are relative to the configuration directory by default (principally dbdir). Only the paths referring to the documents themselves (e.g. topdirs values) should be absolute (in general, they are only used when indexing anyway). Only the first point needs an explicit user action, the &RCL; defaults are compatible with the third one, and the second is natural. If, after the move, the configuration directory needs to be copied out of the dataset (for example because the thumb drive is too slow), you can set the curidxconfdir, variable inside the copied configuration to define the location of the moved one. For example if /home/me/mydata is now mounted onto /media/me/somelabel, but the configuration directory and index has been copied to /tmp/tempconfig, you would set curidxconfdir to /media/me/somelabel/recoll-confdir inside /tmp/tempconfig/recoll.conf. orgidxconfdir would still be /home/me/mydata/recoll-confdir in the original and the copy. If you are regularly copying the configuration out of the dataset, it will be useful to write a script to automate the procedure. This can't really be done inside &RCL; because there are probably many possible variants. One example would be to copy the configuration to make it writable, but keep the index data on the medium because it is too big - in this case, the script would also need to set dbdir in the copied configuration. The same set of modifications (&RCL; 1.24) has also made it possible to run queries from a readonly configuration directory (with slightly reduced function of course, such as not recording the query history). &LIN;: indexing visited Web pages With the help of a Firefox extension, &RCL; can index the Internet pages that you visit. The extension has a long history: it was initially designed for the Beagle indexer, then adapted to &RCL; and the Firefox XUL API. The current version of the extension is located in the Mozilla add-ons repository uses the WebExtensions API, and works with current Firefox versions. The extension works by copying visited Web pages to an indexing queue directory, which &RCL; then processes, storing the data into a local cache, then indexing it, then removing the file from the queue. The local cache is not an archiveAs mentioned above, a copy of the indexed Web pages is retained by Recoll in a local cache (from which data is fetched for previews, or when resetting the index). The cache is not changed by an index reset, just read for indexing. The cache has a maximum size, which can be adjusted from the Index configuration / Web history panel (webcachemaxmbs parameter in recoll.conf). Once the maximum size is reached, old pages are erased to make room for new ones. The pages which you want to keep indefinitely need to be explicitly archived elsewhere. Using a very high value for the cache size can avoid data erasure, but see the above 'Howto' page for more details and gotchas. The visited Web pages indexing feature can be enabled on the &RCL; side from the GUI Index configuration panel, or by editing the configuration file (set processwebqueue to 1). The &RCL; GUI has a tool to list and edit the contents of the Web cache. (ToolsWebcache editor) The recollindex command has two options to help manage the Web cache: will recover the space from erased entries. It may need to use twice the disk space currently needed for the Web cache. will extract all current entries into pairs of metadata and data files created inside destdir You can find more details on Web indexing, its usage and configuration in a Recoll 'Howto' entry. &LIN;: using extended attributes User extended attributes are named pieces of information that most modern file systems can attach to any file. &RCL; processes all extended attributes as document fields. Note that most fields are not indexed by default, you need to activate them by defining a prefix in the fields configuration file. A freedesktop standard defines a few special attributes, which are handled as such by &RCL;: mime_type If set, this overrides any other determination of the file MIME type. charset If set, this defines the file character set (mostly useful for plain text files). By default, other attributes are handled as &RCL; fields of the same name. On Linux, the user prefix is removed from the name. The name translation can be configured more precisely, also inside the fields configuration file. Setting the document modification/creation date Date fields are processed specially by &RCL;. For obscure and uninteresting reasons, you should use modificationdate as extended attribute name for setting this value. Also, the date string should be an ASCII integer representing the Unix time (seconds since the epoch). An example Linux command line for setting this particular field follow. The substituted date prints the example date parameter in Unix time format (seconds since the epoch). setfattr -n user.modificationdate -v `date -d '2022-09-30 08:30:00' +%s` /some/file The date substitution will then be automatic, you do not need to customize the fields file. &LIN;: importing external tags During indexing, it is possible to import metadata for each file by executing commands. This allows, for example, extracting tag data from an external application and storing it in a field for indexing. See the section about the metadatacmds field in the main configuration chapter for a description of the configuration syntax. For example, if you would want &RCL; to use tags managed by tmsu in a field named tags, you would add the following to the configuration file: [/some/area/of/the/fs] metadatacmds = ; tags = tmsu tags %f Depending on the tmsu version, you may need/want to add options like --database=/some/db. You may want to restrict this processing to a subset of the directory tree, because it may slow down indexing a bit ([some/area/of/the/fs]). Note the initial semi-colon after the equal sign. In the example above, the output of tmsu is used to set a field named tags. The field name is arbitrary and could be tmsu or myfield just the same, but tags is an alias for the standard &RCL; keywords field, and the tmsu output will just augment its contents. This will avoid the need to extend the field configuration. Once re-indexing is performed (you will need to force the file reindexing, &RCL; will not detect the need by itself), you will be able to search from the query language, through any of its aliases: tags:some/alternate/values or tags:all,these,values. Tags changes will not be detected by the indexer if the file itself did not change. One possible workaround would be to update the file ctime when you modify the tags, which would be consistent with how extended attributes function. A pair of chmod commands could accomplish this, or a touch -a. Alternatively, just couple the tag update with a recollindex -e -i /path/to/the/file. Searching Introduction Getting answers to specific queries is of course the whole point of &RCL;. The multiple provided interfaces always understand simple queries made of one or several words, and return appropriate results in most cases. In order to make the most of &RCL; though, it may be worthwhile to understand how it processes your input. Five different modes exist: In All Terms mode, &RCL; looks for documents containing all your input terms. The Query Language mode behaves like All Terms in the absence of special input, but it can also do much more. This is the best mode for getting the most of &RCL;. It is usable from all possible interfaces (GUI, command line, WEB UI, ...), and is described here. In Any Term mode, &RCL; looks for documents containing any your input terms, preferring those which contain more. In File Name mode, &RCL; will only match file names, not content. Using a small subset of the index allows things like left-hand wildcards without performance issues, and may sometimes be useful. The GUI Advanced Search mode is actually not more powerful than the query language, but it helps you build complex queries without having to remember the language, and avoids any interpretation ambiguity, as it bypasses the user input parser. These five input modes are supported by the different user interfaces which are described in the following sections. Searching with the Qt graphical user interface The recoll program provides the main user interface for searching. It is based on the Qt library. recoll has two search interfaces: Simple search (the default, on the main screen) has a single entry field where you can enter multiple words or a query language query. Advanced search (a panel accessed through the Tools menu or the toolbox bar icon) has multiple entry fields, which you may use to build a logical condition, with additional filtering on file type, location in the file system, modification date, and size. The Advanced Search tool is easier to use, but not actually more powerful, than the Simple Search in query language mode. Its name is historical, but Assisted Search would probably have been a better designation. In most text areas, you can enter the terms as you think them, even if they contain embedded punctuation or other non-textual characters (e.g. &RCL; can handle things like email addresses). The main case where you should enter text differently from how it is printed is for east-asian languages (Chinese, Japanese, Korean). Words composed of single or multiple characters should be entered separated by white space in this case (they would typically be printed without white space). Some searches can be quite complex, and you may want to re-use them later, perhaps with some tweaking. &RCL; can save and restore searches. See Saving and restoring queries. Simple search Start the recoll program. Possibly choose a search mode: Any term, All terms, File name or Query language. Enter search term(s) in the text field at the top of the window. Click the Search button or hit the Enter key to start the search. The initial default search mode is Query language. Without special directives, this will look for documents containing all of the search terms (the ones with more terms will get better scores), just like the All Terms mode. Any term will search for documents where at least one of the terms appear. File name will exclusively look for file names, not contents All search modes allow terms to be expanded with wildcards characters (*, ?, []). See the section about wildcards for more details. In all modes except File name, you can search for exact phrases (adjacent words in a given order) by enclosing the input inside double quotes. Ex: "virtual reality". The Query Language features are described in a separate section. When using a stripped index (the default), character case has no influence on search, except that you can disable stem expansion for any term by capitalizing it. E.g.: a search for floor will also normally look for flooring, floored, etc., but a search for Floor will only look for floor, in any character case. Stemming can also be disabled globally in the preferences. When using a raw index, the rules are a bit more complicated. &RCL; remembers the last few searches that you performed. You can directly access the search history by clicking the clock button on the right of the search entry, while the latter is empty. Otherwise, the history is used for entry completion (see next). Only the search texts are remembered, not the mode (all/any/file name). While text is entered in the search area, recoll will display possible completions, filtered from the history and the index search terms. This can be disabled with a GUI Preferences option. Double-clicking on a word in the result list or a preview window will insert it into the simple search entry field. You can cut and paste any text into an All terms or Any term search field, punctuation, newlines and all - except for wildcard characters (single ? characters are ok). &RCL; will process it and produce a meaningful search. This is what most differentiates this mode from the Query Language mode, where you have to care about the syntax. The File name search mode will specifically look for file names. The point of having a separate file name search is that wildcard expansion can be performed more efficiently on a small subset of the index (allowing wildcards on the left of terms without excessive cost). Things to know: White space in the entry should match white space in the file name, and is not treated specially. The search is insensitive to character case and accents, independently of the type of index. An entry without any wildcard character and not capitalized will be prepended and appended with '*' (e.g.: etc -> *etc*, but Etc -> etc). If you have a big index (many files), excessively generic fragments may result in inefficient searches. The result list After starting a search, a list of results will instantly be displayed in the main window. By default, the document list is presented in order of relevance (how well the application estimates that the document matches the query). You can sort the results by ascending or descending date by using the vertical arrows in the toolbar. Each result is displayed as a structured text paragraph. The standard format is typically adequate, but the content and presentation are entirely customisable. Most results will contain Preview and Open clickable links. Clicking the Preview link will open an internal preview window for the document. Further Preview clicks for the same search will open tabs in the existing preview window. You can use Shift+Click to force the creation of another preview window, which may be useful to view the documents side by side. (You can also browse successive results in a single preview window by typing Shift+ArrowUp/Down in the window). Clicking the Open link will start an external viewer for the document. By default, &RCL; lets the desktop choose the appropriate application for most document types. See further for customizing the applications. The Preview and Open links may not be present for all entries. They are only available, respectively, for documents with MIME types that &RCL; can extract text from, and for documents that have a configured viewer. However, you can modify the configuration to adjust this behavior. In more detail: The Preview link will appear for documents with a MIME type present in the [index] section of the mimeconf file, and, only if the textunknownasplain configuration variable is set, for all types identified as a subtype of text (text/*). The Open link will appear for documents with a MIME type present in the [view] section of the mimeview configuration file. If textunknownasplain is set and no specific viewer is found for a subtype of text, the viewer for text/plain will be used. You can click on the Query details link at the top of the results page to see the actual Xapian query, after stem expansion and other processing. Double-clicking on any word inside the result list or a preview window will insert it into the simple search text. The result list is divided into pages. You can change the page size in the preferences. Use the arrow buttons in the toolbar or the links at the bottom of the page to browse the results. Customising the viewers By default &RCL; lets the desktop choose what application should be used to open a given document, with exceptions. The details of this behaviour can be customized with the Preferences GUI configuration User interface Choose editor applications dialog or by editing the mimeview configuration file. When Use desktop preferences, at the top of the dialog, is checked, the desktop default is generally used, but there is a small default list of exceptions, for MIME types where the &RCL; choice should override the desktop one. These are applications which are well integrated with &RCL;, for example, on Linux, evince for viewing PDF and Postscript files because of its support for opening the document at a specific page and passing a search string as an argument. You can add or remove document types to the exceptions by using the dialog. If you prefer to completely customize the choice of applications, you can uncheck Use desktop preferences, in which case the &RCL; predefined applications will be used, and can be changed for each document type. This is probably not the most convenient approach in most cases. In all cases, the applications choice dialog accepts multiple selections of MIME types in the top section, and lets you define how they are processed in the bottom one. In most cases, you will be using %f as a place holder to be replaced by the file name in the application command line. You may also change the choice of applications by editing the mimeview configuration file if you find this more convenient. Under &LIN;, each result list entry also has a right-click menu with an Open With entry. This lets you choose an application from the list of those which registered with the desktop for the document MIME type, on a case by case basis. No results: the spelling suggestions When a search yields no result, and if the aspell dictionary is configured, &RCL; will try to check for misspellings among the query terms, and will propose lists of replacements. Clicking on one of the suggestions will replace the word and restart the search. You can hold any of the modifier keys (Ctrl, Shift, etc.) while clicking if you would rather stay on the suggestion screen because several terms need replacement. The result list right-click menu Apart from the preview and edit links, you can display a pop-up menu by right-clicking over a paragraph in the result list. This menu has the following entries: Preview Open Open With Run Script Copy File Name Copy Url Save to File Find similar Preview Parent document Open Parent document Open Snippets Window The Preview and Open entries do the same thing as the corresponding links. Open With (&LIN;) lets you open the document with one of the applications claiming to be able to handle its MIME type (the information comes from the .desktop files in /usr/share/applications). Run Script allows starting an arbitrary command on the result file. It will only appear for results which are top-level files. See further for a more detailed description. The Copy File Name and Copy Url copy the relevant data to the clipboard, for later pasting. Save to File allows saving the contents of a result document to a chosen file. This entry will only appear if the document does not correspond to an existing file, but is a subdocument inside such a file (e.g.: an email attachment). It is especially useful to extract attachments with no associated editor. The Open/Preview Parent document entries allow working with the higher level document (e.g. the email message an attachment comes from). &RCL; is sometimes not totally accurate as to what it can or can't do in this area. For example the Parent entry will also appear for an email which is part of an mbox folder file, but you can't actually visualize the mbox (there will be an error dialog if you try). If the document is a top-level file, Open Parent will start the default file manager on the enclosing filesystem directory. The Find similar entry will select a number of relevant term from the current document and enter them into the simple search field. You can then start a simple search, with a good chance of finding documents related to the current result. I can't remember a single instance where this function was actually useful to me... The Open Snippets Window entry will only appear for documents which support page breaks (typically PDF, Postscript, DVI). The snippets window lists extracts from the document, taken around search terms occurrences, along with the corresponding page number, as links which can be used to start the native viewer on the appropriate page. If the viewer supports it, its search function will also be primed with one of the search terms. The result table As an alternative to the result list, the results can also be displayed in spreadsheet-like fashion. You can switch to this presentation by clicking the table-like icon in the toolbar (this is a toggle, click again to restore the list). Clicking on the column headers will allow sorting by the values in the column. You can click again to invert the order, and use the header right-click menu to reset sorting to the default relevance order (you can also use the sort-by-date arrows to do this). Both the list and the table display the same underlying results. The sort order set from the table is still active if you switch back to the list mode. You can click twice on a date sort arrow to reset it from there. The header right-click menu allows adding or deleting columns. The columns can be resized, and their order can be changed (by dragging). All the changes are recorded when you quit recoll Hovering over a table row will update the detail area at the bottom of the window with the corresponding values. You can click the row to freeze the display. The bottom area is equivalent to a result list paragraph, with links for starting a preview or a native application, and an equivalent right-click menu. Typing Esc (the Escape key) will unfreeze the display. Using Shift-click on a row will display the document extracted text (somewhat like a preview) instead of the document details. The functions of Click and Shift-Click can be reversed in the GUI preferences. The filters panel By default, the GUI displays the filters panel on the left of the results area. This is new in version 1.32. You can adjust the width of the panel, and hide it by squeezing it completely. The width will be memorized for the next session. The panel currently has two areas, for filtering the results by dates, or by filesystem location. The panel is only active in Query Language search mode, and its effect is to add date: and dir: clauses to the actual search. The dates filter can be activated by clicking the checkbox. It has two assisted date entry widgets, for the minimum and maximum dates of the search period. The directory filter displays a subset of the filesystem directories, reduced to the indexed area, as defined by the topdirs list and the name exclusion parameters. You can independantly select and deselect directories by clicking them. Note that selecting a directory will activate the whole subtree for searching, there is no need to select the subdirectories, and no way to exclude some of them (use Query language dir: clauses if this is needed). Running arbitrary commands on result files Apart from the Open and Open With operations, which allow starting an application on a result document (or a temporary copy), based on its MIME type, it is also possible to run arbitrary commands on results which are top-level files, using the Run Script entry in the results pop-up menu. The commands which will appear in the Run Script submenu must be defined by .desktop files inside the scripts subdirectory of the current configuration directory. Here follows an example of a .desktop file, which could be named for example, ~/.recoll/scripts/myscript.desktop (the exact file name inside the directory is irrelevant): [Desktop Entry] Type=Application Name=MyFirstScript Exec=/home/me/bin/tryscript %F MimeType=*/* The Name attribute defines the label which will appear inside the Run Script menu. The Exec attribute defines the program to be run, which does not need to actually be a script, of course. The MimeType attribute is not used, but needs to exist. The commands defined this way can also be used from links inside the result paragraph. As an example, it might make sense to write a script which would move the document to the trash and purge it from the &RCL; index. &LIN;: displaying thumbnails The default format for the result list entries and the detail area of the result table display an icon for each result document. The icon is either a generic one determined from the MIME type, or a thumbnail of the document appearance. Thumbnails are only displayed if found in the standard freedesktop location, where they would typically have been created by a file manager. Recoll has no capability to create thumbnails. A relatively simple trick is to use the Open parent document/folder entry in the result list popup menu. This should open a file manager window on the containing directory, which should in turn create the thumbnails (depending on your settings). Restarting the search should then display the thumbnails. There are also some pointers about thumbnail generation in the &RCL; FAQ. The preview window The preview window opens when you first click a Preview link inside the result list. Subsequent preview requests for a given search open new tabs in the existing window (except if you hold the Shift key while clicking which will open a new window for side by side viewing). Starting another search and requesting a preview will create a new preview window. The old one stays open until you close it. You can close a preview tab by typing Ctrl-W (Ctrl + W) in the window. Closing the last tab, or using the window manager button in the top of the frame will also close the window. You can display successive or previous documents from the result list inside a preview tab by typing Shift+Down or Shift+Up (Down and Up are the arrow keys). A right-click menu in the text area allows switching between displaying the main text or the contents of fields associated to the document (e.g.: author, abtract, etc.). This is especially useful in cases where the term match did not occur in the main text but in one of the fields. In the case of images, you can switch between three displays: the image itself, the image metadata as extracted by exiftool and the fields, which is the metadata stored in the index. You can print the current preview window contents by typing Ctrl-P (Ctrl + P) in the window text. Searching inside the preview The preview window has an internal search capability, mostly controlled by the panel at the bottom of the window, which works in two modes: as a classical editor incremental search, where we look for the text entered in the entry zone, or as a way to walk the matches between the document and the &RCL; query that found it. Incremental text search The preview tabs have an internal incremental search function. You initiate the search either by typing a / (slash) or CTL-F inside the text area or by clicking into the Search for: text field and entering the search string. You can then use the Next and Previous buttons to find the next/previous occurrence. You can also type F3 inside the text area to get to the next occurrence. If you have a search string entered and you use Ctrl-Up/Ctrl-Down to browse the results, the search is initiated for each successive document. If the string is found, the cursor will be positioned at the first occurrence of the search string. Walking the match lists If the entry area is empty when you click the Next or Previous buttons, the editor will be scrolled to show the next match to any search term (the next highlighted zone). If you select a search group from the dropdown list and click Next or Previous, the match list for this group will be walked. This is not the same as a text search, because the occurrences will include non-exact matches (as caused by stemming or wildcards). The search will revert to the text mode as soon as you edit the entry area. The Query Fragments window The Query Fragments window can be used to control filtering query language elements modifying the current query, simply by clicking a button. This can be useful to save typing, or avoid memorizing, simple clauses of common usage (e.g. selecting only standalone documents or attachments, or filtering out WEB results, selecting a file system subtree, a file type, etc.). Selecting the Tools Query Fragments menu entry will open the dialog. The contents of the window are entirely customizable, and defined by the contents of a XML text file, named fragment-buttons.xml and which will be looked for in the current index configuration directory. The sample file distributed with &RCL; contains a number of example filters. This will be automatically copied to the configuration directory if the file does not exist in there (e.g. ~/.recoll/fragment-buttons.xml under Linux and Mac OS, $HOME/AppData/Local/Recoll/fragment-buttons.xml for Windows). Editing the copy will allow you to configure the tool for your needs . The fragment-buttons.xml file was named fragbuts.xml up to &RCL; version 1.31.0. This was deemed too close to offensive for native English speakers, so that the file was renamed. An existing fragbuts.xml will still be used if fragment-buttons.xml does not exist. No automatic renaming will be performed. Here follows an example window: And the corresponding configuration file: -rclbes:BGL rclbes:BGL issub:0 issub:1 date:2010-01-01/2010-12-31 ext:cpp OR ext:cxx dir:/my/great/directory ]]> There are two types of groupings radiobuttons and buttons, each defining a line of checkbuttons or radiobuttons inside the window. Any number of buttons can be selected, but the radiobuttons in a line are exclusive. Buttons are defined by a fragbutton section, which provides the label for a button, and the Query Language fragment which will be added (as an AND filter) before performing the query if the button is active. dir:/my/great/directory ]]> It is also possible to add message elements inside the groups, for documenting the behaviour. message elements have a label but no frag element. Example: ]]> The label contents are interpreted as HTML. Take care to replace opening < characters with the &lt; entity if you use tags. The only thing that you need to know about XML for editing this file is that any opening tag like <label> needs to be matched by a closing tag after the value: </label>. You will normally edit the file with a regular text editor, like, e.g. vi or notepad. Double-clicking the file in a file manager may not work, because this usually opens it in a WEB browser, which will not let you modify the contents. Assisted Complex Search (A.K.A. "Advanced Search") The advanced search dialog helps you build more complex queries without memorizing the search language constructs. It can be opened through the Tools menu or through the main toolbar. &RCL; keeps a history of searches. See Advanced search history. The dialog has two tabs: The first tab lets you specify terms to search for, and permits specifying multiple clauses which are combined to build the search. The second tab allows filtering the results according to file size, date of modification, MIME type, or location. Click on the Start Search button in the advanced search dialog, or type Enter in any text field to start the search. The button in the main window always performs a simple search. Click on the Show query details link at the top of the result page to see the query expansion. Advanced search: the "find" tab This part of the dialog lets you construct a query by combining multiple clauses of different types. Each entry field is configurable for the following modes: All terms. Any term. None of the terms. Phrase (exact terms in order within an adjustable window). Proximity (terms in any order within an adjustable window). Filename search. Additional entry fields can be created by clicking the Add clause button. When searching, the non-empty clauses will be combined either with an AND or an OR conjunction, depending on the choice made on the left (All clauses or Any clause). Entries of all types except "Phrase" and "Near" accept a mix of single words and phrases enclosed in double quotes. Stemming and wildcard expansion will be performed as for simple search. Phrase and Proximity searches These two clauses look for a group of terms in specified relative positions. They differ in the sense that the order of input terms is significant for phrase searches, but not for proximity searches. The latter do not impose an order on the words. In both cases, an adjustable number (slack) of non-matched words may be accepted between the searched ones. For phrase searches, the default count is zero (exact match). For proximity searches it is ten (meaning that two search terms, would be matched if found within a window of twelve words). Examples: a phrase search for quick fox with a slack of 0 will match quick fox but not quick brown fox. With a slack of 1 it will match the latter, but not fox quick. A proximity search for quick fox with the default slack will match the latter, and also a fox is a cunning and quick animal. The slack can be adjusted with the counter to the left of the input area Advanced search: the "filter" tab This part of the dialog has several sections which allow filtering the results of a search according to a number of criteria The first section allows filtering by dates of last modification. You can specify both a minimum and a maximum date. The initial values are set according to the oldest and newest documents found in the index. The next section allows filtering the results by file size. There are two entries for minimum and maximum size. Enter decimal numbers. You can use suffix multipliers: k/K, m/M, g/G, t/T for 10E3, 10E6, 10E9, 10E12 respectively. The next section allows filtering the results by their MIME types, or MIME categories (e.g.: media/text/message/etc.). You can transfer the types between two boxes, to define which will be included or excluded by the search. The state of the file type selection can be saved as the default (the file type filter will not be activated at program start-up, but the lists will be in the restored state). The bottom section allows restricting the search results to a sub-tree of the indexed area. You can use the Invert checkbox to search for files not in the sub-tree instead. If you use directory filtering often and on big subsets of the file system, you may think of setting up multiple indexes instead, as the performance may be better. You can use relative/partial paths for filtering. E.g., entering dirA/dirB would match either /dir1/dirA/dirB/myfile1 or /dir2/dirA/dirB/someother/myfile2. Advanced search history The advanced search tool memorizes the last 100 searches performed. You can walk the saved searches by using the up and down arrow keys while the keyboard focus belongs to the advanced search dialog. The complex search history can be erased, along with the one for simple search, by selecting the File Erase Search History menu entry. The term explorer tool &RCL; automatically manages the expansion of search terms to their derivatives (e.g.: plural/singular, verb inflections). But there are other cases where the exact search term is not known. For example, you may not remember the exact spelling, or only know the beginning of the name. The search will only propose replacement terms with spelling variations when no matching document were found. In some cases, both proper spellings and mispellings are present in the index, and it may be interesting to look for them explicitly. The term explorer tool (started from the toolbar icon or from the Term explorer entry of the Tools menu) can be used to search the full index terms list, or (later addition), display some statistics or other index information. It has several modes of operations: Wildcard In this mode of operation, you can enter a search string with shell-like wildcards (*, ?, []). e.g.: xapi* would display all index terms beginning with xapi. (More about wildcards here). Regular expression This mode will accept a regular expression as input. Example: word[0-9]+. The expression is implicitly anchored at the beginning. E.g.: press will match pression but not expression. You can use .*press to match the latter, but be aware that this will cause a full index term list scan, which can be quite long. Stem expansion This mode will perform the usual stem expansion normally done as part user input processing. As such it is probably mostly useful to demonstrate the process. Spelling/Phonetic In this mode, you enter the term as you think it is spelled, and &RCL; will do its best to find index terms that sound like your entry. This mode uses the Aspell spelling application, which must be installed on your system for things to work (if your documents contain non-ascii characters, &RCL; needs an aspell version newer than 0.60 for UTF-8 support). The language which is used to build the dictionary out of the index terms (which is done at the end of an indexing pass) is the one defined by your NLS environment. Weird things will probably happen if languages are mixed up. Show index statistics This will print a long list of boring numbers about the index List files which could not be indexed This will show the files which caused errors, usually because recollindex could not translate their format into text. Note that in cases where &RCL; does not know the beginning of the string to search for (e.g. a wildcard expression like *coll), the expansion can take quite a long time because the full index term list will have to be processed. The expansion is currently limited at 10000 results for wildcards and regular expressions. It is possible to change the limit in the configuration file. Double-clicking on a term in the result list will insert it into the simple search entry field. You can also cut/paste between the result list and any entry field (the end of lines will be taken care of). Multiple indexes See the section describing the use of multiple indexes for generalities. Only the aspects concerning the recoll GUI are described here. A recoll program instance is always associated with a main index, which is the one to be updated when requested from the File menu, but it can use any number of external &RCL; indexes for searching. The external indexes can be selected through the External Indexes tab in the preferences dialog, which can be reached either trough: Preferences GUI Configuration External Index Dialog or Query External index dialog . Index selection is performed in two phases. A set of all usable indexes must first be defined, and then the subset of indexes to be used for searching. These parameters are retained across program executions (there are kept separately for each &RCL; configuration). The set of all indexes is usually quite stable, while the active ones might typically be adjusted quite frequently. The main index (defined by RECOLL_CONFDIR) is always active. If this is undesirable, you can set up your base configuration to index an empty directory. When adding a new index to the set, you can select either a &RCL; configuration directory, or directly a &XAP; index directory. In the first case, the &XAP; index directory will be obtained from the selected configuration. If the external index is actually located on a volume mounted from another machine, and references remote files, there may be a need to adjust the result paths so that they match the locally mounted ones (for opening documents). This can be done by using the path translation facility. As building the set of all indexes can be a little tedious when done through the user interface, you can use the RECOLL_EXTRA_DBS environment variable to provide an initial set. This might typically be set up by a system administrator so that every user does not have to do it. The variable should define a colon-separated list of index directories, e.g.: export RECOLL_EXTRA_DBS=/some/place/xapiandb:/some/other/db On Windows, use semi-colons (;) as separators instead of colons. Another environment variable, RECOLL_ACTIVE_EXTRA_DBS allows adding to the active list of indexes. This variable was suggested and implemented by a &RCL; user. It is mostly useful if you use scripts to mount external volumes with &RCL; indexes. By using RECOLL_EXTRA_DBS and RECOLL_ACTIVE_EXTRA_DBS, you can add and activate the index for the mounted volume when starting recoll. Unreachable indexes will automatically be deactivated when starting up. Document history Documents that you actually view (with the internal preview or an external tool) are entered into the document history, which is remembered. You can display the history list by using the Tools/Doc History menu entry. You can erase the document history by using the Erase document history entry in the File menu. Sorting search results and collapsing duplicates The documents in a result list are normally sorted in order of relevance. It is possible to specify a different sort order, either by using the vertical arrows in the GUI toolbox to sort by date, or switching to the result table display and clicking on any header. The sort order chosen inside the result table remains active if you switch back to the result list, until you click one of the vertical arrows, until both are unchecked (you are back to sort by relevance). Sort parameters are remembered between program invocations, but result sorting is normally always inactive when the program starts. It is possible to keep the sorting activation state between program invocations by checking the Remember sort activation state option in the preferences. It is also possible to hide duplicate entries inside the result list (documents with the exact same contents as the displayed one). The test of identity is based on an MD5 hash of the document container, not only of the text contents (so that e.g., a text document with an image added will not be a duplicate of the text only). Duplicates hiding is controlled by an entry in the GUI configuration dialog, and is off by default. When a result document does have undisplayed duplicates, a Dups link will be shown with the result list entry. Clicking the link will display the paths (URLs + ipaths) for the duplicate entries. Keyboard shortcuts A number of common actions within the graphical interface can be triggered through keyboard shortcuts. As of &RCL; 1.29, many of the shortcut values can be customised from a screen in the GUI preferences. Most shortcuts are specific to a given context (e.g. within a preview window, within the result table). Most shortcuts can be changed to a preferred value by using the GUI shortcut editor: Preferences GUI configuration Shortcuts . In order to change a shortcut, just click the corresponding cell in the Shortcut column, and type the desired sequence. Keyboard shortcuts DescriptionDefault value Context: almost everywhere Program exit Ctrl+Q Context: advanced search Load the next entry from the search history Up Load the previous entry from the search history Down Context: main window Clear search. This will move the keyboard cursor to the simple search entry and erase the current text Ctrl+S Move the keyboard cursor to the search entry area without erasing the current text Ctrl+L Move the keyboard cursor to the search entry area without erasing the current text Ctrl+Shift+S Toggle displaying the current results as a table or as a list Ctrl+T Context: main window, when showing the results as a table Move the keyboard cursor to currently the selected row in the table, or to the first one if none is selected Ctrl+R Jump to row 0-9 or a-z in the table Ctrl+[0-9] or Ctrl+Shift+[a-z] Cancel the current selectionEsc Context: preview window Close the preview window Esc Close the current tab Ctrl+W Open a print dialog for the current tab contents Ctrl+P Load the next result from the list to the current tab Shift+Down Load the previous result from the list to the current tab Shift+Up Context: result table Copy the text contained in the selected document to the clipboard Ctrl+G Copy the text contained in the selected document to the clipboard, then exit recoll Ctrl+Alt+Shift+G Open the current document Ctrl+O Open the current document and exit Recoll Ctrl+Alst+Shift+O Show a full preview for the current document Ctrl+D Toggle showing the column names Ctrl+H Show a snippets (keyword in context) list for the current document Ctrl+E Toggle showing the row letters/numbers Ctrl+V Context: snippets window Close the snippets window Esc Find in the snippets list (method #1) Ctrl+F Find in the snippets list (method #2) / Find the next instance of the search term F3 Find the previous instance of the search term Shift+F3
Search tips Terms and search expansion Term completion While typing into the simple search entry, a popup menu will appear and show completions for the current string. Values preceded by a clock icon come from the history, those preceded by a magnifier icon come from the index terms. This can be disabled in the preferences. Picking up new terms from result or preview text Double-clicking on a word in the result list or in a preview window will copy it to the simple search entry field. Wildcards Wildcards can be used inside search terms in all forms of searches. More about wildcards. Automatic suffixes Words like odt or ods can be automatically turned into query language ext:xxx clauses. This can be enabled in the Search preferences panel in the GUI. Disabling stem expansion Entering a capitalized word in any search field will prevent stem expansion (no search for gardening if you enter Garden instead of garden). This is the only case where character case should make a difference for a &RCL; search. You can also disable stem expansion or change the stemming language in the preferences. Finding related documents Selecting the Find similar documents entry in the result list paragraph right-click menu will select a set of "interesting" terms from the current result, and insert them into the simple search entry field. You can then possibly edit the list and start a search to find documents which may be apparented to the current result. File names File names are added as terms during indexing, and you can specify them as ordinary terms in normal search fields (&RCL; used to index all directories in the file path as terms. This has been abandoned as it did not seem really useful). Alternatively, you can use the specific file name search which will only look for file names, and may be faster than the generic search especially when using wildcards. Working with phrases and proximity Phrases searches A phrase can be looked for by enclosing a number of terms in double quotes. Example: "user manual" will look only for occurrences of user immediately followed by manual. You can use the "Phrase" field of the advanced search dialog to the same effect. Phrases can be entered along simple terms in all simple or advanced search entry fields, except "Phrase". Proximity searches A proximity search differs from a phrase search in that it does not impose an order on the terms. Proximity searches can be entered by specifying the "Proximity" type in the advanced search, or by postfixing a phrase search with a 'p'. Example: "user manual"p would also match "manual user". Also see the modifier section from the query language documentation. AutoPhrases This option can be set in the preferences dialog. If it is set, a phrase will be automatically built and added to simple searches when looking for Any terms. This will not change radically the results, but will give a relevance boost to the results where the search terms appear as a phrase. E.g.: searching for virtual reality will still find all documents where either virtual or reality or both appear, but those which contain virtual reality should appear sooner in the list. Phrase searches can slow down a query if most of the terms in the phrase are common. If the autophrase option is on, very common terms will be removed from the automatically constructed phrase. The removal threshold can be adjusted from the search preferences. Phrases and abbreviations Dotted abbreviations like I.B.M. are also automatically indexed as a word without the dots: IBM. Searching for the word inside a phrase (e.g.: "the IBM company") will only match the dotted abrreviation if you increase the phrase slack (using the advanced search panel control, or the o query language modifier). Literal occurrences of the word will be matched normally. Others Using fields You can use the query language and field specifications to only search certain parts of documents. This can be especially helpful with email, for example only searching emails from a specific originator: search tips from:helpfulgui Adjusting the result table columns When displaying results in table mode, you can use a right click on the table headers to activate a pop-up menu which will let you adjust what columns are displayed. You can drag the column headers to adjust their order. You can click them to sort by the field displayed in the column. You can also save the result list in CSV format. Changing the GUI geometry It is possible to configure the GUI in wide form factor by dragging the toolbars to one of the sides (their location is remembered between sessions), and moving the category filters to a menu (can be set in the Preferences GUI configuration User interface panel). Query explanation You can get an exact description of what the query looked for, including stem expansion, and Boolean operators used, by clicking on the result list header. Advanced search history You can display any of the last 100 complex searches performed by using the up and down arrow keys while the advanced search panel is active. Forced opening of a preview window You can use Shift+Click on a result list Preview link to force the creation of a preview window instead of a new tab in the existing one. Saving and restoring queries Both simple and advanced query dialogs save recent history, but the amount is limited: old queries will eventually be forgotten. Also, important queries may be difficult to find among others. This is why both types of queries can also be explicitly saved to files, from the GUI menus: File Save last query / Load last query The default location for saved queries is a subdirectory of the current configuration directory, but saved queries are ordinary files and can be written or moved anywhere. Some of the saved query parameters are part of the preferences (e.g. autophrase or the active external indexes), and may differ when the query is loaded from the time it was saved. In this case, &RCL; will warn of the differences, but will not change the user preferences. Customizing the search interface You can customize some aspects of the search interface by using the GUI configuration entry in the Preferences menu. There are several tabs in the dialog, dealing with the interface itself, the parameters used for searching and returning results, and what indexes are searched. User interface parameters: Highlight color for query terms: Terms from the user query are highlighted in the result list samples and the preview window. The color can be chosen here. Any Qt color string should work (e.g. red, #ff0000). The default is blue. Style sheet: The name of a Qt style sheet text file which is applied to the whole Recoll application on startup. The default value is empty, but there is a skeleton style sheet (recoll.qss) inside the /usr/share/recoll/examples directory. Using a style sheet, you can change most recoll graphical parameters: colors, fonts, etc. See the sample file for a few simple examples. You should be aware that parameters (e.g.: the background color) set inside the &RCL; GUI style sheet will override global system preferences, with possible strange side effects: for example if you set the foreground to a light color and the background to a dark one in the desktop preferences, but only the background is set inside the &RCL; style sheet, and it is light too, then text will appear light-on-light inside the &RCL; GUI. Maximum text size highlighted for preview Inserting highlights on search term inside the text before inserting it in the preview window involves quite a lot of processing, and can be disabled over the given text size to speed up loading. Prefer HTML to plain text for preview if set, Recoll will display HTML as such inside the preview window. If this causes problems with the Qt HTML display, you can uncheck it to display the plain text version instead. Activate links in preview if set, Recoll will turn HTTP links found inside plain text into proper HTML anchors, and clicking a link inside a preview window will start the default browser on the link target. Plain text to HTML line style: when displaying plain text inside the preview window, &RCL; tries to preserve some of the original text line breaks and indentation. It can either use PRE HTML tags, which will well preserve the indentation but will force horizontal scrolling for long lines, or use BR tags to break at the original line breaks, which will let the editor introduce other line breaks according to the window width, but will lose some of the original indentation. The third option has been available in recent releases and is probably now the best one: use PRE tags with line wrapping. Choose editor application: this opens a dialog which allows you to select the application to be used to open each MIME type. The default is to use the xdg-open utility, but you can use this dialog to override it, setting exceptions for MIME types that will still be opened according to &RCL; preferences. This is useful for passing parameters like page numbers or search strings to applications that support them (e.g. evince). This cannot be done with xdg-open which only supports passing one parameter. Disable Qt autocompletion in search entry: this will disable the completion popup. Il will only appear, and display the full history, either if you enter only white space in the search area, or if you click the clock button on the right of the area. Document filter choice style: this will let you choose if the document categories are displayed as a list or a set of buttons, or a menu. Start with simple search mode: this lets you choose the value of the simple search type on program startup. Either a fixed value (e.g. Query Language, or the value in use when the program last exited. Start with advanced search dialog open : If you use this dialog frequently, checking the entries will get it to open when recoll starts. Remember sort activation state if set, Recoll will remember the sort tool stat between invocations. It normally starts with sorting disabled. Result list parameters: Number of results in a result page Result list font: There is quite a lot of information shown in the result list, and you may want to customize the font and/or font size. The rest of the fonts used by &RCL; are determined by your generic Qt config (try the qtconfig command). Edit result list paragraph format string: allows you to change the presentation of each result list entry. See the result list customisation section. Edit result page HTML header insert: allows you to define text inserted at the end of the result page HTML header. More detail in the result list customisation section. Date format: allows specifying the format used for displaying dates inside the result list. This should be specified as an strftime() string (man strftime). Abstract snippet separator: for synthetic abstracts built from index data, which are usually made of several snippets from different parts of the document, this defines the snippet separator, an ellipsis by default. Search parameters: Hide duplicate results: decides if result list entries are shown for identical documents found in different places. Stemming language: stemming obviously depends on the document's language. This listbox will let you chose among the stemming databases which were built during indexing (this is set in the main configuration file), or later added with recollindex -s (See the recollindex manual). Stemming languages which are dynamically added will be deleted at the next indexing pass unless they are also added in the configuration file. Automatically add phrase to simple searches: a phrase will be automatically built and added to simple searches when looking for Any terms. This will give a relevance boost to the results where the search terms appear as a phrase (consecutive and in order). Autophrase term frequency threshold percentage: very frequent terms should not be included in automatic phrase searches for performance reasons. The parameter defines the cutoff percentage (percentage of the documents where the term appears). Replace abstracts from documents: this decides if we should synthesize and display an abstract in place of an explicit abstract found within the document itself. Dynamically build abstracts: this decides if &RCL; tries to build document abstracts (lists of snippets) when displaying the result list. Abstracts are constructed by taking context from the document information, around the search terms. Synthetic abstract size: adjust to taste... Synthetic abstract context words: how many words should be displayed around each term occurrence. Query language magic file name suffixes: a list of words which automatically get turned into ext:xxx file name suffix clauses when starting a query language query (e.g.: doc xls xlsx...). This will save some typing for people who use file types a lot when querying. External indexes: This panel will let you browse for additional indexes that you may want to search. External indexes are designated by their database directory (e.g.: /home/someothergui/.recoll/xapiandb, /usr/local/recollglobal/xapiandb). Once entered, the indexes will appear in the External indexes list, and you can chose which ones you want to use at any moment by checking or unchecking their entries. Your main database (the one the current configuration indexes to), is always implicitly active. If this is not desirable, you can set up your configuration so that it indexes, for example, an empty directory. An alternative indexer may also need to implement a way of purging the index from stale data, The result list format Recoll normally uses a full function HTML processor to display the result list and the snippets window. Depending on the version, this may be based on either Qt WebKit or Qt WebEngine. It is then possible to completely customise the result list with full support for CSS and Javascript. It is also possible to build &RCL; to use a simpler Qt QTextBrowser widget to display the HTML, which may be necessary if the ones above are not ported on the system, or to reduce the application size and dependencies. There are limits to what you can do in this case, but it is still possible to decide what data each result will contain, and how it will be displayed. The result list presentation can be customized by adjusting two elements: The paragraph format HTML code inside the header section. This is also used for the snippets window. The paragraph format and the header fragment can be edited from the Result list tab of the GUI configuration. The header fragment is used both for the result list and the snippets window. The snippets list is a table and has a snippets class attribute. Each paragraph in the result list is a table, with class respar, but this can be changed by editing the paragraph format. There are a few examples on the page about customising the result list on the &RCL; Web site. The paragraph format This is an arbitrary HTML string which will be transformed by printf-like % substitutions to show the results. Any literal % character in the input must be quoted as %%. E.g. <table style="width: 100%;"> should be entered as <table style="width: 100%%;">. The following substitutions will be performed: %AAbstract %DDate %IIcon image name. This is normally determined from the MIME type. The associations are defined inside the mimeconf configuration file. If a thumbnail for the file is found at the standard Freedesktop location, this will be displayed instead. %KKeywords (if any) %LPrecooked Preview, Edit, and possibly Snippets links %MMIME type %Nresult Number inside the result page %PParent folder Url. In the case of an embedded document, this is the parent folder for the top level container file. %RRelevance percentage %SSize information %TTitle or Filename if not set. %tTitle or empty. %(filename)File name. %UUrl In addition to the predefined values above, all strings like %(fieldname) will be replaced by the value of the field named fieldname for this document. Only stored fields can be accessed in this way, the value of indexed but not stored fields is not known at this point in the search process (see field configuration). There are currently very few fields stored by default, apart from the values above (only author and filename), so this feature will need some custom local configuration to be useful. An example candidate would be the recipient field which is generated by the message input handlers. The format of the Preview, Edit, and Snippets links is <a href="P%N">, <a href="E%N"> and <a href="A%N"> where docnum (%N) expands to the document number inside the result page). A link target defined as "F%N" will open the document corresponding to the %P parent folder expansion, usually creating a file manager window on the folder where the container file resides. E.g.: <a href="F%N">%P</a> A link target defined as R%N|scriptname will run the corresponding script on the result file (if the document is embedded, the script will be started on the top-level parent). See the section about defining scripts. The default value for the paragraph format string is: \n" "\n" "\n" "%L  %S   %T
\n" "%M %D    %U %i
\n" "%A %K\n" "\n" ]]>
You may, for example, try the following for a more web-like experience: %T
%A%U - %S - %L ]]>
Note that the P%N link in the above paragraph makes the title a preview link. Or the clean looking: %L %R   %T&
%S  %U
%A
%K ]]>
These samples, and some others are on the web site, with pictures to show how they look. It is also possible to define the value of the snippet separator inside the abstract section.
Searching with the KDE KIO slave The &RCL; KIO slave allows performing a &RCL; search by entering an appropriate URL in a KDE open dialog, or a Dolphin URL. The results are displayed as directory entries. The instructions for building this module are located in the source tree. See: kde/kio/recoll/00README.txt. Some Linux distributions do package the kio-recoll module, so check before diving into the build process, maybe it's already out there ready for one-click installation. Searching on the command line There are several ways to obtain search results as a text stream, without a graphical interface: By passing option to the recoll program, or by calling it as recollq (through a link). By using the actual recollq program. By writing a custom Python program, using the Recoll Python API. The first two methods work in the same way and accept/need the same arguments (except for the additional to recoll). The query to be executed is specified as command line arguments. recollq is not always built by default. You can use the Makefile in the query directory to build it. This is a very simple program, and if you can program a little c++, you may find it useful to taylor its output format to your needs. Apart from being easily customised, recollq is only really useful on systems where the Qt libraries are not available, else it is redundant with recoll -t. recollq has a man page. The Usage string follows: Runs a recoll query and displays result lines. Default: will interpret the argument(s) as a xesam query string. Query elements: * Implicit AND, exclusion, field spec: t1 -t2 title:t3 * OR has priority: t1 OR t2 t3 OR t4 means (t1 OR t2) AND (t3 OR t4) * Phrase: "t1 t2" (needs additional quoting on cmd line) -o Emulate the GUI simple search in ANY TERM mode. -a Emulate the GUI simple search in ALL TERMS mode. -f Emulate the GUI simple search in filename mode. -q is just ignored (compatibility with the recoll GUI command line). Common options: -c : specify config directory, overriding $RECOLL_CONFDIR. -C : collapse duplicates -d also dump file contents. -n [first-] define the result slice. The default value for [first] is 0. Without the option, the default max count is 2000. Use n=0 for no limit. -b : basic. Just output urls, no mime types or titles. -Q : no result lines, just the processed query and result count. -m : dump the whole document meta[] array for each result. -A : output the document abstracts. -p : show snippets, with page numbers instead of the concatenated abstract. -g : show snippets, with line numbers instead of the concatenated abstract. -S fld : sort by field . -D : sort descending. -s stemlang : set stemming language to use (must exist in index...). Use -s "" to turn off stem expansion. -T : use the parameter (Thesaurus) for word expansion. -i : additional index, several can be given. -e use url encoding (%xx) for urls. -E use exact result count instead of lower bound estimate. -F : output exactly these fields for each result. The field values are encoded in base64, output in one line and separated by one space character. This is the recommended format for use by other programs. Use a normal query with option -m to see the field names. Use -F '' to output all fields, but you probably also want option -N in this case. -N : with -F, print the (plain text) field names before the field values. --extract_to : extract the first result to filepath, which must not exist. Use a -n option with an offset to select the appropriate result. ]]> Sample execution: recollq 'ilur -nautique mime:text/html' Recoll query: ((((ilur:(wqf=11) OR ilurs) AND_NOT (nautique:(wqf=11) OR nautiques OR nautiqu OR nautiquement)) FILTER Ttext/html)) 4 results text/html [file:///Users/dockes/projets/bateaux/ilur/comptes.html] [comptes.html] 18593 bytes text/html [file:///Users/dockes/projets/nautique/webnautique/articles/ilur1/index.html] [Constructio... text/html [file:///Users/dockes/projets/pagepers/index.html] [psxtcl/writemime/recoll]... text/html [file:///Users/dockes/projets/bateaux/ilur/factEtCie/recu-chasse-maree.... The query language The &RCL; query language was based on the now defunct Xesam user search language specification. It allows defining general boolean searches within the main body text or specific fields, and has many additional features, broadly equivalent to those provided by complex search interface in the GUI. The query language processor is activated in the GUI simple search entry when the search mode selector is set to Query Language. It can also be used from the command line search, the KIO slave, or the WEB UI. If the results of a query language search puzzle you and you doubt what has been actually searched for, you can use the GUI Show Query link at the top of the result list to check the exact query which was finally executed by Xapian. General syntax Here follows a sample request that we are going to explain: author:"john doe" Beatles OR Lennon Live OR Unplugged -potatoes This would search for all documents with John Doe appearing as a phrase in the author field (exactly what this is would depend on the document type, e.g.: the From: header, for an email message), and containing either beatles or lennon and either live or unplugged but not potatoes (in any part of the document). An element is composed of an optional field specification, and a value, separated by a colon (the field separator is the last colon in the element). Examples: Eugenie author:balzac dc:title:grandet dc:title:"eugenie grandet" The colon, if present, means "contains". Xesam defines other relations, which are mostly unsupported for now (except in special cases, described further down). All elements in the search entry are normally combined with an implicit AND. It is possible to specify that elements be OR'ed instead, as in Beatles OR Lennon. The OR must be entered literally (capitals), and it has priority over the AND associations: word1 word2 OR word3 means word1 AND (word2 OR word3) not (word1 AND word2) OR word3. You can use parentheses to group elements (from version 1.21), which will sometimes make things clearer, and may allow expressing combinations which would have been difficult otherwise. An element preceded by a - specifies a term that should not appear. By default, words inside double-quotes define a phrase search (the order of words is significant), so that title:"prejudice pride" is not the same as title:prejudice title:pride, and is unlikely to find a result. This can be changed by using modifiers. Words inside phrases and capitalized words are not stem-expanded. Wildcards may be used anywhere inside a term. Specifying a wildcard on the left of a term can produce a very slow search (or even an incorrect one if the expansion is truncated because of excessive size). Also see More about wildcards. To save you some typing, &RCL; versions 1.20 and later interpret a field value given as a comma-separated list of terms as an AND list and a slash-separated list as an OR list. No white space is allowed. So author:john,lennon will search for documents with john and lennon inside the author field (in any order), and author:john/ringo would search for john or ringo. This behaviour is only triggered by a field prefix: without it, comma- or slash- separated input will produce a phrase search. However, you can use a text field name to search the main text this way, as an alternate to using an explicit OR, e.g. text:napoleon/bonaparte would generate a search for napoleon or bonaparte in the main text body. Modifiers can be set on a double-quote value, for example to specify a proximity search (unordered). See the modifier section. No space must separate the final double-quote and the modifiers value, e.g. "two one"po10 &RCL; currently manages the following default fields: title, subject or caption are synonyms which specify data to be searched for in the document title or subject. author or from for searching the documents originators. recipient or to for searching the documents recipients. keyword for searching the document-specified keywords (few documents actually have any). filename for the document's file name. You can use the shorter fn alias. This value is not set for all documents: internal documents contained inside a compound one (for example an EPUB section) do not inherit the container file name any more, this was replaced by an explicit field (see next). Sub-documents can still have a filename, if it is implied by the document format, for example the attachment file name for an email attachment. containerfilename, aliased as cfn. This is set for all documents, both top-level and contained sub-documents, and is always the name of the filesystem file which contains the data. The terms from this field can only be matched by an explicit field specification (as opposed to terms from filename which are also indexed as general document content). This avoids getting matches for all the sub-documents when searching for the container file name. ext specifies the file name extension (Ex: ext:html). rclmd5 the MD5 checksum for the document. This is used for displaying the duplicates of a search result (when querying with the option to collapse duplicate results). Incidentally, this could be used to find the duplicates of any given file by computing its MD5 checksum and executing a query with just the rclmd5 value. You can define aliases for field names, in order to use your preferred denomination or to save typing (e.g. the predefined fn and cfn aliases defined for filename and containerfilename). See the section about the fields file. The document input handlers have the possibility to create other fields with arbitrary names, and aliases may be defined in the configuration, so that the exact field search possibilities may be different for you if someone took care of the customisation. Special field-like specifiers The field syntax also supports a few field-like, but special, criteria, for which the values are interpreted differently. Regular processing does not apply (for example the slash- or comma- separated lists don't work). A list follows. dir for filtering the results on file location. For example, dir:/home/me/somedir will restrict the search to results found anywhere under the /home/me/somedir directory (including subdirectories). Tilde expansion will be performed as usual. Wildcards will be expanded, but please have a look at an important limitation of wildcards in path filters. You can also use relative paths. For example, dir:share/doc would match either /usr/share/doc or /usr/local/share/doc. -dir will find results not in the specified location. Several dir clauses can be specified, both positive and negative. For example the following makes sense: dir:recoll dir:src -dir:utils -dir:common This would select results which have both recoll and src in the path (in any order), and which have not either utils or common. You can also use OR conjunctions with dir: clauses. On &LIN;, a special aspect of dir clauses is that the values in the index are not transcoded to UTF-8, and never lower-cased or unaccented, but stored as binary. This means that you need to enter the values in the exact lower or upper case, and that searches for names with diacritics may sometimes be impossible because of character set conversion issues. Non-ASCII UNIX file paths are an unending source of trouble and are best avoided. You need to use double-quotes around the path value if it contains space characters. The shortcut syntax to define OR or AND lists within fields with commas or slash characters is not available. size for filtering the results on file size. Example: size<10000. You can use <, > or = as operators. You can specify a range like the following: size>100 size<1000. The usual k/K, m/M, g/G, t/T can be used as (decimal) multipliers. Ex: size>1k to search for files bigger than 1000 bytes. date for searching or filtering on dates. The syntax for the argument is based on the ISO8601 standard for dates and time intervals. Only dates are supported, no times. The general syntax is 2 elements separated by a / character. Each element can be a date or a period of time. Periods are specified as PnYnMnD. The n numbers are the respective numbers of years, months or days, any of which may be missing. Dates are specified as YYYY-MM-DD. The days and months parts may be missing. If the / is present but an element is missing, the missing element is interpreted as the lowest or highest date in the index. Examples: 2001-03-01/2002-05-01 the basic syntax for an interval of dates. 2001-03-01/P1Y2M the same specified with a period. 2001/ from the beginning of 2001 to the latest date in the index. 2001 the whole year of 2001 P2D/ means 2 days ago up to now if there are no documents with dates in the future. /2003 all documents from 2003 or older. Periods can also be specified with small letters (e.g.: p2y). mime or format for specifying the MIME type. These clauses are processed apart from the normal Boolean logic of the search: multiple values will be OR'ed (instead of the normal AND). You can specify types to be excluded, with the usual -, and use wildcards. Example: mime:text/* -mime:text/plain. Specifying an explicit boolean operator before a mime specification is not supported and will produce strange results. type or rclcat for specifying the category (as in text/media/presentation/etc.). The classification of MIME types in categories is defined in the &RCL; configuration (mimeconf), and can be modified or extended. The default category names are those which permit filtering results in the main GUI screen. Categories are OR'ed like MIME types above, and can be negated with -. issub for specifying that only standalone (issub:0) or only embedded (issub:1) documents should be returned as results. mime, rclcat, size, issub and date criteria always affect the whole query (they are applied as a final filter), even if set with other terms inside a parenthese. mime (or the equivalent rclcat) is the only field with an OR default. You do need to use OR with ext terms for example. Range clauses &RCL; 1.24 and later support range clauses on fields which have been configured to support it. No default field uses them currently, so this paragraph is only interesting if you modified the fields configuration and possibly use a custom input handler. A range clause looks like one of the following: myfield:small..big myfield:small.. myfield:..big The nature of the clause is indicated by the two dots .., and the effect is to filter the results for which the myfield value is in the possibly open-ended interval. See the section about the fields configuration file for the details of configuring a field for range searches (list them in the [values] section). Modifiers Some characters are recognized as search modifiers when found immediately after the closing double quote of a phrase, as in "some term"modifierchars. The actual "phrase" can be a single term of course. Supported modifiers: l can be used to turn off stemming (mostly makes sense with p because stemming is off by default for phrases, but see also x further down). o can be used to specify a "slack" for both phrase and proximity searches: the number of additional terms that may be found between the specified ones. If o is followed by an integer number, this is the slack, else the default is 10. The default slack (with no o) is 0 for phrase searches and 10 for proximity searches. p can be used to turn an ordered phrase search into an unordered proximity one. Example: "order any in"p. You can find a little more detail about phrase and proximity searches here. s (1.22) can be used to turn off synonym expansion, if a synonyms file is in place. x (1.33.2) will enable the expansion of terms inside a phrase search (the default is for phrases to be searched verbatim). Also see the stemexpandphrases in the configuration section, for changing the default behaviour. A weight can be specified for a query element by specifying a decimal value at the start of the modifiers. Example: "Important"2.5. The following only make sense on indexes which are capable of case and diacritics sensitivity (not the default): C will turn on case sensitivity. D will turn on diacritics sensitivity (if the index supports it). e (explicit) will turn on diacritics sensitivity and case sensitivity, and prevent stem expansion. Wildcards and anchored searches Some special characters are interpreted by &RCL; in search strings to expand or specialize the search. Wildcards expand a root term in controlled ways. Anchor characters can restrict a search to succeed only if the match is found at or near the beginning of the document or one of its fields. Wildcards All words entered in &RCL; search fields will be processed for wildcard expansion before the request is finally executed. The wildcard characters are: * which matches 0 or more characters. ? which matches a single character. [] which allow defining sets of characters to be matched (ex: [abc] matches a single character which may be 'a' or 'b' or 'c', [0-9] matches any number. You should be aware of a few things when using wildcards. Using a wildcard character at the beginning of a word can make for a slow search because &RCL; will have to scan the whole index term list to find the matches. However, this is much less a problem for field searches, and queries like author:*@domain.com can sometimes be very useful. For &RCL; version 18 only, when working with a raw index (preserving character case and diacritics), the literal part of a wildcard expression will be matched exactly for case and diacritics. This is not true any more for versions 19 and later. Using a * at the end of a word can produce more matches than you would think, and strange search results. You can use the term explorer tool to check what completions exist for a given term. You can also see exactly what search was performed by clicking on the link at the top of the result list. In general, for natural language terms, stem expansion will produce better results than an ending * (stem expansion is turned off when any wildcard character appears in the term). Wildcards and path filtering Due to the way that &RCL; processes wildcards inside dir path filtering clauses, they will have a multiplicative effect on the query size. A clause containing wildcards in several paths elements, like, for example, dir:/home/me/*/*/docdir, will almost certainly fail if your indexed tree is of any realistic size. Depending on the case, you may be able to work around the issue by specifying the paths elements more narrowly, with a constant prefix, or by using 2 separate dir: clauses instead of multiple wildcards, as in dir:/home/me dir:docdir. The latter query is not equivalent to the initial one because it does not specify a number of directory levels, but that's the best we can do (and it may be actually more useful in some cases). Anchored searches Two characters are used to specify that a search hit should occur at the beginning or at the end of the text. ^ at the beginning of a term or phrase constrains the search to happen at the start, $ at the end force it to happen at the end. As this function is implemented as a phrase search it is possible to specify a maximum distance at which the hit should occur, either through the controls of the advanced search panel, or using the query language, for example, as in: "^someterm"o10 which would force someterm to be found within 10 terms of the start of the text. This can be combined with a field search as in somefield:"^someterm"o10 or somefield:someterm$. This feature can also be used with an actual phrase search, but in this case, the distance applies to the whole phrase and anchor, so that, for example, bla bla my unexpected term at the beginning of the text would be a match for "^my term"o5. Anchored searches can be very useful for searches inside somewhat structured documents like scientific articles, in case explicit metadata has not been supplied, for example for looking for matches inside the abstract or the list of authors (which occur at the top of the document). Using Synonyms (1.22) Term synonyms and text search: in general, there are two main ways to use term synonyms for searching text: At index creation time, they can be used to alter the indexed terms, either increasing or decreasing their number, by expanding the original terms to all synonyms, or by reducing all synonym terms to a canonical one. At query time, they can be used to match texts containing terms which are synonyms of the ones specified by the user, either by expanding the query for all synonyms, or by reducing the user entry to canonical terms (the latter only works if the corresponding processing has been performed while creating the index). &RCL; only uses synonyms at query time. A user query term which part of a synonym group will be optionally expanded into an OR query for all terms in the group. Synonym groups are defined inside ordinary text files. Each line in the file defines a group. Example: hi hello "good morning" # not sure about "au revoir" though. Is this english ? bye goodbye "see you" \ "au revoir" As usual, lines beginning with a # are comments, empty lines are ignored, and lines can be continued by ending them with a backslash. Multi-word synonyms are supported, but be aware that these will generate phrase queries, which may degrade performance and will disable stemming expansion for the phrase terms. The contents of the synonyms file must be casefolded (not only lowercased), because this is what expected at the point in the query processing where it is used. There are a few cases where this makes a difference, for example, German sharp s should be expressed as ss, Greek final sigma as sigma. For reference, Python3 has an easy way to casefold words (str.casefold()). The synonyms file can be specified in the Search parameters tab of the GUI configuration Preferences menu entry, or as an option for command-line searches. Once the file is defined, the use of synonyms can be enabled or disabled directly from the Preferences menu. The synonyms are searched for matches with user terms after the latter are stem-expanded, but the contents of the synonyms file itself is not subjected to stem expansion. This means that a match will not be found if the form present in the synonyms file is not present anywhere in the document set (same with accents when using a raw index). The synonyms function is probably not going to help you find your letters to Mr. Smith. It is best used for domain-specific searches. For example, it was initially suggested by a user performing searches among historical documents: the synonyms file would contains nicknames and aliases for each of the persons of interest. Path translations In some cases, the document paths stored inside the index do not match the actual ones, so that document previews and accesses will fail. This can occur in a number of circumstances: When using multiple indexes it is a relatively common occurrence that some will actually reside on a remote volume, for example mounted via NFS. In this case, the paths used to access the documents on the local machine are not necessarily the same than the ones used while indexing on the remote machine. For example, /home/me may have been used as a topdirs elements while indexing, but the directory might be mounted as /net/server/home/me on the local machine. The case may also occur with removable disks. It is perfectly possible to configure an index to live with the documents on the removable disk, but it may happen that the disk is not mounted at the same place so that the documents paths from the index are invalid. In some case, the path adjustments can be automated. As a last example, one could imagine that a big directory has been moved, but that it is currently inconvenient to run the indexer. &RCL; has a facility for rewriting access paths when extracting the data from the index. The translations can be defined for the main index and for any additional query index. In the above NFS example, &RCL; could be instructed to rewrite any file:///home/me URL from the index to file:///net/server/home/me, allowing accesses from the client. The translations are defined in the ptrans configuration file, which can be edited by hand or from the GUI external indexes configuration dialog: PreferencesExternal index dialog, then click the Paths translations button on the right below the index list: translations will be set for the main index if no external index is currently selected in the list, or else for the currently selected index. Example entry from a <filename>ptrans</filename> file: [/path/to/external/xapiandb] /some/index/path = /some/local/path This would decide that, for the index stored in /path/to/external/xapiandb, any occurence of /some/index/path should be replaced with /some/local/path when presenting a result. Windows noteAt the moment, the path comparisons done for path translation under MS Windows are case sensitive (this will be fixed at some point). Use the natural character case as displayed in the file explorer. Example: [Z:/some/mounted/xapiandb] C: = Z: Search case and diacritics sensitivity For &RCL; versions 1.18 and later, and when working with a raw index (not the default), searches can be sensitive to character case and diacritics. How this happens is controlled by configuration variables and what search data is entered. The general default is that searches entered without upper-case or accented characters are insensitive to case and diacritics. An entry of resume will match any of Resume, RESUME, résumé, Résumé etc. Two configuration variables can automate switching on sensitivity (they were documented but actually did nothing until &RCL; 1.22): autodiacsensIf this is set, search sensitivity to diacritics will be turned on as soon as an accented character exists in a search term. When the variable is set to true, resume will start a diacritics-unsensitive search, but résumé will be matched exactly. The default value is false. autocasesensIf this is set, search sensitivity to character case will be turned on as soon as an upper-case character exists in a search term except for the first one. When the variable is set to true, us or Us will start a diacritics-unsensitive search, but US will be matched exactly. The default value is true (contrary to autodiacsens). As in the past, capitalizing the first letter of a word will turn off its stem expansion and have no effect on case-sensitivity. You can also explicitly activate case and diacritics sensitivity by using modifiers with the query language. C will make the term case-sensitive, and D will make it diacritics-sensitive. Examples: "us"C will search for the term us exactly (Us will not be a match). "resume"D will search for the term resume exactly (résumé will not be a match). When either case or diacritics sensitivity is activated, stem expansion is turned off. Having both does not make much sense. Desktop integration Being independent of the desktop type has its drawbacks: &RCL; desktop integration is minimal. However there are a few tools available: Users of recent Ubuntu-derived distributions, or any other Gnome desktop systems (e.g. Fedora) can install the Recoll GSSP (Gnome Shell Search Provider). The KDE KIO Slave was described in a previous section. It can provide search results inside Dolphin. If you use an oldish version of Ubuntu Linux, you may find the Ubuntu Unity Lens module useful. There is also an independently developed Krunner plugin. Here follow a few other things that may help. Hotkeying recoll It is surprisingly convenient to be able to show or hide the &RCL; GUI with a single keystroke. Recoll comes with a small Python script, based on the libwnck window manager interface library, which will allow you to do just this. The detailed instructions are on this wiki page. The KDE Kicker Recoll applet This is probably obsolete now. Anyway: The &RCL; source tree contains the source code to the recoll_applet, a small application derived from the find_applet. This can be used to add a small &RCL; launcher to the KDE panel. The applet is not automatically built with the main &RCL; programs, nor is it included with the main source distribution (because the KDE build boilerplate makes it relatively big). You can download its source from the recoll.org download page. Use the omnipotent configure;make;make install incantation to build and install. You can then add the applet to the panel by right-clicking the panel and choosing the Add applet entry. The recoll_applet has a small text window where you can type a &RCL; query (in query language form), and an icon which can be used to restrict the search to certain types of files. It is quite primitive, and launches a new recoll GUI instance every time (even if it is already running). You may find it useful anyway.
Programming interface &RCL; has an Application Programming Interface, usable both for indexing and searching, currently accessible from the Python language. Another less radical way to extend the application is to write input handlers for new types of documents. The processing of metadata attributes for documents (fields) is highly configurable. Writing a document input handler TerminologyThe small programs or pieces of code which handle the processing of the different document types for &RCL; used to be called filters, which is still reflected in the name of the directory which holds them and many configuration variables. They were named this way because one of their primary functions is to filter out the formatting directives and keep the text content. However these modules may have other behaviours, and the term input handler is now progressively substituted in the documentation. filter is still used in many places though. &RCL; input handlers cooperate to translate from the multitude of input document formats, simple ones as opendocument, acrobat, or compound ones such as Zip or Email, into the final &RCL; indexing input format, which is plain text (in many cases the processing pipeline has an intermediary HTML step, which may be used for better previewing presentation). Most input handlers are executable programs or scripts. A few handlers are coded in C++ and live inside recollindex. This latter kind will not be described here. There are two kinds of external executable input handlers: Simple exec handlers run once and exit. They can be bare programs like antiword, or scripts using other programs. They are very simple to write, because they just need to print the converted document to the standard output. Their output can be plain text or HTML. HTML is usually preferred because it can store metadata fields and it allows preserving some of the formatting for the GUI preview. However, these handlers have limitations: They can only process one document per file. The output MIME type must be known and fixed. The character encoding, if relevant, must be known and fixed (or possibly just depending on location). Multiple execm handlers can process multiple files (sparing the process startup time which can be very significant), or multiple documents per file (e.g.: for archives or multi-chapter publications). They communicate with the indexer through a simple protocol, but are nevertheless a bit more complicated than the older kind. Most of the new handlers are written in Python (exception: rclimg which is written in Perl because exiftool has no real Python equivalent). The Python handlers use common modules to factor out the boilerplate, which can make them very simple in favorable cases. The subdocuments output by these handlers can be directly indexable (text or HTML), or they can be other simple or compound documents that will need to be processed by another handler. In both cases, handlers deal with regular file system files, and can process either a single document, or a linear list of documents in each file. &RCL; is responsible for performing up to date checks, deal with more complex embedding and other upper level issues. A simple handler returning a document in text/plain format, can transfer no metadata to the indexer. Generic metadata, like document size or modification date, will be gathered and stored by the indexer. Handlers that produce text/html format can return an arbitrary amount of metadata inside HTML meta tags. These will be processed according to the directives found in the fields configuration file. The handlers that can handle multiple documents per file return a single piece of data to identify each document inside the file. This piece of data, called an ipath will be sent back by &RCL; to extract the document at query time, for previewing, or for creating a temporary file to be opened by a viewer. These handlers can also return metadata either as HTML meta tags, or as named data through the communication protocol. The following section describes the simple handlers, and the next one gives a few explanations about the execm ones. You could conceivably write a simple handler with only the elements in the manual. This will not be the case for the other ones, for which you will have to look at the code. Simple input handlers &RCL; simple handlers are usually shell-scripts, but this is in no way necessary. Extracting the text from the native format is the difficult part. Outputting the format expected by &RCL; is trivial. Happily enough, most document formats have translators or text extractors which can be called from the handler. In some cases the output of the translating program is completely appropriate, and no intermediate shell-script is needed. Input handlers are called with a single argument which is the source file name. They should output the result to stdout. When writing a handler, you should decide if it will output plain text or HTML. Plain text is simpler, but you will not be able to add metadata or vary the output character encoding (this will be defined in a configuration file). Additionally, some formatting may be easier to preserve when previewing HTML. Actually the deciding factor is metadata: &RCL; has a way to extract metadata from the HTML header and use it for field searches.. The RECOLL_FILTER_FORPREVIEW environment variable (values yes, no) tells the handler if the operation is for indexing or previewing. Some handlers use this to output a slightly different format, for example stripping uninteresting repeated keywords (e.g.: Subject: for email) when indexing. This is not essential. You should look at one of the simple handlers, for example rclps for a starting point. Don't forget to make your handler executable before testing ! "Multiple" handlers If you can program and want to write an execm handler, it should not be too difficult to make sense of one of the existing handlers. The existing handlers differ in the amount of helper code which they are using: rclimg is written in Perl and handles the execm protocol all by itself (showing how trivial it is). All the Python handlers share at least the rclexecm.py module, which handles the communication. Have a look at, for example, rclzip.py for a handler which uses rclexecm.py directly. Most Python handlers which process single-document files by executing another command are further abstracted by using the rclexec1.py module. See for example rclrtf.py for a simple one, or rcldoc.py for a slightly more complicated one (possibly executing several commands). Handlers which extract text from an XML document by using an XSLT style sheet are now executed inside recollindex, with only the style sheet stored in the filters/ directory. These can use a single style sheet (e.g. abiword.xsl), or two sheets for the data and metadata (e.g. opendoc-body.xsl and opendoc-meta.xsl). The mimeconf configuration file defines how the sheets are used, have a look. Before the C++ import, the xsl-based handlers used a common module rclgenxslt.py, it is still around but unused at the moment. The handler for OpenXML presentations is still the Python version because the format did not fit with what the C++ code does. It would be a good base for another similar issue. There is a sample trivial handler based on rclexecm.py, with many comments, not actually used by &RCL;. It would index a text file as one document per line. Look for rcltxtlines.py in the src/filters directory in the online &RCL; Git repository (the sample not in the distributed release at the moment). You can also have a look at the slightly more complex rclzip.py which uses Zip file paths as identifiers (ipath). execm handlers sometimes need to make a choice for the nature of the ipath elements that they use in communication with the indexer. Here are a few guidelines: Use ASCII or UTF-8 (if the identifier is an integer print it, for example, like printf %d would do). If at all possible, the data should make some kind of sense when printed to a log file to help with debugging. &RCL; uses a colon (:) as a separator to store a complex path internally (for deeper embedding). Colons inside the ipath elements output by a handler will be escaped, but would be a bad choice as a handler-specific separator (mostly, again, for debugging issues). In any case, the main goal is that it should be easy for the handler to extract the target document, given the file name and the ipath element. execm handlers will also produce a document with a null ipath element. Depending on the type of document, this may have some associated data (e.g. the body of an email message), or none (typical for an archive file). If it is empty, this document will be useful anyway for some operations, as the parent of the actual data documents. Telling &RCL; about the handler There are two elements that link a file to the handler which should process it: the association of file to MIME type and the association of a MIME type with a handler. The association of files to MIME types is mostly based on name suffixes. The types are defined inside the mimemap file. Example: .doc = application/msword If no suffix association is found for the file name, &RCL; will try to execute a system command (typically file -i or xdg-mime) to determine a MIME type. The second element is the association of MIME types to handlers in the mimeconf file. A sample will probably be better than a long explanation: [index] application/msword = exec antiword -t -i 1 -m UTF-8;\ mimetype = text/plain ; charset=utf-8 application/ogg = exec rclogg text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html application/x-chm = execm rclchm.py The fragment specifies that: application/msword files are processed by executing the antiword program, which outputs text/plain encoded in utf-8. application/ogg files are processed by the rclogg script, with default output type (text/html, with encoding specified in the header, or utf-8 by default). text/rtf is processed by unrtf, which outputs text/html. The iso-8859-1 encoding is specified because it is not the utf-8 default, and not output by unrtf in the HTML header section. application/x-chm is processed by a persistent handler. This is determined by the execm keyword. Input handler output Both the simple and persistent input handlers can return any MIME type to Recoll, which will further process the data according to the MIME configuration. Most input filters filters produce either text/plain or text/html data. There are exceptions, for example, filters which process archive file (zip, tar, etc.) will usually return the documents as they are found, without processing them further. There is nothing to say about text/plain output, except that its character encoding should be consistent with what is specified in the mimeconf file. For filters producing HTML, the output could be very minimal like the following example: Some text content ]]> You should take care to escape some characters inside the text by transforming them into appropriate entities. At the very minimum, "&" should be transformed into "&amp;", "<" should be transformed into "&lt;". This is not always properly done by external helper programs which output HTML, and of course never by those which output plain text. When encapsulating plain text in an HTML body, the display of a preview may be improved by enclosing the text inside <pre> tags. The character set needs to be specified in the header. It does not need to be UTF-8 (&RCL; will take care of translating it), but it must be accurate for good results. &RCL; will process meta tags inside the header as possible document fields candidates. Documents fields can be processed by the indexer in different ways, for searching or displaying inside query results. This is described in a following section. By default, the indexer will process the standard header fields if they are present: title, meta/description, and meta/keywords are both indexed and stored for query-time display. A predefined non-standard meta tag will also be processed by &RCL; without further configuration: if a date tag is present and has the right format, it will be used as the document date (for display and sorting), in preference to the file modification date. The date format should be as follows: <meta name="date" content="YYYY-mm-dd HH:MM:SS"> or <meta name="date" content="YYYY-mm-ddTHH:MM:SS"> Example: <meta name="date" content="2013-02-24 17:50:00"> Input handlers also have the possibility to "invent" field names. This should also be output as meta tags: <meta name="somefield" content="Some textual data" /> You can embed HTML markup inside the content of custom fields, for improving the display inside result lists. In this case, add a (wildly non-standard) markup attribute to tell &RCL; that the value is HTML and should not be escaped for display. <meta name="somefield" markup="html" content="Some <i>textual</i> data" /> As written above, the processing of fields is described in a further section. Persistent filters can use another, probably simpler, method to produce metadata, by calling the setfield() helper method. This avoids the necessity to produce HTML, and any issue with HTML quoting. See, for example, rclaudio.py in &RCL; 1.23 and later for an example of handler which outputs text/plain and uses setfield() to produce metadata. Page numbers The indexer will interpret ^L characters in the handler output as indicating page breaks, and will record them. At query time, this allows starting a viewer on the right page for a hit or a snippet. Currently, only the PDF, Postscript and DVI handlers generate page breaks. Field data processing Fields are named pieces of information in or about documents, like title, author, abstract. The field values for documents can appear in several ways during indexing: either output by input handlers as meta fields in the HTML header section, or extracted from file extended attributes, or added as attributes of the Doc object when using the API, or again synthetized internally by &RCL;. The &RCL; query language allows searching for text in a specific field. &RCL; defines a number of default fields. Additional ones can be output by handlers, and described in the fields configuration file. Fields can be: indexed, meaning that their terms are separately stored in inverted lists (with a specific prefix), and that a field-specific search is possible. stored, meaning that their value is recorded in the index data record for the document, and can be returned and displayed with search results. A field can be either or both indexed and stored. This and other aspects of fields handling is defined inside the fields configuration file. Some fields may also designated as supporting range queries, meaning that the results may be selected for an interval of its values. See the configuration section for more details. The sequence of events for field processing is as follows: During indexing, recollindex scans all meta fields in HTML documents (most document types are transformed into HTML at some point). It compares the name for each element to the configuration defining what should be done with fields (the fields file) If the name for the meta element matches one for a field that should be indexed, the contents are processed and the terms are entered into the index with the prefix defined in the fields file. If the name for the meta element matches one for a field that should be stored, the content of the element is stored with the document data record, from which it can be extracted and displayed at query time. At query time, if a field search is performed, the index prefix is computed and the match is only performed against appropriately prefixed terms in the index. At query time, the field can be displayed inside the result list by using the appropriate directive in the definition of the result list paragraph format. All fields are displayed on the fields screen of the preview window (which you can reach through the right-click menu). This is independent of the fact that the search which produced the results used the field or not. You can find more information in the section about the fields file, or in comments inside the file. You can also have a look at the example in the FAQs area, detailing how one could add a page count field to pdf documents for displaying inside result lists. Python API Introduction The &RCL; Python programming interface can be used both for searching and for creating/updating an index. Bindings exist for Python2 and Python3 (Jan 2021: python2 support will be dropped soon). The search interface is used in a number of active projects: the &RCL; Gnome Shell Search Provider , the &RCL; Web UI, and the upmpdcli UPnP Media Server, in addition to many small scripts. The index update section of the API may be used to create and update &RCL; indexes on specific configurations (separate from the ones created by recollindex). The resulting databases can be queried alone, or in conjunction with regular ones, through the GUI or any of the query interfaces. The search API is modeled along the Python database API version 2.0 specification (early versions used the version 1.0 spec). The recoll package contains two modules: The recoll module contains functions and classes used to query (or update) the index. The rclextract module contains functions and classes used at query time to access document data. The recoll module must be imported before rclextract There is a good chance that your system repository has packages for the Recoll Python API, sometimes in a package separate from the main one (maybe named something like python-recoll). Else refer to the Building from source chapter. As an introduction, the following small sample will run a query and list the title and url for each of the results. The python/samples source directory contains several examples of Python programming with &RCL;, exercising the extension more completely, and especially its data extraction features. You can also take a look at the source for the Recoll WebUI, the upmpdcli local media server, or the Gnome Shell Search Provider. Interface elements A few elements in the interface are specific and and need an explanation. ipath This data value (set as a field in the Doc object) is stored, along with the URL, but not indexed by &RCL;. Its contents are not interpreted by the index layer, and its use is up to the application. For example, the &RCL; file system indexer uses the ipath to store the part of the document access path internal to (possibly imbricated) container documents. ipath in this case is a vector of access elements (e.g, the first part could be a path inside a zip file to an archive member which happens to be an mbox file, the second element would be the message sequential number inside the mbox etc.). url and ipath are returned in every search result and define the access to the original document. ipath is empty for top-level document/files (e.g. a PDF document which is a filesystem file). The &RCL; GUI knows about the structure of the ipath values used by the filesystem indexer, and uses it for such functions as opening the parent of a given document. udi An udi (unique document identifier) identifies a document. Because of limitations inside the index engine, it is restricted in length (to 200 bytes), which is why a regular URI cannot be used. The structure and contents of the udi is defined by the application and opaque to the index engine. For example, the internal file system indexer uses the complete document path (file path + internal path), truncated to length, the suppressed part being replaced by a hash value. The udi is not explicit in the query interface (it is used "under the hood" by the rclextract module), but it is an explicit element of the update interface. parent_udi If this attribute is set on a document when entering it in the index, it designates its physical container document. In a multilevel hierarchy, this may not be the immediate parent. If the indexer uses the purge() method, then the use of parent_udi is mandatory for subdocuments. Else it is optional, but its use by an indexer may simplify index maintenance, as &RCL; will automatically delete all children defined by parent_udi == udi when the document designated by udi is destroyed. e.g. if a Zip archive contains entries which are themselves containers, like mbox files, all the subdocuments inside the Zip file (mbox, messages, message attachments, etc.) would have the same parent_udi, matching the udi for the Zip file, and all would be destroyed when the Zip file (identified by its udi) is removed from the index. Stored and indexed fields The fields file inside the &RCL; configuration defines which document fields are either indexed (searchable), stored (retrievable with search results), or both. Apart from a few standard/internal fields, only the stored fields are retrievable through the Python search interface. Log messages for Python scripts Two specific configuration variables: pyloglevel and pylogfilename allow overriding the generic values for Python programs. Set pyloglevel to 2 to suppress default startup messages (printed at level 3). Python search interface The recoll module connect(confdir=None, extra_dbs=None, writable = False) The connect() function connects to one or several &RCL; index(es) and returns a Db object. This call initializes the recoll module, and it should always be performed before any other call or object creation. confdir may specify a configuration directory. The usual defaults apply. extra_dbs is a list of additional indexes (Xapian directories). writable decides if we can index new data through this connection. Examples: from recoll import recoll # Opening the default db db = recoll.connect() # Opening the default db and a pair of additional indexes db = recoll.connect(extra_dbs=["/home/me/.someconfdir/xapiandb", "/data/otherconf/xapiandb"]) The Db class A Db object is created by a connect() call and holds a connection to a Recoll index. Db.query(), Db.cursor()These (synonym) methods return a blank Query object for this index. Db.termMatch(match_type, expr, field='', maxlen=-1, casesens=False, diacsens=False, lang='english') Expand an expression against the index term list. Performs the basic function from the GUI term explorer tool. match_type can be one of wildcard, regexp or stem. Returns a list of terms expanded from the input expression. Db.setAbstractParams(maxchars, contextwords)Set the parameters used to build snippets (sets of keywords in context text fragments). maxchars defines the maximum total size of the abstract. contextwords defines how many terms are shown around the keyword. Db.close()Closes the connection. You can't do anything with the Db object after this. The Query class A Query object (equivalent to a cursor in the Python DB API) is created by a Db.query() call. It is used to execute index searches. Query.sortby(fieldname, ascending=True) Sets the sorting order for future searches for using fieldname, in ascending or descending order. Must be called before executing the search. Query.execute(query_string, stemming=1, stemlang="english", fetchtext=False, collapseduplicates=False) Starts a search for query_string, a &RCL; search language string. If the index stores the document texts and fetchtext is True, store the document extracted text in doc.text. Query.executesd(SearchData, fetchtext=False, collapseduplicates=False) Starts a search for the query defined by the SearchData object. If the index stores the document texts and fetchtext is True, store the document extracted text in doc.text. Query.fetchmany(size=query.arraysize) Fetches the next Doc objects in the current search results, and returns them as an array of the required size, which is by default the value of the arraysize data member. Query.fetchone() Fetches the next Doc object from the current search results. Generates a StopIteration exception if there are no results left. Query.__iter__() and Query.next() So that things like for doc in query: will work. Example: from recoll import recoll db = recoll.connect() q = db.query() nres = q.execute("some query") for doc in q: print("%s" % doc.title) Query.close() Closes the query. The object is unusable after the call. Query.scroll(value, mode='relative') Adjusts the position in the current result set. mode can be relative or absolute. Query.getgroups() Retrieves the expanded query terms as a list of pairs. Meaningful only after executexx In each pair, the first entry is a list of user terms (of size one for simple terms, or more for group and phrase clauses), the second a list of query terms as derived from the user terms and used in the Xapian Query. Query.getxquery() Return the Xapian query description as a Unicode string. Meaningful only after executexx. Query.highlight(text, ishtml = 0, methods = object) Will insert <span "class=rclmatch">, </span> tags around the match areas in the input text and return the modified text. ishtml can be set to indicate that the input text is HTML and that HTML special characters should not be escaped. methods if set should be an object with methods startMatch(i) and endMatch() which will be called for each match and should return a begin and end tag Query.makedocabstract(doc, methods = object)) Create a snippets abstract for doc (a Doc object) by selecting text around the match terms. If methods is set, will also perform highlighting. See the highlight method. Query.getsnippets(doc, maxoccs = -1, ctxwords = -1, sortbypage=False, methods = object) Will return a list of extracts from the result document by selecting text around the match terms. Each entry in the result list is a triple: page number, term, text. By default, the most relevants snippets appear first in the list. Set sortbypage to sort by page number instead. If methods is set, the fragments will be highlighted (see the highlight method). If maxoccs is set, it defines the maximum result list length. ctxwords allows adjusting the individual snippet context size. Query.arraysize Default number of records processed by fetchmany (r/w). Query.rowcountNumber of records returned by the last execute. Query.rownumber Next index to be fetched from results. Normally increments after each fetchone() call, but can be set/reset before the call to effect seeking (equivalent to using scroll()). Starts at 0. The Doc class A Doc object contains index data for a given document. The data is extracted from the index when searching, or set by the indexer program when updating. The Doc object has many attributes to be read or set by its user. It mostly matches the Rcl::Doc C++ object. Some of the attributes are predefined, but, especially when indexing, others can be set, the name of which will be processed as field names by the indexing configuration. Inputs can be specified as Unicode or strings. Outputs are Unicode objects. All dates are specified as Unix timestamps, printed as strings. Please refer to the rcldb/rcldoc.cpp C++ file for a full description of the predefined attributes. Here follows a short list. url the document URL but see also getbinurl() ipath the document ipath for embedded documents. fbytes, dbytes the document file and text sizes. fmtime, dmtime the document file and document times. xdocid the document Xapian document ID. This is useful if you want to access the document through a direct Xapian operation. mtype the document MIME type. Fields stored by default: author, filename, keywords, recipient At query time, only the fields that are defined as stored either by default or in the fields configuration file will be meaningful in the Doc object. The document processed text may be present or not, depending if the index stores the text at all, and if it does, on the fetchtext query execute option. See also the rclextract module for accessing document contents. get(key), [] operator Retrieve the named document attribute. You can also use getattr(doc, key) or doc.key. doc.key = value Set the the named document attribute. You can also use setattr(doc, key, value). getbinurl() Retrieve the URL in byte array format (no transcoding), for use as parameter to a system call. setbinurl(url) Set the URL in byte array format (no transcoding). items() Return a dictionary of doc object keys/values keys() list of doc object keys (attribute names). The SearchData class A SearchData object allows building a query by combining clauses, for execution by Query.executesd(). It can be used in replacement of the query language approach. The interface is going to change a little, so no detailed doc for now... addclause(type='and'|'or'|'excl'|'phrase'|'near'|'sub', qstring=string, slack=0, field='', stemming=1, subSearch=SearchData) The rclextract module Prior to &RCL; 1.25, index queries could not provide document content because it was never stored. &RCL; 1.25 and later usually store the document text, which can be optionally retrieved when running a query (see query.execute() above - the result is always plain text). Independantly, the rclextract module can give access to the original document and to the document text content, possibly as an HTML version. Accessing the original document is particularly useful if it is embedded (e.g. an email attachment). You need to import the recoll module before the rclextract module. The Extractor class Extractor(doc) An Extractor object is built from a Doc object, output from a query. Extractor.textextract(ipath) Extract document defined by ipath and return a Doc object. The doc.text field has the document text converted to either text/plain or text/html according to doc.mimetype. The typical use would be as follows: from recoll import recoll, rclextract qdoc = query.fetchone() extractor = rclextract.Extractor(qdoc) doc = extractor.textextract(qdoc.ipath) # use doc.text, e.g. for previewing Passing qdoc.ipath to textextract() is redundant, but reflects the fact that the Extractor object actually has the capability to access the other entries in a compound document. Extractor.idoctofile(ipath, targetmtype, outfile='') Extracts document into an output file, which can be given explicitly or will be created as a temporary file to be deleted by the caller. Typical use: from recoll import recoll, rclextract qdoc = query.fetchone() extractor = rclextract.Extractor(qdoc) filename = extractor.idoctofile(qdoc.ipath, qdoc.mimetype) In all cases the output is a copy, even if the requested document is a regular system file, which may be wasteful in some cases. If you want to avoid this, you can test for a simple file document as follows: not doc.ipath and (not "rclbes" in doc.keys() or doc["rclbes"] == "FS") Search API usage example The following sample would query the index with a user language string. See the python/samples directory inside the &RCL; source for other examples. The recollgui subdirectory has a very embryonic GUI which demonstrates the highlighting and data extraction functions. 5: nres = 5 for i in range(nres): doc = query.fetchone() print("Result #%d" % (query.rownumber)) for k in ("title", "size"): print("%s : %s" % (k, getattr(doc, k))) print("%s\n" % db.makeDocAbstract(doc, query)) ]]> Python indexing interface Recoll external indexers The &RCL; indexer is capable of processing many different document formats. However, some forms of data storage do not lend themselves easily to standard processing because of their great variability. A canonical example would be data in an SQL database. While it might be possible to create a configurable filter to process data from a database, the many variations in storage organisation and SQL dialects make this difficult. &RCL; can instead support external indexers where all the responsibility to handle the data format is delegated to an external script. The script language has to be Python 3 at the moment, because this is the only language for which an API binding exists. Up to &RCL; 1.35, such an indexer had to work on a separate &RCL; index, which would be added as an external index for querying from the main one, and for which a separate indexing schedule had to be managed. The reason was that the main document indexer purge pass (removal of deleted documents) would also remove all the documents belonging to the external indexer, as they were not seen during the filesystem walk (and conversely, the external indexer purge pass would delete all the regular document entries). As of &RCL; 1.36, an improvement and new API call allows external indexers to be fully integrated, and work on the main index, with updates triggered from the normal recollindex program. An external indexer has to do the same work as the &RCL; file system indexer: look for modified documents, extract their text, call the API for indexing them, and the one for purging the data for deleted documents. A description of the API method follows, but you can also jump ahead for a look at some sample pseudo-code and a pair of actual implementations, one of which does something useful. The Python indexing API There are two parts in the indexing interface: Methods inside the recoll module allow the foreign indexer to update the index. An interface based on scripts execution is defined for executing the indexer (from recollindex) and to allow either the GUI or the rclextract module to access original document data for previewing or editing. Two sample scripts are included with the &RCL; source and described in more detail a bit further. Python indexing interface methods The update methods are part of the recoll module. The connect() method is used with a writable=true parameter to obtain a writable Db object. The following Db object methods are then available. addOrUpdate(udi, doc, parent_udi=None) Add or update index data for a given document. The udi string must define a unique id for the document. It is an opaque interface element and not interpreted inside &RCL;. doc is a Doc object, created from the data to be indexed (the main text should be in doc.text). If parent_udi is set, this is a unique identifier for the top-level container, the document for which needUpdate() would be called (e.g. for the filesystem indexer, this would be the one which is an actual file). The doc url and possibly ipath fields should also be set to allow access to the actual document after a query. Other fields should also be set to allow further access to the data, see the description further down: rclbes, sig, mimetype. Of course, any standard or custom &RCL; field can also be added. delete(udi) Purge the index from all data for udi, and all documents (if any) which have udi as parent_udi. needUpdate(udi, sig) Test if the index needs to be updated for the document identified by udi. If this call is to be used, the doc.sig field should contain a signature value when calling addOrUpdate(). The needUpdate() call then compares its parameter value with the stored sig for udi. sig is an opaque value, compared as a string. The filesystem indexer uses a concatenation of the decimal string values for file size and update time, but a hash of the contents could also be used. As a side effect, if the return value is false (the index is up to date), the call will set the existence flag for the document (and any subdocument defined by its parent_udi), so that a later purge() call will preserve them. The use of needUpdate() and purge() is optional, and the indexer may use another method for checking the need to reindex or to delete stale entries. preparePurge(backend_name) Mark all documents which do *not* belong to backend_name. backend_name is the value chosen for the rclbes field for the indexer documents (e.g. "MBOX", "JOPLIN"... for the samples). This is a mandatory call before starting an update if the index is shared with other backends and you are going to call purge() after the update, else all documents for other backends will be deleted from the index by the purge. purge() Delete all documents that were not touched during the just finished indexing pass (since preparePurge()). These are the documents for which the needUpdate() call was not performed, indicating that they no longer exist in the storage system. createStemDbs(lang|sequence of langs) Create stemming dictionaries for query stemming expansion. Note that this is not needed at all if the indexing is done from the recollindex program, as it will perform this action after calling all the external indexers. Should be called when done updating the index. Available only after &RCL; 1.34.3. As an alternative, you can close the index and execute: recollindex -c <confdir> -s <lang(s)> The Python module currently has no interface to the Aspell speller functions, so the same approach can be used for creating the spelling dictionary (with option -S) (again, not needed if recollindex is driving the indexing). Query data access for external indexers &RCL; has internal methods to access document data for its internal (filesystem) indexer. An external indexer needs to provide data access methods if it needs integration with the GUI (e.g. preview function), or support for the rclextract module. An external indexer needs to provide two commands, for fetching data (typically for previewing) and for computing the document signature (for up-to-date checks when opening or previewing). The sample MBOX and JOPLIN implementations use the same script with different parameters to perform both operations, but this is just a choice. A third command must be provided for performing the indexing proper. The "fetch" and "makesig" scripts are called with three additional arguments: udi, url, ipath. These were set by the indexer and stored with the document by the addOrUpdate() call described above. Not all arguments are needed in all cases, the script will use what it needs to perform the requested operation. The caller expects the result data on stdout. recollindex will set the RECOLL_CONFDIR environment variable when executing the scripts, so that the configuration can be created as rclconf = rclconfig.RclConfig() if needed, and the configuration directory obtained as confdir = rclconf.getConfDir() External indexers configuration The index data and the access method are linked by the rclbes (recoll backend storage) Doc field. You should set this to a short string value identifying your indexer (e.g. the filesystem indexer uses either FS or an empty value, the Web history indexer uses BGL, the Joplin notes indexer uses JOPLIN). The link is actually performed inside a backends configuration file (stored in the configuration directory). This defines commands to execute to access data from the specified indexer. Example, for the mbox indexing sample found in the &RCL; source (which sets rclbes="MBOX"): [MBOX] fetch = /path/to/recoll/src/python/samples/rclmbox.py fetch makesig = /path/to/recoll/src/python/samples/rclmbox.py makesig index = /path/to/recoll/src/python/samples/rclmbox.py index When updating the index, the recollindex will execute the value of the the index parameter, if present (it may not be present if this concerns an external index). If an external indexer needs to store additional configuration parameters, e.g. path to a specific instance of the indexed application, etc., I suggest storing them inside recoll.conf, with a backend-specific prefix (e.g. joplin_db, mbox_directory) and using methods from the rclconfig module to access them. External indexer samples First a quick look at an indexer main part, using pseudo-Python3 code: # Connect to the recoll index. This will use the RECOLL_CONFDIR variable, set # by the parent recollindex process, to use the right index. rcldb = recoll.connect(writable=1) # Important: tell the Recoll db that we are going to update documents for the # MYBACK backend. All other documents will be marked as present so as # not to be affect by the subsequent purge. rcldb.preparePurge("MYBACK") # Walk your dataset (of course your code will not look like this) for mydoc in mydoclist: # Compute the doc unique identifier and the signature corresponding to its update state # (e.g. mtime and size for a file). udi = mydoc.udi() sig = mydoc.sig() # Check with recoll if the document needs updating. This has the side-effect or marking # it present. if not rcldb.needUpdate(udi, sig): continue # The document data does not exist in the index or needs updating. Create and add a Recoll # Doc object doc = recoll.Doc() doc.mimetype = "some/type" # Say that the document belongs to this indexer doc.rclbes = "MYBACK" # The url will be passed back to you along with the udi if the fetch # method is called later (for previewing), or may be used for opening the document with # its native app from Recoll. The udi has a maximum size because it is used as a Xapian # term. The url has no such limitation. doc.url = "someurl" doc.sig = sig # Of course add other fields like "text" (duh), "author" etc. See the samples. doc.text = mydoc.text() # [...] # Then add or update the data in the index. self.db.addOrUpdate(udi, doc) # Finally call purge to delete the data for documents which were not seen at all. db.purge() The Recoll source tree has two samples of external indexers. rclmbox.py indexes a directory containing mbox folder files. Of course it is not really useful because &RCL; can do this by itself, but it exercises most features in the update interface, and it has both top-level and embedded documents so it demonstrates the uses of the ipath values. rcljoplin.py indexes a Joplin application main notes SQL table. Joplin sets an an update date attribute for each record in the table, so each note record can be processed as a standalone document (no ipath necessary). The sample has full preview and open support (the latter using a Joplin callback URL which allows displaying the result note inside the native app), so it could actually be useful to perform a unified search of the Joplin data and the regular &RCL; data. See the comments inside the scripts for more information. Using an external indexer index in conjunction with a regular one When adding an external indexer to a regular one for unified querying, some elements of the foreign index configuration should be copied or merged into the main index configuration. At the very least, the backends file needs to be copied or merged, and also possibly data from the mimeconf and mimeview files. See the rcljoplin.py sample for an example. Installation and configuration Installing a binary copy &RCL; binary copies are always distributed as regular packages for your system. They can be obtained either through the system's normal software distribution framework (e.g. Debian/Ubuntu apt, FreeBSD ports, etc.), or from some type of "backports" repository providing versions newer than the standard ones, or found on the &RCL; Web site in some cases. The most up-to-date information about Recoll packages can usually be found on the Recoll Web site downloads page The &WIN; version of Recoll comes in a self-contained setup file, there is nothing else to install. On &LIN;, the package management tools will automatically install hard dependencies for packages obtained from a proper package repository. You will have to deal with them by hand for downloaded packages (for example, when dpkg complains about missing dependencies). In all cases, you will have to check or install supporting applications for the file types that you want to index beyond those that are natively processed by &RCL; (text, HTML, email files, and a few others). You should also maybe have a look at the configuration section (but this may not be necessary for a quick test with default parameters). Most parameters can be more conveniently set from the GUI interface. Supporting packages The &WIN; installation of &RCL; is self-contained. &WIN; users can skip this section. &RCL; uses external applications to index some file types. You need to install them for the file types that you wish to have indexed (these are run-time optional dependencies. None is needed for building or running &RCL; except for indexing their specific file type). After an indexing pass, the commands that were found missing can be displayed from the recoll File menu. The list is stored in the missing text file inside the configuration directory. The past has proven that I was unable to maintain an up to date application list in this manual. Please check &RCLAPPS; for a complete list along with links to the home pages or best source/patches pages, and misc tips. What follows is only a very short extract of the stable essentials. PDF files need pdftotext which is part of Poppler (usually comes with the poppler-utils package). Avoid the original one from Xpdf. MS Word documents need antiword. It is also useful to have wvWare installed as it may be be used as a fallback for some files which antiword does not handle. RTF files need unrtf, which, in its older versions, has much trouble with non-western character sets. Many Linux distributions carry outdated unrtf versions. Check &RCLAPPS; for details. Pictures: &RCL; uses the Exiftool Perl package to extract tag information. Most image file formats are supported. Up to &RCL; 1.24, many XML-based formats need the xsltproc command, which usually comes with libxslt. These are: abiword, fb2 ebooks, kword, openoffice, opendocument svg. &RCL; 1.25 and later process them internally (using libxslt). Building from source Prerequisites The following prerequisites are described in broad terms and Debian package names. The dependencies should be available as packages on most common Unix derivatives, and it should be quite uncommon that you would have to build one of them. Finding the right package name for other systems is left to the sagacity of the reader. If you do not need the GUI, you can avoid all GUI dependencies by disabling its build. (See the configure section further down: --disable-qtgui). The shopping list follows: If you start from git code, you will need the git command (git), and the autoconf, automake and libtool triad (autoconf, automake, libtool). These are not needed for building from tar distributions. The pkg-config command and gettext package are needed for configuring the build (pkg-config, gettext). The make command (make which will actually install GNU make on Linux). If you get strange error messages about dependency tracking from configure, you probably forgot to install make. A C++ compiler with at least C++14 compatibility (g++ or clang). Versions 1.33.4 and older only require c++11. The bison command is not generally needed, but might be if some file modification times are not right. If you want to process CHM files, you will need libchm (libchm-dev), else you can set the --disable-python-chm option to the configure command. For building the documentation: the xsltproc command, and the Docbook XML and style sheet files. You can avoid this dependency by disabling documentation building with the --disable-userdoc configure option. Development files for Xapian core (libxapian-dev). If you are building Xapian for an older CPU (before Pentium 4 or Athlon 64), you need to add the flag to the configure command. Else all Xapian applications will crash with an illegal instruction error. Development files for libxslt (libxslt1-dev). Development files for zlib (zlib1g-dev). Development files for libaspell (libaspell-dev). Can be avoided with the --disable-aspell option to configure. If you want to build the GUI: development files for Qt 5. Else give the --disable-qtgui option to the configure command. You will probably need qtbase5-dev and qttools5-dev-tools I can never remember what other packages to install, but installing the Qt5 Webkit, (or Qt5 Webengine if you set --enable-webengine) will usually bring them as dependencies (libqt5webkit5-dev or qtwebengine5dev). The &RCL; GUI can use either Webkit or Webengine for displaying the results. Development files for Python3 (python3-all-dev, python3-setuptools). You can use the --disable-python-module option for disabling the build of the Python extension. On older systems still supporting Python2, you can also install python2-dev and python-setuptools. The build will test for the presence of the python2 and/or python3 commands and behave accordingly. You may also need libiconv. On Linux systems, the iconv interface is part of libc and you should not need to do anything special. Check the &RCL; download page for up to date version information. Building &RCL; has been built on Linux, FreeBSD, MacOS, and Solaris, most versions after 2005 should be ok, maybe some older ones too (Solaris 8 used to be ok). Current &RCL; versions (1.34 and later) need a c++14 compiler and Qt5, so they will not build on old systems, but if really needed, you can probably find an older version which will work for you. If you build on another system, and need to modify things, I would very much welcome patches. Configure options: will disable the code for phonetic matching of search terms. or will enable the code for real time indexing. Inotify support is enabled by default on Linux systems. will enable sending Zeitgeist events about the visited search results, and needs the qzeitgeist package. will disable the Qt graphical interface, which allows building the indexer and the command line search program in absence of a Qt environment. will implement the result list with a Qt QTextBrowser instead of a WebKit widget if you do not or can't depend on the latter. will enable the use of Qt Webengine (only meaningful if the Qt GUI is enabled), in place or Qt Webkit. will build the recoll GUI program with debug symbols. This makes it very big (~50MB), which is why it is stripped by default. is available from version 1.19 to suppress multithreading inside the indexing process. You can also use the run-time configuration to restrict recollindex to using a single thread, but the compile-time option may disable a few more unused locks. This only applies to the use of multithreading for the core index processing (data input). The &RCL; monitor mode always uses at least two threads of execution. will avoid building the Python module. will avoid building the Python libchm interface used to index CHM files. will enable splitting camelCase words. This is not enabled by default as it has the unfortunate side-effect of making some phrase searches quite confusing: ie, "MySQL manual" would be matched by "MySQL manual" and "my sql manual" but not "mysql manual" (only inside phrase searches). Specify the version of the 'file' command to use (e.g.: --with-file-command=/usr/local/bin/file). Can be useful to enable the gnu version on systems where the native one is bad. Disable X11 connection monitoring inside recollindex. Together with --disable-qtgui, this allows building recoll without Qt and X11. will avoid building the user manual. This avoids having to install the Docbook XML/XSL files and the TeX toolchain used for translating the manual to PDF. Enable building the recollq command line query tool (recoll -t without need for Qt). This is done by default if --disable-qtgui is set but this option enables forcing it. (&RCL; versions up to 1.21 only) will compile &RCL; with position-dependant code. This is incompatible with building the KIO or the Python or PHP extensions, but might yield very marginally faster code. Disable the automatic installation of systemd unit files. Normally unit files are installed if the install path can be detected. Provide an install path for the systemd system unit template file. Provide an install path for the systemd user unit file. Of course the usual autoconf configure options, like apply. Normal procedure, for source extracted from a tar distribution) cd recoll-xxx ./configure [options] make (practices usual hardship-repelling invocations) Building from git code When building from source cloned from the git repository, you also need to install autoconf, automake, and libtool and you must execute sh autogen.sh in the top source directory before running configure. Installing Use make install in the root of the source tree. This will copy the commands to prefix/bin and the sample configuration files, scripts and other shared data to prefix/share/recoll. Python API package The Python interface can be found in the source tree, under the python/recoll directory. The normal &RCL; build procedure (see above) installs the API package for both Python2 and Python3. The Python2 version will probably go away in the near future. The python/recoll/ directory contains the usual setup.py. After configuring and building the main &RCL; code, you can use the script to build and install the Python module for a non-standard Python version. cd recoll-xxx/python/recoll pythonX setup.py build sudo pythonX setup.py install Configuration overview Most of the parameters specific to the recoll GUI are set through the Preferences menu and stored in the standard Qt place ($HOME/.config/Recoll.org/recoll.conf). You probably do not want to edit this by hand. &RCL; indexing options are set inside text configuration files located in a configuration directory. There can be several such directories, each of which defines the parameters for one index. The configuration files can be edited by hand or through the Index configuration dialog (Preferences menu). The GUI tool will try to respect your formatting and comments as much as possible, so it is quite possible to use both approaches on the same configuration. For each index, there are at least two sets of configuration files. System-wide configuration files are kept in a directory named like /usr/share/recoll/examples, and define default values, shared by all indexes (the values in these files are often commented out, and just present to indicate the default coded in the program). For each index, a parallel set of files defines the customized parameters. On &LIN;, the default location of the customized configuration is the .recoll directory in your home. On &WIN; it is C:/Users/[you]/AppData/Local/Recoll. Most people will only use this directory. The default location for the configuration directory can be changed, or others can be added for separate indexes with the RECOLL_CONFDIR environment variable or the option parameter to recoll and recollindex. In addition (as of &RCL; version 1.19.7), it is possible to specify two additional configuration directories which will be stacked before and after the user configuration directory. These are defined by the RECOLL_CONFTOP and RECOLL_CONFMID environment variables. Values from configuration files inside the top directory will override user ones, values from configuration files inside the middle directory will override system ones and be overridden by user ones. These two variables may be of use to applications which augment &RCL; functionality, and need to add configuration data without disturbing the user's files. Please note that the two, currently single, values will probably be interpreted as colon-separated lists in the future: do not use colon characters inside the directory paths. If the default configuration directory does not exist when recoll or recollindex are started, it will be created with a set of empty configuration files. recoll will give you a chance to edit the configuration file before starting indexing. recollindex will proceed immediately. To avoid mistakes, the automatic directory creation will only occur for the default location, not if or RECOLL_CONFDIR were used (in the latter cases, you will have to create the directory). All configuration files share the same format. For example, a short extract of the main configuration file might look as follows: # Space-separated list of files and directories to index. topdirs = ~/docs /usr/share/doc [~/somedirectory-with-utf8-txt-files] defaultcharset = utf-8 There are three kinds of lines: Comment (starts with #) or empty. Parameter affectation (name = value). Section definition ([somedirname]). Long lines can be broken by ending each incomplete part with a backslash (\). Depending on the type of configuration file, section definitions either separate groups of parameters or allow redefining some parameters for a directory sub-tree. They stay in effect until another section definition, or the end of file, is encountered. Some of the parameters used for indexing are looked up hierarchically from the current directory location upwards. Not all parameters can be meaningfully redefined, this is specified for each in the next section. Global parameters must not be defined in a directory subsection, else they will not be found at all by the &RCL; code, which looks for them at the top level (e.g. skippedPaths). When found at the beginning of a file path, the tilde character (~) is expanded to the name of the user's home directory, as a shell would do. Some parameters are lists of strings. White space is used for separation. List elements with embedded spaces can be quoted using double-quotes. Double quotes inside these elements can be escaped with a backslash. No value inside a configuration file can contain a newline character. Long lines can be continued by escaping the physical newline with backslash, even inside quoted strings. astringlist = "some string \ with spaces" thesame = "some string with spaces" Parameters which are not part of string lists can't be quoted, and leading and trailing space characters are stripped before the value is used. Quotes processing is ONLY applied to parameter values which are lists. Double quoting a single value like, e.g. dbdir will result in an incorrect value, with quotes included. This is quite confusing, and may have been a design mistake but it is much too late to fix. Encoding issues Most of the configuration parameters are plain ASCII. Two particular sets of values may cause encoding issues: File path parameters may contain non-ascii characters and should use the exact same byte values as found in the file system directory. Usually, this means that the configuration file should use the system default locale encoding. The unac_except_trans parameter should be encoded in UTF-8. If your system locale is not UTF-8, and you need to also specify non-ascii file paths, this poses a difficulty because common text editors cannot handle multiple encodings in a single file. In this relatively unlikely case, you can edit the configuration file as two separate text files with appropriate encodings, and concatenate them to create the complete configuration. Environment variables RECOLL_CONFDIR Defines the main configuration directory. RECOLL_TMPDIR, TMPDIR Locations for temporary files, in this order of priority. The default if none of these is set is to use /tmp. Big temporary files may be created during indexing, mostly for decompressing, and also for processing, e.g. email attachments. RECOLL_CONFTOP, RECOLL_CONFMID Allow adding configuration directories with priorities below and above the user directory (see above the Configuration overview section for details). RECOLL_EXTRA_DBS, RECOLL_ACTIVE_EXTRA_DBS Help for setting up external indexes. See this paragraph for explanations. RECOLL_DATADIR Defines replacement for the default location of Recoll data files, normally found in, e.g., /usr/share/recoll). RECOLL_FILTERSDIR Defines replacement for the default location of Recoll filters, normally found in, e.g., /usr/share/recoll/filters). ASPELL_PROG aspell program to use for creating the spelling dictionary. The result has to be compatible with the libaspell which &RCL; is using. &RCLCONF; The fields file This file contains information about dynamic fields handling in &RCL;. Some very basic fields have hard-wired behaviour, and, mostly, you should not change the original data inside the fields file. But you can create custom fields fitting your data and handle them just like they were native ones. The fields file has several sections, which each define an aspect of fields processing. Quite often, you'll have to modify several sections to obtain the desired behaviour. We will only give a short description here, you should refer to the comments inside the default file for more detailed information. Field names should be lowercase alphabetic ASCII. [prefixes] A field becomes indexed (searchable) by having a prefix defined in this section. There is a more complete explanation of what prefixes are in used by a standard recoll installation. In a nutshell: extension prefixes should be all caps, begin with XY, and short. E.g. XYMFLD. [values] Fields listed in this section will be stored as &XAP; values inside the index. This makes them available for range queries, allowing to filter results according to the field value. This feature currently supports string and integer data. See the comments in the file for more detail [stored] A field becomes stored (displayable inside results) by having its name listed in this section (typically with an empty value). [aliases] This section defines lists of synonyms for the canonical names used inside the [prefixes] and [stored] sections [queryaliases] This section also defines aliases for the canonic field names, with the difference that the substitution will only be used at query time, avoiding any possibility that the value would pick-up random metadata from documents. handler-specific sections Some input handlers may need specific configuration for handling fields. Only the email message handler currently has such a section (named [mail]). It allows indexing arbitrary email headers in addition to the ones indexed by default. Other such sections may appear in the future. Here follows a small example of a personal fields file. This would extract a specific email header and use it as a searchable field, with data displayable inside result lists. (Side note: as the email handler does no decoding on the values, only plain ascii headers can be indexed, and only the first occurrence will be used for headers that occur several times). [prefixes] # Index mailmytag contents (with the given prefix) mailmytag = XMTAG [stored] # Store mailmytag inside the document data record (so that it can be # displayed - as %(mailmytag) - in result lists). mailmytag = [queryaliases] filename = fn containerfilename = cfn [mail] # Extract the X-My-Tag mail header, and use it internally with the # mailmytag field name x-my-tag = mailmytag Extended attributes in the fields file &RCL; versions 1.19 and later process user extended file attributes as documents fields by default. Attributes are processed as fields of the same name, after removing the user prefix on Linux. The [xattrtofields] section of the fields file allows specifying translations from extended attributes names to &RCL; field names. An empty translation disables use of the corresponding attribute data. The mimemap file mimemap specifies the file name extension to MIME type mappings. For file names without an extension, or with an unknown one, a system command (file , or xdg-mime) will be executed to determine the MIME type (this can be switched off, or the command changed inside the main configuration file). All extension values in mimemap must be entered in lower case. File names extensions are lower-cased for comparison during indexing, meaning that an upper case mimemap entry will never be matched. The mappings can be specified on a per-subtree basis, which may be useful in some cases. Example: okular notes have a .xml extension but should be handled specially, which is possible because they are usually all located in one place. Example: [~/.kde/share/apps/okular/docdata] .xml = application/x-okular-notes The recoll_noindex mimemap variable has been moved to recoll.conf and renamed to noContentSuffixes, while keeping the same function, as of &RCL; version 1.21. For older &RCL; versions, see the documentation for noContentSuffixes but use recoll_noindex in mimemap. The mimeconf file The main purpose of the mimeconf file is to specify how the different MIME types are handled for indexing. This is done in the [index] section, which should not be modified casually. See the comments in the file. The file also contains other definitions which affect the query language and the GUI, and which, in retrospect, should have been stored elsewhere. The [icons] section allows you to change the icons which are displayed by the recoll GUI in the result lists (the values are the basenames of the png images inside the iconsdir directory (which is itself defined in recoll.conf). The [categories] section defines the groupings of MIME types into categories as used when adding an rclcat clause to a query language query. rclcat clauses are also used by the default guifilters buttons in the GUI (see next). The filter controls appear at the top of the recoll GUI, either as checkboxes just above the result list, or as a dropbox in the tool area. By default, they are labeled: media, message, other, presentation, spreadsheet and text, and each maps to a document category. This is determined in the [guifilters] section, where each control is defined by a variable naming a query language fragment. A simple example will hopefully make things clearer. [guifilters] Big Books = dir:"~/My Books" size>10K My Docs = dir:"~/My Documents" Small Books = dir:"~/My Books" size<10K System Docs = dir:/usr/share/doc The above definition would create four filter checkboxes, labelled Big Books, My Docs, etc. The text after the equal sign must be a valid query language fragment, and, when the button is checked, it will be combined with the rest of the query with an AND conjunction. Any name text before a colon character will be erased in the display, but used for sorting. You can use this to display the checkboxes in any order you like. For example, the following would do exactly the same as above, but ordering the checkboxes in the reverse order. [guifilters] d:Big Books = dir:"~/My Books" size>10K c:My Docs = dir:"~/My Documents" b:Small Books = dir:"~/My Books" size<10K a:System Docs = dir:/usr/share/doc As you may have guessed, The default [guifilters] section looks like: [guifilters] text = rclcat:text spreadsheet = rclcat:spreadsheet presentation = rclcat:presentation media = rclcat:media message = rclcat:message other = rclcat:other The mimeview file mimeview specifies which programs are started when you click on an Open link in a result list. E.g.: HTML is normally displayed using firefox, but you may prefer Konqueror, your openoffice.org program might be named oofice instead of openoffice etc. Changes to this file can be done by direct editing, or through the recoll GUI preferences dialog. If Use desktop preferences to choose document editor is checked in the &RCL; GUI preferences, all mimeview entries will be ignored except the one labelled application/x-all (which is set to use xdg-open by default). In this case, the xallexcepts top level variable defines a list of MIME type exceptions which will be processed according to the local entries instead of being passed to the desktop. This is so that specific &RCL; options such as a page number or a search string can be passed to applications that support them, such as the evince viewer. As for the other configuration files, the normal usage is to have a mimeview inside your own configuration directory, with just the non-default entries, which will override those from the central configuration file. All viewer definition entries must be placed under a [view] section. The keys in the file are normally MIME types. You can add an application tag to specialize the choice for an area of the filesystem (using a localfields specification in mimeconf). The syntax for the key is mimetype|tag The nouncompforviewmts entry, (placed at the top level, outside of the [view] section), holds a list of MIME types that should not be uncompressed before starting the viewer (if they are found compressed, e.g.: mydoc.doc.gz). The right side of each assignment holds a command to be executed for opening the file. The following substitutions are performed: %D Document date %f File name. This may be the name of a temporary file if it was necessary to create one (e.g.: to extract a subdocument from a container). %i Internal path, for subdocuments of containers. The format depends on the container type. If this appears in the command line, &RCL; will not create a temporary file to extract the subdocument, expecting the called application (possibly a script) to be able to handle it. %MMIME type %pPage index. Only significant for a subset of document types, currently only PDF, Postscript and DVI files. If it is set, a significant term will be chosen in the query, and %p will be substituted with the first page where the term appears. Can be used to start the editor at the right page for a match or snippet. %lLine number. Only significant for document types with relevant line breaks, mostly text/plain and analogs. If it is set, a significant term will be chosen in the query, and %p will be substituted with the first line where the term appears. %sSearch term. The value will only be set for documents with indexed page or line numbers and if %p or %l is also used. The value will be one of the matched search terms. It would allow pre-setting the value in the "Find" entry inside Evince for example, for easy highlighting of the term. %uUrl. In addition to the predefined values above, all strings like %(fieldname) will be replaced by the value of the field named fieldname for the document. This could be used in combination with field customisation to help with opening the document. The <filename>ptrans</filename> file ptrans specifies query-time path translations. These can be useful in multiple cases. The file has a section for any index which needs translations, either the main one or additional query indexes. The sections are named with the &XAP; index directory names. No slash character should exist at the end of the paths (all comparisons are textual). An example should make things sufficiently clear [/home/me/.recoll/xapiandb] /this/directory/moved = /to/this/place [/path/to/additional/xapiandb] /server/volume1/docdir = /net/server/volume1/docdir /server/volume2/docdir = /net/server/volume2/docdir Examples of configuration adjustments Adding an external viewer for an non-indexed type Imagine that you have some kind of file which does not have indexable content, but for which you would like to have a functional Open link in the result list (when found by file name). The file names end in .blob and can be displayed by application blobviewer. You need two entries in the configuration files for this to work: In $RECOLL_CONFDIR/mimemap (typically ~/.recoll/mimemap), add the following line: .blob = application/x-blobapp Note that the MIME type is made up here, and you could call it diesel/oil just the same. In $RECOLL_CONFDIR/mimeview under the [view] section, add: application/x-blobapp = blobviewer %f We are supposing that blobviewer wants a file name parameter here, you would use %u if it liked URLs better. If you just wanted to change the application used by &RCL; to display a MIME type which it already knows, you would just need to edit mimeview. The entries you add in your personal file override those in the central configuration, which you do not need to alter. mimeview can also be modified from the Gui. Adding indexing support for a new file type Let us now imagine that the above .blob files actually contain indexable text and that you know how to extract it with a command line program. Getting &RCL; to index the files is easy. You need to perform the above alteration, and also to add data to the mimeconf file (typically in ~/.recoll/mimeconf): Under the [index] section, add the following line (more about the rclblob indexing script later): application/x-blobapp = exec rclblob Or if the files are mostly text and you don't need to process them for indexing: application/x-blobapp = internal text/plain Under the [icons] section, you should choose an icon to be displayed for the files inside the result lists. Icons are normally 64x64 pixels PNG files which live in /usr/share/recoll/images. Under the [categories] section, you should add the MIME type where it makes sense (you can also create a category). Categories may be used for filtering in advanced search. The rclblob handler should be an executable program or script which exists inside /usr/share/recoll/filters. It will be given a file name as argument and should output the text or html contents on the standard output. The filter programming section describes in more detail how to write an input handler.
recoll-1.36.1/doc/user/recoll.conf.xml0000644000175000017500000015637314465152375014507 00000000000000 Recoll main configuration file, recoll.conf Parameters affecting what documents we index topdirs Space-separated list of files or directories to recursively index. Default to ~ (indexes $HOME). You can use symbolic links in the list, they will be followed, independently of the value of the followLinks variable. monitordirs Space-separated list of files or directories to monitor for updates. When running the real-time indexer, this allows monitoring only a subset of the whole indexed area. The elements must be included in the tree defined by the 'topdirs' members. skippedNames Files and directories which should be ignored. White space separated list of wildcard patterns (simple ones, not paths, must contain no '/' characters), which will be tested against file and directory names. Have a look at the default configuration for the initial value, some entries may not suit your situation. The easiest way to see it is through the GUI Index configuration "local parameters" panel. The list in the default configuration does not exclude hidden directories (names beginning with a dot), which means that it may index quite a few things that you do not want. On the other hand, email user agents like Thunderbird usually store messages in hidden directories, and you probably want this indexed. One possible solution is to have ".*" in "skippedNames", and add things like "~/.thunderbird" "~/.evolution" to "topdirs". Not even the file names are indexed for patterns in this list, see the "noContentSuffixes" variable for an alternative approach which indexes the file names. Can be redefined for any subtree. skippedNames- List of name endings to remove from the default skippedNames list. skippedNames+ List of name endings to add to the default skippedNames list. onlyNames Regular file name filter patterns If this is set, only the file names not in skippedNames and matching one of the patterns will be considered for indexing. Can be redefined per subtree. Does not apply to directories. noContentSuffixes List of name endings (not necessarily dot-separated suffixes) for which we don't try MIME type identification, and don't uncompress or index content. Only the names will be indexed. This complements the now obsoleted recoll_noindex list from the mimemap file, which will go away in a future release (the move from mimemap to recoll.conf allows editing the list through the GUI). This is different from skippedNames because these are name ending matches only (not wildcard patterns), and the file name itself gets indexed normally. This can be redefined for subdirectories. noContentSuffixes- List of name endings to remove from the default noContentSuffixes list. noContentSuffixes+ List of name endings to add to the default noContentSuffixes list. skippedPaths Absolute paths we should not go into. Space-separated list of wildcard expressions for absolute filesystem paths (for files or directories). The variable must be defined at the top level of the configuration file, not in a subsection. Any value in the list must be textually consistent with the values in topdirs, no attempts are made to resolve symbolic links. In practise, if, as is frequently the case, /home is a link to /usr/home, your default topdirs will have a single entry '~' which will be translated to '/home/yourlogin'. In this case, any skippedPaths entry should start with '/home/yourlogin' *not* with '/usr/home/yourlogin'. The index and configuration directories will automatically be added to the list. The expressions are matched using 'fnmatch(3)' with the FNM_PATHNAME flag set by default. This means that '/' characters must be matched explicitly. You can set 'skippedPathsFnmPathname' to 0 to disable the use of FNM_PATHNAME (meaning that '/*/dir3' will match '/dir1/dir2/dir3'). The default value contains the usual mount point for removable media to remind you that it is in most cases a bad idea to have Recoll work on these Explicitly adding '/media/xxx' to the 'topdirs' variable will override this. skippedPathsFnmPathname Set to 0 to override use of FNM_PATHNAME for matching skipped paths. nowalkfn File name which will cause its parent directory to be skipped. Any directory containing a file with this name will be skipped as if it was part of the skippedPaths list. Ex: .recoll-noindex daemSkippedPaths skippedPaths equivalent specific to real time indexing. This enables having parts of the tree which are initially indexed but not monitored. If daemSkippedPaths is not set, the daemon uses skippedPaths. zipUseSkippedNames Use skippedNames inside Zip archives. Fetched directly by the rclzip.py handler. Skip the patterns defined by skippedNames inside Zip archives. Can be redefined for subdirectories. See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html zipSkippedNames Space-separated list of wildcard expressions for names that should be ignored inside zip archives. This is used directly by the zip handler. If zipUseSkippedNames is not set, zipSkippedNames defines the patterns to be skipped inside archives. If zipUseSkippedNames is set, the two lists are concatenated and used. Can be redefined for subdirectories. See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html followLinks Follow symbolic links during indexing. The default is to ignore symbolic links to avoid multiple indexing of linked files. No effort is made to avoid duplication when this option is set to true. This option can be set individually for each of the 'topdirs' members by using sections. It can not be changed below the 'topdirs' level. Links in the 'topdirs' list itself are always followed. indexedmimetypes Restrictive list of indexed mime types. Normally not set (in which case all supported types are indexed). If it is set, only the types from the list will have their contents indexed. The names will be indexed anyway if indexallfilenames is set (default). MIME type names should be taken from the mimemap file (the values may be different from xdg-mime or file -i output in some cases). Can be redefined for subtrees. excludedmimetypes List of excluded MIME types. Lets you exclude some types from indexing. MIME type names should be taken from the mimemap file (the values may be different from xdg-mime or file -i output in some cases) Can be redefined for subtrees. nomd5types Don't compute md5 for these types. md5 checksums are used only for deduplicating results, and can be very expensive to compute on multimedia or other big files. This list lets you turn off md5 computation for selected types. It is global (no redefinition for subtrees). At the moment, it only has an effect for external handlers (exec and execm). The file types can be specified by listing either MIME types (e.g. audio/mpeg) or handler names (e.g. rclaudio.py). compressedfilemaxkbs Size limit for compressed files. We need to decompress these in a temporary directory for identification, which can be wasteful in some cases. Limit the waste. Negative means no limit. 0 results in no processing of any compressed file. Default 100 MB. textfilemaxmbs Size limit for text files. Mostly for skipping monster logs. Default 20 MB. Use a value of -1 to disable. textunknownasplain Process unknown text/xxx files as text/plain Allows indexing misc. text files identified as text/whatever by 'file' or 'xdg-mime' without having to explicitely set config entries for them. This works fine for indexing (but will cause processing of a lot of garbage though), but the documents indexed this way will be opened by the desktop viewer, even if text/plain has a specific editor. indexallfilenames Index the file names of unprocessed files Index the names of files the contents of which we don't index because of an excluded or unsupported MIME type. usesystemfilecommand Use a system command for file MIME type guessing as a final step in file type identification This is generally useful, but will usually cause the indexing of many bogus 'text' files. See 'systemfilecommand' for the command used. systemfilecommand Command used to guess MIME types if the internal methods fails This should be a "file -i" workalike. The file path will be added as a last parameter to the command line. "xdg-mime" works better than the traditional "file" command, and is now the configured default (with a hard-coded fallback to "file") processwebqueue Decide if we process the Web queue. The queue is a directory where the Recoll Web browser plugins create the copies of visited pages. textfilepagekbs Page size for text files. If this is set, text/plain files will be divided into documents of approximately this size. Will reduce memory usage at index time and help with loading data in the preview window at query time. Particularly useful with very big files, such as application or system logs. Also see textfilemaxmbs and compressedfilemaxkbs. membermaxkbs Size limit for archive members. This is passed to the filters in the environment as RECOLL_FILTER_MAXMEMBERKB. Parameters affecting how we generate terms and organize the index indexStripChars Decide if we store character case and diacritics in the index. If we do, searches sensitive to case and diacritics can be performed, but the index will be bigger, and some marginal weirdness may sometimes occur. The default is a stripped index. When using multiple indexes for a search, this parameter must be defined identically for all. Changing the value implies an index reset. indexStoreDocText Decide if we store the documents' text content in the index. Storing the text allows extracting snippets from it at query time, instead of building them from index position data. Newer Xapian index formats have rendered our use of positions list unacceptably slow in some cases. The last Xapian index format with good performance for the old method is Chert, which is default for 1.2, still supported but not default in 1.4 and will be dropped in 1.6. The stored document text is translated from its original format to UTF-8 plain text, but not stripped of upper-case, diacritics, or punctuation signs. Storing it increases the index size by 10-20% typically, but also allows for nicer snippets, so it may be worth enabling it even if not strictly needed for performance if you can afford the space. The variable only has an effect when creating an index, meaning that the xapiandb directory must not exist yet. Its exact effect depends on the Xapian version. For Xapian 1.4, if the variable is set to 0, the Chert format will be used, and the text will not be stored. If the variable is 1, Glass will be used, and the text stored. For Xapian 1.2, and for versions after 1.5 and newer, the index format is always the default, but the variable controls if the text is stored or not, and the abstract generation method. With Xapian 1.5 and later, and the variable set to 0, abstract generation may be very slow, but this setting may still be useful to save space if you do not use abstract generation at all. nonumbers Decides if terms will be generated for numbers. For example "123", "1.5e6", 192.168.1.4, would not be indexed if nonumbers is set ("value123" would still be). Numbers are often quite interesting to search for, and this should probably not be set except for special situations, ie, scientific documents with huge amounts of numbers in them, where setting nonumbers will reduce the index size. This can only be set for a whole index, not for a subtree. dehyphenate Determines if we index 'coworker' also when the input is 'co-worker'. This is new in version 1.22, and on by default. Setting the variable to off allows restoring the previous behaviour. backslashasletter Process backslash as normal letter. This may make sense for people wanting to index TeX commands as such but is not of much general use. underscoreasletter Process underscore as normal letter. This makes sense in so many cases that one wonders if it should not be the default. maxtermlength Maximum term length. Words longer than this will be discarded. The default is 40 and used to be hard-coded, but it can now be adjusted. You need an index reset if you change the value. nocjk Decides if specific East Asian (Chinese Korean Japanese) characters/word splitting is turned off. This will save a small amount of CPU if you have no CJK documents. If your document base does include such text but you are not interested in searching it, setting nocjk may be a significant time and space saver. cjkngramlen This lets you adjust the size of n-grams used for indexing CJK text. The default value of 2 is probably appropriate in most cases. A value of 3 would allow more precision and efficiency on longer words, but the index will be approximately twice as large. indexstemminglanguages Languages for which to create stemming expansion data. Stemmer names can be found by executing 'recollindex -l', or this can also be set from a list in the GUI. The values are full language names, e.g. english, french... defaultcharset Default character set. This is used for files which do not contain a character set definition (e.g.: text/plain). Values found inside files, e.g. a 'charset' tag in HTML documents, will override it. If this is not set, the default character set is the one defined by the NLS environment ($LC_ALL, $LC_CTYPE, $LANG), or ultimately iso-8859-1 (cp-1252 in fact). If for some reason you want a general default which does not match your LANG and is not 8859-1, use this variable. This can be redefined for any sub-directory. unac_except_trans A list of characters, encoded in UTF-8, which should be handled specially when converting text to unaccented lowercase. For example, in Swedish, the letter a with diaeresis has full alphabet citizenship and should not be turned into an a. Each element in the space-separated list has the special character as first element and the translation following. The handling of both the lowercase and upper-case versions of a character should be specified, as appartenance to the list will turn-off both standard accent and case processing. The value is global and affects both indexing and querying. We also convert a few confusing Unicode characters (quotes, hyphen) to their ASCII equivalent to avoid "invisible" search failures. Examples: Swedish: unac_except_trans = ää Ää öö Öö üü Üü ßss œoe Œoe æae Æae ffff fifi flfl åå Åå ’' ❜' ʼ' ‐- . German: unac_except_trans = ää Ää öö Öö üü Üü ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐- . French: you probably want to decompose oe and ae and nobody would type a German ß unac_except_trans = ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐- . The default for all until someone protests follows. These decompositions are not performed by unac, but it is unlikely that someone would type the composed forms in a search. unac_except_trans = ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐- maildefcharset Overrides the default character set for email messages which don't specify one. This is mainly useful for readpst (libpst) dumps, which are utf-8 but do not say so. localfields Set fields on all files (usually of a specific fs area). Syntax is the usual: name = value ; attr1 = val1 ; [...] value is empty so this needs an initial semi-colon. This is useful, e.g., for setting the rclaptg field for application selection inside mimeview. testmodifusemtime Use mtime instead of ctime to test if a file has been modified. The time is used in addition to the size, which is always used. Setting this can reduce re-indexing on systems where extended attributes are used (by some other application), but not indexed, because changing extended attributes only affects ctime. Notes: - This may prevent detection of change in some marginal file rename cases (the target would need to have the same size and mtime). - You should probably also set noxattrfields to 1 in this case, except if you still prefer to perform xattr indexing, for example if the local file update pattern makes it of value (as in general, there is a risk for pure extended attributes updates without file modification to go undetected). Perform a full index reset after changing this. noxattrfields Disable extended attributes conversion to metadata fields. This probably needs to be set if testmodifusemtime is set. metadatacmds Define commands to gather external metadata, e.g. tmsu tags. There can be several entries, separated by semi-colons, each defining which field name the data goes into and the command to use. Don't forget the initial semi-colon. All the field names must be different. You can use aliases in the "field" file if necessary. As a not too pretty hack conceded to convenience, any field name beginning with "rclmulti" will be taken as an indication that the command returns multiple field values inside a text blob formatted as a recoll configuration file ("fieldname = fieldvalue" lines). The rclmultixx name will be ignored, and field names and values will be parsed from the data. Example: metadatacmds = ; tags = tmsu tags %f; rclmulti1 = cmdOutputsConf %f Parameters affecting where and how we store things cachedir Top directory for Recoll data. Recoll data directories are normally located relative to the configuration directory (e.g. ~/.recoll/xapiandb, ~/.recoll/mboxcache). If 'cachedir' is set, the directories are stored under the specified value instead (e.g. if cachedir is ~/.cache/recoll, the default dbdir would be ~/.cache/recoll/xapiandb). This affects dbdir, webcachedir, mboxcachedir, aspellDicDir, which can still be individually specified to override cachedir. Note that if you have multiple configurations, each must have a different cachedir, there is no automatic computation of a subpath under cachedir. maxfsoccuppc Maximum file system occupation over which we stop indexing. The value is a percentage, corresponding to what the "Capacity" df output column shows. The default value is 0, meaning no checking. dbdir Xapian database directory location. This will be created on first indexing. If the value is not an absolute path, it will be interpreted as relative to cachedir if set, or the configuration directory (-c argument or $RECOLL_CONFDIR). If nothing is specified, the default is then ~/.recoll/xapiandb/ idxstatusfile Name of the scratch file where the indexer process updates its status. Default: idxstatus.txt inside the configuration directory. mboxcachedir Directory location for storing mbox message offsets cache files. This is normally 'mboxcache' under cachedir if set, or else under the configuration directory, but it may be useful to share a directory between different configurations. mboxcacheminmbs Minimum mbox file size over which we cache the offsets. There is really no sense in caching offsets for small files. The default is 5 MB. mboxmaxmsgmbs Maximum mbox member message size in megabytes. Size over which we assume that the mbox format is bad or we misinterpreted it, at which point we just stop processing the file. webcachedir Directory where we store the archived web pages. This is only used by the web history indexing code Default: cachedir/webcache if cachedir is set, else $RECOLL_CONFDIR/webcache webcachemaxmbs Maximum size in MB of the Web archive. This is only used by the web history indexing code. Default: 40 MB. Reducing the size will not physically truncate the file. webqueuedir The path to the Web indexing queue. This used to be hard-coded in the old plugin as ~/.recollweb/ToIndex so there would be no need or possibility to change it, but the WebExtensions plugin now downloads the files to the user Downloads directory, and a script moves them to webqueuedir. The script reads this value from the config so it has become possible to change it. webdownloadsdir The path to browser downloads directory. This is where the new browser add-on extension has to create the files. They are then moved by a script to webqueuedir. webcachekeepinterval Page recycle interval By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. aspellDicDir Aspell dictionary storage directory location. The aspell dictionary (aspdict.(lang).rws) is normally stored in the directory specified by cachedir if set, or under the configuration directory. filtersdir Directory location for executable input handlers. If RECOLL_FILTERSDIR is set in the environment, we use it instead. Defaults to $prefix/share/recoll/filters. Can be redefined for subdirectories. iconsdir Directory location for icons. The only reason to change this would be if you want to change the icons displayed in the result list. Defaults to $prefix/share/recoll/images Parameters affecting indexing performance and resource usage idxflushmb Threshold (megabytes of new data) where we flush from memory to disk index. Setting this allows some control over memory usage by the indexer process. A value of 0 means no explicit flushing, which lets Xapian perform its own thing, meaning flushing every $XAPIAN_FLUSH_THRESHOLD documents created, modified or deleted: as memory usage depends on average document size, not only document count, the Xapian approach is is not very useful, and you should let Recoll manage the flushes. The program compiled value is 0. The configured default value (from this file) is now 50 MB, and should be ok in many cases. You can set it as low as 10 to conserve memory, but if you are looking for maximum speed, you may want to experiment with values between 20 and 200. In my experience, values beyond this are always counterproductive. If you find otherwise, please drop me a note. filtermaxseconds Maximum external filter execution time in seconds. Default 1200 (20mn). Set to 0 for no limit. This is mainly to avoid infinite loops in postscript files (loop.ps) filtermaxmbytes Maximum virtual memory space for filter processes (setrlimit(RLIMIT_AS)), in megabytes. Note that this includes any mapped libs (there is no reliable Linux way to limit the data space only), so we need to be a bit generous here. Anything over 2000 will be ignored on 32 bits machines. The high default value is needed because of java-based handlers (pdftk) which need a lot of VM (most of it text), esp. pdftk when executed from Python rclpdf.py. You can use a much lower value if you don't need Java. thrQSizes Stage input queues configuration. There are three internal queues in the indexing pipeline stages (file data extraction, terms generation, index update). This parameter defines the queue depths for each stage (three integer values). If a value of -1 is given for a given stage, no queue is used, and the thread will go on performing the next stage. In practise, deep queues have not been shown to increase performance. Default: a value of 0 for the first queue tells Recoll to perform autoconfiguration based on the detected number of CPUs (no need for the two other values in this case). Use thrQSizes = -1 -1 -1 to disable multithreading entirely. thrTCounts Number of threads used for each indexing stage. The three stages are: file data extraction, terms generation, index update). The use of the counts is also controlled by some special values in thrQSizes: if the first queue depth is 0, all counts are ignored (autoconfigured); if a value of -1 is used for a queue depth, the corresponding thread count is ignored. It makes no sense to use a value other than 1 for the last stage because updating the Xapian index is necessarily single-threaded (and protected by a mutex). Miscellaneous parameters loglevel Log file verbosity 1-6. A value of 2 will print only errors and warnings. 3 will print information like document updates, 4 is quite verbose and 6 very verbose. logfilename Log file destination. Use 'stderr' (default) to write to the console. idxloglevel Override loglevel for the indexer. idxlogfilename Override logfilename for the indexer. helperlogfilename Destination file for external helpers standard error output. The external program error output is left alone by default, e.g. going to the terminal when the recoll[index] program is executed from the command line. Use /dev/null or a file inside a non-existent directory to completely suppress the output. daemloglevel Override loglevel for the indexer in real time mode. The default is to use the idx... values if set, else the log... values. daemlogfilename Override logfilename for the indexer in real time mode. The default is to use the idx... values if set, else the log... values. pyloglevel Override loglevel for the python module. pylogfilename Override logfilename for the python module. idxnoautopurge Do not purge data for deleted or inaccessible files This can be overridden by recollindex command line options and may be useful if some parts of the document set may predictably be inaccessible at times, so that you would only run the purge after making sure that everything is there. orgidxconfdir Original location of the configuration directory. This is used exclusively for movable datasets. Locating the configuration directory inside the directory tree makes it possible to provide automatic query time path translations once the data set has moved (for example, because it has been mounted on another location). curidxconfdir Current location of the configuration directory. Complement orgidxconfdir for movable datasets. This should be used if the configuration directory has been copied from the dataset to another location, either because the dataset is readonly and an r/w copy is desired, or for performance reasons. This records the original moved location before copy, to allow path translation computations. For example if a dataset originally indexed as '/home/me/mydata/config' has been mounted to '/media/me/mydata', and the GUI is running from a copied configuration, orgidxconfdir would be '/home/me/mydata/config', and curidxconfdir (as set in the copied configuration) would be '/media/me/mydata/config'. idxrundir Indexing process current directory. The input handlers sometimes leave temporary files in the current directory, so it makes sense to have recollindex chdir to some temporary directory. If the value is empty, the current directory is not changed. If the value is (literal) tmp, we use the temporary directory as set by the environment (RECOLL_TMPDIR else TMPDIR else /tmp). If the value is an absolute path to a directory, we go there. checkneedretryindexscript Script used to heuristically check if we need to retry indexing files which previously failed. The default script checks the modified dates on /usr/bin and /usr/local/bin. A relative path will be looked up in the filters dirs, then in the path. Use an absolute path to do otherwise. recollhelperpath Additional places to search for helper executables. This is used, e.g., on Windows by the Python code, and on Mac OS by the bundled recoll.app (because I could find no reliable way to tell launchd to set the PATH). The example below is for Windows. Use ':' as entry separator for Mac and Ux-like systems, ';' is for Windows only. idxabsmlen Length of abstracts we store while indexing. Recoll stores an abstract for each indexed file. The text can come from an actual 'abstract' section in the document or will just be the beginning of the document. It is stored in the index so that it can be displayed inside the result lists without decoding the original file. The idxabsmlen parameter defines the size of the stored abstract. The default value is 250 bytes. The search interface gives you the choice to display this stored text or a synthetic abstract built by extracting text around the search terms. If you always prefer the synthetic abstract, you can reduce this value and save a little space. idxmetastoredlen Truncation length of stored metadata fields. This does not affect indexing (the whole field is processed anyway), just the amount of data stored in the index for the purpose of displaying fields inside result lists or previews. The default value is 150 bytes which may be too low if you have custom fields. idxtexttruncatelen Truncation length for all document texts. Only index the beginning of documents. This is not recommended except if you are sure that the interesting keywords are at the top and have severe disk space issues. idxsynonyms Name of the index-time synonyms file. This is used for indexing multiword synonyms as single terms, which in turn is only useful if you want to perform proximity searches with such terms. idxniceprio "nice" process priority for the indexing processes. Default: 19 (lowest) Appeared with 1.26.5. Prior versions were fixed at 19. noaspell Disable aspell use. The aspell dictionary generation takes time, and some combinations of aspell version, language, and local terms, result in aspell crashing, so it sometimes makes sense to just disable the thing. aspellLanguage Language definitions to use when creating the aspell dictionary. The value must match a set of aspell language definition files. You can type "aspell dicts" to see a list The default if this is not set is to use the NLS environment to guess the value. The values are the 2-letter language codes (e.g. 'en', 'fr'...) aspellAddCreateParam Additional option and parameter to aspell dictionary creation command. Some aspell packages may need an additional option (e.g. on Debian Jessie: --local-data-dir=/usr/lib/aspell). See Debian bug 772415. aspellKeepStderr Set this to have a look at aspell dictionary creation errors. There are always many, so this is mostly for debugging. monauxinterval Auxiliary database update interval. The real time indexer only updates the auxiliary databases (stemdb, aspell) periodically, because it would be too costly to do it for every document change. The default period is one hour. monixinterval Minimum interval (seconds) between processings of the indexing queue. The real time indexer does not process each event when it comes in, but lets the queue accumulate, to diminish overhead and to aggregate multiple events affecting the same file. Default 30 S. mondelaypatterns Timing parameters for the real time indexing. Definitions for files which get a longer delay before reindexing is allowed. This is for fast-changing files, that should only be reindexed once in a while. A list of wildcardPattern:seconds pairs. The patterns are matched with fnmatch(pattern, path, 0) You can quote entries containing white space with double quotes (quote the whole entry, not the pattern). The default is empty. Example: mondelaypatterns = *.log:20 "*with spaces.*:30" monioniceclass ionice class for the indexing process. Despite the misleading name, and on platforms where this is supported, this affects all indexing processes, not only the real time/monitoring ones. The default value is 3 (use lowest "Idle" priority). monioniceclassdata ionice class level parameter if the class supports it. The default is empty, as the default "Idle" class has no levels. Query-time parameters (no impact on the index) autodiacsens auto-trigger diacritics sensitivity (raw index only). IF the index is not stripped, decide if we automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the "D" modifier to specify diacritics sensitivity. Default is no. autocasesens auto-trigger case sensitivity (raw index only). IF the index is not stripped (see indexStripChars), decide if we automatically trigger character case sensitivity if the search term has upper-case characters in any but the first position. Else you need to use the query language and the "C" modifier to specify character-case sensitivity. Default is yes. maxTermExpand Maximum query expansion count for a single term (e.g.: when using wildcards). This only affects queries, not indexing. We used to not limit this at all (except for filenames where the limit was too low at 1000), but it is unreasonable with a big index. Default 10000. maxXapianClauses Maximum number of clauses we add to a single Xapian query. This only affects queries, not indexing. In some cases, the result of term expansion can be multiplicative, and we want to avoid eating all the memory. Default 50000. snippetMaxPosWalk Maximum number of positions we walk while populating a snippet for the result list. The default of 1,000,000 may be insufficient for very big documents, the consequence would be snippets with possibly meaning-altering missing words. thumbnailercmd Command to use for generating thumbnails. If set, this should be a path to a command or script followed by its constant arguments. Four arguments will be appended before execution: the document URL, MIME type, target icon SIZE (e.g. 128), and output file PATH. The command should generate a thumbnail from these values. E.g. if the MIME is video, a script could use: ffmpegthumbnailer -iURL -oPATH -sSIZE. stemexpandphrases Default to applying stem expansion to phrase terms. Recoll normally does not apply stem expansion to terms inside phrase searches. Setting this parameter will change the default behaviour to expanding terms inside phrases. If set, you can use a 'l' modifier to disable expansion for a specific instance. autoSpellRarityThreshold Inverse of the ratio of term occurrence to total db terms over which we look for spell neighbours for automatic query expansion When a term is very uncommon, we may (depending on user choice) look for spelling variations which would be more common and possibly add them to the query. autoSpellSelectionThreshold Ratio of spell neighbour frequency over user input term frequency beyond which we include the neighbour in the query. When a term has been selected for spelling expansion because of its rarity, we only include spelling neighbours which are more common by this ratio. kioshowsubdocs Show embedded document results in KDE dolphin/kio and krunner Embedded documents may clutter the results and are not always easily usable from the kio or krunner environment. Setting this variable will restrict the results to standalone documents. Parameters for the PDF input script pdfocr Attempt OCR of PDF files with no text content. This can be defined in subdirectories. The default is off because OCR is so very slow. pdfattach Enable PDF attachment extraction by executing pdftk (if available). This is normally disabled, because it does slow down PDF indexing a bit even if not one attachment is ever found. pdfextrameta Extract text from selected XMP metadata tags. This is a space-separated list of qualified XMP tag names. Each element can also include a translation to a Recoll field name, separated by a '|' character. If the second element is absent, the tag name is used as the Recoll field names. You will also need to add specifications to the "fields" file to direct processing of the extracted data. pdfextrametafix Define name of XMP field editing script. This defines the name of a script to be loaded for editing XMP field values. The script should define a 'MetaFixer' class with a metafix() method which will be called with the qualified tag name and value of each selected field, for editing or erasing. A new instance is created for each document, so that the object can keep state for, e.g. eliminating duplicate values. Parameters for OCR processing ocrprogs OCR modules to try. The top OCR script will try to load the corresponding modules in order and use the first which reports being capable of performing OCR on the input file. Modules for tesseract (tesseract) and ABBYY FineReader (abbyy) are present in the standard distribution. For compatibility with the previous version, if this is not defined at all, the default value is "tesseract". Use an explicit empty value if needed. A value of "abbyy tesseract" will try everything. ocrcachedir Location for caching OCR data. The default if this is empty or undefined is to store the cached OCR data under $RECOLL_CONFDIR/ocrcache. tesseractlang Language to assume for tesseract OCR. Important for improving the OCR accuracy. This can also be set through the contents of a file in the currently processed directory. See the rclocrtesseract.py script. Example values: eng, fra... See the tesseract documentation. tesseractcmd Path for the tesseract command. Do not quote. This is mostly useful on Windows, or for specifying a non-default tesseract command. E.g. on Windows. tesseractcmd = C:/ProgramFiles(x86)/Tesseract-OCR/tesseract.exe abbyylang Language to assume for abbyy OCR. Important for improving the OCR accuracy. This can also be set through the contents of a file in the currently processed directory. See the rclocrabbyy.py script. Typical values: English, French... See the ABBYY documentation. abbyyocrcmd Path for the abbyy command The ABBY directory is usually not in the path, so you should set this. Parameters for running speech to text conversion speechtotext Activate speech to text conversion The only possible value at the moment is "whisper" for using the OpenAI whisper program. sttmodel Name of the whisper model sttdevice Name of the device to be used by for whisper Parameters for miscellaneous specific handlers orgmodesubdocs Index org-mode level 1 sections as separate sub-documents This is the default. If set to false, org-mode files will be indexed as plain text Parameters set for specific locations mhmboxquirks Enable thunderbird/mozilla-seamonkey mbox format quirks Set this for the directory where the email mbox files are stored. recoll-1.36.1/doc/user/Makefile0000644000175000017500000000553714410615043013176 00000000000000 # Wherever docbook.xsl and chunk.xsl live. # NOTE: THIS IS HARDCODED inside custom.xsl (for changing the output # charset), which needs to change if the stylesheet location changes. # Necessity of custom.xsl: # http://www.sagehill.net/docbookxsl/OutputEncoding.html # Fbsd #XSLDIR="/usr/local/share/xsl/docbook/" # Mac #XSLDIR="/opt/local/share/xsl/docbook-xsl/" #Linux XSLDIR="/usr/share/xml/docbook/stylesheet/docbook-xsl/" UTILBUILDS=/home/dockes/tmp/builds/medocutils/ # Options common to the single-file and chunked versions commonoptions=--stringparam section.autolabel 1 \ --stringparam section.autolabel.max.depth 2 \ --stringparam section.label.includes.component.label 1 \ --stringparam toc.max.depth 3 \ --stringparam autotoc.label.in.hyperlink 0 \ --stringparam abstract.notitle.enabled 1 \ --stringparam html.stylesheet docbook-xsl.css \ --stringparam generate.toc "book toc,title,figure,table,example,equation" # index.html chunk format target replaced by nicer webhelp (needs separate # make) in webhelp/ subdir all: usermanual.html webh usermanual.pdf webh: make -C webhelp usermanual.html: usermanual.xml recoll.conf.xml xsltproc --xinclude ${commonoptions} \ -o tmpfile.html custom.xsl $< -tidy -indent tmpfile.html > usermanual.html rm -f tmpfile.html index.html: usermanual.xml recoll.conf.xml xsltproc ${commonoptions} \ --stringparam use.id.as.filename 1 \ --stringparam root.filename index \ "${XSLDIR}/html/chunk.xsl" $< usermanual.pdf: usermanual.xml recoll.conf.xml dblatex --xslt-opts="--xinclude" -tpdf $< recoll.conf.xml: ../../sampleconf/recoll.conf test -x $(UTILBUILDS)/confxml && $(UTILBUILDS)/confxml --docbook \ --idprefix=RCL.INSTALL.CONFIG.RECOLLCONF \ ../../sampleconf/recoll.conf > recoll.conf.xml || touch recoll.conf.xml # Generating a restructured text version, for uploading to readthedocs. # Does not really work, the internal links are botched. pandoc # generates something like: # `configuration <#RCL.INDEXING.CONFIG>`__ # when it should be: # :ref:`RCL.INDEXING.CONFIG` # Also with the second form, the link text is the section heading (can't be # chosen), which is not nice. Else, the change could probably be done by a # script. # Also could not get readthedocs to generate the left pane TOC? could # probably be fixed... #usermanual-rst: recoll.conf.xml # tail -n +2 recoll.conf.xml > rcl-conf-tail.xml # sed -e '/xi:include/r rcl-conf-tail.xml' \ # < usermanual.xml > full-man.xml # sed -i -e '/xi:include/d' -e '//d' full-man.xml # pandoc -s -f docbook -t rst full-man.xml > \ # ../../../docs/index.rst # rm -f rcl-conf-tail.xml full-man.xml # not needed with pandoc 2.x -@echo fix termmatch and execute clean: rm -f RCL.*.html usermanual.pdf usermanual.html index.html \ tmpfile.html rcl-conf-tail.xml full-man.xml recoll-1.36.1/doc/user/custom.xsl0000644000175000017500000000067314410615043013574 00000000000000 recoll-1.36.1/doc/user/docbook.css0000644000175000017500000000733214410615043013663 00000000000000/* * Copyright (c) 2001, 2003, 2010 The FreeBSD Documentation Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: doc/share/misc/docbook.css,v 1.15 2010/03/20 04:15:01 hrs Exp $ */ BODY ADDRESS { line-height: 1.3; margin: .6em 0; } BODY BLOCKQUOTE { margin-top: .75em; line-height: 1.5; margin-bottom: .75em; } HTML BODY { margin: 1em 8% 1em 10%; line-height: 1.2; } .LEGALNOTICE { font-size: small; font-variant: small-caps; } BODY DIV { margin: 0; } DL { margin: .8em 0; line-height: 1.2; } BODY FORM { margin: .6em 0; } H1, H2, H3, H4, H5, H6, DIV.EXAMPLE P B, .QUESTION, DIV.TABLE P B, DIV.PROCEDURE P B { color: #990000; } BODY H1, BODY H2, BODY H3, BODY H4, BODY H5, BODY H6 { line-height: 1.3; margin-left: 0; } BODY H1, BODY H2 { margin: .8em 0 0 -4%; } BODY H3, BODY H4 { margin: .8em 0 0 -3%; } BODY H5 { margin: .8em 0 0 -2%; } BODY H6 { margin: .8em 0 0 -1%; } BODY HR { margin: .6em; border-width: 0 0 1px 0; border-style: solid; border-color: #cecece; } BODY IMG.NAVHEADER { margin: 0 0 0 -4%; } OL { margin: 0 0 0 5%; line-height: 1.2; } BODY PRE { margin: .75em 0; line-height: 1.0; font-family: monospace; } BODY TD, BODY TH { line-height: 1.2; } UL, BODY DIR, BODY MENU { margin: 0 0 0 5%; line-height: 1.2; } HTML { margin: 0; padding: 0; } BODY P B.APPLICATION { color: #000000; } .FILENAME { color: #007a00; } .GUIMENU, .GUIMENUITEM, .GUISUBMENU, .GUILABEL, .INTERFACE, .SHORTCUT, .SHORTCUT .KEYCAP { font-weight: bold; } .GUIBUTTON { background-color: #CFCFCF; padding: 2px; } .ACCEL { background-color: #F0F0F0; text-decoration: underline; } .SCREEN { padding: 1ex; } .PROGRAMLISTING { padding: 1ex; background-color: #eee; border: 1px solid #ccc; } @media screen { /* hide from IE3 */ a[href]:hover { background: #ffa } } BLOCKQUOTE.NOTE { color: #222; background: #eee; border: 1px solid #ccc; padding: 0.4em 0.4em; width: 85%; } BLOCKQUOTE.TIP { color: #004F00; background: #d8ecd6; border: 1px solid green; padding: 0.2em 2em; width: 85%; } BLOCKQUOTE.IMPORTANT { font-style:italic; border: 1px solid #a00; border-left: 12px solid #c00; padding: 0.1em 1em; } BLOCKQUOTE.WARNING { color: #9F1313; background: #f8e8e8; border: 1px solid #e59595; padding: 0.2em 2em; width: 85%; } .EXAMPLE { background: #fefde6; border: 1px solid #f1bb16; margin: 1em 0; padding: 0.2em 2em; width: 90%; } .INFORMALTABLE TABLE.CALSTABLE TR TD { padding-left: 1em; padding-right: 1em; } recoll-1.36.1/doc/user/usermanual.html0000644000175000017500000217136714516133172014614 00000000000000 Recoll user manual

Recoll user manual

Jean-Francois Dockes

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at the following location: GNU web site.

This document introduces full text search notions and describes the installation and use of the Recoll application. This version describes Recoll 1.36.


Table of Contents

1. Introduction
1.1. Giving it a try
1.2. Full text search
1.3. Recoll overview
2. Indexing
2.1. Introduction
2.1.1. Indexing modes
2.1.2. Configurations, multiple indexes
2.1.3. Document types
2.1.4. Indexing failures
2.1.5. Recovery
2.2. Index storage
2.2.1. Xapian index formats
2.2.2. Security aspects
2.2.3. Special considerations for big indexes
2.3. Index configuration
2.3.1. The index configuration GUI
2.3.2. Multiple indexes
2.3.3. Index case and diacritics sensitivity
2.3.4. Indexing threads configuration (Unix-like systems)
2.4. Index update scheduling
2.4.1. Periodic indexing
2.4.2. Real time indexing
2.5. Miscellaneous indexing notes
2.5.1. The PDF input handler
2.5.2. Running OCR on image documents
2.5.3. Running a speech to text program on audio files
2.5.4. Removable volumes
2.5.5. Unix-like systems: indexing visited Web pages
2.5.6. Unix-like systems: using extended attributes
2.5.7. Unix-like systems: importing external tags
3. Searching
3.1. Introduction
3.2. Searching with the Qt graphical user interface
3.2.1. Simple search
3.2.2. The result list
3.2.3. The result table
3.2.4. The filters panel
3.2.5. Running arbitrary commands on result files
3.2.6. Unix-like systems: displaying thumbnails
3.2.7. The preview window
3.2.8. The Query Fragments window
3.2.9. Assisted Complex Search (A.K.A. "Advanced Search")
3.2.10. The term explorer tool
3.2.11. Multiple indexes
3.2.12. Document history
3.2.13. Sorting search results and collapsing duplicates
3.2.14. Keyboard shortcuts
3.2.15. Search tips
3.2.16. Saving and restoring queries
3.2.17. Customizing the search interface
3.3. Searching with the KDE KIO slave
3.4. Searching on the command line
3.5. The query language
3.5.1. General syntax
3.5.2. Special field-like specifiers
3.5.3. Range clauses
3.5.4. Modifiers
3.6. Wildcards and anchored searches
3.6.1. Wildcards
3.6.2. Anchored searches
3.7. Using Synonyms (1.22)
3.8. Path translations
3.9. Search case and diacritics sensitivity
3.10. Desktop integration
3.10.1. Hotkeying recoll
3.10.2. The KDE Kicker Recoll applet
4. Programming interface
4.1. Writing a document input handler
4.1.1. Simple input handlers
4.1.2. "Multiple" handlers
4.1.3. Telling Recoll about the handler
4.1.4. Input handler output
4.1.5. Page numbers
4.2. Field data processing
4.3. Python API
4.3.1. Introduction
4.3.2. Interface elements
4.3.3. Log messages for Python scripts
4.3.4. Python search interface
4.3.5. Python indexing interface
5. Installation and configuration
5.1. Installing a binary copy
5.2. Supporting packages
5.3. Building from source
5.3.1. Prerequisites
5.3.2. Building
5.3.3. Installing
5.3.4. Python API package
5.4. Configuration overview
5.4.1. Environment variables
5.4.2. Recoll main configuration file, recoll.conf
5.4.3. The fields file
5.4.4. The mimemap file
5.4.5. The mimeconf file
5.4.6. The mimeview file
5.4.7. The ptrans file
5.4.8. Examples of configuration adjustments

List of Tables

3.1. Keyboard shortcuts

Chapter 1. Introduction

This document introduces full text search notions and describes the installation and use of the Recoll application. It is updated for Recoll 1.36.

Recoll was for a long time dedicated to Unix-like systems. It was only lately (2015) ported to MS-Windows. Many references in this manual, especially file locations, are specific to Unix, and not valid on Windows, where some described features are also not available. The manual will be progressively updated. Until this happens, on Windows, most references to shared files can be translated by looking under the Recoll installation directory (Typically C:/Program Files (x86)/Recoll). Especially, anything referenced inside /usr/share in this document will be found in the Share subdirectory of the installation). The user configuration is stored by default under AppData/Local/Recoll inside the user directory, along with the index itself.

1.1. Giving it a try

If you do not like reading manuals (who does?) but wish to give Recoll a try, just install the application and start the recoll graphical user interface (GUI), which will ask permission to index your home directory, allowing you to search immediately after indexing completes.

Do not do this if your home directory contains a huge number of documents and you do not want to wait or are very short on disk space. In this case, you may first want to customize the configuration to restrict the indexed area. From the recoll GUI go to: PreferencesIndexing configuration, then adjust the Top directories section, which defines the directories from which the filesystem exploration starts.

On Unix-like systems, you may need to install the appropriate supporting applications for document types that need them (for example antiword for Microsoft Word files). The Windows package is self-contained and includes most useful auxiliary programs.

1.2. Full text search

Recoll is a full text search application, which means that it finds your data by content rather than by external attributes (like the file name). You specify words (terms) which should or should not appear in the text you are looking for, and receive in return a list of matching documents, ordered so that the most relevant documents will appear first.

You do not need to remember in what file or email message you stored a given piece of information. You just ask for related terms, and the tool will return a list of documents where these terms are prominent, in a similar way to Internet search engines.

Full text search applications try to determine which documents are most relevant to the search terms you provide. Computer algorithms for determining relevance can be very complex, and in general are inferior to the power of the human mind to rapidly determine relevance. The quality of relevance guessing is probably the most important aspect when evaluating a search application. Recoll relies on the Xapian probabilistic information retrieval library to determine relevance.

In many cases, you are looking for all the forms of a word, including plurals, different tenses for a verb, or terms derived from the same root or stem (example: floor, floors, floored, flooring...). Queries are usually automatically expanded to all such related terms (words that reduce to the same stem). This can be prevented for searching for a specific form.

Stemming, by itself, does not accommodate for misspellings or phonetic searches. A full text search application may also support this form of approximation. For example, a search for aliterattion returning no result might propose alliteration, alteration, alterations, or altercation as possible replacement terms. Recoll bases its suggestions on the actual index contents, so that suggestions may be made for words which would not appear in a standard dictionary.

1.3. Recoll overview

Recoll uses the Xapian information retrieval library as its storage and retrieval engine. Xapian is a very mature package using a sophisticated probabilistic ranking model.

The Xapian library manages an index database which describes where terms appear in your document files. It efficiently processes the complex queries which are produced by the Recoll query expansion mechanism, and is in charge of the all-important relevance computation task.

Recoll provides the mechanisms and interface to get data into and out of the index. This includes translating the many possible document formats into pure text, handling term variations (using Xapian stemmers), and spelling approximations (using the aspell speller), interpreting user queries and presenting results.

In a shorter way, Recoll does the dirty footwork, Xapian deals with the intelligent parts of the process.

The Xapian index can be big (roughly the size of the original document set), but it is not a document archive. Recoll can only display documents that still exist at the place from which they were indexed.

Recoll stores all internal data in Unicode UTF-8 format, and it can index many types of files with different character sets, encodings, and languages into the same index. It can process documents embedded inside other documents (for example a PDF document stored inside a Zip archive sent as an email attachment...), down to an arbitrary depth.

Stemming is the process by which Recoll reduces words to their radicals so that searching does not depend, for example, on a word being singular or plural (floor, floors), or on a verb tense (flooring, floored). Because the mechanisms used for stemming depend on the specific grammatical rules for each language, there is a separate Xapian stemmer module for most common languages where stemming makes sense.

Recoll stores the unstemmed versions of terms in the main index and uses auxiliary databases for term expansion (one for each stemming language), which means that you can switch stemming languages between searches, or add a language without needing a full reindex.

Storing documents written in different languages in the same index is possible, and commonly done. In this situation, you can specify several stemming languages for the index.

Recoll currently makes no attempt at automatic language recognition, which means that the stemmer will sometimes be applied to terms from other languages with potentially strange results. In practise, even if this introduces possibilities of confusion, this approach has been proven quite useful, and it is much less cumbersome than separating your documents according to what language they are written in.

By default, Recoll strips most accents and diacritics from terms, and converts them to lower case before either storing them in the index or searching for them. As a consequence, it is impossible to search for a particular capitalization of a term (US / us), or to discriminate two terms based on diacritics (sake / saké, mate / maté).

Recoll can optionally store the raw terms, without accent stripping or case conversion. In this configuration, default searches will behave as before, but it is possible to perform searches sensitive to case and diacritics. This is described in more detail in the section about index case and diacritics sensitivity.

Recoll uses many parameters to define exactly what to index, and how to classify and decode the source documents. These are kept in configuration files. A default configuration is copied into a standard location (usually something like /usr/share/recoll/examples) during installation. The default values set by the configuration files in this directory may be overridden by values set inside your personal configuration. With the default configuration, Recoll will index your home directory with generic parameters. Most common parameters can be set by using configuration menus in the recoll GUI. Some less common parameters can only be set by editing the text files.

The indexing process is started automatically (after asking permission), the first time you execute the recoll GUI. Indexing can also be performed by executing the recollindex command. Recoll indexing is multithreaded by default when appropriate hardware resources are available, and can perform multiple tasks in parallel for text extraction, segmentation and index updates.

Searches are usually performed inside the recoll GUI, which has many options to help you find what you are looking for. However, there are other ways to query the index:

Chapter 2. Indexing

2.1. Introduction

Indexing is the process by which the set of documents is analyzed and the data entered into the database. Recoll indexing is normally incremental: documents will only be processed if they have been modified since the last run. On the first execution, all documents will need processing. A full index build can be forced later by specifying an option to the indexing command (recollindex -z or -Z).

recollindex skips files which caused an error during a previous pass. This is a performance optimization, and the command line option -k can be set to retry failed files, for example after updating an input handler.

The following sections give an overview of different aspects of the indexing processes and configuration, with links to detailed sections.

Depending on your data, temporary files may be needed during indexing, some of them possibly quite big. You can use the RECOLL_TMPDIR or TMPDIR environment variables to determine where they are created (the default is to use /tmp). Using TMPDIR has the nice property that it may also be taken into account by auxiliary commands executed by recollindex.

2.1.1. Indexing modes

Recoll indexing can be performed along two main modes:

  • Periodic (or batch) indexingrecollindex is executed at discrete times. On Unix-like systems, the typical usage is to have a nightly run programmed into your cron file. On Windows, the Task Scheduler can be used to run indexing. In both cases, the Recoll GUI includes a simplified interface to configure the system scheduler.

  • Real time indexingrecollindex runs permanently as a daemon and uses a file system alteration monitor (e.g. inotify on Unix-like systems) to detect file changes. New or updated files are indexed at once. Monitoring a big file system tree can consume significant system resources.

Choosing an indexing mode

The choice between the two methods is mostly a matter of preference, and they can be combined by setting up multiple indexes (e.g.: use periodic indexing on a big documentation directory, and real time indexing on a small home directory), or by configuring the index so that only a subset of the tree will be monitored.

The choice of method and the parameters used can be configured from the recoll GUI: PreferencesIndexing schedule dialog.

2.1.2. Configurations, multiple indexes

Recoll supports defining multiple indexes, each defined by its own configuration directory. A configuration directory contains several files which describe what should be indexed and how.

When recoll or recollindex is first executed, it creates a default configuration directory. This configuration is the one used for indexing and querying when no specific configuration is specified. It is located in $HOME/.recoll/ for Unix-like systems and %LOCALAPPDATA%/Recoll on Windows (typically C:/Users/[me]/Appdata/Local/Recoll).

All configuration parameters have defaults, defined in system-wide files. Without further customisation, the default configuration will process your complete home directory, with a reasonable set of defaults. It can be adjusted to process a different area of the file system, select files in different ways, and many other things.

In some cases, it may be useful to create additional configuration directories, for example, to separate personal and shared indexes, or to take advantage of the organization of your data to improve search precision.

In order to do this, you would create an empty directory in a location of your choice, and then instruct recoll or recollindex to use it by setting either a command line option (-c /some/directory), or an environment variable (RECOLL_CONFDIR=/some/directory). Any modification performed by the commands (e.g. configuration customisation or searches by recoll or index creation by recollindex) would then apply to the new directory and not to the default one.

Once multiple indexes are created, you can use each of them separately by setting the -c option or the RECOLL_CONFDIR environment variable when starting a command, to select the desired index.

It is also possible to instruct one configuration to query one or several other indexes in addition to its own, by using the External index function in the recoll GUI, or some equivalent in the command line and programming tools.

A plausible usage scenario for the multiple index feature would be for a system administrator to set up a central index for shared data, that you choose to search or not in addition to your personal data. Of course, there are other possibilities. for example, there are many cases where you know the subset of files that should be searched, and where narrowing the search can improve the results. You can achieve approximately the same effect by using a directory filter clause in a search, but multiple indexes may have better performance and may be worth the trouble in some cases.

A more advanced use case would be to use multiple indexes to improve indexing performance, by updating several indexes in parallel (using multiple CPU cores and disks, or possibly several machines), and then merging them, or querying them in parallel.

See the section about configuring multiple indexes for more detail

2.1.3. Document types

Recoll knows about quite a few different document types. The parameters for document types recognition and processing are set in configuration files.

Most file types, like HTML or word processing files, only hold one document. Some file types, like email folders or zip archives, can hold many individually indexed documents, which may themselves be compound ones. Such hierarchies can go quite deep, and Recoll can process, for example, a LibreOffice document stored as an attachment to an email message inside an email folder archived in a zip file...

recollindex processes plain text, HTML, OpenDocument (Open/LibreOffice), email formats, and a few others internally.

Other file types (e.g.: postscript, pdf, ms-word, rtf ...) need external applications for preprocessing. The list is in the installation section. After every indexing operation, Recoll updates a list of commands that would be needed for indexing existing files types. This list can be displayed by selecting the menu option FileShow Missing Helpers in the recoll GUI. It is stored in the missing text file inside the configuration directory.

After installing a missing handler, you may need to tell recollindex to retry the failed files, by adding option -k to the command line, or by using the GUI FileSpecial indexing menu. This is because recollindex, in its default operation mode, will not retry files which caused an error during an earlier pass. In special cases, it may be useful to reset the data for a category of files before indexing. See the recollindex manual page. If your index is not too big, it may be simpler to just reset it.

By default, Recoll will try to index any file type that it has a way to read. This is sometimes not desirable, and there are ways to either exclude some types, or on the contrary define a positive list of types to be indexed. In the latter case, any type not in the list will be ignored.

Excluding files by name can be done by adding wildcard name patterns to the skippedNames list, which can be done from the GUI Index configuration menu. Excluding by type can be done by setting the excludedmimetypes list in the configuration file. This can be redefined for subdirectories.

You can also define an exclusive list of MIME types to be indexed (no others will be indexed), by setting the indexedmimetypes configuration variable. Example:

indexedmimetypes = text/html application/pdf

It is possible to redefine this parameter for subdirectories. Example:

[/path/to/my/dir]
indexedmimetypes = application/pdf

(When using sections like this, don't forget that they remain in effect until the end of the file or another section indicator).

excludedmimetypes or indexedmimetypes, can be set either by editing the configuration file (recoll.conf) for the index, or by using the GUI index configuration tool.

Note about MIME types

When editing the indexedmimetypes or excludedmimetypes lists, you should use the MIME values listed in the mimemap file or in Recoll result lists in preference to file -i output: there are a number of differences. The file -i output should only be used for files without extensions, or for which the extension is not listed in mimemap

2.1.4. Indexing failures

Indexing may fail for some documents, for a number of reasons: a helper program may be missing, the document may be corrupt, we may fail to uncompress a file because no file system space is available, etc.

The Recoll indexer in versions 1.21 and later does not retry failed files by default, because some indexing failures can be quite costly (for example failing to uncompress a big file because of insufficient disk space). Retrying will only occur if an explicit option (-k) is set on the recollindex command line, or if a script executed when recollindex starts up says so. The script is defined by a configuration variable (checkneedretryindexscript), and makes a rather lame attempt at deciding if a helper command may have been installed, by checking if any of the common bin directories have changed.

2.1.5. Recovery

In the rare case where the index becomes corrupted (which can signal itself by weird search results or crashes), the index files need to be erased before restarting a clean indexing pass. Just delete the xapiandb directory (see next section), or, alternatively, start the next recollindex with the -z option, which will reset the database before indexing. The difference between the two methods is that the second will not change the current index format, which may be undesirable if a newer format is supported by the Xapian version.

2.2. Index storage

The default location for the index data is the xapiandb subdirectory of the Recoll configuration directory, typically $HOME/.recoll/xapiandb/ on Unix-like systems or C:/Users/[me]/Appdata/Local/Recoll/xapiandb on Windows. This can be changed via two different methods (with different purposes):

  1. For a given configuration directory, you can specify a non-default storage location for the index by setting the dbdir parameter in the configuration file (see the configuration section). This method would mainly be of use if you wanted to keep the configuration directory in its default location, but desired another location for the index, typically out of disk occupation or performance concerns.

  2. You can specify a different configuration directory by setting the RECOLL_CONFDIR environment variable, or using the -c option to the Recoll commands. This method would typically be used to index different areas of the file system to different indexes. For example, if you were to issue the following command:

    recoll -c ~/.indexes-email

    Then Recoll would use configuration files stored in ~/.indexes-email/ and, (unless specified otherwise in recoll.conf) would look for the index in ~/.indexes-email/xapiandb/.

    Using multiple configuration directories and configuration options allows you to tailor multiple configurations and indexes to handle whatever subset of the available data you wish to make searchable.

There are quite a few more parameters which can be set in the configuration file itself for tailoring Recoll data storage. They are described in a section of the configuration chapter.

The size of the index is determined by the size of the set of documents, but the ratio can vary a lot. For a typical mixed set of documents, the index size will often be close to the data set size. In specific cases (a set of compressed mbox files for example), the index can become much bigger than the documents. It may also be much smaller if the documents contain a lot of images or other non-indexed data (an extreme example being a set of mp3 files where only the tags would be indexed).

Of course, images, sound and video do not increase the index size, which means that in most cases, the space used by the index will be negligible compared to the total amount of data on the computer.

The index data directory (xapiandb) only contains data that can be completely rebuilt by an index run (as long as the original documents exist), and it can always be destroyed safely.

2.2.1. Xapian index formats

Xapian versions usually support several formats for index storage. A given major Xapian version will have a current format, used to create new indexes, and will also support the format from the previous major version.

Xapian will not convert automatically an existing index from the older format to the newer one. If you want to upgrade to the new format, or if a very old index needs to be converted because its format is not supported any more, you will have to explicitly delete the old index (typically ~/.recoll/xapiandb), then run a normal indexing command. Using recollindex option -z would not work in this situation.

2.2.2. Security aspects

The Recoll index does not hold complete copies of the indexed documents (it almost does after version 1.24). But it does hold enough data to allow for an almost complete reconstruction. If confidential data is indexed, access to the database directory should be restricted.

Recoll will create the configuration directory with a mode of 0700 (access by owner only). As the index data directory is by default a sub-directory of the configuration directory, this should result in appropriate protection.

If you use another setup, you should think of the kind of protection you need for your index, set the directory and files access modes appropriately, and also maybe adjust the umask used during index updates.

2.2.3. Special considerations for big indexes

This only needs concern you if your index is going to be bigger than around 5 GBytes. Beyond 10 GBytes, it becomes a serious issue. Most people have much smaller indexes. For reference, 5 GBytes would be around 2000 bibles, a lot of text. If you have a huge text dataset (remember: images don't count, the text content of PDFs is typically less than 5% of the file size), read on.

The amount of writing performed by Xapian during index creation is not linear with the index size (it is somewhere between linear and quadratic). For big indexes this becomes a performance issue, and may even be an SSD disk wear issue.

The problem can be mitigated by observing the following rules:

  • Partition the data set and create several indexes of reasonable size rather than a huge one. These indexes can then be queried in parallel (using the Recoll external indexes facility), or merged using xapian-compact.

  • Have a lot of RAM available and set the idxflushmb Recoll configuration parameter as high as you can without swapping (experimentation will be needed). 200 would be a minimum in this context.

  • Use Xapian 1.4.10 or newer, as this version brought a significant improvement in the amount of writes.

2.3. Index configuration

Variables stored inside the Recoll configuration files control which areas of the file system are indexed, and how files are processed. The values can be set by editing the text files. Most of the more commonly used ones can also be adjusted by using the dialogs in the recoll GUI.

The first time you start recoll, you will be asked whether or not you would like it to build the index. If you want to adjust the configuration before indexing, just click Cancel at this point, which will get you into the configuration interface. If you exit at this point, recoll will have created a default configuration directory with empty configuration files, which you can then edit.

The configuration is documented inside the installation chapter of this document, or in the recoll.conf(5) manual page. Both documents are automatically generated from the comments inside the configuration file.

The most immediately useful variable is probably topdirs, which lists the subtrees and files to be indexed.

The applications needed to index file types other than text, HTML or email (e.g.: pdf, postscript, ms-word...) are described in the external packages section.

There are two incompatible types of Recoll indexes, depending on the treatment of character case and diacritics. A further section describes the two types in more detail. The default type is appropriate in most cases.

2.3.1. The index configuration GUI

Most index configuration parameters can be set from the recoll GUI (set RECOLL_CONFDIR or use the -c option to affect a non-default index.)

The interface is started from the PreferencesIndex Configuration menu entry. It is divided in four tabs, Global parameters, Local parameters, Web history (details) and Search parameters.

The Global parameters tab allows setting global variables, like the lists of top/start directories, skipped paths, or stemming languages.

The Local parameters tab allows setting variables that can be redefined for subdirectories. This second tab has an initially empty list of customisation directories, to which you can add. The variables are then set for the currently selected directory (or at the top level if the empty line is selected).

The Search parameters section defines parameters which are used at query time, but are global to an index and affect all search tools, not only the GUI.

The meaning for most entries in the interface is self-evident and documented by a ToolTip popup on the text label. For more detail, you may need to refer to the configuration section of this guide.

The configuration tool normally respects the comments and most of the formatting inside the configuration file, so that it is quite possible to use it on hand-edited files, which you might nevertheless want to backup first...

2.3.2. Multiple indexes

Multiple Recoll indexes can be created by using several configuration directories which would typically be set to index different areas of the file system.

A specific configuration can be selected by setting the RECOLL_CONFDIR environment variable or giving the -c option to recoll and recollindex.

The recollindex program, used for creating or updating indexes, always works on a single index. The different configurations are entirely independent (no parameters are ever shared between configurations when indexing).

All the search interfaces (recoll, recollq, the Python API, etc.) operate with a main configuration, from which both configuration and index data are used, and can also query data from multiple additional indexes. Only the index data from additional indexes is used, their configuration parameters are ignored. This implies that some parameters should be consistent among index configurations which are to be used together.

When searching, the current main index (defined by RECOLL_CONFDIR or -c) is always active. If this is undesirable, you can set up your base configuration to index an empty directory.

Index configuration parameters can be set either by using a text editor on the files, or, for most parameters, by using the recoll index configuration GUI. In the latter case, the configuration directory for which parameters are modified is the one which was selected by RECOLL_CONFDIR or the -c parameter, and there is no way to switch configurations within the GUI.

See the configuration section for a detailed description of the parameters

Some configuration parameters must be consistent among a set of multiple indexes used together for searches. Most importantly, all indexes to be queried concurrently must have the same option concerning character case and diacritics stripping, but there are other constraints. Most of the relevant parameters affect the term generation.

Using multiple configurations implies a small level of command line or file manager usage. The user must explicitly create additional configuration directories, the GUI will not do it. This is to avoid mistakenly creating additional directories when an argument is mistyped. Also, the GUI or the indexer must be launched with a specific option or environment to work on the right configuration.

Creating and using an additional index: example

The following applies to Unix-like systems

Initially creating the configuration and index:

              mkdir /path/to/my/new/config

Configuring the new index can be done from the recoll GUI, launched from the command line to pass the -c option (you could create a desktop file to do it for you), and then using the GUI index configuration tool to set up the index.

              recoll -c /path/to/my/new/config

Alternatively, you can just start a text editor on the main configuration file:

              someEditor /path/to/my/new/config/recoll.conf
            

Creating and updating the index can be done from the command line:

recollindex -c /path/to/my/new/config
            

or from the File menu of a GUI launched with the same option (recoll, see above).

The same GUI would also let you set up batch indexing for the new index. Real time indexing can only be set up from the GUI for the default index (the menu entry will be inactive if the GUI was started with a non-default -c option).

The new index can be queried alone with:

              recoll -c /path/to/my/new/config

Or, in parallel with the default index, by starting recoll without a -c option, and using the External Indexes tab in the preferences dialog, which can be reached either trough: PreferencesGUI ConfigurationExternal Index Dialog or QueryExternal index dialog. See the GUI external indexes section for more details.

2.3.3. Index case and diacritics sensitivity

As of Recoll version 1.18 you have a choice of building an index with terms stripped of character case and diacritics, or one with raw terms. For a source term of Résumé, the former will store resume, the latter Résumé.

Each type of index allows performing searches insensitive to case and diacritics: with a raw index, the user entry will be expanded to match all case and diacritics variations present in the index. With a stripped index, the search term will be stripped before searching.

A raw index allows using case and diacritics to discriminate between terms, e.g., returning different results when searching for US and us or resume and résumé. Read the section about search case and diacritics sensitivity for more details.

The type of index to be created is controlled by the indexStripChars configuration variable which can only be changed by editing the configuration file. Any change implies an index reset (not automated by Recoll), and all indexes in a search must be set in the same way (again, not checked by Recoll).

Recoll creates a stripped index by default if indexStripChars is not set.

As a cost for added capability, a raw index will be slightly bigger than a stripped one (around 10%). Also, searches will be more complex, so probably slightly slower, and the feature is relatively little used, so that a certain amount of weirdness cannot be excluded.

One of the most adverse consequence of using a raw index is that some phrase and proximity searches may become impossible: because each term needs to be expanded, and all combinations searched for, the multiplicative expansion may become unmanageable.

2.3.4. Indexing threads configuration (Unix-like systems)

Note: you don't probably don't need to read this. The default automatic configuration is fine is most cases. Only the part about disabling multithreading may be more commonly useful, so I'll prepend it here. In recoll.conf:

            thrQSizes = -1 -1 -1
          

The Recoll indexing process recollindex can use multiple threads to speed up indexing on multiprocessor systems. The work done to index files is divided in several stages and some of the stages can be executed by multiple threads. The stages are:

  1. File system walking: this is always performed by the main thread.

  2. File conversion and data extraction.

  3. Text processing (splitting, stemming, etc.).

  4. Xapian index update.

You can also read a longer document about the transformation of Recoll indexing to multithreading.

The threads configuration is controlled by two configuration file parameters.

thrQSizes

This variable defines the job input queues configuration. There are three possible queues for stages 2, 3 and 4, and this parameter should give the queue depth for each stage (three integer values). If a value of -1 is used for a given stage, no queue is used, and the thread will go on performing the next stage. In practise, deep queues have not been shown to increase performance. A value of 0 for the first queue tells Recoll to perform autoconfiguration (no need for anything else in this case, thrTCounts is not used) - this is the default configuration.

thrTCounts

This defines the number of threads used for each stage. If a value of -1 is used for one of the queue depths, the corresponding thread count is ignored. It makes no sense to use a value other than 1 for the last stage because updating the Xapian index is necessarily single-threaded (and protected by a mutex).

Note

If the first value in thrQSizes is 0, thrTCounts is ignored.

The following example would use three queues (of depth 2), and 4 threads for converting source documents, 2 for processing their text, and one to update the index. This was tested to be the best configuration on the test system (quadri-processor with multiple disks).

            thrQSizes = 2 2 2
            thrTCounts =  4 2 1
          

The following example would use a single queue, and the complete processing for each document would be performed by a single thread (several documents will still be processed in parallel in most cases). The threads will use mutual exclusion when entering the index update stage. In practise the performance would be close to the precedent case in general, but worse in certain cases (e.g. a Zip archive would be performed purely sequentially), so the previous approach is preferred. YMMV... The 2 last values for thrTCounts are ignored.

            thrQSizes = 2 -1 -1
            thrTCounts =  6 1 1
          

The following example would disable multithreading. Indexing will be performed by a single thread.

thrQSizes = -1 -1 -1

2.4. Index update scheduling

2.4.1. Periodic indexing

Running the indexer

The recollindex program performs index updates. You can start it either from the command line or from the File menu in the recoll GUI program. When started from the GUI, the indexing will run on the same configuration recoll was started on. When started from the command line, recollindex will use the RECOLL_CONFDIR variable or accept a -c confdir option to specify a non-default configuration directory.

If the recoll program finds no index when it starts, it will automatically start indexing (except if canceled).

The GUI File menu has entries to start or stop the current indexing operation. When indexing is not currently running, you have a choice between Update Index or Rebuild Index. The first choice only processes changed files, the second one erases the index before starting so that all files are processed.

On Linux and Windows, the GUI can be used to manage the indexing operation. Stopping the indexer can be done from the recoll GUI FileStop Indexing menu entry.

On Linux, the recollindex indexing process can be interrupted by sending an interrupt (Ctrl-C, SIGINT) or terminate (SIGTERM) signal.

When stopped, some time may elapse before recollindex exits, because it needs to properly flush and close the index.

After an interruption, the index will be somewhat inconsistent because some operations which are normally performed at the end of the indexing pass will have been skipped (for example, the stemming and spelling databases will be inexistent or out of date). You just need to restart indexing at a later time to restore consistency. The indexing will restart at the interruption point (the full file tree will be traversed, but files that were indexed up to the interruption and for which the index is still up to date will not need to be reindexed).

recollindex command line

recollindex has many options which are listed in its manual page. Only a few will be described here.

Option -z will reset the index when starting. This is almost the same as destroying the index files (the nuance is that the Xapian format version will not be changed).

Option -Z will force the update of all documents without resetting the index first. This will not have the "clean start" aspect of -z, but the advantage is that the index will remain available for querying while it is rebuilt, which can be a significant advantage if it is very big (some installations need days for a full index rebuild).

Option -k will force retrying files which previously failed to be indexed, for example because of a missing helper program.

Of special interest also, maybe, are the -i and -f options. -i allows indexing an explicit list of files (given as command line parameters or read on stdin). -f tells recollindex to ignore file selection parameters from the configuration. Together, these options allow building a custom file selection process for some area of the file system, by adding the top directory to the skippedPaths list and using an appropriate file selection method to build the file list to be fed to recollindex -if. Trivial example:

    find . -name indexable.txt -print | recollindex -if

recollindex -i will not descend into subdirectories specified as parameters, but just add them as index entries. It is up to the external file selection method to build the complete file list.

Linux: using cron to automate indexing

The most common way to set up indexing is to have a cron task execute it every night. For example the following crontab entry would do it every day at 3:30AM (supposing recollindex is in your PATH):

30 3 * * * recollindex > /some/tmp/dir/recolltrace 2>&1

Or, using anacron:

1  15  su mylogin -c "recollindex recollindex > /tmp/rcltraceme 2>&1"

The Recoll GUI has dialogs to manage crontab entries for recollindex. You can reach them from the PreferencesIndexing Schedule menu. They only work with the good old cron, and do not give access to all features of cron scheduling. Entries created via the tool are marked with a RCLCRON_RCLINDEX= marker so that the tool knows which entries belong to it. As a side effect, this sets an environment variable for the process, but it's not actually used, this is just a marker.

The usual command to edit your crontab is crontab -e (which will usually start the vi editor to edit the file). You may have more sophisticated tools available on your system.

Please be aware that there may be differences between your usual interactive command line environment and the one seen by crontab commands. Especially the PATH variable may be of concern. Please check the crontab manual pages about possible issues.

2.4.2. Real time indexing

Real time monitoring/indexing is performed by starting the recollindex -m command. With this option, recollindex will permanently monitor file changes and update the index.

On Windows systems, the monitoring process is started from the recoll GUI File menu. On Unix-like systems, there are other possibilities, see the following sections.

When this is in use, the recoll GUI File menu makes two operations available: Stop and Trigger incremental pass.

Trigger incremental pass has the same effect as restarting the indexer, and will cause a complete walk of the indexed area, processing the changed files, then switch to monitoring. This is only marginally useful, maybe in cases where the indexer is configured to delay updates, or to force an immediate rebuild of the stemming and phonetic data, which are only processed at intervals by the real time indexer.

While it is convenient that data is indexed in real time, repeated indexing can generate a significant load on the system when files such as email folders change. Also, monitoring large file trees by itself significantly taxes system resources. You probably do not want to enable it if your system is short on resources. Periodic indexing is adequate in most cases.

As of Recoll 1.24, you can set the monitordirs configuration variable to specify that only a subset of your indexed files will be monitored for instant indexing. In this situation, an incremental pass on the full tree can be triggered by either restarting the indexer, or just running recollindex, which will notify the running process. The recoll GUI also has a menu entry for this.

Unix-like systems: automatic daemon start with systemd

The installation contains two example files (in share/recoll/examples) for starting the indexing daemon with systemd.

recollindex.service would be used for starting recollindex as a user service. The indexer will start when the user logs in and run while there is a session open for them.

recollindex@.service is a template service which would be used for starting the indexer at boot time, running as a specific user. It can be useful when running the text search as a shared service (e.g. when users access it through the WEB UI).

If configured to do so, the unit files should have been installed in your system's default systemd paths (usually /usr/lib/systemd/system/ and /usr/lib/systemd/user/). If not, you may need to copy the files there before starting the service.

With the unit files installed in the proper location, the user unit can be started with the following commands:

systemctl --user daemon-reload
systemctl --user enable --now recollindex.service

The system unit file can be enabled for a particular user by running, as root:

systemctl daemon-reload
systemctl enable --now recollindex@username.service

(A valid user name should be substituted for username, of course.)

Unix-like systems: automatic daemon start from the desktop session

Under KDE, Gnome and some other desktop environments, the daemon can automatically started when you log in, by creating a desktop file inside the ~/.config/autostart directory. This can be done for you by the Recoll GUI. Use the Preferences->Indexing Schedule menu.

With older X11 setups, starting the daemon is normally performed as part of the user session script.

The rclmon.sh script can be used to easily start and stop the daemon. It can be found in the examples directory (typically /usr/local/[share/]recoll/examples).

For example, a good old xdm-based session could have a .xsession script with the following lines at the end:

recollconf=$HOME/.recoll-home
recolldata=/usr/local/share/recoll
RECOLL_CONFDIR=$recollconf $recolldata/examples/rclmon.sh start

fvwm

The indexing daemon gets started, then the window manager, for which the session waits.

By default the indexing daemon will monitor the state of the X11 session, and exit when it finishes, it is not necessary to kill it explicitly. (The X11 server monitoring can be disabled with option -x to recollindex).

If you use the daemon completely out of an X11 session, you need to add option -x to disable X11 session monitoring (else the daemon will not start).

Miscellaneous details

Logging. By default, the messages from the indexing daemon will be sent to the same file as those from the interactive commands (logfilename). You may want to change this by setting the daemlogfilename and daemloglevel configuration parameters. Also the log file will only be truncated when the daemon starts. If the daemon runs permanently, the log file may grow quite big, depending on the log level.

Unix-like systems: increasing resources for inotify. On Linux systems, monitoring a big tree may need increasing the resources available to inotify, which are normally defined in /etc/sysctl.conf.

### inotify
#
# cat  /proc/sys/fs/inotify/max_queued_events   - 16384
# cat  /proc/sys/fs/inotify/max_user_instances  - 128
# cat  /proc/sys/fs/inotify/max_user_watches    - 16384
#
# -- Change to:
#
fs.inotify.max_queued_events=32768
fs.inotify.max_user_instances=256
fs.inotify.max_user_watches=32768

Especially, you will need to trim your tree or adjust the max_user_watches value if indexing exits with a message about errno ENOSPC (28) from inotify_add_watch.

Slowing down the reindexing rate for fast changing files. When using the real time monitor, it may happen that some files need to be indexed, but change so often that they impose an excessive load for the system.Recoll provides a configuration option to specify the minimum time before which a file, specified by a wildcard pattern, cannot be reindexed. See the mondelaypatterns parameter in the configuration section.

2.5. Miscellaneous indexing notes

2.5.1. The PDF input handler

The PDF format is very important for scientific and technical documentation, and document archival. It has extensive facilities for storing metadata along with the document, and these facilities are actually used in the real world.

In consequence, the rclpdf.py PDF input handler has more complex capabilities than most others, and it is also more configurable. Specifically, rclpdf.py has the following features:

  • It can be configured to extract specific metadata tags from an XMP packet.

  • It can extract PDF attachments.

  • It can automatically perform OCR if the document text is empty. This is done by executing an external program and is now described in a separate section, because the OCR framework can also be used with non-PDF image files.

XMP fields extraction

The rclpdf.py script in Recoll version 1.23.2 and later can extract XMP metadata fields by executing the pdfinfo command (usually found with poppler-utils). This is controlled by the pdfextrameta configuration variable, which specifies which tags to extract and, possibly, how to rename them.

The pdfextrametafix variable can be used to designate a file with Python code to edit the metadata fields (available for Recoll 1.23.3 and later. 1.23.2 has equivalent code inside the handler script). Example:

import sys
import re

class MetaFixer(object):
def __init__(self):
pass

def metafix(self, nm, txt):
if nm == 'bibtex:pages':
txt = re.sub(r'--', '-', txt)
elif nm == 'someothername':
# do something else
pass
elif nm == 'stillanother':
# etc.
pass

return txt
def wrapup(self, metaheaders):
pass

If the 'metafix()' method is defined, it is called for each metadata field. A new MetaFixer object is created for each PDF document (so the object can keep state for, for example, eliminating duplicate values). If the 'wrapup()' method is defined, it is called at the end of XMP fields processing with the whole metadata as parameter, as an array of '(nm, val)' pairs, allowing an alternate approach for editing or adding/deleting fields.

See this page for a more detailed discussion about indexing PDF XMP properties.

PDF attachment indexing

If pdftk is installed, and if the the pdfattach configuration variable is set, the PDF input handler will try to extract PDF attachments for indexing as sub-documents of the PDF file. This is disabled by default, because it slows down PDF indexing a bit even if not one attachment is ever found (PDF attachments are uncommon in my experience).

2.5.2. Running OCR on image documents

The Recoll PDF handler has the ability to call an external OCR program if the processed file has no text content. The OCR data is stored in a cache of separate files, avoiding any modification of the originals.

It must be noted that, if modifying the files (or a copy) is acceptable, then running something like OCRmyPDF to add a text layer to the PDF itself is a better solution (e.g. allowing Recoll to position the PDF viewer on the search target when opening the document, and permitting secondary search in the native tool).

To enable the Recoll OCR feature, you need to install one of the supported OCR applications (tesseract or ABBYY), enable OCR in the PDF handler (by setting pdfocr to 1 in the index configuration file), and tell Recoll how to run the OCR by setting configuration variables. All parameters can be localized in subdirectories through the usual main configuration mechanism (path sections).

Example configuration fragment in recoll.conf:

pdfocr = 1
ocrprogs = tesseract
pdfocrlang = eng

This facility got a major update in Recoll 1.26.5. Older versions had a more limited, non-caching capability to execute an external OCR program in the PDF handler. The new function has the following features:

  • The OCR output is cached, stored as separate files. The caching is ultimately based on a hash value of the original file contents, so that it is immune to file renames. A first path-based layer ensures fast operation for unchanged (unmoved files), and the data hash (which is still orders of magnitude faster than OCR) is only re-computed if the file has moved. OCR is only performed if the file was not previously processed or if it changed.

  • The support for a specific program is implemented in a simple Python module. It should be straightforward to add support for any OCR engine with a capability to run from the command line.

  • Modules initially exist for tesseract (Linux and Windows), and ABBYY FineReader (Linux, tested with version 11). ABBYY FineReader is a commercial closed source program, but it sometimes perform better than tesseract.

  • The OCR is currently only called from the PDF handler, but there should be no problem using it for other image types.

2.5.3. Running a speech to text program on audio files

If the OpenAI Whisper program is available and the appropriate parameters set in the configuration files, the Recoll audio file handler will run speech to text recognition on audio files and the resulting text will be indexed. See the the FAQ entry for more details.

The results of the speech recognition will be cached in the same manner as the results of image OCR.

2.5.4. Removable volumes

Recoll used to have no support for indexing removable volumes (portable disks, USB keys, etc.). Recent versions have improved the situation and support indexing removable volumes in two different ways:

  • By indexing the volume in the main, fixed, index, and ensuring that the volume data is not purged if the indexing runs while the volume is mounted. (since Recoll 1.25.2).

  • By storing a volume index on the volume itself (since Recoll 1.24).

Indexing removable volumes in the main index

As of version 1.25.2, Recoll provides a simple way to ensure that the index data for an absent volume will not be purged. Two conditions must be met:

  • The volume mount point must be a member of the topdirs list.

  • The mount directory must be empty (when the volume is not mounted).

If recollindex finds that one of the topdirs is empty when starting up, any existing data for the tree will be preserved by the indexing pass (no purge for this area).

Self contained volumes

As of Recoll 1.24, it has become possible to build self-contained datasets including a Recoll configuration directory and index together with the indexed documents, and to move such a dataset around (for example copying it to an USB drive), without having to adjust the configuration for querying the index.

Note

This is a query-time feature only. The index must only be updated in its original location. If an update is necessary in a different location, the index must be reset.

The principle of operation is that the configuration stores the location of the original configuration directory, which must reside on the movable volume. If the volume is later mounted elsewhere, Recoll adjusts the paths stored inside the index by the difference between the original and current locations of the configuration directory.

To make a long story short, here follows a script to create a Recoll configuration and index under a given directory (given as single parameter). The resulting data set (files + recoll directory) can later to be moved to a CDROM or thumb drive. Longer explanations come after the script.

#!/bin/sh

fatal()
{
echo $*;exit 1
}
usage()
{
fatal "Usage: init-recoll-volume.sh <top-directory>"
}

test $# = 1 || usage
topdir=$1
test -d "$topdir" || fatal $topdir should be a directory

confdir="$topdir/recoll-config"
test ! -d "$confdir" || fatal $confdir should not exist

mkdir "$confdir"
cd "$topdir"
topdir=`pwd`
cd "$confdir"
confdir=`pwd`

(echo topdirs = '"'$topdir'"'; \
echo orgidxconfdir = $topdir/recoll-config) > "$confdir/recoll.conf"

recollindex -c "$confdir"

The examples below will assume that you have a dataset under /home/me/mydata/, with the index configuration and data stored inside /home/me/mydata/recoll-confdir.

In order to be able to run queries after the dataset has been moved, you must ensure the following:

  • The main configuration file must define the orgidxconfdir variable to be the original location of the configuration directory (orgidxconfdir=/home/me/mydata/recoll-confdir must be set inside /home/me/mydata/recoll-confdir/recoll.conf in the example above).

  • The configuration directory must exist with the documents, somewhere under the directory which will be moved. E.g. if you are moving /home/me/mydata around, the configuration directory must exist somewhere below this point, for example /home/me/mydata/recoll-confdir, or /home/me/mydata/sub/recoll-confdir.

  • You should keep the default locations for the index elements which are relative to the configuration directory by default (principally dbdir). Only the paths referring to the documents themselves (e.g. topdirs values) should be absolute (in general, they are only used when indexing anyway).

Only the first point needs an explicit user action, the Recoll defaults are compatible with the third one, and the second is natural.

If, after the move, the configuration directory needs to be copied out of the dataset (for example because the thumb drive is too slow), you can set the curidxconfdir, variable inside the copied configuration to define the location of the moved one. For example if /home/me/mydata is now mounted onto /media/me/somelabel, but the configuration directory and index has been copied to /tmp/tempconfig, you would set curidxconfdir to /media/me/somelabel/recoll-confdir inside /tmp/tempconfig/recoll.conf. orgidxconfdir would still be /home/me/mydata/recoll-confdir in the original and the copy.

If you are regularly copying the configuration out of the dataset, it will be useful to write a script to automate the procedure. This can't really be done inside Recoll because there are probably many possible variants. One example would be to copy the configuration to make it writable, but keep the index data on the medium because it is too big - in this case, the script would also need to set dbdir in the copied configuration.

The same set of modifications (Recoll 1.24) has also made it possible to run queries from a readonly configuration directory (with slightly reduced function of course, such as not recording the query history).

2.5.5. Unix-like systems: indexing visited Web pages

With the help of a Firefox extension, Recoll can index the Internet pages that you visit. The extension has a long history: it was initially designed for the Beagle indexer, then adapted to Recoll and the Firefox XUL API. The current version of the extension is located in the Mozilla add-ons repository uses the WebExtensions API, and works with current Firefox versions.

The extension works by copying visited Web pages to an indexing queue directory, which Recoll then processes, storing the data into a local cache, then indexing it, then removing the file from the queue.

The local cache is not an archive

As mentioned above, a copy of the indexed Web pages is retained by Recoll in a local cache (from which data is fetched for previews, or when resetting the index). The cache is not changed by an index reset, just read for indexing. The cache has a maximum size, which can be adjusted from the Index configuration / Web history panel (webcachemaxmbs parameter in recoll.conf). Once the maximum size is reached, old pages are erased to make room for new ones. The pages which you want to keep indefinitely need to be explicitly archived elsewhere. Using a very high value for the cache size can avoid data erasure, but see the above 'Howto' page for more details and gotchas.

The visited Web pages indexing feature can be enabled on the Recoll side from the GUI Index configuration panel, or by editing the configuration file (set processwebqueue to 1).

The Recoll GUI has a tool to list and edit the contents of the Web cache. (ToolsWebcache editor)

The recollindex command has two options to help manage the Web cache:

  • --webcache-compact will recover the space from erased entries. It may need to use twice the disk space currently needed for the Web cache.
  • --webcache-burst destdir will extract all current entries into pairs of metadata and data files created inside destdir

You can find more details on Web indexing, its usage and configuration in a Recoll 'Howto' entry.

2.5.6. Unix-like systems: using extended attributes

User extended attributes are named pieces of information that most modern file systems can attach to any file.

Recoll processes all extended attributes as document fields. Note that most fields are not indexed by default, you need to activate them by defining a prefix in the fields configuration file.

A freedesktop standard defines a few special attributes, which are handled as such by Recoll:

mime_type

If set, this overrides any other determination of the file MIME type.

charset

If set, this defines the file character set (mostly useful for plain text files).

By default, other attributes are handled as Recoll fields of the same name.

On Linux, the user prefix is removed from the name.

The name translation can be configured more precisely, also inside the fields configuration file.

Setting the document modification/creation date

Date fields are processed specially by Recoll. For obscure and uninteresting reasons, you should use modificationdate as extended attribute name for setting this value. Also, the date string should be an ASCII integer representing the Unix time (seconds since the epoch). An example Linux command line for setting this particular field follow. The substituted date prints the example date parameter in Unix time format (seconds since the epoch).

              setfattr -n user.modificationdate -v `date -d '2022-09-30 08:30:00' +%s` /some/file
            
The date substitution will then be automatic, you do not need to customize the fields file.

2.5.7. Unix-like systems: importing external tags

During indexing, it is possible to import metadata for each file by executing commands. This allows, for example, extracting tag data from an external application and storing it in a field for indexing.

See the section about the metadatacmds field in the main configuration chapter for a description of the configuration syntax.

For example, if you would want Recoll to use tags managed by tmsu in a field named tags, you would add the following to the configuration file:

[/some/area/of/the/fs]
          metadatacmds = ; tags = tmsu tags %f
        

Note

Depending on the tmsu version, you may need/want to add options like --database=/some/db.

You may want to restrict this processing to a subset of the directory tree, because it may slow down indexing a bit ([some/area/of/the/fs]).

Note the initial semi-colon after the equal sign.

In the example above, the output of tmsu is used to set a field named tags. The field name is arbitrary and could be tmsu or myfield just the same, but tags is an alias for the standard Recoll keywords field, and the tmsu output will just augment its contents. This will avoid the need to extend the field configuration.

Once re-indexing is performed (you will need to force the file reindexing, Recoll will not detect the need by itself), you will be able to search from the query language, through any of its aliases: tags:some/alternate/values or tags:all,these,values.

Tags changes will not be detected by the indexer if the file itself did not change. One possible workaround would be to update the file ctime when you modify the tags, which would be consistent with how extended attributes function. A pair of chmod commands could accomplish this, or a touch -a. Alternatively, just couple the tag update with a recollindex -e -i /path/to/the/file.

Chapter 3. Searching

3.1. Introduction

Getting answers to specific queries is of course the whole point of Recoll. The multiple provided interfaces always understand simple queries made of one or several words, and return appropriate results in most cases.

In order to make the most of Recoll though, it may be worthwhile to understand how it processes your input. Five different modes exist:

  • In All Terms mode, Recoll looks for documents containing all your input terms.

  • The Query Language mode behaves like All Terms in the absence of special input, but it can also do much more. This is the best mode for getting the most of Recoll. It is usable from all possible interfaces (GUI, command line, WEB UI, ...), and is described here.

  • In Any Term mode, Recoll looks for documents containing any your input terms, preferring those which contain more.

  • In File Name mode, Recoll will only match file names, not content. Using a small subset of the index allows things like left-hand wildcards without performance issues, and may sometimes be useful.

  • The GUI Advanced Search mode is actually not more powerful than the query language, but it helps you build complex queries without having to remember the language, and avoids any interpretation ambiguity, as it bypasses the user input parser.

These five input modes are supported by the different user interfaces which are described in the following sections.

3.2. Searching with the Qt graphical user interface

The recoll program provides the main user interface for searching. It is based on the Qt library.

recoll has two search interfaces:

  • Simple search (the default, on the main screen) has a single entry field where you can enter multiple words or a query language query.

  • Advanced search (a panel accessed through the Tools menu or the toolbox bar icon) has multiple entry fields, which you may use to build a logical condition, with additional filtering on file type, location in the file system, modification date, and size.

The Advanced Search tool is easier to use, but not actually more powerful, than the Simple Search in query language mode. Its name is historical, but Assisted Search would probably have been a better designation.

In most text areas, you can enter the terms as you think them, even if they contain embedded punctuation or other non-textual characters (e.g. Recoll can handle things like email addresses).

The main case where you should enter text differently from how it is printed is for east-asian languages (Chinese, Japanese, Korean). Words composed of single or multiple characters should be entered separated by white space in this case (they would typically be printed without white space).

Some searches can be quite complex, and you may want to re-use them later, perhaps with some tweaking. Recoll can save and restore searches. See Saving and restoring queries.

3.2.1. Simple search

  1. Start the recoll program.

  2. Possibly choose a search mode: Any term, All terms, File name or Query language.

  3. Enter search term(s) in the text field at the top of the window.

  4. Click the Search button or hit the Enter key to start the search.

The initial default search mode is Query language. Without special directives, this will look for documents containing all of the search terms (the ones with more terms will get better scores), just like the All Terms mode.

Any term will search for documents where at least one of the terms appear.

File name will exclusively look for file names, not contents

All search modes allow terms to be expanded with wildcards characters (*, ?, []). See the section about wildcards for more details.

In all modes except File name, you can search for exact phrases (adjacent words in a given order) by enclosing the input inside double quotes. Ex: "virtual reality".

The Query Language features are described in a separate section.

When using a stripped index (the default), character case has no influence on search, except that you can disable stem expansion for any term by capitalizing it. E.g.: a search for floor will also normally look for flooring, floored, etc., but a search for Floor will only look for floor, in any character case. Stemming can also be disabled globally in the preferences. When using a raw index, the rules are a bit more complicated.

Recoll remembers the last few searches that you performed. You can directly access the search history by clicking the clock button on the right of the search entry, while the latter is empty. Otherwise, the history is used for entry completion (see next). Only the search texts are remembered, not the mode (all/any/file name).

While text is entered in the search area, recoll will display possible completions, filtered from the history and the index search terms. This can be disabled with a GUI Preferences option.

Double-clicking on a word in the result list or a preview window will insert it into the simple search entry field.

You can cut and paste any text into an All terms or Any term search field, punctuation, newlines and all - except for wildcard characters (single ? characters are ok). Recoll will process it and produce a meaningful search. This is what most differentiates this mode from the Query Language mode, where you have to care about the syntax.

The File name search mode will specifically look for file names. The point of having a separate file name search is that wildcard expansion can be performed more efficiently on a small subset of the index (allowing wildcards on the left of terms without excessive cost). Things to know:

  • White space in the entry should match white space in the file name, and is not treated specially.

  • The search is insensitive to character case and accents, independently of the type of index.

  • An entry without any wildcard character and not capitalized will be prepended and appended with '*' (e.g.: etc -> *etc*, but Etc -> etc).

  • If you have a big index (many files), excessively generic fragments may result in inefficient searches.

3.2.2. The result list

After starting a search, a list of results will instantly be displayed in the main window.

By default, the document list is presented in order of relevance (how well the application estimates that the document matches the query). You can sort the results by ascending or descending date by using the vertical arrows in the toolbar.

Each result is displayed as a structured text paragraph. The standard format is typically adequate, but the content and presentation are entirely customisable.

Most results will contain Preview and Open clickable links.

Clicking the Preview link will open an internal preview window for the document. Further Preview clicks for the same search will open tabs in the existing preview window. You can use Shift+Click to force the creation of another preview window, which may be useful to view the documents side by side. (You can also browse successive results in a single preview window by typing Shift+ArrowUp/Down in the window).

Clicking the Open link will start an external viewer for the document. By default, Recoll lets the desktop choose the appropriate application for most document types. See further for customizing the applications.

The Preview and Open links may not be present for all entries. They are only available, respectively, for documents with MIME types that Recoll can extract text from, and for documents that have a configured viewer. However, you can modify the configuration to adjust this behavior. In more detail:

  • The Preview link will appear for documents with a MIME type present in the [index] section of the mimeconf file, and, only if the textunknownasplain configuration variable is set, for all types identified as a subtype of text (text/*).

  • The Open link will appear for documents with a MIME type present in the [view] section of the mimeview configuration file. If textunknownasplain is set and no specific viewer is found for a subtype of text, the viewer for text/plain will be used.

You can click on the Query details link at the top of the results page to see the actual Xapian query, after stem expansion and other processing.

Double-clicking on any word inside the result list or a preview window will insert it into the simple search text.

The result list is divided into pages. You can change the page size in the preferences. Use the arrow buttons in the toolbar or the links at the bottom of the page to browse the results.

Customising the viewers

By default Recoll lets the desktop choose what application should be used to open a given document, with exceptions.

The details of this behaviour can be customized with the PreferencesGUI configurationUser interfaceChoose editor applications dialog or by editing the mimeview configuration file.

When Use desktop preferences, at the top of the dialog, is checked, the desktop default is generally used, but there is a small default list of exceptions, for MIME types where the Recoll choice should override the desktop one. These are applications which are well integrated with Recoll, for example, on Linux, evince for viewing PDF and Postscript files because of its support for opening the document at a specific page and passing a search string as an argument. You can add or remove document types to the exceptions by using the dialog.

If you prefer to completely customize the choice of applications, you can uncheck Use desktop preferences, in which case the Recoll predefined applications will be used, and can be changed for each document type. This is probably not the most convenient approach in most cases.

In all cases, the applications choice dialog accepts multiple selections of MIME types in the top section, and lets you define how they are processed in the bottom one. In most cases, you will be using %f as a place holder to be replaced by the file name in the application command line.

You may also change the choice of applications by editing the mimeview configuration file if you find this more convenient.

Under Unix-like systems, each result list entry also has a right-click menu with an Open With entry. This lets you choose an application from the list of those which registered with the desktop for the document MIME type, on a case by case basis.

No results: the spelling suggestions

When a search yields no result, and if the aspell dictionary is configured, Recoll will try to check for misspellings among the query terms, and will propose lists of replacements. Clicking on one of the suggestions will replace the word and restart the search. You can hold any of the modifier keys (Ctrl, Shift, etc.) while clicking if you would rather stay on the suggestion screen because several terms need replacement.

The result list right-click menu

Apart from the preview and edit links, you can display a pop-up menu by right-clicking over a paragraph in the result list. This menu has the following entries:

  • Preview

  • Open

  • Open With

  • Run Script

  • Copy File Name

  • Copy Url

  • Save to File

  • Find similar

  • Preview Parent document

  • Open Parent document

  • Open Snippets Window

The Preview and Open entries do the same thing as the corresponding links.

Open With (Unix-like systems) lets you open the document with one of the applications claiming to be able to handle its MIME type (the information comes from the .desktop files in /usr/share/applications).

Run Script allows starting an arbitrary command on the result file. It will only appear for results which are top-level files. See further for a more detailed description.

The Copy File Name and Copy Url copy the relevant data to the clipboard, for later pasting.

Save to File allows saving the contents of a result document to a chosen file. This entry will only appear if the document does not correspond to an existing file, but is a subdocument inside such a file (e.g.: an email attachment). It is especially useful to extract attachments with no associated editor.

The Open/Preview Parent document entries allow working with the higher level document (e.g. the email message an attachment comes from). Recoll is sometimes not totally accurate as to what it can or can't do in this area. For example the Parent entry will also appear for an email which is part of an mbox folder file, but you can't actually visualize the mbox (there will be an error dialog if you try).

If the document is a top-level file, Open Parent will start the default file manager on the enclosing filesystem directory.

The Find similar entry will select a number of relevant term from the current document and enter them into the simple search field. You can then start a simple search, with a good chance of finding documents related to the current result. I can't remember a single instance where this function was actually useful to me...

The Open Snippets Window entry will only appear for documents which support page breaks (typically PDF, Postscript, DVI). The snippets window lists extracts from the document, taken around search terms occurrences, along with the corresponding page number, as links which can be used to start the native viewer on the appropriate page. If the viewer supports it, its search function will also be primed with one of the search terms.

3.2.3. The result table

As an alternative to the result list, the results can also be displayed in spreadsheet-like fashion. You can switch to this presentation by clicking the table-like icon in the toolbar (this is a toggle, click again to restore the list).

Clicking on the column headers will allow sorting by the values in the column. You can click again to invert the order, and use the header right-click menu to reset sorting to the default relevance order (you can also use the sort-by-date arrows to do this).

Both the list and the table display the same underlying results. The sort order set from the table is still active if you switch back to the list mode. You can click twice on a date sort arrow to reset it from there.

The header right-click menu allows adding or deleting columns. The columns can be resized, and their order can be changed (by dragging). All the changes are recorded when you quit recoll

Hovering over a table row will update the detail area at the bottom of the window with the corresponding values. You can click the row to freeze the display. The bottom area is equivalent to a result list paragraph, with links for starting a preview or a native application, and an equivalent right-click menu. Typing Esc (the Escape key) will unfreeze the display.

Using Shift-click on a row will display the document extracted text (somewhat like a preview) instead of the document details. The functions of Click and Shift-Click can be reversed in the GUI preferences.

3.2.4. The filters panel

By default, the GUI displays the filters panel on the left of the results area. This is new in version 1.32. You can adjust the width of the panel, and hide it by squeezing it completely. The width will be memorized for the next session.

The panel currently has two areas, for filtering the results by dates, or by filesystem location.

The panel is only active in Query Language search mode, and its effect is to add date: and dir: clauses to the actual search.

The dates filter can be activated by clicking the checkbox. It has two assisted date entry widgets, for the minimum and maximum dates of the search period.

The directory filter displays a subset of the filesystem directories, reduced to the indexed area, as defined by the topdirs list and the name exclusion parameters. You can independantly select and deselect directories by clicking them. Note that selecting a directory will activate the whole subtree for searching, there is no need to select the subdirectories, and no way to exclude some of them (use Query language dir: clauses if this is needed).

3.2.5. Running arbitrary commands on result files

Apart from the Open and Open With operations, which allow starting an application on a result document (or a temporary copy), based on its MIME type, it is also possible to run arbitrary commands on results which are top-level files, using the Run Script entry in the results pop-up menu.

The commands which will appear in the Run Script submenu must be defined by .desktop files inside the scripts subdirectory of the current configuration directory.

Here follows an example of a .desktop file, which could be named for example, ~/.recoll/scripts/myscript.desktop (the exact file name inside the directory is irrelevant):

          [Desktop Entry]
          Type=Application
          Name=MyFirstScript
          Exec=/home/me/bin/tryscript %F
          MimeType=*/*
        

The Name attribute defines the label which will appear inside the Run Script menu. The Exec attribute defines the program to be run, which does not need to actually be a script, of course. The MimeType attribute is not used, but needs to exist.

The commands defined this way can also be used from links inside the result paragraph.

As an example, it might make sense to write a script which would move the document to the trash and purge it from the Recoll index.

3.2.6. Unix-like systems: displaying thumbnails

The default format for the result list entries and the detail area of the result table display an icon for each result document. The icon is either a generic one determined from the MIME type, or a thumbnail of the document appearance. Thumbnails are only displayed if found in the standard freedesktop location, where they would typically have been created by a file manager.

Recoll has no capability to create thumbnails. A relatively simple trick is to use the Open parent document/folder entry in the result list popup menu. This should open a file manager window on the containing directory, which should in turn create the thumbnails (depending on your settings). Restarting the search should then display the thumbnails.

There are also some pointers about thumbnail generation in the Recoll FAQ.

3.2.7. The preview window

The preview window opens when you first click a Preview link inside the result list.

Subsequent preview requests for a given search open new tabs in the existing window (except if you hold the Shift key while clicking which will open a new window for side by side viewing).

Starting another search and requesting a preview will create a new preview window. The old one stays open until you close it.

You can close a preview tab by typing Ctrl-W (Ctrl + W) in the window. Closing the last tab, or using the window manager button in the top of the frame will also close the window.

You can display successive or previous documents from the result list inside a preview tab by typing Shift+Down or Shift+Up (Down and Up are the arrow keys).

A right-click menu in the text area allows switching between displaying the main text or the contents of fields associated to the document (e.g.: author, abtract, etc.). This is especially useful in cases where the term match did not occur in the main text but in one of the fields. In the case of images, you can switch between three displays: the image itself, the image metadata as extracted by exiftool and the fields, which is the metadata stored in the index.

You can print the current preview window contents by typing Ctrl-P (Ctrl + P) in the window text.

Searching inside the preview

The preview window has an internal search capability, mostly controlled by the panel at the bottom of the window, which works in two modes: as a classical editor incremental search, where we look for the text entered in the entry zone, or as a way to walk the matches between the document and the Recoll query that found it.

Incremental text search

The preview tabs have an internal incremental search function. You initiate the search either by typing a / (slash) or CTL-F inside the text area or by clicking into the Search for: text field and entering the search string. You can then use the Next and Previous buttons to find the next/previous occurrence. You can also type F3 inside the text area to get to the next occurrence.

If you have a search string entered and you use Ctrl-Up/Ctrl-Down to browse the results, the search is initiated for each successive document. If the string is found, the cursor will be positioned at the first occurrence of the search string.

Walking the match lists

If the entry area is empty when you click the Next or Previous buttons, the editor will be scrolled to show the next match to any search term (the next highlighted zone). If you select a search group from the dropdown list and click Next or Previous, the match list for this group will be walked. This is not the same as a text search, because the occurrences will include non-exact matches (as caused by stemming or wildcards). The search will revert to the text mode as soon as you edit the entry area.

3.2.8. The Query Fragments window

The Query Fragments window can be used to control filtering query language elements modifying the current query, simply by clicking a button. This can be useful to save typing, or avoid memorizing, simple clauses of common usage (e.g. selecting only standalone documents or attachments, or filtering out WEB results, selecting a file system subtree, a file type, etc.).

Selecting the ToolsQuery Fragments menu entry will open the dialog.

The contents of the window are entirely customizable, and defined by the contents of a XML text file, named fragment-buttons.xml and which will be looked for in the current index configuration directory. The sample file distributed with Recoll contains a number of example filters. This will be automatically copied to the configuration directory if the file does not exist in there (e.g. ~/.recoll/fragment-buttons.xml under Linux and Mac OS, $HOME/AppData/Local/Recoll/fragment-buttons.xml for Windows). Editing the copy will allow you to configure the tool for your needs .

Note

The fragment-buttons.xml file was named fragbuts.xml up to Recoll version 1.31.0. This was deemed too close to offensive for native English speakers, so that the file was renamed. An existing fragbuts.xml will still be used if fragment-buttons.xml does not exist. No automatic renaming will be performed.

Here follows an example window:

And the corresponding configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<fragbuttons version="1.0">

  <radiobuttons>
    <!-- Toggle WEB queue results inclusion -->
    <fragbutton>
      <label>Include Web Results</label>
      <frag></frag>
    </fragbutton>
    <fragbutton>
      <label>Exclude Web Results</label>
      <frag>-rclbes:BGL</frag>
    </fragbutton>
    <fragbutton>
      <label>Only Web Results</label>
      <frag>rclbes:BGL</frag>
    </fragbutton>
  </radiobuttons>

  <radiobuttons>
    <!-- Standalone vs embedded switch -->
    <fragbutton>
      <label>Include embedded documents</label>
      <frag></frag>
    </fragbutton>
    <fragbutton>
      <label>Only standalone documents</label>
      <frag>issub:0</frag>
    </fragbutton>
    <fragbutton>
      <label>Only embedded documents</label>
      <frag>issub:1</frag>
    </fragbutton>
  </radiobuttons>

  <buttons>
    <fragbutton>
      <label>Example: Year 2010</label>
      <frag>date:2010-01-01/2010-12-31</frag>
    </fragbutton>
    <fragbutton>
      <label>Example: c++ files</label>
      <frag>ext:cpp OR ext:cxx</frag>
    </fragbutton>
    <fragbutton>
      <label>Example: My Great Directory</label>
      <frag>dir:/my/great/directory</frag>
    </fragbutton>
  </buttons>
</fragbuttons>
        

There are two types of groupings radiobuttons and buttons, each defining a line of checkbuttons or radiobuttons inside the window. Any number of buttons can be selected, but the radiobuttons in a line are exclusive.

Buttons are defined by a fragbutton section, which provides the label for a button, and the Query Language fragment which will be added (as an AND filter) before performing the query if the button is active.

    <fragbutton>
      <label>Example: My Great Directory</label>
      <frag>dir:/my/great/directory</frag>
    </fragbutton>
  

It is also possible to add message elements inside the groups, for documenting the behaviour. message elements have a label but no frag element. Example:

  <buttons>
    <message>
      <label>This is a message</label> 
    </message>
  </buttons>
  

The label contents are interpreted as HTML. Take care to replace opening < characters with the &lt; entity if you use tags.

The only thing that you need to know about XML for editing this file is that any opening tag like <label> needs to be matched by a closing tag after the value: </label>.

You will normally edit the file with a regular text editor, like, e.g. vi or notepad. Double-clicking the file in a file manager may not work, because this usually opens it in a WEB browser, which will not let you modify the contents.

3.2.9. Assisted Complex Search (A.K.A. "Advanced Search")

The advanced search dialog helps you build more complex queries without memorizing the search language constructs. It can be opened through the Tools menu or through the main toolbar.

Recoll keeps a history of searches. See Advanced search history.

The dialog has two tabs:

  1. The first tab lets you specify terms to search for, and permits specifying multiple clauses which are combined to build the search.

  2. The second tab allows filtering the results according to file size, date of modification, MIME type, or location.

Click on the Start Search button in the advanced search dialog, or type Enter in any text field to start the search. The button in the main window always performs a simple search.

Click on the Show query details link at the top of the result page to see the query expansion.

Advanced search: the "find" tab

This part of the dialog lets you construct a query by combining multiple clauses of different types. Each entry field is configurable for the following modes:

  • All terms.

  • Any term.

  • None of the terms.

  • Phrase (exact terms in order within an adjustable window).

  • Proximity (terms in any order within an adjustable window).

  • Filename search.

Additional entry fields can be created by clicking the Add clause button.

When searching, the non-empty clauses will be combined either with an AND or an OR conjunction, depending on the choice made on the left (All clauses or Any clause).

Entries of all types except "Phrase" and "Near" accept a mix of single words and phrases enclosed in double quotes. Stemming and wildcard expansion will be performed as for simple search.

Phrase and Proximity searches

These two clauses look for a group of terms in specified relative positions. They differ in the sense that the order of input terms is significant for phrase searches, but not for proximity searches. The latter do not impose an order on the words. In both cases, an adjustable number (slack) of non-matched words may be accepted between the searched ones. For phrase searches, the default count is zero (exact match). For proximity searches it is ten (meaning that two search terms, would be matched if found within a window of twelve words).

Examples: a phrase search for quick fox with a slack of 0 will match quick fox but not quick brown fox. With a slack of 1 it will match the latter, but not fox quick. A proximity search for quick fox with the default slack will match the latter, and also a fox is a cunning and quick animal.

The slack can be adjusted with the counter to the left of the input area

Advanced search: the "filter" tab

This part of the dialog has several sections which allow filtering the results of a search according to a number of criteria

  • The first section allows filtering by dates of last modification. You can specify both a minimum and a maximum date. The initial values are set according to the oldest and newest documents found in the index.

  • The next section allows filtering the results by file size. There are two entries for minimum and maximum size. Enter decimal numbers. You can use suffix multipliers: k/K, m/M, g/G, t/T for 10E3, 10E6, 10E9, 10E12 respectively.

  • The next section allows filtering the results by their MIME types, or MIME categories (e.g.: media/text/message/etc.).

    You can transfer the types between two boxes, to define which will be included or excluded by the search.

    The state of the file type selection can be saved as the default (the file type filter will not be activated at program start-up, but the lists will be in the restored state).

  • The bottom section allows restricting the search results to a sub-tree of the indexed area. You can use the Invert checkbox to search for files not in the sub-tree instead. If you use directory filtering often and on big subsets of the file system, you may think of setting up multiple indexes instead, as the performance may be better.

    You can use relative/partial paths for filtering. E.g., entering dirA/dirB would match either /dir1/dirA/dirB/myfile1 or /dir2/dirA/dirB/someother/myfile2.

Advanced search history

The advanced search tool memorizes the last 100 searches performed. You can walk the saved searches by using the up and down arrow keys while the keyboard focus belongs to the advanced search dialog.

The complex search history can be erased, along with the one for simple search, by selecting the FileErase Search History menu entry.

3.2.10. The term explorer tool

Recoll automatically manages the expansion of search terms to their derivatives (e.g.: plural/singular, verb inflections). But there are other cases where the exact search term is not known. For example, you may not remember the exact spelling, or only know the beginning of the name.

The search will only propose replacement terms with spelling variations when no matching document were found. In some cases, both proper spellings and mispellings are present in the index, and it may be interesting to look for them explicitly.

The term explorer tool (started from the toolbar icon or from the Term explorer entry of the Tools menu) can be used to search the full index terms list, or (later addition), display some statistics or other index information. It has several modes of operations:

Wildcard

In this mode of operation, you can enter a search string with shell-like wildcards (*, ?, []). e.g.: xapi* would display all index terms beginning with xapi. (More about wildcards here).

Regular expression

This mode will accept a regular expression as input. Example: word[0-9]+. The expression is implicitly anchored at the beginning. E.g.: press will match pression but not expression. You can use .*press to match the latter, but be aware that this will cause a full index term list scan, which can be quite long.

Stem expansion

This mode will perform the usual stem expansion normally done as part user input processing. As such it is probably mostly useful to demonstrate the process.

Spelling/Phonetic

In this mode, you enter the term as you think it is spelled, and Recoll will do its best to find index terms that sound like your entry. This mode uses the Aspell spelling application, which must be installed on your system for things to work (if your documents contain non-ascii characters, Recoll needs an aspell version newer than 0.60 for UTF-8 support). The language which is used to build the dictionary out of the index terms (which is done at the end of an indexing pass) is the one defined by your NLS environment. Weird things will probably happen if languages are mixed up.

Show index statistics

This will print a long list of boring numbers about the index

List files which could not be indexed

This will show the files which caused errors, usually because recollindex could not translate their format into text.

Note that in cases where Recoll does not know the beginning of the string to search for (e.g. a wildcard expression like *coll), the expansion can take quite a long time because the full index term list will have to be processed. The expansion is currently limited at 10000 results for wildcards and regular expressions. It is possible to change the limit in the configuration file.

Double-clicking on a term in the result list will insert it into the simple search entry field. You can also cut/paste between the result list and any entry field (the end of lines will be taken care of).

3.2.11. Multiple indexes

See the section describing the use of multiple indexes for generalities. Only the aspects concerning the recoll GUI are described here.

A recoll program instance is always associated with a main index, which is the one to be updated when requested from the File menu, but it can use any number of external Recoll indexes for searching. The external indexes can be selected through the External Indexes tab in the preferences dialog, which can be reached either trough: PreferencesGUI ConfigurationExternal Index Dialog or QueryExternal index dialog.

Index selection is performed in two phases. A set of all usable indexes must first be defined, and then the subset of indexes to be used for searching. These parameters are retained across program executions (there are kept separately for each Recoll configuration). The set of all indexes is usually quite stable, while the active ones might typically be adjusted quite frequently.

The main index (defined by RECOLL_CONFDIR) is always active. If this is undesirable, you can set up your base configuration to index an empty directory.

When adding a new index to the set, you can select either a Recoll configuration directory, or directly a Xapian index directory. In the first case, the Xapian index directory will be obtained from the selected configuration.

If the external index is actually located on a volume mounted from another machine, and references remote files, there may be a need to adjust the result paths so that they match the locally mounted ones (for opening documents). This can be done by using the path translation facility.

As building the set of all indexes can be a little tedious when done through the user interface, you can use the RECOLL_EXTRA_DBS environment variable to provide an initial set. This might typically be set up by a system administrator so that every user does not have to do it. The variable should define a colon-separated list of index directories, e.g.:

export RECOLL_EXTRA_DBS=/some/place/xapiandb:/some/other/db

On Windows, use semi-colons (;) as separators instead of colons.

Another environment variable, RECOLL_ACTIVE_EXTRA_DBS allows adding to the active list of indexes. This variable was suggested and implemented by a Recoll user. It is mostly useful if you use scripts to mount external volumes with Recoll indexes. By using RECOLL_EXTRA_DBS and RECOLL_ACTIVE_EXTRA_DBS, you can add and activate the index for the mounted volume when starting recoll. Unreachable indexes will automatically be deactivated when starting up.

3.2.12. Document history

Documents that you actually view (with the internal preview or an external tool) are entered into the document history, which is remembered.

You can display the history list by using the Tools/Doc History menu entry.

You can erase the document history by using the Erase document history entry in the File menu.

3.2.13. Sorting search results and collapsing duplicates

The documents in a result list are normally sorted in order of relevance. It is possible to specify a different sort order, either by using the vertical arrows in the GUI toolbox to sort by date, or switching to the result table display and clicking on any header. The sort order chosen inside the result table remains active if you switch back to the result list, until you click one of the vertical arrows, until both are unchecked (you are back to sort by relevance).

Sort parameters are remembered between program invocations, but result sorting is normally always inactive when the program starts. It is possible to keep the sorting activation state between program invocations by checking the Remember sort activation state option in the preferences.

It is also possible to hide duplicate entries inside the result list (documents with the exact same contents as the displayed one). The test of identity is based on an MD5 hash of the document container, not only of the text contents (so that e.g., a text document with an image added will not be a duplicate of the text only). Duplicates hiding is controlled by an entry in the GUI configuration dialog, and is off by default.

When a result document does have undisplayed duplicates, a Dups link will be shown with the result list entry. Clicking the link will display the paths (URLs + ipaths) for the duplicate entries.

3.2.14. Keyboard shortcuts

A number of common actions within the graphical interface can be triggered through keyboard shortcuts. As of Recoll 1.29, many of the shortcut values can be customised from a screen in the GUI preferences. Most shortcuts are specific to a given context (e.g. within a preview window, within the result table).

Most shortcuts can be changed to a preferred value by using the GUI shortcut editor: PreferencesGUI configurationShortcuts. In order to change a shortcut, just click the corresponding cell in the Shortcut column, and type the desired sequence.

Table 3.1. Keyboard shortcuts

Description Default value
Context: almost everywhere
Program exit Ctrl+Q
Context: advanced search
Load the next entry from the search history Up
Load the previous entry from the search history Down
Context: main window
Clear search. This will move the keyboard cursor to the simple search entry and erase the current text Ctrl+S
Move the keyboard cursor to the search entry area without erasing the current text Ctrl+L
Move the keyboard cursor to the search entry area without erasing the current text Ctrl+Shift+S
Toggle displaying the current results as a table or as a list Ctrl+T
Context: main window, when showing the results as a table
Move the keyboard cursor to currently the selected row in the table, or to the first one if none is selected Ctrl+R
Jump to row 0-9 or a-z in the table Ctrl+[0-9] or Ctrl+Shift+[a-z]
Cancel the current selection Esc
Context: preview window
Close the preview window Esc
Close the current tab Ctrl+W
Open a print dialog for the current tab contents Ctrl+P
Load the next result from the list to the current tab Shift+Down
Load the previous result from the list to the current tab Shift+Up
Context: result table
Copy the text contained in the selected document to the clipboard Ctrl+G
Copy the text contained in the selected document to the clipboard, then exit recoll Ctrl+Alt+Shift+G
Open the current document Ctrl+O
Open the current document and exit Recoll Ctrl+Alst+Shift+O
Show a full preview for the current document Ctrl+D
Toggle showing the column names Ctrl+H
Show a snippets (keyword in context) list for the current document Ctrl+E
Toggle showing the row letters/numbers Ctrl+V
Context: snippets window
Close the snippets window Esc
Find in the snippets list (method #1) Ctrl+F
Find in the snippets list (method #2) /
Find the next instance of the search term F3
Find the previous instance of the search term Shift+F3

3.2.15. Search tips

Terms and search expansion

Term completion. While typing into the simple search entry, a popup menu will appear and show completions for the current string. Values preceded by a clock icon come from the history, those preceded by a magnifier icon come from the index terms. This can be disabled in the preferences.

Picking up new terms from result or preview text. Double-clicking on a word in the result list or in a preview window will copy it to the simple search entry field.

Wildcards. Wildcards can be used inside search terms in all forms of searches. More about wildcards.

Automatic suffixes. Words like odt or ods can be automatically turned into query language ext:xxx clauses. This can be enabled in the Search preferences panel in the GUI.

Disabling stem expansion. Entering a capitalized word in any search field will prevent stem expansion (no search for gardening if you enter Garden instead of garden). This is the only case where character case should make a difference for a Recoll search. You can also disable stem expansion or change the stemming language in the preferences.

Finding related documents. Selecting the Find similar documents entry in the result list paragraph right-click menu will select a set of "interesting" terms from the current result, and insert them into the simple search entry field. You can then possibly edit the list and start a search to find documents which may be apparented to the current result.

File names. File names are added as terms during indexing, and you can specify them as ordinary terms in normal search fields (Recoll used to index all directories in the file path as terms. This has been abandoned as it did not seem really useful). Alternatively, you can use the specific file name search which will only look for file names, and may be faster than the generic search especially when using wildcards.

Working with phrases and proximity

Phrases searches. A phrase can be looked for by enclosing a number of terms in double quotes. Example: "user manual" will look only for occurrences of user immediately followed by manual. You can use the "Phrase" field of the advanced search dialog to the same effect. Phrases can be entered along simple terms in all simple or advanced search entry fields, except "Phrase".

Proximity searches. A proximity search differs from a phrase search in that it does not impose an order on the terms. Proximity searches can be entered by specifying the "Proximity" type in the advanced search, or by postfixing a phrase search with a 'p'. Example: "user manual"p would also match "manual user". Also see the modifier section from the query language documentation.

AutoPhrases. This option can be set in the preferences dialog. If it is set, a phrase will be automatically built and added to simple searches when looking for Any terms. This will not change radically the results, but will give a relevance boost to the results where the search terms appear as a phrase. E.g.: searching for virtual reality will still find all documents where either virtual or reality or both appear, but those which contain virtual reality should appear sooner in the list.

Phrase searches can slow down a query if most of the terms in the phrase are common. If the autophrase option is on, very common terms will be removed from the automatically constructed phrase. The removal threshold can be adjusted from the search preferences.

Phrases and abbreviations. Dotted abbreviations like I.B.M. are also automatically indexed as a word without the dots: IBM. Searching for the word inside a phrase (e.g.: "the IBM company") will only match the dotted abrreviation if you increase the phrase slack (using the advanced search panel control, or the o query language modifier). Literal occurrences of the word will be matched normally.

Others

Using fields. You can use the query language and field specifications to only search certain parts of documents. This can be especially helpful with email, for example only searching emails from a specific originator: search tips from:helpfulgui

Adjusting the result table columns. When displaying results in table mode, you can use a right click on the table headers to activate a pop-up menu which will let you adjust what columns are displayed. You can drag the column headers to adjust their order. You can click them to sort by the field displayed in the column. You can also save the result list in CSV format.

Changing the GUI geometry. It is possible to configure the GUI in wide form factor by dragging the toolbars to one of the sides (their location is remembered between sessions), and moving the category filters to a menu (can be set in the PreferencesGUI configurationUser interface panel).

Query explanation. You can get an exact description of what the query looked for, including stem expansion, and Boolean operators used, by clicking on the result list header.

Advanced search history. You can display any of the last 100 complex searches performed by using the up and down arrow keys while the advanced search panel is active.

Forced opening of a preview window. You can use Shift+Click on a result list Preview link to force the creation of a preview window instead of a new tab in the existing one.

3.2.16. Saving and restoring queries

Both simple and advanced query dialogs save recent history, but the amount is limited: old queries will eventually be forgotten. Also, important queries may be difficult to find among others. This is why both types of queries can also be explicitly saved to files, from the GUI menus: FileSave last query / Load last query

The default location for saved queries is a subdirectory of the current configuration directory, but saved queries are ordinary files and can be written or moved anywhere.

Some of the saved query parameters are part of the preferences (e.g. autophrase or the active external indexes), and may differ when the query is loaded from the time it was saved. In this case, Recoll will warn of the differences, but will not change the user preferences.

3.2.17. Customizing the search interface

You can customize some aspects of the search interface by using the GUI configuration entry in the Preferences menu.

There are several tabs in the dialog, dealing with the interface itself, the parameters used for searching and returning results, and what indexes are searched.

User interface parameters: 

  • Highlight color for query terms: Terms from the user query are highlighted in the result list samples and the preview window. The color can be chosen here. Any Qt color string should work (e.g. red, #ff0000). The default is blue.

  • Style sheet: The name of a Qt style sheet text file which is applied to the whole Recoll application on startup. The default value is empty, but there is a skeleton style sheet (recoll.qss) inside the /usr/share/recoll/examples directory. Using a style sheet, you can change most recoll graphical parameters: colors, fonts, etc. See the sample file for a few simple examples.

    You should be aware that parameters (e.g.: the background color) set inside the Recoll GUI style sheet will override global system preferences, with possible strange side effects: for example if you set the foreground to a light color and the background to a dark one in the desktop preferences, but only the background is set inside the Recoll style sheet, and it is light too, then text will appear light-on-light inside the Recoll GUI.

  • Maximum text size highlighted for preview Inserting highlights on search term inside the text before inserting it in the preview window involves quite a lot of processing, and can be disabled over the given text size to speed up loading.

  • Prefer HTML to plain text for preview if set, Recoll will display HTML as such inside the preview window. If this causes problems with the Qt HTML display, you can uncheck it to display the plain text version instead.

  • Activate links in preview if set, Recoll will turn HTTP links found inside plain text into proper HTML anchors, and clicking a link inside a preview window will start the default browser on the link target.

  • Plain text to HTML line style: when displaying plain text inside the preview window, Recoll tries to preserve some of the original text line breaks and indentation. It can either use PRE HTML tags, which will well preserve the indentation but will force horizontal scrolling for long lines, or use BR tags to break at the original line breaks, which will let the editor introduce other line breaks according to the window width, but will lose some of the original indentation. The third option has been available in recent releases and is probably now the best one: use PRE tags with line wrapping.

  • Choose editor application: this opens a dialog which allows you to select the application to be used to open each MIME type. The default is to use the xdg-open utility, but you can use this dialog to override it, setting exceptions for MIME types that will still be opened according to Recoll preferences. This is useful for passing parameters like page numbers or search strings to applications that support them (e.g. evince). This cannot be done with xdg-open which only supports passing one parameter.

  • Disable Qt autocompletion in search entry: this will disable the completion popup. Il will only appear, and display the full history, either if you enter only white space in the search area, or if you click the clock button on the right of the area.

  • Document filter choice style: this will let you choose if the document categories are displayed as a list or a set of buttons, or a menu.

  • Start with simple search mode: this lets you choose the value of the simple search type on program startup. Either a fixed value (e.g. Query Language, or the value in use when the program last exited.

  • Start with advanced search dialog open : If you use this dialog frequently, checking the entries will get it to open when recoll starts.

  • Remember sort activation state if set, Recoll will remember the sort tool stat between invocations. It normally starts with sorting disabled.

Result list parameters: 

  • Number of results in a result page

  • Result list font: There is quite a lot of information shown in the result list, and you may want to customize the font and/or font size. The rest of the fonts used by Recoll are determined by your generic Qt config (try the qtconfig command).

  • Edit result list paragraph format string: allows you to change the presentation of each result list entry. See the result list customisation section.

  • Edit result page HTML header insert: allows you to define text inserted at the end of the result page HTML header. More detail in the result list customisation section.

  • Date format: allows specifying the format used for displaying dates inside the result list. This should be specified as an strftime() string (man strftime).

  • Abstract snippet separator: for synthetic abstracts built from index data, which are usually made of several snippets from different parts of the document, this defines the snippet separator, an ellipsis by default.

Search parameters: 

  • Hide duplicate results: decides if result list entries are shown for identical documents found in different places.

  • Stemming language: stemming obviously depends on the document's language. This listbox will let you chose among the stemming databases which were built during indexing (this is set in the main configuration file), or later added with recollindex -s (See the recollindex manual). Stemming languages which are dynamically added will be deleted at the next indexing pass unless they are also added in the configuration file.

  • Automatically add phrase to simple searches: a phrase will be automatically built and added to simple searches when looking for Any terms. This will give a relevance boost to the results where the search terms appear as a phrase (consecutive and in order).

  • Autophrase term frequency threshold percentage: very frequent terms should not be included in automatic phrase searches for performance reasons. The parameter defines the cutoff percentage (percentage of the documents where the term appears).

  • Replace abstracts from documents: this decides if we should synthesize and display an abstract in place of an explicit abstract found within the document itself.

  • Dynamically build abstracts: this decides if Recoll tries to build document abstracts (lists of snippets) when displaying the result list. Abstracts are constructed by taking context from the document information, around the search terms.

  • Synthetic abstract size: adjust to taste...

  • Synthetic abstract context words: how many words should be displayed around each term occurrence.

  • Query language magic file name suffixes: a list of words which automatically get turned into ext:xxx file name suffix clauses when starting a query language query (e.g.: doc xls xlsx...). This will save some typing for people who use file types a lot when querying.

External indexes: This panel will let you browse for additional indexes that you may want to search. External indexes are designated by their database directory (e.g.: /home/someothergui/.recoll/xapiandb, /usr/local/recollglobal/xapiandb).

Once entered, the indexes will appear in the External indexes list, and you can chose which ones you want to use at any moment by checking or unchecking their entries.

Your main database (the one the current configuration indexes to), is always implicitly active. If this is not desirable, you can set up your configuration so that it indexes, for example, an empty directory. An alternative indexer may also need to implement a way of purging the index from stale data,

The result list format

Recoll normally uses a full function HTML processor to display the result list and the snippets window. Depending on the version, this may be based on either Qt WebKit or Qt WebEngine. It is then possible to completely customise the result list with full support for CSS and Javascript.

It is also possible to build Recoll to use a simpler Qt QTextBrowser widget to display the HTML, which may be necessary if the ones above are not ported on the system, or to reduce the application size and dependencies. There are limits to what you can do in this case, but it is still possible to decide what data each result will contain, and how it will be displayed.

The result list presentation can be customized by adjusting two elements:

  • The paragraph format

  • HTML code inside the header section. This is also used for the snippets window.

The paragraph format and the header fragment can be edited from the Result list tab of the GUI configuration.

The header fragment is used both for the result list and the snippets window. The snippets list is a table and has a snippets class attribute. Each paragraph in the result list is a table, with class respar, but this can be changed by editing the paragraph format.

There are a few examples on the page about customising the result list on the Recoll Web site.

The paragraph format

This is an arbitrary HTML string which will be transformed by printf-like % substitutions to show the results.

Note

Any literal % character in the input must be quoted as %%. E.g. <table style="width: 100%;"> should be entered as <table style="width: 100%%;">.

The following substitutions will be performed:

  • %A. Abstract

  • %D. Date

  • %I. Icon image name. This is normally determined from the MIME type. The associations are defined inside the mimeconf configuration file. If a thumbnail for the file is found at the standard Freedesktop location, this will be displayed instead.

  • %K. Keywords (if any)

  • %L. Precooked Preview, Edit, and possibly Snippets links

  • %M. MIME type

  • %N. result Number inside the result page

  • %P. Parent folder Url. In the case of an embedded document, this is the parent folder for the top level container file.

  • %R. Relevance percentage

  • %S. Size information

  • %T. Title or Filename if not set.

  • %t. Title or empty.

  • %(filename). File name.

  • %U. Url

In addition to the predefined values above, all strings like %(fieldname) will be replaced by the value of the field named fieldname for this document. Only stored fields can be accessed in this way, the value of indexed but not stored fields is not known at this point in the search process (see field configuration). There are currently very few fields stored by default, apart from the values above (only author and filename), so this feature will need some custom local configuration to be useful. An example candidate would be the recipient field which is generated by the message input handlers.

The format of the Preview, Edit, and Snippets links is <a href="P%N">, <a href="E%N"> and <a href="A%N"> where docnum (%N) expands to the document number inside the result page).

A link target defined as "F%N" will open the document corresponding to the %P parent folder expansion, usually creating a file manager window on the folder where the container file resides. E.g.:

<a href="F%N">%P</a>

A link target defined as R%N|scriptname will run the corresponding script on the result file (if the document is embedded, the script will be started on the top-level parent). See the section about defining scripts.

The default value for the paragraph format string is:

            "<table class=\"respar\">\n"
            "<tr>\n"
            "<td><a href='%U'><img src='%I' width='64'></a></td>\n"
            "<td>%L &nbsp;<i>%S</i> &nbsp;&nbsp;<b>%T</b><br>\n"
            "<span style='white-space:nowrap'><i>%M</i>&nbsp;%D</span>&nbsp;&nbsp;&nbsp; <i>%U</i>&nbsp;%i<br>\n"
            "%A %K</td>\n"
            "</tr></table>\n"
            

You may, for example, try the following for a more web-like experience:

            <u><b><a href="P%N">%T</a></b></u><br>
            %A<font color=#008000>%U - %S</font> - %L
            

Note that the P%N link in the above paragraph makes the title a preview link. Or the clean looking:

            <img src="%I" align="left">%L <font color="#900000">%R</font>
            &nbsp;&nbsp;<b>%T&</b><br>%S&nbsp;
            <font color="#808080"><i>%U</i></font>
            <table bgcolor="#e0e0e0">
            <tr><td><div>%A</div></td></tr>
            </table>%K
            

These samples, and some others are on the web site, with pictures to show how they look.

It is also possible to define the value of the snippet separator inside the abstract section.

3.3. Searching with the KDE KIO slave

The Recoll KIO slave allows performing a Recoll search by entering an appropriate URL in a KDE open dialog, or a Dolphin URL. The results are displayed as directory entries.

The instructions for building this module are located in the source tree. See: kde/kio/recoll/00README.txt. Some Linux distributions do package the kio-recoll module, so check before diving into the build process, maybe it's already out there ready for one-click installation.

3.4. Searching on the command line

There are several ways to obtain search results as a text stream, without a graphical interface:

  • By passing option -t to the recoll program, or by calling it as recollq (through a link).

  • By using the actual recollq program.

  • By writing a custom Python program, using the Recoll Python API.

The first two methods work in the same way and accept/need the same arguments (except for the additional -t to recoll). The query to be executed is specified as command line arguments.

recollq is not always built by default. You can use the Makefile in the query directory to build it. This is a very simple program, and if you can program a little c++, you may find it useful to taylor its output format to your needs. Apart from being easily customised, recollq is only really useful on systems where the Qt libraries are not available, else it is redundant with recoll -t.

recollq has a man page. The Usage string follows:

recollq: usage:
 -P: Show the date span for all the documents present in the index.
 [-o|-a|-f] [-q] <query string>
 Runs a recoll query and displays result lines. 
  Default: will interpret the argument(s) as a xesam query string.
  Query elements: 
   * Implicit AND, exclusion, field spec:  t1 -t2 title:t3
   * OR has priority: t1 OR t2 t3 OR t4 means (t1 OR t2) AND (t3 OR t4)
   * Phrase: "t1 t2" (needs additional quoting on cmd line)
 -o Emulate the GUI simple search in ANY TERM mode.
 -a Emulate the GUI simple search in ALL TERMS mode.
 -f Emulate the GUI simple search in filename mode.
 -q is just ignored (compatibility with the recoll GUI command line).
Common options:
 -c <configdir> : specify config directory, overriding $RECOLL_CONFDIR.
 -C : collapse duplicates
 -d also dump file contents.
 -n [first-]<cnt> define the result slice. The default value for [first]
    is 0. Without the option, the default max count is 2000.
    Use n=0 for no limit.
 -b : basic. Just output urls, no mime types or titles.
 -Q : no result lines, just the processed query and result count.
 -m : dump the whole document meta[] array for each result.
 -A : output the document abstracts.
    -p <cnt> : show <cnt> snippets, with page numbers instead of the concatenated abstract.
    -g <cnt> : show <cnt> snippets, with line numbers instead of the concatenated abstract.
 -S fld : sort by field <fld>.
   -D : sort descending.
 -s stemlang : set stemming language to use (must exist in index...).
    Use -s "" to turn off stem expansion.
 -T <synonyms file>: use the parameter (Thesaurus) for word expansion.
 -i <dbdir> : additional index, several can be given.
 -e use url encoding (%xx) for urls.
 -E use exact result count instead of lower bound estimate.
 -F <field name list> : output exactly these fields for each result.
    The field values are encoded in base64, output in one line and 
    separated by one space character. This is the recommended format 
    for use by other programs. Use a normal query with option -m to 
    see the field names. Use -F '' to output all fields, but you probably
    also want option -N in this case.
   -N : with -F, print the (plain text) field names before the field values.
 --extract_to <filepath> : extract the first result to filepath, which must not exist.
      Use a -n option with an offset to select the appropriate result.
      

Sample execution:

recollq 'ilur -nautique mime:text/html'
Recoll query: ((((ilur:(wqf=11) OR ilurs) AND_NOT (nautique:(wqf=11) OR nautiques OR nautiqu OR nautiquement)) FILTER Ttext/html))
4 results
text/html       [file:///Users/dockes/projets/bateaux/ilur/comptes.html]      [comptes.html]  18593   bytes   
text/html       [file:///Users/dockes/projets/nautique/webnautique/articles/ilur1/index.html] [Constructio...
text/html       [file:///Users/dockes/projets/pagepers/index.html]    [psxtcl/writemime/recoll]...
text/html       [file:///Users/dockes/projets/bateaux/ilur/factEtCie/recu-chasse-maree....
      

3.5. The query language

The Recoll query language was based on the now defunct Xesam user search language specification. It allows defining general boolean searches within the main body text or specific fields, and has many additional features, broadly equivalent to those provided by complex search interface in the GUI.

The query language processor is activated in the GUI simple search entry when the search mode selector is set to Query Language. It can also be used from the command line search, the KIO slave, or the WEB UI.

If the results of a query language search puzzle you and you doubt what has been actually searched for, you can use the GUI Show Query link at the top of the result list to check the exact query which was finally executed by Xapian.

3.5.1. General syntax

Here follows a sample request that we are going to explain:

        author:"john doe" Beatles OR Lennon Live OR Unplugged -potatoes
      

This would search for all documents with John Doe appearing as a phrase in the author field (exactly what this is would depend on the document type, e.g.: the From: header, for an email message), and containing either beatles or lennon and either live or unplugged but not potatoes (in any part of the document).

An element is composed of an optional field specification, and a value, separated by a colon (the field separator is the last colon in the element). Examples:

  • Eugenie
  • author:balzac
  • dc:title:grandet
  • dc:title:"eugenie grandet"

The colon, if present, means "contains". Xesam defines other relations, which are mostly unsupported for now (except in special cases, described further down).

All elements in the search entry are normally combined with an implicit AND. It is possible to specify that elements be OR'ed instead, as in Beatles OR Lennon. The OR must be entered literally (capitals), and it has priority over the AND associations: word1 word2 OR word3 means word1 AND (word2 OR word3) not (word1 AND word2) OR word3.

You can use parentheses to group elements (from version 1.21), which will sometimes make things clearer, and may allow expressing combinations which would have been difficult otherwise.

An element preceded by a - specifies a term that should not appear.

By default, words inside double-quotes define a phrase search (the order of words is significant), so that title:"prejudice pride" is not the same as title:prejudice title:pride, and is unlikely to find a result. This can be changed by using modifiers.

Words inside phrases and capitalized words are not stem-expanded. Wildcards may be used anywhere inside a term. Specifying a wildcard on the left of a term can produce a very slow search (or even an incorrect one if the expansion is truncated because of excessive size). Also see More about wildcards.

To save you some typing, Recoll versions 1.20 and later interpret a field value given as a comma-separated list of terms as an AND list and a slash-separated list as an OR list. No white space is allowed. So

author:john,lennon

will search for documents with john and lennon inside the author field (in any order), and

author:john/ringo

would search for john or ringo. This behaviour is only triggered by a field prefix: without it, comma- or slash- separated input will produce a phrase search. However, you can use a text field name to search the main text this way, as an alternate to using an explicit OR, e.g. text:napoleon/bonaparte would generate a search for napoleon or bonaparte in the main text body.

Modifiers can be set on a double-quote value, for example to specify a proximity search (unordered). See the modifier section. No space must separate the final double-quote and the modifiers value, e.g. "two one"po10

Recoll currently manages the following default fields:

  • title, subject or caption are synonyms which specify data to be searched for in the document title or subject.

  • author or from for searching the documents originators.

  • recipient or to for searching the documents recipients.

  • keyword for searching the document-specified keywords (few documents actually have any).

  • filename for the document's file name. You can use the shorter fn alias. This value is not set for all documents: internal documents contained inside a compound one (for example an EPUB section) do not inherit the container file name any more, this was replaced by an explicit field (see next). Sub-documents can still have a filename, if it is implied by the document format, for example the attachment file name for an email attachment.

  • containerfilename, aliased as cfn. This is set for all documents, both top-level and contained sub-documents, and is always the name of the filesystem file which contains the data. The terms from this field can only be matched by an explicit field specification (as opposed to terms from filename which are also indexed as general document content). This avoids getting matches for all the sub-documents when searching for the container file name.

  • ext specifies the file name extension (Ex: ext:html).

  • rclmd5 the MD5 checksum for the document. This is used for displaying the duplicates of a search result (when querying with the option to collapse duplicate results). Incidentally, this could be used to find the duplicates of any given file by computing its MD5 checksum and executing a query with just the rclmd5 value.

You can define aliases for field names, in order to use your preferred denomination or to save typing (e.g. the predefined fn and cfn aliases defined for filename and containerfilename). See the section about the fields file.

The document input handlers have the possibility to create other fields with arbitrary names, and aliases may be defined in the configuration, so that the exact field search possibilities may be different for you if someone took care of the customisation.

3.5.2. Special field-like specifiers

The field syntax also supports a few field-like, but special, criteria, for which the values are interpreted differently. Regular processing does not apply (for example the slash- or comma- separated lists don't work). A list follows.

  • dir for filtering the results on file location. For example, dir:/home/me/somedir will restrict the search to results found anywhere under the /home/me/somedir directory (including subdirectories).

    Tilde expansion will be performed as usual. Wildcards will be expanded, but please have a look at an important limitation of wildcards in path filters.

    You can also use relative paths. For example, dir:share/doc would match either /usr/share/doc or /usr/local/share/doc.

    -dir will find results not in the specified location.

    Several dir clauses can be specified, both positive and negative. For example the following makes sense:

    dir:recoll dir:src -dir:utils -dir:common

    This would select results which have both recoll and src in the path (in any order), and which have not either utils or common.

    You can also use OR conjunctions with dir: clauses.

    On Unix-like systems, a special aspect of dir clauses is that the values in the index are not transcoded to UTF-8, and never lower-cased or unaccented, but stored as binary. This means that you need to enter the values in the exact lower or upper case, and that searches for names with diacritics may sometimes be impossible because of character set conversion issues. Non-ASCII UNIX file paths are an unending source of trouble and are best avoided.

    You need to use double-quotes around the path value if it contains space characters.

    The shortcut syntax to define OR or AND lists within fields with commas or slash characters is not available.

  • size for filtering the results on file size. Example: size<10000. You can use <, > or = as operators. You can specify a range like the following: size>100 size<1000. The usual k/K, m/M, g/G, t/T can be used as (decimal) multipliers. Ex: size>1k to search for files bigger than 1000 bytes.

  • date for searching or filtering on dates. The syntax for the argument is based on the ISO8601 standard for dates and time intervals. Only dates are supported, no times. The general syntax is 2 elements separated by a / character. Each element can be a date or a period of time. Periods are specified as PnYnMnD. The n numbers are the respective numbers of years, months or days, any of which may be missing. Dates are specified as YYYY-MM-DD. The days and months parts may be missing. If the / is present but an element is missing, the missing element is interpreted as the lowest or highest date in the index. Examples:

    • 2001-03-01/2002-05-01 the basic syntax for an interval of dates.

    • 2001-03-01/P1Y2M the same specified with a period.

    • 2001/ from the beginning of 2001 to the latest date in the index.

    • 2001 the whole year of 2001

    • P2D/ means 2 days ago up to now if there are no documents with dates in the future.

    • /2003 all documents from 2003 or older.

    Periods can also be specified with small letters (e.g.: p2y).

  • mime or format for specifying the MIME type. These clauses are processed apart from the normal Boolean logic of the search: multiple values will be OR'ed (instead of the normal AND). You can specify types to be excluded, with the usual -, and use wildcards. Example: mime:text/* -mime:text/plain. Specifying an explicit boolean operator before a mime specification is not supported and will produce strange results.

  • type or rclcat for specifying the category (as in text/media/presentation/etc.). The classification of MIME types in categories is defined in the Recoll configuration (mimeconf), and can be modified or extended. The default category names are those which permit filtering results in the main GUI screen. Categories are OR'ed like MIME types above, and can be negated with -.

  • issub for specifying that only standalone (issub:0) or only embedded (issub:1) documents should be returned as results.

Note

mime, rclcat, size, issub and date criteria always affect the whole query (they are applied as a final filter), even if set with other terms inside a parenthese.

Note

mime (or the equivalent rclcat) is the only field with an OR default. You do need to use OR with ext terms for example.

3.5.3. Range clauses

Recoll 1.24 and later support range clauses on fields which have been configured to support it. No default field uses them currently, so this paragraph is only interesting if you modified the fields configuration and possibly use a custom input handler.

A range clause looks like one of the following:

myfield:small..big
myfield:small..
myfield:..big
        

The nature of the clause is indicated by the two dots .., and the effect is to filter the results for which the myfield value is in the possibly open-ended interval.

See the section about the fields configuration file for the details of configuring a field for range searches (list them in the [values] section).

3.5.4. Modifiers

Some characters are recognized as search modifiers when found immediately after the closing double quote of a phrase, as in "some term"modifierchars. The actual "phrase" can be a single term of course. Supported modifiers:

  • l can be used to turn off stemming (mostly makes sense with p because stemming is off by default for phrases, but see also x further down).

  • o can be used to specify a "slack" for both phrase and proximity searches: the number of additional terms that may be found between the specified ones. If o is followed by an integer number, this is the slack, else the default is 10. The default slack (with no o) is 0 for phrase searches and 10 for proximity searches.

  • p can be used to turn an ordered phrase search into an unordered proximity one. Example: "order any in"p. You can find a little more detail about phrase and proximity searches here.

  • s (1.22) can be used to turn off synonym expansion, if a synonyms file is in place.

  • x (1.33.2) will enable the expansion of terms inside a phrase search (the default is for phrases to be searched verbatim). Also see the stemexpandphrases in the configuration section, for changing the default behaviour.

  • A weight can be specified for a query element by specifying a decimal value at the start of the modifiers. Example: "Important"2.5.

The following only make sense on indexes which are capable of case and diacritics sensitivity (not the default):

  • C will turn on case sensitivity.

  • D will turn on diacritics sensitivity (if the index supports it).

  • e (explicit) will turn on diacritics sensitivity and case sensitivity, and prevent stem expansion.

3.6. Wildcards and anchored searches

Some special characters are interpreted by Recoll in search strings to expand or specialize the search. Wildcards expand a root term in controlled ways. Anchor characters can restrict a search to succeed only if the match is found at or near the beginning of the document or one of its fields.

3.6.1. Wildcards

All words entered in Recoll search fields will be processed for wildcard expansion before the request is finally executed.

The wildcard characters are:

  • * which matches 0 or more characters.

  • ? which matches a single character.

  • [] which allow defining sets of characters to be matched (ex: [abc] matches a single character which may be 'a' or 'b' or 'c', [0-9] matches any number.

You should be aware of a few things when using wildcards.

  • Using a wildcard character at the beginning of a word can make for a slow search because Recoll will have to scan the whole index term list to find the matches. However, this is much less a problem for field searches, and queries like author:*@domain.com can sometimes be very useful.

  • For Recoll version 18 only, when working with a raw index (preserving character case and diacritics), the literal part of a wildcard expression will be matched exactly for case and diacritics. This is not true any more for versions 19 and later.

  • Using a * at the end of a word can produce more matches than you would think, and strange search results. You can use the term explorer tool to check what completions exist for a given term. You can also see exactly what search was performed by clicking on the link at the top of the result list. In general, for natural language terms, stem expansion will produce better results than an ending * (stem expansion is turned off when any wildcard character appears in the term).

Wildcards and path filtering

Due to the way that Recoll processes wildcards inside dir path filtering clauses, they will have a multiplicative effect on the query size. A clause containing wildcards in several paths elements, like, for example, dir:/home/me/*/*/docdir, will almost certainly fail if your indexed tree is of any realistic size.

Depending on the case, you may be able to work around the issue by specifying the paths elements more narrowly, with a constant prefix, or by using 2 separate dir: clauses instead of multiple wildcards, as in dir:/home/me dir:docdir. The latter query is not equivalent to the initial one because it does not specify a number of directory levels, but that's the best we can do (and it may be actually more useful in some cases).

3.6.2. Anchored searches

Two characters are used to specify that a search hit should occur at the beginning or at the end of the text. ^ at the beginning of a term or phrase constrains the search to happen at the start, $ at the end force it to happen at the end.

As this function is implemented as a phrase search it is possible to specify a maximum distance at which the hit should occur, either through the controls of the advanced search panel, or using the query language, for example, as in:

"^someterm"o10

which would force someterm to be found within 10 terms of the start of the text. This can be combined with a field search as in somefield:"^someterm"o10 or somefield:someterm$.

This feature can also be used with an actual phrase search, but in this case, the distance applies to the whole phrase and anchor, so that, for example, bla bla my unexpected term at the beginning of the text would be a match for "^my term"o5.

Anchored searches can be very useful for searches inside somewhat structured documents like scientific articles, in case explicit metadata has not been supplied, for example for looking for matches inside the abstract or the list of authors (which occur at the top of the document).

3.7. Using Synonyms (1.22)

Term synonyms and text search: in general, there are two main ways to use term synonyms for searching text:

  • At index creation time, they can be used to alter the indexed terms, either increasing or decreasing their number, by expanding the original terms to all synonyms, or by reducing all synonym terms to a canonical one.

  • At query time, they can be used to match texts containing terms which are synonyms of the ones specified by the user, either by expanding the query for all synonyms, or by reducing the user entry to canonical terms (the latter only works if the corresponding processing has been performed while creating the index).

Recoll only uses synonyms at query time. A user query term which part of a synonym group will be optionally expanded into an OR query for all terms in the group.

Synonym groups are defined inside ordinary text files. Each line in the file defines a group.

Example:

        hi hello "good morning"

        # not sure about "au revoir" though. Is this english ?
        bye goodbye "see you" \
        "au revoir" 
      

As usual, lines beginning with a # are comments, empty lines are ignored, and lines can be continued by ending them with a backslash.

Multi-word synonyms are supported, but be aware that these will generate phrase queries, which may degrade performance and will disable stemming expansion for the phrase terms.

The contents of the synonyms file must be casefolded (not only lowercased), because this is what expected at the point in the query processing where it is used. There are a few cases where this makes a difference, for example, German sharp s should be expressed as ss, Greek final sigma as sigma. For reference, Python3 has an easy way to casefold words (str.casefold()).

The synonyms file can be specified in the Search parameters tab of the GUI configuration Preferences menu entry, or as an option for command-line searches.

Once the file is defined, the use of synonyms can be enabled or disabled directly from the Preferences menu.

The synonyms are searched for matches with user terms after the latter are stem-expanded, but the contents of the synonyms file itself is not subjected to stem expansion. This means that a match will not be found if the form present in the synonyms file is not present anywhere in the document set (same with accents when using a raw index).

The synonyms function is probably not going to help you find your letters to Mr. Smith. It is best used for domain-specific searches. For example, it was initially suggested by a user performing searches among historical documents: the synonyms file would contains nicknames and aliases for each of the persons of interest.

3.8. Path translations

In some cases, the document paths stored inside the index do not match the actual ones, so that document previews and accesses will fail. This can occur in a number of circumstances:

  • When using multiple indexes it is a relatively common occurrence that some will actually reside on a remote volume, for example mounted via NFS. In this case, the paths used to access the documents on the local machine are not necessarily the same than the ones used while indexing on the remote machine. For example, /home/me may have been used as a topdirs elements while indexing, but the directory might be mounted as /net/server/home/me on the local machine.

  • The case may also occur with removable disks. It is perfectly possible to configure an index to live with the documents on the removable disk, but it may happen that the disk is not mounted at the same place so that the documents paths from the index are invalid. In some case, the path adjustments can be automated.

  • As a last example, one could imagine that a big directory has been moved, but that it is currently inconvenient to run the indexer.

Recoll has a facility for rewriting access paths when extracting the data from the index. The translations can be defined for the main index and for any additional query index.

In the above NFS example, Recoll could be instructed to rewrite any file:///home/me URL from the index to file:///net/server/home/me, allowing accesses from the client.

The translations are defined in the ptrans configuration file, which can be edited by hand or from the GUI external indexes configuration dialog: PreferencesExternal index dialog, then click the Paths translations button on the right below the index list: translations will be set for the main index if no external index is currently selected in the list, or else for the currently selected index.

Example entry from a ptrans file: 

[/path/to/external/xapiandb]
/some/index/path = /some/local/path

This would decide that, for the index stored in /path/to/external/xapiandb, any occurence of /some/index/path should be replaced with /some/local/path when presenting a result.

Windows note

At the moment, the path comparisons done for path translation under MS Windows are case sensitive (this will be fixed at some point). Use the natural character case as displayed in the file explorer. Example:

[Z:/some/mounted/xapiandb]
C: = Z:

3.9. Search case and diacritics sensitivity

For Recoll versions 1.18 and later, and when working with a raw index (not the default), searches can be sensitive to character case and diacritics. How this happens is controlled by configuration variables and what search data is entered.

The general default is that searches entered without upper-case or accented characters are insensitive to case and diacritics. An entry of resume will match any of Resume, RESUME, résumé, Résumé etc.

Two configuration variables can automate switching on sensitivity (they were documented but actually did nothing until Recoll 1.22):

autodiacsens

If this is set, search sensitivity to diacritics will be turned on as soon as an accented character exists in a search term. When the variable is set to true, resume will start a diacritics-unsensitive search, but résumé will be matched exactly. The default value is false.

autocasesens

If this is set, search sensitivity to character case will be turned on as soon as an upper-case character exists in a search term except for the first one. When the variable is set to true, us or Us will start a diacritics-unsensitive search, but US will be matched exactly. The default value is true (contrary to autodiacsens).

As in the past, capitalizing the first letter of a word will turn off its stem expansion and have no effect on case-sensitivity.

You can also explicitly activate case and diacritics sensitivity by using modifiers with the query language. C will make the term case-sensitive, and D will make it diacritics-sensitive. Examples:

        "us"C
      

will search for the term us exactly (Us will not be a match).

        "resume"D
      

will search for the term resume exactly (résumé will not be a match).

When either case or diacritics sensitivity is activated, stem expansion is turned off. Having both does not make much sense.

3.10. Desktop integration

Being independent of the desktop type has its drawbacks: Recoll desktop integration is minimal. However there are a few tools available:

  • Users of recent Ubuntu-derived distributions, or any other Gnome desktop systems (e.g. Fedora) can install the Recoll GSSP (Gnome Shell Search Provider).

  • The KDE KIO Slave was described in a previous section. It can provide search results inside Dolphin.

  • If you use an oldish version of Ubuntu Linux, you may find the Ubuntu Unity Lens module useful.

  • There is also an independently developed Krunner plugin.

Here follow a few other things that may help.

3.10.1. Hotkeying recoll

It is surprisingly convenient to be able to show or hide the Recoll GUI with a single keystroke. Recoll comes with a small Python script, based on the libwnck window manager interface library, which will allow you to do just this. The detailed instructions are on this wiki page.

3.10.2. The KDE Kicker Recoll applet

This is probably obsolete now. Anyway:

The Recoll source tree contains the source code to the recoll_applet, a small application derived from the find_applet. This can be used to add a small Recoll launcher to the KDE panel.

The applet is not automatically built with the main Recoll programs, nor is it included with the main source distribution (because the KDE build boilerplate makes it relatively big). You can download its source from the recoll.org download page. Use the omnipotent configure;make;make install incantation to build and install.

You can then add the applet to the panel by right-clicking the panel and choosing the Add applet entry.

The recoll_applet has a small text window where you can type a Recoll query (in query language form), and an icon which can be used to restrict the search to certain types of files. It is quite primitive, and launches a new recoll GUI instance every time (even if it is already running). You may find it useful anyway.

Chapter 4. Programming interface

Recoll has an Application Programming Interface, usable both for indexing and searching, currently accessible from the Python language.

Another less radical way to extend the application is to write input handlers for new types of documents.

The processing of metadata attributes for documents (fields) is highly configurable.

4.1. Writing a document input handler

Terminology

The small programs or pieces of code which handle the processing of the different document types for Recoll used to be called filters, which is still reflected in the name of the directory which holds them and many configuration variables. They were named this way because one of their primary functions is to filter out the formatting directives and keep the text content. However these modules may have other behaviours, and the term input handler is now progressively substituted in the documentation. filter is still used in many places though.

Recoll input handlers cooperate to translate from the multitude of input document formats, simple ones as opendocument, acrobat, or compound ones such as Zip or Email, into the final Recoll indexing input format, which is plain text (in many cases the processing pipeline has an intermediary HTML step, which may be used for better previewing presentation). Most input handlers are executable programs or scripts. A few handlers are coded in C++ and live inside recollindex. This latter kind will not be described here.

There are two kinds of external executable input handlers:

  • Simple exec handlers run once and exit. They can be bare programs like antiword, or scripts using other programs. They are very simple to write, because they just need to print the converted document to the standard output. Their output can be plain text or HTML. HTML is usually preferred because it can store metadata fields and it allows preserving some of the formatting for the GUI preview. However, these handlers have limitations:

    • They can only process one document per file.

    • The output MIME type must be known and fixed.

    • The character encoding, if relevant, must be known and fixed (or possibly just depending on location).

  • Multiple execm handlers can process multiple files (sparing the process startup time which can be very significant), or multiple documents per file (e.g.: for archives or multi-chapter publications). They communicate with the indexer through a simple protocol, but are nevertheless a bit more complicated than the older kind. Most of the new handlers are written in Python (exception: rclimg which is written in Perl because exiftool has no real Python equivalent). The Python handlers use common modules to factor out the boilerplate, which can make them very simple in favorable cases. The subdocuments output by these handlers can be directly indexable (text or HTML), or they can be other simple or compound documents that will need to be processed by another handler.

In both cases, handlers deal with regular file system files, and can process either a single document, or a linear list of documents in each file. Recoll is responsible for performing up to date checks, deal with more complex embedding and other upper level issues.

A simple handler returning a document in text/plain format, can transfer no metadata to the indexer. Generic metadata, like document size or modification date, will be gathered and stored by the indexer.

Handlers that produce text/html format can return an arbitrary amount of metadata inside HTML meta tags. These will be processed according to the directives found in the fields configuration file.

The handlers that can handle multiple documents per file return a single piece of data to identify each document inside the file. This piece of data, called an ipath will be sent back by Recoll to extract the document at query time, for previewing, or for creating a temporary file to be opened by a viewer. These handlers can also return metadata either as HTML meta tags, or as named data through the communication protocol.

The following section describes the simple handlers, and the next one gives a few explanations about the execm ones. You could conceivably write a simple handler with only the elements in the manual. This will not be the case for the other ones, for which you will have to look at the code.

4.1.1. Simple input handlers

Recoll simple handlers are usually shell-scripts, but this is in no way necessary. Extracting the text from the native format is the difficult part. Outputting the format expected by Recoll is trivial. Happily enough, most document formats have translators or text extractors which can be called from the handler. In some cases the output of the translating program is completely appropriate, and no intermediate shell-script is needed.

Input handlers are called with a single argument which is the source file name. They should output the result to stdout.

When writing a handler, you should decide if it will output plain text or HTML. Plain text is simpler, but you will not be able to add metadata or vary the output character encoding (this will be defined in a configuration file). Additionally, some formatting may be easier to preserve when previewing HTML. Actually the deciding factor is metadata: Recoll has a way to extract metadata from the HTML header and use it for field searches..

The RECOLL_FILTER_FORPREVIEW environment variable (values yes, no) tells the handler if the operation is for indexing or previewing. Some handlers use this to output a slightly different format, for example stripping uninteresting repeated keywords (e.g.: Subject: for email) when indexing. This is not essential.

You should look at one of the simple handlers, for example rclps for a starting point.

Don't forget to make your handler executable before testing !

4.1.2. "Multiple" handlers

If you can program and want to write an execm handler, it should not be too difficult to make sense of one of the existing handlers.

The existing handlers differ in the amount of helper code which they are using:

  • rclimg is written in Perl and handles the execm protocol all by itself (showing how trivial it is).

  • All the Python handlers share at least the rclexecm.py module, which handles the communication. Have a look at, for example, rclzip.py for a handler which uses rclexecm.py directly.

  • Most Python handlers which process single-document files by executing another command are further abstracted by using the rclexec1.py module. See for example rclrtf.py for a simple one, or rcldoc.py for a slightly more complicated one (possibly executing several commands).

  • Handlers which extract text from an XML document by using an XSLT style sheet are now executed inside recollindex, with only the style sheet stored in the filters/ directory. These can use a single style sheet (e.g. abiword.xsl), or two sheets for the data and metadata (e.g. opendoc-body.xsl and opendoc-meta.xsl). The mimeconf configuration file defines how the sheets are used, have a look. Before the C++ import, the xsl-based handlers used a common module rclgenxslt.py, it is still around but unused at the moment. The handler for OpenXML presentations is still the Python version because the format did not fit with what the C++ code does. It would be a good base for another similar issue.

There is a sample trivial handler based on rclexecm.py, with many comments, not actually used by Recoll. It would index a text file as one document per line. Look for rcltxtlines.py in the src/filters directory in the online Recoll Git repository (the sample not in the distributed release at the moment).

You can also have a look at the slightly more complex rclzip.py which uses Zip file paths as identifiers (ipath).

execm handlers sometimes need to make a choice for the nature of the ipath elements that they use in communication with the indexer. Here are a few guidelines:

  • Use ASCII or UTF-8 (if the identifier is an integer print it, for example, like printf %d would do).

  • If at all possible, the data should make some kind of sense when printed to a log file to help with debugging.

  • Recoll uses a colon (:) as a separator to store a complex path internally (for deeper embedding). Colons inside the ipath elements output by a handler will be escaped, but would be a bad choice as a handler-specific separator (mostly, again, for debugging issues).

In any case, the main goal is that it should be easy for the handler to extract the target document, given the file name and the ipath element.

execm handlers will also produce a document with a null ipath element. Depending on the type of document, this may have some associated data (e.g. the body of an email message), or none (typical for an archive file). If it is empty, this document will be useful anyway for some operations, as the parent of the actual data documents.

4.1.3. Telling Recoll about the handler

There are two elements that link a file to the handler which should process it: the association of file to MIME type and the association of a MIME type with a handler.

The association of files to MIME types is mostly based on name suffixes. The types are defined inside the mimemap file. Example:


            .doc = application/msword
          

If no suffix association is found for the file name, Recoll will try to execute a system command (typically file -i or xdg-mime) to determine a MIME type.

The second element is the association of MIME types to handlers in the mimeconf file. A sample will probably be better than a long explanation:


          [index]
          application/msword = exec antiword -t -i 1 -m UTF-8;\
          mimetype = text/plain ; charset=utf-8

          application/ogg = exec rclogg

          text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html

          application/x-chm = execm rclchm.py
        

The fragment specifies that:

  • application/msword files are processed by executing the antiword program, which outputs text/plain encoded in utf-8.

  • application/ogg files are processed by the rclogg script, with default output type (text/html, with encoding specified in the header, or utf-8 by default).

  • text/rtf is processed by unrtf, which outputs text/html. The iso-8859-1 encoding is specified because it is not the utf-8 default, and not output by unrtf in the HTML header section.

  • application/x-chm is processed by a persistent handler. This is determined by the execm keyword.

4.1.4. Input handler output

Both the simple and persistent input handlers can return any MIME type to Recoll, which will further process the data according to the MIME configuration.

Most input filters filters produce either text/plain or text/html data. There are exceptions, for example, filters which process archive file (zip, tar, etc.) will usually return the documents as they are found, without processing them further.

There is nothing to say about text/plain output, except that its character encoding should be consistent with what is specified in the mimeconf file.

For filters producing HTML, the output could be very minimal like the following example:

          <html>
            <head>
              <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
            </head>
            <body>
              Some text content
            </body>
          </html>
          

You should take care to escape some characters inside the text by transforming them into appropriate entities. At the very minimum, "&" should be transformed into "&amp;", "<" should be transformed into "&lt;". This is not always properly done by external helper programs which output HTML, and of course never by those which output plain text.

When encapsulating plain text in an HTML body, the display of a preview may be improved by enclosing the text inside <pre> tags.

The character set needs to be specified in the header. It does not need to be UTF-8 (Recoll will take care of translating it), but it must be accurate for good results.

Recoll will process meta tags inside the header as possible document fields candidates. Documents fields can be processed by the indexer in different ways, for searching or displaying inside query results. This is described in a following section.

By default, the indexer will process the standard header fields if they are present: title, meta/description, and meta/keywords are both indexed and stored for query-time display.

A predefined non-standard meta tag will also be processed by Recoll without further configuration: if a date tag is present and has the right format, it will be used as the document date (for display and sorting), in preference to the file modification date. The date format should be as follows:

          <meta name="date" content="YYYY-mm-dd HH:MM:SS">
          or
          <meta name="date" content="YYYY-mm-ddTHH:MM:SS">
        

Example:

          <meta name="date" content="2013-02-24 17:50:00">
        

Input handlers also have the possibility to "invent" field names. This should also be output as meta tags:

          <meta name="somefield" content="Some textual data" />
        

You can embed HTML markup inside the content of custom fields, for improving the display inside result lists. In this case, add a (wildly non-standard) markup attribute to tell Recoll that the value is HTML and should not be escaped for display.

          <meta name="somefield" markup="html" content="Some <i>textual</i> data" />
        

As written above, the processing of fields is described in a further section.

Persistent filters can use another, probably simpler, method to produce metadata, by calling the setfield() helper method. This avoids the necessity to produce HTML, and any issue with HTML quoting. See, for example, rclaudio.py in Recoll 1.23 and later for an example of handler which outputs text/plain and uses setfield() to produce metadata.

4.1.5. Page numbers

The indexer will interpret ^L characters in the handler output as indicating page breaks, and will record them. At query time, this allows starting a viewer on the right page for a hit or a snippet. Currently, only the PDF, Postscript and DVI handlers generate page breaks.

4.2. Field data processing

Fields are named pieces of information in or about documents, like title, author, abstract.

The field values for documents can appear in several ways during indexing: either output by input handlers as meta fields in the HTML header section, or extracted from file extended attributes, or added as attributes of the Doc object when using the API, or again synthetized internally by Recoll.

The Recoll query language allows searching for text in a specific field.

Recoll defines a number of default fields. Additional ones can be output by handlers, and described in the fields configuration file.

Fields can be:

  • indexed, meaning that their terms are separately stored in inverted lists (with a specific prefix), and that a field-specific search is possible.

  • stored, meaning that their value is recorded in the index data record for the document, and can be returned and displayed with search results.

A field can be either or both indexed and stored. This and other aspects of fields handling is defined inside the fields configuration file.

Some fields may also designated as supporting range queries, meaning that the results may be selected for an interval of its values. See the configuration section for more details.

The sequence of events for field processing is as follows:

  • During indexing, recollindex scans all meta fields in HTML documents (most document types are transformed into HTML at some point). It compares the name for each element to the configuration defining what should be done with fields (the fields file)

  • If the name for the meta element matches one for a field that should be indexed, the contents are processed and the terms are entered into the index with the prefix defined in the fields file.

  • If the name for the meta element matches one for a field that should be stored, the content of the element is stored with the document data record, from which it can be extracted and displayed at query time.

  • At query time, if a field search is performed, the index prefix is computed and the match is only performed against appropriately prefixed terms in the index.

  • At query time, the field can be displayed inside the result list by using the appropriate directive in the definition of the result list paragraph format. All fields are displayed on the fields screen of the preview window (which you can reach through the right-click menu). This is independent of the fact that the search which produced the results used the field or not.

You can find more information in the section about the fields file, or in comments inside the file.

You can also have a look at the example in the FAQs area, detailing how one could add a page count field to pdf documents for displaying inside result lists.

4.3. Python API

4.3.1. Introduction

The Recoll Python programming interface can be used both for searching and for creating/updating an index. Bindings exist for Python2 and Python3 (Jan 2021: python2 support will be dropped soon).

The search interface is used in a number of active projects: the Recoll Gnome Shell Search Provider , the Recoll Web UI, and the upmpdcli UPnP Media Server, in addition to many small scripts.

The index update section of the API may be used to create and update Recoll indexes on specific configurations (separate from the ones created by recollindex). The resulting databases can be queried alone, or in conjunction with regular ones, through the GUI or any of the query interfaces.

The search API is modeled along the Python database API version 2.0 specification (early versions used the version 1.0 spec).

The recoll package contains two modules:

  • The recoll module contains functions and classes used to query (or update) the index.

  • The rclextract module contains functions and classes used at query time to access document data. The recoll module must be imported before rclextract

There is a good chance that your system repository has packages for the Recoll Python API, sometimes in a package separate from the main one (maybe named something like python-recoll). Else refer to the Building from source chapter.

As an introduction, the following small sample will run a query and list the title and url for each of the results. The python/samples source directory contains several examples of Python programming with Recoll, exercising the extension more completely, and especially its data extraction features.

#!/usr/bin/python3

from recoll import recoll

db = recoll.connect()
query = db.query()
nres = query.execute("some query")
results = query.fetchmany(20)
for doc in results:
    print("%s %s" % (doc.url, doc.title))

You can also take a look at the source for the Recoll WebUI, the upmpdcli local media server, or the Gnome Shell Search Provider.

4.3.2. Interface elements

A few elements in the interface are specific and and need an explanation.

ipath

This data value (set as a field in the Doc object) is stored, along with the URL, but not indexed by Recoll. Its contents are not interpreted by the index layer, and its use is up to the application. For example, the Recoll file system indexer uses the ipath to store the part of the document access path internal to (possibly imbricated) container documents. ipath in this case is a vector of access elements (e.g, the first part could be a path inside a zip file to an archive member which happens to be an mbox file, the second element would be the message sequential number inside the mbox etc.). url and ipath are returned in every search result and define the access to the original document. ipath is empty for top-level document/files (e.g. a PDF document which is a filesystem file). The Recoll GUI knows about the structure of the ipath values used by the filesystem indexer, and uses it for such functions as opening the parent of a given document.

udi

An udi (unique document identifier) identifies a document. Because of limitations inside the index engine, it is restricted in length (to 200 bytes), which is why a regular URI cannot be used. The structure and contents of the udi is defined by the application and opaque to the index engine. For example, the internal file system indexer uses the complete document path (file path + internal path), truncated to length, the suppressed part being replaced by a hash value. The udi is not explicit in the query interface (it is used "under the hood" by the rclextract module), but it is an explicit element of the update interface.

parent_udi

If this attribute is set on a document when entering it in the index, it designates its physical container document. In a multilevel hierarchy, this may not be the immediate parent. If the indexer uses the purge() method, then the use of parent_udi is mandatory for subdocuments. Else it is optional, but its use by an indexer may simplify index maintenance, as Recoll will automatically delete all children defined by parent_udi == udi when the document designated by udi is destroyed. e.g. if a Zip archive contains entries which are themselves containers, like mbox files, all the subdocuments inside the Zip file (mbox, messages, message attachments, etc.) would have the same parent_udi, matching the udi for the Zip file, and all would be destroyed when the Zip file (identified by its udi) is removed from the index.

Stored and indexed fields

The fields file inside the Recoll configuration defines which document fields are either indexed (searchable), stored (retrievable with search results), or both. Apart from a few standard/internal fields, only the stored fields are retrievable through the Python search interface.

4.3.3. Log messages for Python scripts

Two specific configuration variables: pyloglevel and pylogfilename allow overriding the generic values for Python programs. Set pyloglevel to 2 to suppress default startup messages (printed at level 3).

4.3.4. Python search interface

The recoll module

connect(confdir=None, extra_dbs=None, writable = False)

The connect() function connects to one or several Recoll index(es) and returns a Db object.

This call initializes the recoll module, and it should always be performed before any other call or object creation.

  • confdir may specify a configuration directory. The usual defaults apply.

  • extra_dbs is a list of additional indexes (Xapian directories).

  • writable decides if we can index new data through this connection.

Examples:

from recoll import recoll

# Opening the default db            
db = recoll.connect()

# Opening the default db and a pair of additional indexes            
db = recoll.connect(extra_dbs=["/home/me/.someconfdir/xapiandb", "/data/otherconf/xapiandb"])
The Db class

A Db object is created by a connect() call and holds a connection to a Recoll index.

Db.query(), Db.cursor()

These (synonym) methods return a blank Query object for this index.

Db.termMatch(match_type, expr, field='', maxlen=-1, casesens=False, diacsens=False, lang='english')

Expand an expression against the index term list. Performs the basic function from the GUI term explorer tool. match_type can be one of wildcard, regexp or stem. Returns a list of terms expanded from the input expression.

Db.setAbstractParams(maxchars, contextwords)

Set the parameters used to build snippets (sets of keywords in context text fragments). maxchars defines the maximum total size of the abstract. contextwords defines how many terms are shown around the keyword.

Db.close()

Closes the connection. You can't do anything with the Db object after this.

The Query class

A Query object (equivalent to a cursor in the Python DB API) is created by a Db.query() call. It is used to execute index searches.

Query.sortby(fieldname, ascending=True)

Sets the sorting order for future searches for using fieldname, in ascending or descending order. Must be called before executing the search.

Query.execute(query_string, stemming=1, stemlang="english", fetchtext=False, collapseduplicates=False)

Starts a search for query_string, a Recoll search language string. If the index stores the document texts and fetchtext is True, store the document extracted text in doc.text.

Query.executesd(SearchData, fetchtext=False, collapseduplicates=False)

Starts a search for the query defined by the SearchData object. If the index stores the document texts and fetchtext is True, store the document extracted text in doc.text.

Query.fetchmany(size=query.arraysize)

Fetches the next Doc objects in the current search results, and returns them as an array of the required size, which is by default the value of the arraysize data member.

Query.fetchone()

Fetches the next Doc object from the current search results. Generates a StopIteration exception if there are no results left.

Query.__iter__() and Query.next()

So that things like for doc in query: will work.

Example:
from recoll import recoll

db = recoll.connect()
q = db.query()
nres = q.execute("some query")
for doc in q:
    print("%s" % doc.title)
              
Query.close()

Closes the query. The object is unusable after the call.

Query.scroll(value, mode='relative')

Adjusts the position in the current result set. mode can be relative or absolute.

Query.getgroups()

Retrieves the expanded query terms as a list of pairs. Meaningful only after executexx In each pair, the first entry is a list of user terms (of size one for simple terms, or more for group and phrase clauses), the second a list of query terms as derived from the user terms and used in the Xapian Query.

Query.getxquery()

Return the Xapian query description as a Unicode string. Meaningful only after executexx.

Query.highlight(text, ishtml = 0, methods = object)

Will insert <span "class=rclmatch">, </span> tags around the match areas in the input text and return the modified text. ishtml can be set to indicate that the input text is HTML and that HTML special characters should not be escaped. methods if set should be an object with methods startMatch(i) and endMatch() which will be called for each match and should return a begin and end tag

Query.makedocabstract(doc, methods = object))

Create a snippets abstract for doc (a Doc object) by selecting text around the match terms. If methods is set, will also perform highlighting. See the highlight method.

Query.getsnippets(doc, maxoccs = -1, ctxwords = -1, sortbypage=False, methods = object)

Will return a list of extracts from the result document by selecting text around the match terms. Each entry in the result list is a triple: page number, term, text. By default, the most relevants snippets appear first in the list. Set sortbypage to sort by page number instead. If methods is set, the fragments will be highlighted (see the highlight method). If maxoccs is set, it defines the maximum result list length. ctxwords allows adjusting the individual snippet context size.

Query.arraysize

Default number of records processed by fetchmany (r/w).

Query.rowcount

Number of records returned by the last execute.

Query.rownumber

Next index to be fetched from results. Normally increments after each fetchone() call, but can be set/reset before the call to effect seeking (equivalent to using scroll()). Starts at 0.

The Doc class

A Doc object contains index data for a given document. The data is extracted from the index when searching, or set by the indexer program when updating. The Doc object has many attributes to be read or set by its user. It mostly matches the Rcl::Doc C++ object. Some of the attributes are predefined, but, especially when indexing, others can be set, the name of which will be processed as field names by the indexing configuration. Inputs can be specified as Unicode or strings. Outputs are Unicode objects. All dates are specified as Unix timestamps, printed as strings. Please refer to the rcldb/rcldoc.cpp C++ file for a full description of the predefined attributes. Here follows a short list.

  • url the document URL but see also getbinurl()

  • ipath the document ipath for embedded documents.

  • fbytes, dbytes the document file and text sizes.

  • fmtime, dmtime the document file and document times.

  • xdocid the document Xapian document ID. This is useful if you want to access the document through a direct Xapian operation.

  • mtype the document MIME type.

  • Fields stored by default: author, filename, keywords, recipient

At query time, only the fields that are defined as stored either by default or in the fields configuration file will be meaningful in the Doc object. The document processed text may be present or not, depending if the index stores the text at all, and if it does, on the fetchtext query execute option. See also the rclextract module for accessing document contents.

get(key), [] operator

Retrieve the named document attribute. You can also use getattr(doc, key) or doc.key.

doc.key = value

Set the the named document attribute. You can also use setattr(doc, key, value).

getbinurl()

Retrieve the URL in byte array format (no transcoding), for use as parameter to a system call.

setbinurl(url)

Set the URL in byte array format (no transcoding).

items()

Return a dictionary of doc object keys/values

keys()

list of doc object keys (attribute names).

The SearchData class

A SearchData object allows building a query by combining clauses, for execution by Query.executesd(). It can be used in replacement of the query language approach. The interface is going to change a little, so no detailed doc for now...

addclause(type='and'|'or'|'excl'|'phrase'|'near'|'sub', qstring=string, slack=0, field='', stemming=1, subSearch=SearchData)

The rclextract module

Prior to Recoll 1.25, index queries could not provide document content because it was never stored. Recoll 1.25 and later usually store the document text, which can be optionally retrieved when running a query (see query.execute() above - the result is always plain text).

Independantly, the rclextract module can give access to the original document and to the document text content, possibly as an HTML version. Accessing the original document is particularly useful if it is embedded (e.g. an email attachment).

You need to import the recoll module before the rclextract module.

The Extractor class
Extractor(doc)

An Extractor object is built from a Doc object, output from a query.

Extractor.textextract(ipath)

Extract document defined by ipath and return a Doc object. The doc.text field has the document text converted to either text/plain or text/html according to doc.mimetype. The typical use would be as follows:

from recoll import recoll, rclextract

qdoc = query.fetchone()
extractor = rclextract.Extractor(qdoc)
doc = extractor.textextract(qdoc.ipath)
# use doc.text, e.g. for previewing

Passing qdoc.ipath to textextract() is redundant, but reflects the fact that the Extractor object actually has the capability to access the other entries in a compound document.

Extractor.idoctofile(ipath, targetmtype, outfile='')

Extracts document into an output file, which can be given explicitly or will be created as a temporary file to be deleted by the caller. Typical use:

from recoll import recoll, rclextract

qdoc = query.fetchone()
extractor = rclextract.Extractor(qdoc)
filename = extractor.idoctofile(qdoc.ipath, qdoc.mimetype)

In all cases the output is a copy, even if the requested document is a regular system file, which may be wasteful in some cases. If you want to avoid this, you can test for a simple file document as follows:

not doc.ipath and (not "rclbes" in doc.keys() or doc["rclbes"] == "FS")

Search API usage example

The following sample would query the index with a user language string. See the python/samples directory inside the Recoll source for other examples. The recollgui subdirectory has a very embryonic GUI which demonstrates the highlighting and data extraction functions.

#!/usr/bin/python3

from recoll import recoll

db = recoll.connect()
db.setAbstractParams(maxchars=80, contextwords=4)

query = db.query()
nres = query.execute("some user question")
print("Result count: %d" % nres)
if nres > 5:
    nres = 5
for i in range(nres):
    doc = query.fetchone()
    print("Result #%d" % (query.rownumber))
    for k in ("title", "size"):
        print("%s : %s" % (k, getattr(doc, k)))
    print("%s\n" % db.makeDocAbstract(doc, query))

4.3.5. Python indexing interface

Recoll external indexers

The Recoll indexer is capable of processing many different document formats. However, some forms of data storage do not lend themselves easily to standard processing because of their great variability. A canonical example would be data in an SQL database. While it might be possible to create a configurable filter to process data from a database, the many variations in storage organisation and SQL dialects make this difficult.

Recoll can instead support external indexers where all the responsibility to handle the data format is delegated to an external script. The script language has to be Python 3 at the moment, because this is the only language for which an API binding exists.

Up to Recoll 1.35, such an indexer had to work on a separate Recoll index, which would be added as an external index for querying from the main one, and for which a separate indexing schedule had to be managed. The reason was that the main document indexer purge pass (removal of deleted documents) would also remove all the documents belonging to the external indexer, as they were not seen during the filesystem walk (and conversely, the external indexer purge pass would delete all the regular document entries).

As of Recoll 1.36, an improvement and new API call allows external indexers to be fully integrated, and work on the main index, with updates triggered from the normal recollindex program.

An external indexer has to do the same work as the Recoll file system indexer: look for modified documents, extract their text, call the API for indexing them, and the one for purging the data for deleted documents.

A description of the API method follows, but you can also jump ahead for a look at some sample pseudo-code and a pair of actual implementations, one of which does something useful.

The Python indexing API

There are two parts in the indexing interface:

  • Methods inside the recoll module allow the foreign indexer to update the index.

  • An interface based on scripts execution is defined for executing the indexer (from recollindex) and to allow either the GUI or the rclextract module to access original document data for previewing or editing.

Two sample scripts are included with the Recoll source and described in more detail a bit further.

Python indexing interface methods

The update methods are part of the recoll module. The connect() method is used with a writable=true parameter to obtain a writable Db object. The following Db object methods are then available.

addOrUpdate(udi, doc, parent_udi=None)

Add or update index data for a given document. The udi string must define a unique id for the document. It is an opaque interface element and not interpreted inside Recoll. doc is a Doc object, created from the data to be indexed (the main text should be in doc.text). If parent_udi is set, this is a unique identifier for the top-level container, the document for which needUpdate() would be called (e.g. for the filesystem indexer, this would be the one which is an actual file). The doc url and possibly ipath fields should also be set to allow access to the actual document after a query. Other fields should also be set to allow further access to the data, see the description further down: rclbes, sig, mimetype. Of course, any standard or custom Recoll field can also be added.

delete(udi)

Purge the index from all data for udi, and all documents (if any) which have udi as parent_udi.

needUpdate(udi, sig)

Test if the index needs to be updated for the document identified by udi. If this call is to be used, the doc.sig field should contain a signature value when calling addOrUpdate(). The needUpdate() call then compares its parameter value with the stored sig for udi. sig is an opaque value, compared as a string.

The filesystem indexer uses a concatenation of the decimal string values for file size and update time, but a hash of the contents could also be used.

As a side effect, if the return value is false (the index is up to date), the call will set the existence flag for the document (and any subdocument defined by its parent_udi), so that a later purge() call will preserve them.

The use of needUpdate() and purge() is optional, and the indexer may use another method for checking the need to reindex or to delete stale entries.

preparePurge(backend_name)

Mark all documents which do *not* belong to backend_name. backend_name is the value chosen for the rclbes field for the indexer documents (e.g. "MBOX", "JOPLIN"... for the samples). This is a mandatory call before starting an update if the index is shared with other backends and you are going to call purge() after the update, else all documents for other backends will be deleted from the index by the purge.

purge()

Delete all documents that were not touched during the just finished indexing pass (since preparePurge()). These are the documents for which the needUpdate() call was not performed, indicating that they no longer exist in the storage system.

createStemDbs(lang|sequence of langs)

Create stemming dictionaries for query stemming expansion. Note that this is not needed at all if the indexing is done from the recollindex program, as it will perform this action after calling all the external indexers. Should be called when done updating the index. Available only after Recoll 1.34.3. As an alternative, you can close the index and execute:

recollindex -c <confdir> -s <lang(s)>

The Python module currently has no interface to the Aspell speller functions, so the same approach can be used for creating the spelling dictionary (with option -S) (again, not needed if recollindex is driving the indexing).

Query data access for external indexers

Recoll has internal methods to access document data for its internal (filesystem) indexer. An external indexer needs to provide data access methods if it needs integration with the GUI (e.g. preview function), or support for the rclextract module.

An external indexer needs to provide two commands, for fetching data (typically for previewing) and for computing the document signature (for up-to-date checks when opening or previewing). The sample MBOX and JOPLIN implementations use the same script with different parameters to perform both operations, but this is just a choice. A third command must be provided for performing the indexing proper.

The "fetch" and "makesig" scripts are called with three additional arguments: udi, url, ipath. These were set by the indexer and stored with the document by the addOrUpdate() call described above. Not all arguments are needed in all cases, the script will use what it needs to perform the requested operation. The caller expects the result data on stdout.

recollindex will set the RECOLL_CONFDIR environment variable when executing the scripts, so that the configuration can be created as

rclconf = rclconfig.RclConfig()

if needed, and the configuration directory obtained as

confdir = rclconf.getConfDir()

External indexers configuration

The index data and the access method are linked by the rclbes (recoll backend storage) Doc field. You should set this to a short string value identifying your indexer (e.g. the filesystem indexer uses either FS or an empty value, the Web history indexer uses BGL, the Joplin notes indexer uses JOPLIN).

The link is actually performed inside a backends configuration file (stored in the configuration directory). This defines commands to execute to access data from the specified indexer. Example, for the mbox indexing sample found in the Recoll source (which sets rclbes="MBOX"):

[MBOX]
fetch = /path/to/recoll/src/python/samples/rclmbox.py fetch
makesig = /path/to/recoll/src/python/samples/rclmbox.py makesig
index = /path/to/recoll/src/python/samples/rclmbox.py index

When updating the index, the recollindex will execute the value of the the index parameter, if present (it may not be present if this concerns an external index).

If an external indexer needs to store additional configuration parameters, e.g. path to a specific instance of the indexed application, etc., I suggest storing them inside recoll.conf, with a backend-specific prefix (e.g. joplin_db, mbox_directory) and using methods from the rclconfig module to access them.

External indexer samples

First a quick look at an indexer main part, using pseudo-Python3 code:

    # Connect to the recoll index. This will use the RECOLL_CONFDIR variable, set
    # by the parent recollindex process, to use the right index.
    rcldb = recoll.connect(writable=1)

    # Important: tell the Recoll db that we are going to update documents for the
    # MYBACK backend. All other documents will be marked as present so as
    # not to be affect by the subsequent purge.
    rcldb.preparePurge("MYBACK")

    # Walk your dataset (of course your code will not look like this)
    for mydoc in mydoclist:
        # Compute the doc unique identifier and the signature corresponding to its update state
        # (e.g. mtime and size for a file).
        udi = mydoc.udi()
        sig = mydoc.sig()
        # Check with recoll if the document needs updating. This has the side-effect or marking
        # it present.
        if not rcldb.needUpdate(udi, sig):
            continue
        # The document data does not exist in the index or needs updating. Create and add a Recoll
        # Doc object
        doc = recoll.Doc()
        doc.mimetype = "some/type"
        # Say that the document belongs to this indexer
        doc.rclbes = "MYBACK"
        # The url will be passed back to you along with the udi if the fetch
        # method is called later (for previewing), or may be used for opening the document with
        # its native app from Recoll. The udi has a maximum size because it is used as a Xapian
        # term. The url has no such limitation.
        doc.url = "someurl"
        doc.sig = sig
        # Of course add other fields like "text" (duh), "author" etc. See the samples.
        doc.text = mydoc.text()
        # [...]
        # Then add or update the data in the index.
        self.db.addOrUpdate(udi, doc)

    # Finally call purge to delete the data for documents which were not seen at all.
    db.purge()

The Recoll source tree has two samples of external indexers.

  • rclmbox.py indexes a directory containing mbox folder files. Of course it is not really useful because Recoll can do this by itself, but it exercises most features in the update interface, and it has both top-level and embedded documents so it demonstrates the uses of the ipath values.
  • rcljoplin.py indexes a Joplin application main notes SQL table. Joplin sets an an update date attribute for each record in the table, so each note record can be processed as a standalone document (no ipath necessary). The sample has full preview and open support (the latter using a Joplin callback URL which allows displaying the result note inside the native app), so it could actually be useful to perform a unified search of the Joplin data and the regular Recoll data.

See the comments inside the scripts for more information.

Using an external indexer index in conjunction with a regular one

When adding an external indexer to a regular one for unified querying, some elements of the foreign index configuration should be copied or merged into the main index configuration. At the very least, the backends file needs to be copied or merged, and also possibly data from the mimeconf and mimeview files. See the rcljoplin.py sample for an example.

Chapter 5. Installation and configuration

5.1. Installing a binary copy

Recoll binary copies are always distributed as regular packages for your system. They can be obtained either through the system's normal software distribution framework (e.g. Debian/Ubuntu apt, FreeBSD ports, etc.), or from some type of "backports" repository providing versions newer than the standard ones, or found on the Recoll Web site in some cases. The most up-to-date information about Recoll packages can usually be found on the Recoll Web site downloads page

The Windows version of Recoll comes in a self-contained setup file, there is nothing else to install.

On Unix-like systems, the package management tools will automatically install hard dependencies for packages obtained from a proper package repository. You will have to deal with them by hand for downloaded packages (for example, when dpkg complains about missing dependencies).

In all cases, you will have to check or install supporting applications for the file types that you want to index beyond those that are natively processed by Recoll (text, HTML, email files, and a few others).

You should also maybe have a look at the configuration section (but this may not be necessary for a quick test with default parameters). Most parameters can be more conveniently set from the GUI interface.

5.2. Supporting packages

Note

The Windows installation of Recoll is self-contained. Windows users can skip this section.

Recoll uses external applications to index some file types. You need to install them for the file types that you wish to have indexed (these are run-time optional dependencies. None is needed for building or running Recoll except for indexing their specific file type).

After an indexing pass, the commands that were found missing can be displayed from the recoll File menu. The list is stored in the missing text file inside the configuration directory.

The past has proven that I was unable to maintain an up to date application list in this manual. Please check http://www.recoll.org/pages/features.html for a complete list along with links to the home pages or best source/patches pages, and misc tips. What follows is only a very short extract of the stable essentials.

  • PDF files need pdftotext which is part of Poppler (usually comes with the poppler-utils package). Avoid the original one from Xpdf.

  • MS Word documents need antiword. It is also useful to have wvWare installed as it may be be used as a fallback for some files which antiword does not handle.

  • RTF files need unrtf, which, in its older versions, has much trouble with non-western character sets. Many Linux distributions carry outdated unrtf versions. Check http://www.recoll.org/pages/features.html for details.

  • Pictures: Recoll uses the Exiftool Perl package to extract tag information. Most image file formats are supported.

  • Up to Recoll 1.24, many XML-based formats need the xsltproc command, which usually comes with libxslt. These are: abiword, fb2 ebooks, kword, openoffice, opendocument svg. Recoll 1.25 and later process them internally (using libxslt).

5.3. Building from source

5.3.1. Prerequisites

The following prerequisites are described in broad terms and Debian package names. The dependencies should be available as packages on most common Unix derivatives, and it should be quite uncommon that you would have to build one of them. Finding the right package name for other systems is left to the sagacity of the reader.

If you do not need the GUI, you can avoid all GUI dependencies by disabling its build. (See the configure section further down: --disable-qtgui).

The shopping list follows:

  • If you start from git code, you will need the git command (git), and the autoconf, automake and libtool triad (autoconf, automake, libtool). These are not needed for building from tar distributions.

  • The pkg-config command and gettext package are needed for configuring the build (pkg-config, gettext).

  • The make command (make which will actually install GNU make on Linux). If you get strange error messages about dependency tracking from configure, you probably forgot to install make.

  • A C++ compiler with at least C++14 compatibility (g++ or clang). Versions 1.33.4 and older only require c++11.

  • The bison command is not generally needed, but might be if some file modification times are not right.

  • If you want to process CHM files, you will need libchm (libchm-dev), else you can set the --disable-python-chm option to the configure command.

  • For building the documentation: the xsltproc command, and the Docbook XML and style sheet files. You can avoid this dependency by disabling documentation building with the --disable-userdoc configure option.

  • Development files for Xapian core (libxapian-dev).

    Important

    If you are building Xapian for an older CPU (before Pentium 4 or Athlon 64), you need to add the --disable-sse flag to the configure command. Else all Xapian applications will crash with an illegal instruction error.

  • Development files for libxslt (libxslt1-dev).

  • Development files for zlib (zlib1g-dev).

  • Development files for libaspell (libaspell-dev). Can be avoided with the --disable-aspell option to configure.

  • If you want to build the GUI: development files for Qt 5. Else give the --disable-qtgui option to the configure command. You will probably need qtbase5-dev and qttools5-dev-tools I can never remember what other packages to install, but installing the Qt5 Webkit, (or Qt5 Webengine if you set --enable-webengine) will usually bring them as dependencies (libqt5webkit5-dev or qtwebengine5dev). The Recoll GUI can use either Webkit or Webengine for displaying the results.

  • Development files for Python3 (python3-all-dev, python3-setuptools). You can use the --disable-python-module option for disabling the build of the Python extension. On older systems still supporting Python2, you can also install python2-dev and python-setuptools. The build will test for the presence of the python2 and/or python3 commands and behave accordingly.

  • You may also need libiconv. On Linux systems, the iconv interface is part of libc and you should not need to do anything special.

Check the Recoll download page for up to date version information.

5.3.2. Building

Recoll has been built on Linux, FreeBSD, MacOS, and Solaris, most versions after 2005 should be ok, maybe some older ones too (Solaris 8 used to be ok). Current Recoll versions (1.34 and later) need a c++14 compiler and Qt5, so they will not build on old systems, but if really needed, you can probably find an older version which will work for you. If you build on another system, and need to modify things, I would very much welcome patches.

Configure options:

--without-aspell will disable the code for phonetic matching of search terms.

--with-fam or --with-inotify will enable the code for real time indexing. Inotify support is enabled by default on Linux systems.

--with-qzeitgeist will enable sending Zeitgeist events about the visited search results, and needs the qzeitgeist package.

--disable-qtgui will disable the Qt graphical interface, which allows building the indexer and the command line search program in absence of a Qt environment.

--disable-webkit will implement the result list with a Qt QTextBrowser instead of a WebKit widget if you do not or can't depend on the latter.

--enable-webengine will enable the use of Qt Webengine (only meaningful if the Qt GUI is enabled), in place or Qt Webkit.

--enable-guidebug will build the recoll GUI program with debug symbols. This makes it very big (~50MB), which is why it is stripped by default.

--disable-idxthreads is available from version 1.19 to suppress multithreading inside the indexing process. You can also use the run-time configuration to restrict recollindex to using a single thread, but the compile-time option may disable a few more unused locks. This only applies to the use of multithreading for the core index processing (data input). The Recoll monitor mode always uses at least two threads of execution.

--disable-python-module will avoid building the Python module.

--disable-python-chm will avoid building the Python libchm interface used to index CHM files.

--enable-camelcase will enable splitting camelCase words. This is not enabled by default as it has the unfortunate side-effect of making some phrase searches quite confusing: ie, "MySQL manual" would be matched by "MySQL manual" and "my sql manual" but not "mysql manual" (only inside phrase searches).

--with-file-command Specify the version of the 'file' command to use (e.g.: --with-file-command=/usr/local/bin/file). Can be useful to enable the gnu version on systems where the native one is bad.

--disable-x11mon Disable X11 connection monitoring inside recollindex. Together with --disable-qtgui, this allows building recoll without Qt and X11.

--disable-userdoc will avoid building the user manual. This avoids having to install the Docbook XML/XSL files and the TeX toolchain used for translating the manual to PDF.

--enable-recollq Enable building the recollq command line query tool (recoll -t without need for Qt). This is done by default if --disable-qtgui is set but this option enables forcing it.

--disable-pic (Recoll versions up to 1.21 only) will compile Recoll with position-dependant code. This is incompatible with building the KIO or the Python or PHP extensions, but might yield very marginally faster code.

--without-systemd Disable the automatic installation of systemd unit files. Normally unit files are installed if the install path can be detected.

--with-system-unit-dir=DIR Provide an install path for the systemd system unit template file.

--with-user-unit-dir=DIR Provide an install path for the systemd user unit file.

Of course the usual autoconf configure options, like --prefix apply.

Normal procedure, for source extracted from a tar distribution)

cd recoll-xxx
./configure [options]
make
(practices usual hardship-repelling invocations)

Building from git code

When building from source cloned from the git repository, you also need to install autoconf, automake, and libtool and you must execute sh autogen.sh in the top source directory before running configure.

5.3.3. Installing

Use make install in the root of the source tree. This will copy the commands to prefix/bin and the sample configuration files, scripts and other shared data to prefix/share/recoll.

5.3.4. Python API package

The Python interface can be found in the source tree, under the python/recoll directory.

The normal Recoll build procedure (see above) installs the API package for both Python2 and Python3. The Python2 version will probably go away in the near future.

The python/recoll/ directory contains the usual setup.py. After configuring and building the main Recoll code, you can use the script to build and install the Python module for a non-standard Python version.

          cd recoll-xxx/python/recoll
          pythonX setup.py build
          sudo pythonX setup.py install
        

5.4. Configuration overview

Most of the parameters specific to the recoll GUI are set through the Preferences menu and stored in the standard Qt place ($HOME/.config/Recoll.org/recoll.conf). You probably do not want to edit this by hand.

Recoll indexing options are set inside text configuration files located in a configuration directory. There can be several such directories, each of which defines the parameters for one index.

The configuration files can be edited by hand or through the Index configuration dialog (Preferences menu). The GUI tool will try to respect your formatting and comments as much as possible, so it is quite possible to use both approaches on the same configuration.

For each index, there are at least two sets of configuration files. System-wide configuration files are kept in a directory named like /usr/share/recoll/examples, and define default values, shared by all indexes (the values in these files are often commented out, and just present to indicate the default coded in the program). For each index, a parallel set of files defines the customized parameters.

On Unix-like systems, the default location of the customized configuration is the .recoll directory in your home. On Windows it is C:/Users/[you]/AppData/Local/Recoll. Most people will only use this directory.

The default location for the configuration directory can be changed, or others can be added for separate indexes with the RECOLL_CONFDIR environment variable or the -c option parameter to recoll and recollindex.

In addition (as of Recoll version 1.19.7), it is possible to specify two additional configuration directories which will be stacked before and after the user configuration directory. These are defined by the RECOLL_CONFTOP and RECOLL_CONFMID environment variables. Values from configuration files inside the top directory will override user ones, values from configuration files inside the middle directory will override system ones and be overridden by user ones. These two variables may be of use to applications which augment Recoll functionality, and need to add configuration data without disturbing the user's files. Please note that the two, currently single, values will probably be interpreted as colon-separated lists in the future: do not use colon characters inside the directory paths.

If the default configuration directory does not exist when recoll or recollindex are started, it will be created with a set of empty configuration files. recoll will give you a chance to edit the configuration file before starting indexing. recollindex will proceed immediately. To avoid mistakes, the automatic directory creation will only occur for the default location, not if -c or RECOLL_CONFDIR were used (in the latter cases, you will have to create the directory).

All configuration files share the same format. For example, a short extract of the main configuration file might look as follows:

        # Space-separated list of files and directories to index.
        topdirs =  ~/docs /usr/share/doc

        [~/somedirectory-with-utf8-txt-files]
        defaultcharset = utf-8
      

There are three kinds of lines:

  • Comment (starts with #) or empty.

  • Parameter affectation (name = value).

  • Section definition ([somedirname]).

Long lines can be broken by ending each incomplete part with a backslash (\).

Depending on the type of configuration file, section definitions either separate groups of parameters or allow redefining some parameters for a directory sub-tree. They stay in effect until another section definition, or the end of file, is encountered. Some of the parameters used for indexing are looked up hierarchically from the current directory location upwards. Not all parameters can be meaningfully redefined, this is specified for each in the next section.

Important

Global parameters must not be defined in a directory subsection, else they will not be found at all by the Recoll code, which looks for them at the top level (e.g. skippedPaths).

When found at the beginning of a file path, the tilde character (~) is expanded to the name of the user's home directory, as a shell would do.

Some parameters are lists of strings. White space is used for separation. List elements with embedded spaces can be quoted using double-quotes. Double quotes inside these elements can be escaped with a backslash.

No value inside a configuration file can contain a newline character. Long lines can be continued by escaping the physical newline with backslash, even inside quoted strings.

        astringlist =  "some string \
        with spaces"
        thesame = "some string with spaces"        
      

Parameters which are not part of string lists can't be quoted, and leading and trailing space characters are stripped before the value is used.

Important

Quotes processing is ONLY applied to parameter values which are lists. Double quoting a single value like, e.g. dbdir will result in an incorrect value, with quotes included. This is quite confusing, and may have been a design mistake but it is much too late to fix.

Encoding issues. Most of the configuration parameters are plain ASCII. Two particular sets of values may cause encoding issues:

  • File path parameters may contain non-ascii characters and should use the exact same byte values as found in the file system directory. Usually, this means that the configuration file should use the system default locale encoding.

  • The unac_except_trans parameter should be encoded in UTF-8. If your system locale is not UTF-8, and you need to also specify non-ascii file paths, this poses a difficulty because common text editors cannot handle multiple encodings in a single file. In this relatively unlikely case, you can edit the configuration file as two separate text files with appropriate encodings, and concatenate them to create the complete configuration.

5.4.1. Environment variables

RECOLL_CONFDIR

Defines the main configuration directory.

RECOLL_TMPDIR, TMPDIR

Locations for temporary files, in this order of priority. The default if none of these is set is to use /tmp. Big temporary files may be created during indexing, mostly for decompressing, and also for processing, e.g. email attachments.

RECOLL_CONFTOP, RECOLL_CONFMID

Allow adding configuration directories with priorities below and above the user directory (see above the Configuration overview section for details).

RECOLL_EXTRA_DBS, RECOLL_ACTIVE_EXTRA_DBS

Help for setting up external indexes. See this paragraph for explanations.

RECOLL_DATADIR

Defines replacement for the default location of Recoll data files, normally found in, e.g., /usr/share/recoll).

RECOLL_FILTERSDIR

Defines replacement for the default location of Recoll filters, normally found in, e.g., /usr/share/recoll/filters).

ASPELL_PROG

aspell program to use for creating the spelling dictionary. The result has to be compatible with the libaspell which Recoll is using.

5.4.2. Recoll main configuration file, recoll.conf

Parameters affecting what documents we index

topdirs

Space-separated list of files or directories to recursively index. Default to ~ (indexes $HOME). You can use symbolic links in the list, they will be followed, independently of the value of the followLinks variable.

monitordirs

Space-separated list of files or directories to monitor for updates. When running the real-time indexer, this allows monitoring only a subset of the whole indexed area. The elements must be included in the tree defined by the 'topdirs' members.

skippedNames

Files and directories which should be ignored. White space separated list of wildcard patterns (simple ones, not paths, must contain no '/' characters), which will be tested against file and directory names.

Have a look at the default configuration for the initial value, some entries may not suit your situation. The easiest way to see it is through the GUI Index configuration "local parameters" panel.

The list in the default configuration does not exclude hidden directories (names beginning with a dot), which means that it may index quite a few things that you do not want. On the other hand, email user agents like Thunderbird usually store messages in hidden directories, and you probably want this indexed. One possible solution is to have ".*" in "skippedNames", and add things like "~/.thunderbird" "~/.evolution" to "topdirs".

Not even the file names are indexed for patterns in this list, see the "noContentSuffixes" variable for an alternative approach which indexes the file names. Can be redefined for any subtree.

skippedNames-

List of name endings to remove from the default skippedNames list.

skippedNames+

List of name endings to add to the default skippedNames list.

onlyNames

Regular file name filter patterns If this is set, only the file names not in skippedNames and matching one of the patterns will be considered for indexing. Can be redefined per subtree. Does not apply to directories.

noContentSuffixes

List of name endings (not necessarily dot-separated suffixes) for which we don't try MIME type identification, and don't uncompress or index content. Only the names will be indexed. This complements the now obsoleted recoll_noindex list from the mimemap file, which will go away in a future release (the move from mimemap to recoll.conf allows editing the list through the GUI). This is different from skippedNames because these are name ending matches only (not wildcard patterns), and the file name itself gets indexed normally. This can be redefined for subdirectories.

noContentSuffixes-

List of name endings to remove from the default noContentSuffixes list.

noContentSuffixes+

List of name endings to add to the default noContentSuffixes list.

skippedPaths

Absolute paths we should not go into. Space-separated list of wildcard expressions for absolute filesystem paths (for files or directories). The variable must be defined at the top level of the configuration file, not in a subsection.

Any value in the list must be textually consistent with the values in topdirs, no attempts are made to resolve symbolic links. In practise, if, as is frequently the case, /home is a link to /usr/home, your default topdirs will have a single entry '~' which will be translated to '/home/yourlogin'. In this case, any skippedPaths entry should start with '/home/yourlogin' *not* with '/usr/home/yourlogin'.

The index and configuration directories will automatically be added to the list.

The expressions are matched using 'fnmatch(3)' with the FNM_PATHNAME flag set by default. This means that '/' characters must be matched explicitly. You can set 'skippedPathsFnmPathname' to 0 to disable the use of FNM_PATHNAME (meaning that '/*/dir3' will match '/dir1/dir2/dir3').

The default value contains the usual mount point for removable media to remind you that it is in most cases a bad idea to have Recoll work on these Explicitly adding '/media/xxx' to the 'topdirs' variable will override this.

skippedPathsFnmPathname

Set to 0 to override use of FNM_PATHNAME for matching skipped paths.

nowalkfn

File name which will cause its parent directory to be skipped. Any directory containing a file with this name will be skipped as if it was part of the skippedPaths list. Ex: .recoll-noindex

daemSkippedPaths

skippedPaths equivalent specific to real time indexing. This enables having parts of the tree which are initially indexed but not monitored. If daemSkippedPaths is not set, the daemon uses skippedPaths.

zipUseSkippedNames

Use skippedNames inside Zip archives. Fetched directly by the rclzip.py handler. Skip the patterns defined by skippedNames inside Zip archives. Can be redefined for subdirectories. See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html

zipSkippedNames

Space-separated list of wildcard expressions for names that should be ignored inside zip archives. This is used directly by the zip handler. If zipUseSkippedNames is not set, zipSkippedNames defines the patterns to be skipped inside archives. If zipUseSkippedNames is set, the two lists are concatenated and used. Can be redefined for subdirectories. See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html

followLinks

Follow symbolic links during indexing. The default is to ignore symbolic links to avoid multiple indexing of linked files. No effort is made to avoid duplication when this option is set to true. This option can be set individually for each of the 'topdirs' members by using sections. It can not be changed below the 'topdirs' level. Links in the 'topdirs' list itself are always followed.

indexedmimetypes

Restrictive list of indexed mime types. Normally not set (in which case all supported types are indexed). If it is set, only the types from the list will have their contents indexed. The names will be indexed anyway if indexallfilenames is set (default). MIME type names should be taken from the mimemap file (the values may be different from xdg-mime or file -i output in some cases). Can be redefined for subtrees.

excludedmimetypes

List of excluded MIME types. Lets you exclude some types from indexing. MIME type names should be taken from the mimemap file (the values may be different from xdg-mime or file -i output in some cases) Can be redefined for subtrees.

nomd5types

Don't compute md5 for these types. md5 checksums are used only for deduplicating results, and can be very expensive to compute on multimedia or other big files. This list lets you turn off md5 computation for selected types. It is global (no redefinition for subtrees). At the moment, it only has an effect for external handlers (exec and execm). The file types can be specified by listing either MIME types (e.g. audio/mpeg) or handler names (e.g. rclaudio.py).

compressedfilemaxkbs

Size limit for compressed files. We need to decompress these in a temporary directory for identification, which can be wasteful in some cases. Limit the waste. Negative means no limit. 0 results in no processing of any compressed file. Default 100 MB.

textfilemaxmbs

Size limit for text files. Mostly for skipping monster logs. Default 20 MB. Use a value of -1 to disable.

textunknownasplain

Process unknown text/xxx files as text/plain Allows indexing misc. text files identified as text/whatever by 'file' or 'xdg-mime' without having to explicitely set config entries for them. This works fine for indexing (but will cause processing of a lot of garbage though), but the documents indexed this way will be opened by the desktop viewer, even if text/plain has a specific editor.

indexallfilenames

Index the file names of unprocessed files Index the names of files the contents of which we don't index because of an excluded or unsupported MIME type.

usesystemfilecommand

Use a system command for file MIME type guessing as a final step in file type identification This is generally useful, but will usually cause the indexing of many bogus 'text' files. See 'systemfilecommand' for the command used.

systemfilecommand

Command used to guess MIME types if the internal methods fails This should be a "file -i" workalike. The file path will be added as a last parameter to the command line. "xdg-mime" works better than the traditional "file" command, and is now the configured default (with a hard-coded fallback to "file")

processwebqueue

Decide if we process the Web queue. The queue is a directory where the Recoll Web browser plugins create the copies of visited pages.

textfilepagekbs

Page size for text files. If this is set, text/plain files will be divided into documents of approximately this size. Will reduce memory usage at index time and help with loading data in the preview window at query time. Particularly useful with very big files, such as application or system logs. Also see textfilemaxmbs and compressedfilemaxkbs.

membermaxkbs

Size limit for archive members. This is passed to the filters in the environment as RECOLL_FILTER_MAXMEMBERKB.

Parameters affecting how we generate terms and organize the index

indexStripChars

Decide if we store character case and diacritics in the index. If we do, searches sensitive to case and diacritics can be performed, but the index will be bigger, and some marginal weirdness may sometimes occur. The default is a stripped index. When using multiple indexes for a search, this parameter must be defined identically for all. Changing the value implies an index reset.

indexStoreDocText

Decide if we store the documents' text content in the index. Storing the text allows extracting snippets from it at query time, instead of building them from index position data.

Newer Xapian index formats have rendered our use of positions list unacceptably slow in some cases. The last Xapian index format with good performance for the old method is Chert, which is default for 1.2, still supported but not default in 1.4 and will be dropped in 1.6.

The stored document text is translated from its original format to UTF-8 plain text, but not stripped of upper-case, diacritics, or punctuation signs. Storing it increases the index size by 10-20% typically, but also allows for nicer snippets, so it may be worth enabling it even if not strictly needed for performance if you can afford the space.

The variable only has an effect when creating an index, meaning that the xapiandb directory must not exist yet. Its exact effect depends on the Xapian version.

For Xapian 1.4, if the variable is set to 0, the Chert format will be used, and the text will not be stored. If the variable is 1, Glass will be used, and the text stored.

For Xapian 1.2, and for versions after 1.5 and newer, the index format is always the default, but the variable controls if the text is stored or not, and the abstract generation method. With Xapian 1.5 and later, and the variable set to 0, abstract generation may be very slow, but this setting may still be useful to save space if you do not use abstract generation at all.

nonumbers

Decides if terms will be generated for numbers. For example "123", "1.5e6", 192.168.1.4, would not be indexed if nonumbers is set ("value123" would still be). Numbers are often quite interesting to search for, and this should probably not be set except for special situations, ie, scientific documents with huge amounts of numbers in them, where setting nonumbers will reduce the index size. This can only be set for a whole index, not for a subtree.

dehyphenate

Determines if we index 'coworker' also when the input is 'co-worker'. This is new in version 1.22, and on by default. Setting the variable to off allows restoring the previous behaviour.

backslashasletter

Process backslash as normal letter. This may make sense for people wanting to index TeX commands as such but is not of much general use.

underscoreasletter

Process underscore as normal letter. This makes sense in so many cases that one wonders if it should not be the default.

maxtermlength

Maximum term length. Words longer than this will be discarded. The default is 40 and used to be hard-coded, but it can now be adjusted. You need an index reset if you change the value.

nocjk

Decides if specific East Asian (Chinese Korean Japanese) characters/word splitting is turned off. This will save a small amount of CPU if you have no CJK documents. If your document base does include such text but you are not interested in searching it, setting nocjk may be a significant time and space saver.

cjkngramlen

This lets you adjust the size of n-grams used for indexing CJK text. The default value of 2 is probably appropriate in most cases. A value of 3 would allow more precision and efficiency on longer words, but the index will be approximately twice as large.

indexstemminglanguages

Languages for which to create stemming expansion data. Stemmer names can be found by executing 'recollindex -l', or this can also be set from a list in the GUI. The values are full language names, e.g. english, french...

defaultcharset

Default character set. This is used for files which do not contain a character set definition (e.g.: text/plain). Values found inside files, e.g. a 'charset' tag in HTML documents, will override it. If this is not set, the default character set is the one defined by the NLS environment ($LC_ALL, $LC_CTYPE, $LANG), or ultimately iso-8859-1 (cp-1252 in fact). If for some reason you want a general default which does not match your LANG and is not 8859-1, use this variable. This can be redefined for any sub-directory.

unac_except_trans

A list of characters, encoded in UTF-8, which should be handled specially when converting text to unaccented lowercase. For example, in Swedish, the letter a with diaeresis has full alphabet citizenship and should not be turned into an a. Each element in the space-separated list has the special character as first element and the translation following. The handling of both the lowercase and upper-case versions of a character should be specified, as appartenance to the list will turn-off both standard accent and case processing. The value is global and affects both indexing and querying. We also convert a few confusing Unicode characters (quotes, hyphen) to their ASCII equivalent to avoid "invisible" search failures.

Examples: Swedish: unac_except_trans = ää Ää öö Öö üü Üü ßss œoe Œoe æae Æae ffff fifi flfl åå Åå ’' ❜' ʼ' ‐- . German: unac_except_trans = ää Ää öö Öö üü Üü ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐- . French: you probably want to decompose oe and ae and nobody would type a German ß unac_except_trans = ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐- . The default for all until someone protests follows. These decompositions are not performed by unac, but it is unlikely that someone would type the composed forms in a search. unac_except_trans = ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐-

maildefcharset

Overrides the default character set for email messages which don't specify one. This is mainly useful for readpst (libpst) dumps, which are utf-8 but do not say so.

localfields

Set fields on all files (usually of a specific fs area). Syntax is the usual: name = value ; attr1 = val1 ; [...] value is empty so this needs an initial semi-colon. This is useful, e.g., for setting the rclaptg field for application selection inside mimeview.

testmodifusemtime

Use mtime instead of ctime to test if a file has been modified. The time is used in addition to the size, which is always used. Setting this can reduce re-indexing on systems where extended attributes are used (by some other application), but not indexed, because changing extended attributes only affects ctime. Notes: - This may prevent detection of change in some marginal file rename cases (the target would need to have the same size and mtime). - You should probably also set noxattrfields to 1 in this case, except if you still prefer to perform xattr indexing, for example if the local file update pattern makes it of value (as in general, there is a risk for pure extended attributes updates without file modification to go undetected). Perform a full index reset after changing this.

noxattrfields

Disable extended attributes conversion to metadata fields. This probably needs to be set if testmodifusemtime is set.

metadatacmds

Define commands to gather external metadata, e.g. tmsu tags. There can be several entries, separated by semi-colons, each defining which field name the data goes into and the command to use. Don't forget the initial semi-colon. All the field names must be different. You can use aliases in the "field" file if necessary. As a not too pretty hack conceded to convenience, any field name beginning with "rclmulti" will be taken as an indication that the command returns multiple field values inside a text blob formatted as a recoll configuration file ("fieldname = fieldvalue" lines). The rclmultixx name will be ignored, and field names and values will be parsed from the data. Example: metadatacmds = ; tags = tmsu tags %f; rclmulti1 = cmdOutputsConf %f

Parameters affecting where and how we store things

cachedir

Top directory for Recoll data. Recoll data directories are normally located relative to the configuration directory (e.g. ~/.recoll/xapiandb, ~/.recoll/mboxcache). If 'cachedir' is set, the directories are stored under the specified value instead (e.g. if cachedir is ~/.cache/recoll, the default dbdir would be ~/.cache/recoll/xapiandb). This affects dbdir, webcachedir, mboxcachedir, aspellDicDir, which can still be individually specified to override cachedir. Note that if you have multiple configurations, each must have a different cachedir, there is no automatic computation of a subpath under cachedir.

maxfsoccuppc

Maximum file system occupation over which we stop indexing. The value is a percentage, corresponding to what the "Capacity" df output column shows. The default value is 0, meaning no checking.

dbdir

Xapian database directory location. This will be created on first indexing. If the value is not an absolute path, it will be interpreted as relative to cachedir if set, or the configuration directory (-c argument or $RECOLL_CONFDIR). If nothing is specified, the default is then ~/.recoll/xapiandb/

idxstatusfile

Name of the scratch file where the indexer process updates its status. Default: idxstatus.txt inside the configuration directory.

mboxcachedir

Directory location for storing mbox message offsets cache files. This is normally 'mboxcache' under cachedir if set, or else under the configuration directory, but it may be useful to share a directory between different configurations.

mboxcacheminmbs

Minimum mbox file size over which we cache the offsets. There is really no sense in caching offsets for small files. The default is 5 MB.

mboxmaxmsgmbs

Maximum mbox member message size in megabytes. Size over which we assume that the mbox format is bad or we misinterpreted it, at which point we just stop processing the file.

webcachedir

Directory where we store the archived web pages. This is only used by the web history indexing code Default: cachedir/webcache if cachedir is set, else $RECOLL_CONFDIR/webcache

webcachemaxmbs

Maximum size in MB of the Web archive. This is only used by the web history indexing code. Default: 40 MB. Reducing the size will not physically truncate the file.

webqueuedir

The path to the Web indexing queue. This used to be hard-coded in the old plugin as ~/.recollweb/ToIndex so there would be no need or possibility to change it, but the WebExtensions plugin now downloads the files to the user Downloads directory, and a script moves them to webqueuedir. The script reads this value from the config so it has become possible to change it.

webdownloadsdir

The path to browser downloads directory. This is where the new browser add-on extension has to create the files. They are then moved by a script to webqueuedir.

webcachekeepinterval

Page recycle interval By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries.

aspellDicDir

Aspell dictionary storage directory location. The aspell dictionary (aspdict.(lang).rws) is normally stored in the directory specified by cachedir if set, or under the configuration directory.

filtersdir

Directory location for executable input handlers. If RECOLL_FILTERSDIR is set in the environment, we use it instead. Defaults to $prefix/share/recoll/filters. Can be redefined for subdirectories.

iconsdir

Directory location for icons. The only reason to change this would be if you want to change the icons displayed in the result list. Defaults to $prefix/share/recoll/images

Parameters affecting indexing performance and resource usage

idxflushmb

Threshold (megabytes of new data) where we flush from memory to disk index. Setting this allows some control over memory usage by the indexer process. A value of 0 means no explicit flushing, which lets Xapian perform its own thing, meaning flushing every $XAPIAN_FLUSH_THRESHOLD documents created, modified or deleted: as memory usage depends on average document size, not only document count, the Xapian approach is is not very useful, and you should let Recoll manage the flushes. The program compiled value is 0. The configured default value (from this file) is now 50 MB, and should be ok in many cases. You can set it as low as 10 to conserve memory, but if you are looking for maximum speed, you may want to experiment with values between 20 and 200. In my experience, values beyond this are always counterproductive. If you find otherwise, please drop me a note.

filtermaxseconds

Maximum external filter execution time in seconds. Default 1200 (20mn). Set to 0 for no limit. This is mainly to avoid infinite loops in postscript files (loop.ps)

filtermaxmbytes

Maximum virtual memory space for filter processes (setrlimit(RLIMIT_AS)), in megabytes. Note that this includes any mapped libs (there is no reliable Linux way to limit the data space only), so we need to be a bit generous here. Anything over 2000 will be ignored on 32 bits machines. The high default value is needed because of java-based handlers (pdftk) which need a lot of VM (most of it text), esp. pdftk when executed from Python rclpdf.py. You can use a much lower value if you don't need Java.

thrQSizes

Stage input queues configuration. There are three internal queues in the indexing pipeline stages (file data extraction, terms generation, index update). This parameter defines the queue depths for each stage (three integer values). If a value of -1 is given for a given stage, no queue is used, and the thread will go on performing the next stage. In practise, deep queues have not been shown to increase performance. Default: a value of 0 for the first queue tells Recoll to perform autoconfiguration based on the detected number of CPUs (no need for the two other values in this case). Use thrQSizes = -1 -1 -1 to disable multithreading entirely.

thrTCounts

Number of threads used for each indexing stage. The three stages are: file data extraction, terms generation, index update). The use of the counts is also controlled by some special values in thrQSizes: if the first queue depth is 0, all counts are ignored (autoconfigured); if a value of -1 is used for a queue depth, the corresponding thread count is ignored. It makes no sense to use a value other than 1 for the last stage because updating the Xapian index is necessarily single-threaded (and protected by a mutex).

Miscellaneous parameters

loglevel

Log file verbosity 1-6. A value of 2 will print only errors and warnings. 3 will print information like document updates, 4 is quite verbose and 6 very verbose.

logfilename

Log file destination. Use 'stderr' (default) to write to the console.

idxloglevel

Override loglevel for the indexer.

idxlogfilename

Override logfilename for the indexer.

helperlogfilename

Destination file for external helpers standard error output. The external program error output is left alone by default, e.g. going to the terminal when the recoll[index] program is executed from the command line. Use /dev/null or a file inside a non-existent directory to completely suppress the output.

daemloglevel

Override loglevel for the indexer in real time mode. The default is to use the idx... values if set, else the log... values.

daemlogfilename

Override logfilename for the indexer in real time mode. The default is to use the idx... values if set, else the log... values.

pyloglevel

Override loglevel for the python module.

pylogfilename

Override logfilename for the python module.

idxnoautopurge

Do not purge data for deleted or inaccessible files This can be overridden by recollindex command line options and may be useful if some parts of the document set may predictably be inaccessible at times, so that you would only run the purge after making sure that everything is there.

orgidxconfdir

Original location of the configuration directory. This is used exclusively for movable datasets. Locating the configuration directory inside the directory tree makes it possible to provide automatic query time path translations once the data set has moved (for example, because it has been mounted on another location).

curidxconfdir

Current location of the configuration directory. Complement orgidxconfdir for movable datasets. This should be used if the configuration directory has been copied from the dataset to another location, either because the dataset is readonly and an r/w copy is desired, or for performance reasons. This records the original moved location before copy, to allow path translation computations. For example if a dataset originally indexed as '/home/me/mydata/config' has been mounted to '/media/me/mydata', and the GUI is running from a copied configuration, orgidxconfdir would be '/home/me/mydata/config', and curidxconfdir (as set in the copied configuration) would be '/media/me/mydata/config'.

idxrundir

Indexing process current directory. The input handlers sometimes leave temporary files in the current directory, so it makes sense to have recollindex chdir to some temporary directory. If the value is empty, the current directory is not changed. If the value is (literal) tmp, we use the temporary directory as set by the environment (RECOLL_TMPDIR else TMPDIR else /tmp). If the value is an absolute path to a directory, we go there.

checkneedretryindexscript

Script used to heuristically check if we need to retry indexing files which previously failed. The default script checks the modified dates on /usr/bin and /usr/local/bin. A relative path will be looked up in the filters dirs, then in the path. Use an absolute path to do otherwise.

recollhelperpath

Additional places to search for helper executables. This is used, e.g., on Windows by the Python code, and on Mac OS by the bundled recoll.app (because I could find no reliable way to tell launchd to set the PATH). The example below is for Windows. Use ':' as entry separator for Mac and Ux-like systems, ';' is for Windows only.

idxabsmlen

Length of abstracts we store while indexing. Recoll stores an abstract for each indexed file. The text can come from an actual 'abstract' section in the document or will just be the beginning of the document. It is stored in the index so that it can be displayed inside the result lists without decoding the original file. The idxabsmlen parameter defines the size of the stored abstract. The default value is 250 bytes. The search interface gives you the choice to display this stored text or a synthetic abstract built by extracting text around the search terms. If you always prefer the synthetic abstract, you can reduce this value and save a little space.

idxmetastoredlen

Truncation length of stored metadata fields. This does not affect indexing (the whole field is processed anyway), just the amount of data stored in the index for the purpose of displaying fields inside result lists or previews. The default value is 150 bytes which may be too low if you have custom fields.

idxtexttruncatelen

Truncation length for all document texts. Only index the beginning of documents. This is not recommended except if you are sure that the interesting keywords are at the top and have severe disk space issues.

idxsynonyms

Name of the index-time synonyms file. This is used for indexing multiword synonyms as single terms, which in turn is only useful if you want to perform proximity searches with such terms.

idxniceprio

"nice" process priority for the indexing processes. Default: 19 (lowest) Appeared with 1.26.5. Prior versions were fixed at 19.

noaspell

Disable aspell use. The aspell dictionary generation takes time, and some combinations of aspell version, language, and local terms, result in aspell crashing, so it sometimes makes sense to just disable the thing.

aspellLanguage

Language definitions to use when creating the aspell dictionary. The value must match a set of aspell language definition files. You can type "aspell dicts" to see a list The default if this is not set is to use the NLS environment to guess the value. The values are the 2-letter language codes (e.g. 'en', 'fr'...)

aspellAddCreateParam

Additional option and parameter to aspell dictionary creation command. Some aspell packages may need an additional option (e.g. on Debian Jessie: --local-data-dir=/usr/lib/aspell). See Debian bug 772415.

aspellKeepStderr

Set this to have a look at aspell dictionary creation errors. There are always many, so this is mostly for debugging.

monauxinterval

Auxiliary database update interval. The real time indexer only updates the auxiliary databases (stemdb, aspell) periodically, because it would be too costly to do it for every document change. The default period is one hour.

monixinterval

Minimum interval (seconds) between processings of the indexing queue. The real time indexer does not process each event when it comes in, but lets the queue accumulate, to diminish overhead and to aggregate multiple events affecting the same file. Default 30 S.

mondelaypatterns

Timing parameters for the real time indexing. Definitions for files which get a longer delay before reindexing is allowed. This is for fast-changing files, that should only be reindexed once in a while. A list of wildcardPattern:seconds pairs. The patterns are matched with fnmatch(pattern, path, 0) You can quote entries containing white space with double quotes (quote the whole entry, not the pattern). The default is empty. Example: mondelaypatterns = *.log:20 "*with spaces.*:30"

monioniceclass

ionice class for the indexing process. Despite the misleading name, and on platforms where this is supported, this affects all indexing processes, not only the real time/monitoring ones. The default value is 3 (use lowest "Idle" priority).

monioniceclassdata

ionice class level parameter if the class supports it. The default is empty, as the default "Idle" class has no levels.

Query-time parameters (no impact on the index)

autodiacsens

auto-trigger diacritics sensitivity (raw index only). IF the index is not stripped, decide if we automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the "D" modifier to specify diacritics sensitivity. Default is no.

autocasesens

auto-trigger case sensitivity (raw index only). IF the index is not stripped (see indexStripChars), decide if we automatically trigger character case sensitivity if the search term has upper-case characters in any but the first position. Else you need to use the query language and the "C" modifier to specify character-case sensitivity. Default is yes.

maxTermExpand

Maximum query expansion count for a single term (e.g.: when using wildcards). This only affects queries, not indexing. We used to not limit this at all (except for filenames where the limit was too low at 1000), but it is unreasonable with a big index. Default 10000.

maxXapianClauses

Maximum number of clauses we add to a single Xapian query. This only affects queries, not indexing. In some cases, the result of term expansion can be multiplicative, and we want to avoid eating all the memory. Default 50000.

snippetMaxPosWalk

Maximum number of positions we walk while populating a snippet for the result list. The default of 1,000,000 may be insufficient for very big documents, the consequence would be snippets with possibly meaning-altering missing words.

thumbnailercmd

Command to use for generating thumbnails. If set, this should be a path to a command or script followed by its constant arguments. Four arguments will be appended before execution: the document URL, MIME type, target icon SIZE (e.g. 128), and output file PATH. The command should generate a thumbnail from these values. E.g. if the MIME is video, a script could use: ffmpegthumbnailer -iURL -oPATH -sSIZE.

stemexpandphrases

Default to applying stem expansion to phrase terms. Recoll normally does not apply stem expansion to terms inside phrase searches. Setting this parameter will change the default behaviour to expanding terms inside phrases. If set, you can use a 'l' modifier to disable expansion for a specific instance.

autoSpellRarityThreshold

Inverse of the ratio of term occurrence to total db terms over which we look for spell neighbours for automatic query expansion When a term is very uncommon, we may (depending on user choice) look for spelling variations which would be more common and possibly add them to the query.

autoSpellSelectionThreshold

Ratio of spell neighbour frequency over user input term frequency beyond which we include the neighbour in the query. When a term has been selected for spelling expansion because of its rarity, we only include spelling neighbours which are more common by this ratio.

kioshowsubdocs

Show embedded document results in KDE dolphin/kio and krunner Embedded documents may clutter the results and are not always easily usable from the kio or krunner environment. Setting this variable will restrict the results to standalone documents.

Parameters for the PDF input script

pdfocr

Attempt OCR of PDF files with no text content. This can be defined in subdirectories. The default is off because OCR is so very slow.

pdfattach

Enable PDF attachment extraction by executing pdftk (if available). This is normally disabled, because it does slow down PDF indexing a bit even if not one attachment is ever found.

pdfextrameta

Extract text from selected XMP metadata tags. This is a space-separated list of qualified XMP tag names. Each element can also include a translation to a Recoll field name, separated by a '|' character. If the second element is absent, the tag name is used as the Recoll field names. You will also need to add specifications to the "fields" file to direct processing of the extracted data.

pdfextrametafix

Define name of XMP field editing script. This defines the name of a script to be loaded for editing XMP field values. The script should define a 'MetaFixer' class with a metafix() method which will be called with the qualified tag name and value of each selected field, for editing or erasing. A new instance is created for each document, so that the object can keep state for, e.g. eliminating duplicate values.

Parameters for OCR processing

ocrprogs

OCR modules to try. The top OCR script will try to load the corresponding modules in order and use the first which reports being capable of performing OCR on the input file. Modules for tesseract (tesseract) and ABBYY FineReader (abbyy) are present in the standard distribution. For compatibility with the previous version, if this is not defined at all, the default value is "tesseract". Use an explicit empty value if needed. A value of "abbyy tesseract" will try everything.

ocrcachedir

Location for caching OCR data. The default if this is empty or undefined is to store the cached OCR data under $RECOLL_CONFDIR/ocrcache.

tesseractlang

Language to assume for tesseract OCR. Important for improving the OCR accuracy. This can also be set through the contents of a file in the currently processed directory. See the rclocrtesseract.py script. Example values: eng, fra... See the tesseract documentation.

tesseractcmd

Path for the tesseract command. Do not quote. This is mostly useful on Windows, or for specifying a non-default tesseract command. E.g. on Windows. tesseractcmd = C:/ProgramFiles(x86)/Tesseract-OCR/tesseract.exe

abbyylang

Language to assume for abbyy OCR. Important for improving the OCR accuracy. This can also be set through the contents of a file in the currently processed directory. See the rclocrabbyy.py script. Typical values: English, French... See the ABBYY documentation.

abbyyocrcmd

Path for the abbyy command The ABBY directory is usually not in the path, so you should set this.

Parameters for running speech to text conversion

speechtotext

Activate speech to text conversion The only possible value at the moment is "whisper" for using the OpenAI whisper program.

sttmodel

Name of the whisper model

sttdevice

Name of the device to be used by for whisper

Parameters for miscellaneous specific handlers

orgmodesubdocs

Index org-mode level 1 sections as separate sub-documents This is the default. If set to false, org-mode files will be indexed as plain text

Parameters set for specific locations

mhmboxquirks

Enable thunderbird/mozilla-seamonkey mbox format quirks Set this for the directory where the email mbox files are stored.

5.4.3. The fields file

This file contains information about dynamic fields handling in Recoll. Some very basic fields have hard-wired behaviour, and, mostly, you should not change the original data inside the fields file. But you can create custom fields fitting your data and handle them just like they were native ones.

The fields file has several sections, which each define an aspect of fields processing. Quite often, you'll have to modify several sections to obtain the desired behaviour.

We will only give a short description here, you should refer to the comments inside the default file for more detailed information.

Field names should be lowercase alphabetic ASCII.

[prefixes]

A field becomes indexed (searchable) by having a prefix defined in this section. There is a more complete explanation of what prefixes are in used by a standard recoll installation. In a nutshell: extension prefixes should be all caps, begin with XY, and short. E.g. XYMFLD.

[values]

Fields listed in this section will be stored as Xapian values inside the index. This makes them available for range queries, allowing to filter results according to the field value. This feature currently supports string and integer data. See the comments in the file for more detail

[stored]

A field becomes stored (displayable inside results) by having its name listed in this section (typically with an empty value).

[aliases]

This section defines lists of synonyms for the canonical names used inside the [prefixes] and [stored] sections

[queryaliases]

This section also defines aliases for the canonic field names, with the difference that the substitution will only be used at query time, avoiding any possibility that the value would pick-up random metadata from documents.

handler-specific sections

Some input handlers may need specific configuration for handling fields. Only the email message handler currently has such a section (named [mail]). It allows indexing arbitrary email headers in addition to the ones indexed by default. Other such sections may appear in the future.

Here follows a small example of a personal fields file. This would extract a specific email header and use it as a searchable field, with data displayable inside result lists. (Side note: as the email handler does no decoding on the values, only plain ascii headers can be indexed, and only the first occurrence will be used for headers that occur several times).

[prefixes]
        # Index mailmytag contents (with the given prefix)
        mailmytag = XMTAG

        [stored]
        # Store mailmytag inside the document data record (so that it can be
        # displayed - as %(mailmytag) - in result lists).
        mailmytag = 

        [queryaliases]
        filename = fn
        containerfilename = cfn

        [mail]
        # Extract the X-My-Tag mail header, and use it internally with the
        # mailmytag field name
        x-my-tag = mailmytag
        

Extended attributes in the fields file

Recoll versions 1.19 and later process user extended file attributes as documents fields by default.

Attributes are processed as fields of the same name, after removing the user prefix on Linux.

The [xattrtofields] section of the fields file allows specifying translations from extended attributes names to Recoll field names. An empty translation disables use of the corresponding attribute data.

5.4.4. The mimemap file

mimemap specifies the file name extension to MIME type mappings.

For file names without an extension, or with an unknown one, a system command (file -i, or xdg-mime) will be executed to determine the MIME type (this can be switched off, or the command changed inside the main configuration file).

All extension values in mimemap must be entered in lower case. File names extensions are lower-cased for comparison during indexing, meaning that an upper case mimemap entry will never be matched.

The mappings can be specified on a per-subtree basis, which may be useful in some cases. Example: okular notes have a .xml extension but should be handled specially, which is possible because they are usually all located in one place. Example:

[~/.kde/share/apps/okular/docdata]
        .xml = application/x-okular-notes

The recoll_noindex mimemap variable has been moved to recoll.conf and renamed to noContentSuffixes, while keeping the same function, as of Recoll version 1.21. For older Recoll versions, see the documentation for noContentSuffixes but use recoll_noindex in mimemap.

5.4.5. The mimeconf file

The main purpose of the mimeconf file is to specify how the different MIME types are handled for indexing. This is done in the [index] section, which should not be modified casually. See the comments in the file.

The file also contains other definitions which affect the query language and the GUI, and which, in retrospect, should have been stored elsewhere.

The [icons] section allows you to change the icons which are displayed by the recoll GUI in the result lists (the values are the basenames of the png images inside the iconsdir directory (which is itself defined in recoll.conf).

The [categories] section defines the groupings of MIME types into categories as used when adding an rclcat clause to a query language query. rclcat clauses are also used by the default guifilters buttons in the GUI (see next).

The filter controls appear at the top of the recoll GUI, either as checkboxes just above the result list, or as a dropbox in the tool area.

By default, they are labeled: media, message, other, presentation, spreadsheet and text, and each maps to a document category. This is determined in the [guifilters] section, where each control is defined by a variable naming a query language fragment.

A simple example will hopefully make things clearer.

[guifilters]

Big Books = dir:"~/My Books" size>10K
My Docs = dir:"~/My Documents"
Small Books = dir:"~/My Books" size<10K
System Docs = dir:/usr/share/doc
        

The above definition would create four filter checkboxes, labelled Big Books, My Docs, etc.

The text after the equal sign must be a valid query language fragment, and, when the button is checked, it will be combined with the rest of the query with an AND conjunction.

Any name text before a colon character will be erased in the display, but used for sorting. You can use this to display the checkboxes in any order you like. For example, the following would do exactly the same as above, but ordering the checkboxes in the reverse order.

[guifilters]

d:Big Books = dir:"~/My Books" size>10K
c:My Docs = dir:"~/My Documents"
b:Small Books = dir:"~/My Books" size<10K
a:System Docs = dir:/usr/share/doc
        

As you may have guessed, The default [guifilters] section looks like:

[guifilters]
text = rclcat:text
spreadsheet = rclcat:spreadsheet
presentation = rclcat:presentation
media = rclcat:media
message = rclcat:message
other = rclcat:other
        

5.4.6. The mimeview file

mimeview specifies which programs are started when you click on an Open link in a result list. E.g.: HTML is normally displayed using firefox, but you may prefer Konqueror, your openoffice.org program might be named oofice instead of openoffice etc.

Changes to this file can be done by direct editing, or through the recoll GUI preferences dialog.

If Use desktop preferences to choose document editor is checked in the Recoll GUI preferences, all mimeview entries will be ignored except the one labelled application/x-all (which is set to use xdg-open by default).

In this case, the xallexcepts top level variable defines a list of MIME type exceptions which will be processed according to the local entries instead of being passed to the desktop. This is so that specific Recoll options such as a page number or a search string can be passed to applications that support them, such as the evince viewer.

As for the other configuration files, the normal usage is to have a mimeview inside your own configuration directory, with just the non-default entries, which will override those from the central configuration file.

All viewer definition entries must be placed under a [view] section.

The keys in the file are normally MIME types. You can add an application tag to specialize the choice for an area of the filesystem (using a localfields specification in mimeconf). The syntax for the key is mimetype|tag

The nouncompforviewmts entry, (placed at the top level, outside of the [view] section), holds a list of MIME types that should not be uncompressed before starting the viewer (if they are found compressed, e.g.: mydoc.doc.gz).

The right side of each assignment holds a command to be executed for opening the file. The following substitutions are performed:

  • %D. Document date

  • %f. File name. This may be the name of a temporary file if it was necessary to create one (e.g.: to extract a subdocument from a container).

  • %i. Internal path, for subdocuments of containers. The format depends on the container type. If this appears in the command line, Recoll will not create a temporary file to extract the subdocument, expecting the called application (possibly a script) to be able to handle it.

  • %M. MIME type

  • %p. Page index. Only significant for a subset of document types, currently only PDF, Postscript and DVI files. If it is set, a significant term will be chosen in the query, and %p will be substituted with the first page where the term appears. Can be used to start the editor at the right page for a match or snippet.

  • %l. Line number. Only significant for document types with relevant line breaks, mostly text/plain and analogs. If it is set, a significant term will be chosen in the query, and %p will be substituted with the first line where the term appears.

  • %s. Search term. The value will only be set for documents with indexed page or line numbers and if %p or %l is also used. The value will be one of the matched search terms. It would allow pre-setting the value in the "Find" entry inside Evince for example, for easy highlighting of the term.

  • %u. Url.

In addition to the predefined values above, all strings like %(fieldname) will be replaced by the value of the field named fieldname for the document. This could be used in combination with field customisation to help with opening the document.

5.4.7. The ptrans file

ptrans specifies query-time path translations. These can be useful in multiple cases.

The file has a section for any index which needs translations, either the main one or additional query indexes. The sections are named with the Xapian index directory names. No slash character should exist at the end of the paths (all comparisons are textual). An example should make things sufficiently clear

          [/home/me/.recoll/xapiandb]
          /this/directory/moved = /to/this/place

          [/path/to/additional/xapiandb]
          /server/volume1/docdir = /net/server/volume1/docdir
          /server/volume2/docdir = /net/server/volume2/docdir
        

5.4.8. Examples of configuration adjustments

Adding an external viewer for an non-indexed type

Imagine that you have some kind of file which does not have indexable content, but for which you would like to have a functional Open link in the result list (when found by file name). The file names end in .blob and can be displayed by application blobviewer.

You need two entries in the configuration files for this to work:

  • In $RECOLL_CONFDIR/mimemap (typically ~/.recoll/mimemap), add the following line:

                .blob = application/x-blobapp
              

    Note that the MIME type is made up here, and you could call it diesel/oil just the same.

  • In $RECOLL_CONFDIR/mimeview under the [view] section, add:

                  application/x-blobapp = blobviewer %f
                

    We are supposing that blobviewer wants a file name parameter here, you would use %u if it liked URLs better.

If you just wanted to change the application used by Recoll to display a MIME type which it already knows, you would just need to edit mimeview. The entries you add in your personal file override those in the central configuration, which you do not need to alter. mimeview can also be modified from the Gui.

Adding indexing support for a new file type

Let us now imagine that the above .blob files actually contain indexable text and that you know how to extract it with a command line program. Getting Recoll to index the files is easy. You need to perform the above alteration, and also to add data to the mimeconf file (typically in ~/.recoll/mimeconf):

  • Under the [index] section, add the following line (more about the rclblob indexing script later):

    application/x-blobapp = exec rclblob

    Or if the files are mostly text and you don't need to process them for indexing:

    application/x-blobapp = internal text/plain
  • Under the [icons] section, you should choose an icon to be displayed for the files inside the result lists. Icons are normally 64x64 pixels PNG files which live in /usr/share/recoll/images.

  • Under the [categories] section, you should add the MIME type where it makes sense (you can also create a category). Categories may be used for filtering in advanced search.

The rclblob handler should be an executable program or script which exists inside /usr/share/recoll/filters. It will be given a file name as argument and should output the text or html contents on the standard output.

The filter programming section describes in more detail how to write an input handler.

recoll-1.36.1/doc/user/docbook-xsl.css0000644000175000017500000000743314410615043014471 00000000000000/* * Copyright (c) 2001, 2003, 2010 The FreeBSD Documentation Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: doc/share/misc/docbook.css,v 1.15 2010/03/20 04:15:01 hrs Exp $ */ body address { line-height: 1.3; margin: .6em 0; } body blockquote { margin-top: .75em; line-height: 1.5; margin-bottom: .75em; } html body { margin: 1em 8% 1em 10%; line-height: 1.2; } .legalnotice { font-size: small; font-variant: small-caps; } body div { margin: 0; } dl { margin: .8em 0; line-height: 1.2; } body form { margin: .6em 0; } h1, h2, h3, h4, h5, h6, div.example p b, .question, div.table p b, div.procedure p b { color: #990000; } body h1, body h2, body h3, body h4, body h5, body h6 { line-height: 1.3; margin-left: 0; } body h1, body h2 { margin: .8em 0 0 -4%; } body h3, body h4 { margin: .8em 0 0 -3%; } body h5 { margin: .8em 0 0 -2%; } body h6 { margin: .8em 0 0 -1%; } body hr { margin: .6em; border-width: 0 0 1px 0; border-style: solid; border-color: #cecece; } body img.navheader { margin: 0 0 0 -4%; } ol { margin: 0 0 0 5%; line-height: 1.2; } body pre { margin: .75em 0; line-height: 1.0; font-family: monospace; } body td, body th { line-height: 1.2; } ul, body dir, body menu { margin: 0 0 0 5%; line-height: 1.2; } html { margin: 0; padding: 0; } body p b.application { color: #000000; } body p span.application { font-weight: bold; color: #000000; } .filename { color: #007a00; } .guimenu, .guimenuitem, .guisubmenu, .guilabel, .interface, .shortcut, .shortcut .keycap { font-weight: bold; } .guibutton { background-color: #cfcfcf; padding: 2px; } .accel { background-color: #f0f0f0; text-decoration: underline; } .screen { padding: 1ex; } .programlisting { padding: 1ex; background-color: #eee; border: 1px solid #ccc; } @media screen { /* hide from ie3 */ a[href]:hover { background: #ffa } } blockquote.note { color: #222; background: #eee; border: 1px solid #ccc; padding: 0.4em 0.4em; width: 85%; } blockquote.tip { color: #004f00; background: #d8ecd6; border: 1px solid green; padding: 0.2em 2em; width: 85%; } blockquote.important { font-style:italic; border: 1px solid #a00; border-left: 12px solid #c00; padding: 0.1em 1em; } blockquote.warning { color: #9f1313; background: #f8e8e8; border: 1px solid #e59595; padding: 0.2em 2em; width: 85%; } .example { background: #fefde6; border: 1px solid #f1bb16; margin: 1em 0; padding: 0.2em 2em; width: 90%; } .informaltable table.calstable tr td { padding-left: 1em; padding-right: 1em; } recoll-1.36.1/doc/prog/0000755000175000017500000000000014521161751011602 500000000000000recoll-1.36.1/doc/prog/top.txt0000644000175000017500000000126014410615043013057 00000000000000/*!@mainpage Recoll: A personal/desktop text-search program \author email address: jfd@recoll.org \section intro Introduction Home page: http://www.recoll.org This is the documentation for Recoll, a personal text search tool. Recoll is written in C++, has a QT-based gui and uses the Xapian full text search engine. Recoll supports a number of document types, the decoding of which is either performed with internal code or an externally executed, program. It should be relatively easy to write a new filter for some yet unsupported file type, and the documentation for this is found here: Writing input filters */ recoll-1.36.1/doc/prog/Makefile0000644000175000017500000000007714410615043013161 00000000000000documentation: doxygen Doxyfile clean: rm -f docprog_html/* recoll-1.36.1/doc/prog/Doxyfile0000644000175000017500000014134214410615043013230 00000000000000# Doxyfile 1.4.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Recoll # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, # Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, # Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, # Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, # Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explicit @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. SHOW_DIRECTORIES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the progam writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../../../src top.txt filters.txt # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = .moc .ui # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = docprog_html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that a graph may be further truncated if the graph's # image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH # and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), # the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO recoll-1.36.1/doc/prog/filters.txt0000644000175000017500000000275214410615043013734 00000000000000/*!@filters \page filters About filters \section filtintro Overview Before a document can be processed either for indexing or previewing, it must be translated into an internal common format. The MimeHandler class defines the virtual interface for filters. There are derived classes for text, html (MimeHandlerHtml), and mail folders (MimeHandlerMail) There is also a derived class (MimeHandlerExec) that will execute an external program to translate the document to simple html (to be further processed by MimeHandlerHtml). To extend Recoll for a new document type, you may either subclass the MimeHandler class (look at one of the existing subclasses), or write an external filter, which will probably be the simpler solution in most cases. \section extfilts External filters Filters are programs (usually shell scripts) that will turn a document of foreign type into something that Recoll can understand. HTML was chosen as a pivot format for its ability to carry structured information. The meta-information tags that Recoll will use at the moment are the following: - title - charset - keywords - description For an example, you can take a look at the rclsoff filter which translates openoffice documents. The filter is executed with the input file name as a parameter and should output the result to stdout. \section extassoc Associating a filter to a mime type This is done in the mimeconf configuration file. Take a look at the file, the format is self-explanatory. */ recoll-1.36.1/doc/man/0000755000175000017500000000000014521161751011406 500000000000000recoll-1.36.1/doc/man/recollindex.10000644000175000017500000002205014465152211013715 00000000000000.\" $Id: recollindex.1,v 1.7 2008-09-05 10:25:54 dockes Exp $ (C) 2005 J.F.Dockes\$ .TH RECOLLINDEX 1 "8 January 2006" .SH NAME recollindex \- indexing command for the Recoll full text search system .SH SYNOPSIS .B recollindex \-h .br .B recollindex [ .B \-z|\-Z ] [ .B \-k ] [ .B \--nopurge ] [ .B \-P ] [ .B \--diagsfile ] .br .B recollindex .B \-m [ .B \-w ] [ .B \-D ] [ .B \-x ] [ .B \-C ] [ .B \-n|-k ] .br .B recollindex .B \-i [ .B \-Z \-k \-f \-P ] [] .br .B recollindex .B \-r [ .B \-Z \-K \-e \-f ] [ .B \-p pattern ] .br .B recollindex .B \-e [] .br .B recollindex .B \-l|-S|-E .br .B recollindex .B \-s .br .B recollindex .B \--webcache-compact .br .B recollindex .B \--webcache-burst .br .B recollindex .B \--notindexed [path [path ...]] .SH DESCRIPTION Create or update a Recoll index. .PP There are several modes of operation. All modes support an optional .B \-c option to specify the configuration directory name, overriding the default or $RECOLL_CONFDIR (or $HOME/.recoll by default). .PP The normal mode will index the set of files described in the configuration. This will incrementally update the index with files that changed since the last run. If option .B \-z is given, the index will be erased before starting. If option .B \-Z is given, the index will not be reset, but all files will be considered as needing reindexing (in place reset). .PP .B recollindex does not process again files which previously failed to index (for example because of a missing helper program). If option .B \-k is given, .B recollindex will try again to process all failed files. Please note that .B recollindex may also decide to retry failed files if the auxiliary checking script defined by the "checkneedretryindexscript" configuration variable indicates that this should happen. .PP The .B \--nopurge option will disable the normal erasure of deleted documents from the index. This can be useful in special cases (when it is known that part of the document set is temporarily not accessible). .PP The .B \-P option will force the purge pass. This is useful only if the .B idxnoautopurge parameter is set in the configuration file. .PP If the option .B \--diagsfile is given, the path given as parameter will be truncated and indexing diagnostics will be written to it. Each line in the file will have a diagnostic type (reason for the file not to be indexed), the file path, and a possible additional piece of information, which can be the MIME type or the archive internal path depending on the issue. The following diagnostic types are currently defined: .IP .B Skipped : the path matches an element of .B skippedPaths or .B skippedNames. .IP .B NoContentSuffix : the file name suffix is found in the .B noContentSuffixes list. .IP .B MissingHelper : a helper program is missing. .IP .B Error : general error (see the log). .IP .B NoHandler: no handler is defined for the MIME type. .IP .B ExcludedMime : the MIME type is part of the .B excludedmimetypes list. .IP .B NotIncludedMime : the .B onlymimetypes list is not empty and the the MIME type is not in it. .PP If option .B \-m is given, recollindex is started for real time monitoring, using the file system monitoring package it was configured for (either fam, gamin, or inotify). This mode must have been explicitly configured when building the package, it is not available by default. The program will normally detach from the controlling terminal and become a daemon. If option .B \-D is given, it will stay in the foreground. Option .B \-w can be used to specify that the program should sleep for the specified time before indexing begins. The default value is 60. The daemon normally monitors the X11 session and exits when it is reset. Option .B \-x disables this X11 session monitoring (daemon will stay alive even if it cannot connect to the X11 server). You need to use this too if you use the daemon without an X11 context. You can use option .B \-n to skip the initial incrementing pass which is normally performed before monitoring starts. Once monitoring is started, the daemon normally monitors the configuration and restarts from scratch if a change is made. You can disable this with option .B \-C .PP .B recollindex \-i will index individual files into the index. The stem expansion and aspell databases will not be updated. The skippedPaths and skippedNames configuration variables will be used, so that some files may be skipped. You can tell recollindex to ignore skippedPaths and skippedNames by setting the .B \-f option. This allows fully custom file selection for a given subtree, for which you would add the top directory to skippedPaths, and use any custom tool to generate the file list (ie: a tool from a source code control system). When run this way, the indexer normally does not perform the deleted files purge pass, because it cannot be sure to have seen all the existing files. You can force a purge pass with .B \-P. .PP .B recollindex \-e will erase data for individual files from the index. The stem expansion databases will not be updated. .PP Options .B \-i and .B \-e can be combined. This will first perform the purge, then the indexing. .PP With options .B \-i or .B \-e , if no file names are given on the command line, they will be read from stdin, so that you could for example run: .PP find /path/to/dir \-print | recollindex \-e \-i .PP to force the reindexing of a directory tree (which has to exist inside the file system area defined by .I topdirs in recoll.conf). You could mostly accomplish the same thing with .PP find /path/to/dir \-print | recollindex \-Z \-i .PP The latter will perform a less thorough job of purging stale sub-documents though. .PP .B recollindex \-r mostly works like .B \-i , but the parameter is a single directory, which will be recursively updated. This mostly does nothing more than .B find topdir | recollindex \-i but it may be more convenient to use when started from another program. This retries failed files by default, use option .B \-K to change. One or multiple .B \-p options can be used to set shell-type selection patterns (e.g.: *.pdf). .PP .B recollindex \-l will list the names of available language stemmers. .PP .B recollindex \-s will build the stem expansion database for a given language, which may or may not be part of the list in the configuration file. If the language is not part of the configuration, the stem expansion database will be deleted at the end of the next normal indexing run. You can get the list of stemmer names from the .B recollindex \-l command. Note that this is mostly for experimental use, the normal way to add a stemming language is to set it in the configuration, either by editing "recoll.conf" or by using the GUI indexing configuration dialog. .br At the time of this writing, the following languages are recognized (out of Xapian's stem.h): .IP \(bu danish .IP \(bu dutch .IP \(bu english Martin Porter's 2002 revision of his stemmer .IP \(bu english_lovins Lovin's stemmer .IP \(bu english_porter Porter's stemmer as described in his 1980 paper .IP \(bu finnish .IP \(bu french .IP \(bu german .IP \(bu italian .IP \(bu norwegian .IP \(bu portuguese .IP \(bu russian .IP \(bu spanish .IP \(bu swedish .PP .B recollindex \-S will rebuild the phonetic/orthographic index. This feature uses the .B aspell package, which must be installed on the system. .PP .B recollindex \-E will check the configuration file for topdirs and other relevant paths existence (to help catch typos). .PP .B recollindex \--webcache-compact will recover the space wasted by erased page instances inside the Web cache. It may temporarily need to use twice the disk space used by the Web cache. .PP .B recollindex \--webcache-burst will extract all entries from the Web cache to files created inside . Each cache entry is extracted as two files, for the data and metadata. .PP .B recollindex \--notindexed [path [path ...]] will check each path and print out those which are absent from the index (with an "ABSENT" prefix), or caused an indexing error (with an "ERROR" prefix). If no paths are given on the command line, the command will read them, one per line, from stdin. .PP Interrupting the command: as indexing can sometimes take a long time, the command can be interrupted by sending an interrupt (Ctrl-C, SIGINT) or terminate (SIGTERM) signal. Some time may elapse before the process exits, because it needs to properly flush and close the index. This can also be done from the recoll GUI (menu entry: File/Stop_Indexing). After such an interruption, the index will be somewhat inconsistent because some operations which are normally performed at the end of the indexing pass will have been skipped (for example, the stemming and spelling databases will be inexistent or out of date). You just need to restart indexing at a later time to restore consistency. The indexing will restart at the interruption point (the full file tree will be traversed, but files that were indexed up to the interruption and for which the index is still up to date will not need to be reindexed). .SH SEE ALSO .PP recoll(1) recoll.conf(5) recoll-1.36.1/doc/man/xadump.10000644000175000017500000000375514410615043012713 00000000000000.TH XADUMP 1 "18 November 2017" .SH NAME xadump \- low level access to a Recoll Xapian index. .SH SYNOPSIS .B xadump [ .B \-d ] [ .B \-e ] .B \-i .B \-D .br .B xadump [ .B \-d ] [ .B \-e ] .B \-i .B \-X .br .B xadump [ .B \-d ] [ .B \-e ] .B \-i [ .B \-x ] .B \-T .br .B xadump [ .B \-d ] [ .B \-e ] .B \-i [ .B \-x ] .B \-r .br .B xadump [ .B \-d ] [ .B \-e ] .B \-t .B \-E .br .B xadump [ .B \-d ] [ .B \-e ] .B \-t .B \-F .br .B xadump [ .B \-d ] [ .B \-e ] .B \-t .B \-P .br .B xadump .B \-T [ .B \-f ] [ .B \-n ] [ .B \-l ] .br .B xadump .B \-q term [term ...] .SH DESCRIPTION The .B xadump command is a low-level access and diagnostic tool for a Xapian index as organized by the Recoll indexer. The index directory to be used is specified with option .B \-d. .PP Options -D, -X, -T and -r take a single .B docid argument specified with option .B \-i. .B \-D displays the document data record. .B \-X deletes all index data for the document. .B \-T prints the term list for the document. Without a docid argument, this option will list the whole index term list. .B \-f can be set to precede each term with its occurrence count (only if no docid is specified). .B \-n can be set to omit the enclosing brackets. .B \-l can be set to skip prefixed terms. .B \-r prints the document text as reconstructed from index data. When option .B \-x is set, terms are printed with each character separated by a space, which can be useful to check some encoding issues. .PP Options -E, -F and -P all need a term argument, specified with .B \-t . .B \-E performs a term existence test. .B \-F retrieves the term frequency for the given term. .B \-P displays the postings for the given term. .PP With option .B \-q, xadump performs a simple AND query on the index, using the given term arguments. recoll-1.36.1/doc/man/recollq.10000644000175000017500000000715614410615043013055 00000000000000.\" $Id: recollq.1,v 1.1 2007-11-13 10:07:35 dockes Exp $ (C) 2005 J.F.Dockes\$ .TH RECOLLQ 1 "13 November 2007" .SH NAME recollq \- command line / standard output Recoll query command. .SH SYNOPSIS .B recollq [ .B \-c ] [ .B \-o | .B \-f | .B \-a ] [ .B \-b ] [ .B \-d ] [ .B \-A ] [ .B \-p ] [ .B \-e ] [ .B \-m ] [ .B \-n <[first-]cnt> ] [ .B \-Q ] [ .B \-s ] [ .B \-S ] [ .B \-D ] [ .B \-i ] [ .B \-F ] [ .B \--extract-to ] .B recollq \-P .SH DESCRIPTION The .B recollq command will execute the Recoll query specified on the command line and print the results to the standard output. It is primarily designed for diagnostics, or piping the data to some other program. The basic format and its variations can be useful for command line querying. The \-F option should exclusively be used for using the output data in another program, as it is the only one for which output is guaranteed to be fully parseable. .PP The .B \-c option specifies the configuration directory name, overriding the default or $RECOLL_CONFDIR. .PP The query string is built by concatenating all arguments found at the end of the command line (after the options). It will be interpreted by default as a query language string. Quoting should be used as needed to escape characters that might be interpreted by the shell (ie: wildcards). .B \-a is specified, the query string will be interpreted as an .I all words simple search query. If .B \-o is specified, the query string will be interpreted as an .I any word simple search query. If .B \-f is specified, the query string will be interpreted as a .I file name simple search query. .PP .B \-b (basic) can be specified to only print the result urls in the output stream. .PP If .B \-d is set, the text for the result files contents will be dumped to stdout. .PP If .B \-m is set, the whole metadata array will be dumped for each document. .PP If .B \-A is set, the document abstracts will be printed. With an additional .B \-p option, snippets with page numbers (when available) will be shown instead. .PP .B \-S sorts the results according to the specified field. Use .B \-D for descending order. .PP .B \-n <[first-]cnt> can be used to set the maximum number of results that should be printed. The default is 2000. Use a value of 0 for no limit. If the argument is of the form first-cnt, it also defines the first result to output (from 0). .PP .B \-s selects the word stemming language. The value should match an existing stemming database (as set in the configuration or added with recollindex \-s). .PP .B \-i adds the specified Xapian index to the set used for the query. Can be specified multiple times. .PP .B \-F (one argument, e.g. "author title") should be used for piping the data to another program. After 2 initial lines showing the actual query and the estimated result counts, it will print one line for each result document. Each line will have exactly the fields requested on the command line. Fields are encoded in base64 and separated by one space character. Empty fields are indicated by consecutive space characters. There is one additional space character at the end of each line. .PP .B \--extract-to Will extract the first result document of the query to the argument path, which must not exist. Use -n first-cnt to select the document. .PP .B recollq \-P (Period) will print the minimum and maximum modification years for documents in the index. .SH SEE ALSO .PP recollindex(1) recollq(1) recoll.conf(5) recoll-1.36.1/doc/man/recoll.conf.50000644000175000017500000010641614410615043013623 00000000000000.TH RECOLL.CONF 5 "14 November 2012" .SH NAME recoll.conf \- main personal configuration file for Recoll .SH DESCRIPTION This file defines the index configuration for the Recoll full-text search system. .LP The system-wide configuration file is normally located inside /usr/[local]/share/recoll/examples. Any parameter set in the common file may be overridden by setting it in the personal configuration file, by default: .IR $HOME/.recoll/recoll.conf .LP Please note while I try to keep this manual page reasonably up to date, it will frequently lag the current state of the software. The best source of information about the configuration are the comments in the system-wide configuration file or the user manual which you can access from the recoll GUI help menu or on the recoll web site. .LP A short extract of the file might look as follows: .IP .nf # Space-separated list of directories to index. topdirs = ~/docs /usr/share/doc [~/somedirectory-with-utf8-txt-files] defaultcharset = utf-8 .fi .LP There are three kinds of lines: .RS .IP \(bu Comment or empty .IP \(bu Parameter affectation .IP \(bu Section definition .RE .LP Empty lines or lines beginning with # are ignored. .LP Affectation lines are in the form 'name = value'. .LP Section lines allow redefining a parameter for a directory subtree. Some of the parameters used for indexing are looked up hierarchically from the more to the less specific. Not all parameters can be meaningfully redefined, this is specified for each in the next section. .LP The tilde character (~) is expanded in file names to the name of the user's home directory. .LP Where values are lists, white space is used for separation, and elements with embedded spaces can be quoted with double-quotes. .SH OPTIONS .TP .BI "topdirs = "string Space-separated list of files or directories to recursively index. Default to ~ (indexes $HOME). You can use symbolic links in the list, they will be followed, independently of the value of the followLinks variable. .TP .BI "monitordirs = "string Space-separated list of files or directories to monitor for updates. When running the real-time indexer, this allows monitoring only a subset of the whole indexed area. The elements must be included in the tree defined by the 'topdirs' members. .TP .BI "skippedNames = "string Files and directories which should be ignored. White space separated list of wildcard patterns (simple ones, not paths, must contain no / ), which will be tested against file and directory names. The list in the default configuration does not exclude hidden directories (names beginning with a dot), which means that it may index quite a few things that you do not want. On the other hand, email user agents like Thunderbird usually store messages in hidden directories, and you probably want this indexed. One possible solution is to have ".*" in "skippedNames", and add things like "~/.thunderbird" "~/.evolution" to "topdirs". Not even the file names are indexed for patterns in this list, see the "noContentSuffixes" variable for an alternative approach which indexes the file names. Can be redefined for any subtree. .TP .BI "skippedNames- = "string List of name endings to remove from the default skippedNames list. .TP .BI "skippedNames+ = "string List of name endings to add to the default skippedNames list. .TP .BI "onlyNames = "string Regular file name filter patterns If this is set, only the file names not in skippedNames and matching one of the patterns will be considered for indexing. Can be redefined per subtree. Does not apply to directories. .TP .BI "noContentSuffixes = "string List of name endings (not necessarily dot-separated suffixes) for which we don't try MIME type identification, and don't uncompress or index content. Only the names will be indexed. This complements the now obsoleted recoll_noindex list from the mimemap file, which will go away in a future release (the move from mimemap to recoll.conf allows editing the list through the GUI). This is different from skippedNames because these are name ending matches only (not wildcard patterns), and the file name itself gets indexed normally. This can be redefined for subdirectories. .TP .BI "noContentSuffixes- = "string List of name endings to remove from the default noContentSuffixes list. .TP .BI "noContentSuffixes+ = "string List of name endings to add to the default noContentSuffixes list. .TP .BI "skippedPaths = "string Absolute paths we should not go into. Space-separated list of wildcard expressions for absolute filesystem paths. Must be defined at the top level of the configuration file, not in a subsection. Can contain files and directories. The database and configuration directories will automatically be added. The expressions are matched using 'fnmatch(3)' with the FNM_PATHNAME flag set by default. This means that '/' characters must be matched explicitly. You can set 'skippedPathsFnmPathname' to 0 to disable the use of FNM_PATHNAME (meaning that '/*/dir3' will match '/dir1/dir2/dir3'). The default value contains the usual mount point for removable media to remind you that it is a bad idea to have Recoll work on these (esp. with the monitor: media gets indexed on mount, all data gets erased on unmount). Explicitly adding '/media/xxx' to the 'topdirs' variable will override this. .TP .BI "skippedPathsFnmPathname = "bool Set to 0 to override use of FNM_PATHNAME for matching skipped paths. .TP .BI "nowalkfn = "string File name which will cause its parent directory to be skipped. Any directory containing a file with this name will be skipped as if it was part of the skippedPaths list. Ex: .recoll-noindex .TP .BI "daemSkippedPaths = "string skippedPaths equivalent specific to real time indexing. This enables having parts of the tree which are initially indexed but not monitored. If daemSkippedPaths is not set, the daemon uses skippedPaths. .TP .BI "zipUseSkippedNames = "bool Use skippedNames inside Zip archives. Fetched directly by the rclzip.py handler. Skip the patterns defined by skippedNames inside Zip archives. Can be redefined for subdirectories. See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html .TP .BI "zipSkippedNames = "string Space-separated list of wildcard expressions for names that should be ignored inside zip archives. This is used directly by the zip handler. If zipUseSkippedNames is not set, zipSkippedNames defines the patterns to be skipped inside archives. If zipUseSkippedNames is set, the two lists are concatenated and used. Can be redefined for subdirectories. See https://www.lesbonscomptes.com/recoll/faqsandhowtos/FilteringOutZipArchiveMembers.html .TP .BI "followLinks = "bool Follow symbolic links during indexing. The default is to ignore symbolic links to avoid multiple indexing of linked files. No effort is made to avoid duplication when this option is set to true. This option can be set individually for each of the 'topdirs' members by using sections. It can not be changed below the 'topdirs' level. Links in the 'topdirs' list itself are always followed. .TP .BI "indexedmimetypes = "string Restrictive list of indexed mime types. Normally not set (in which case all supported types are indexed). If it is set, only the types from the list will have their contents indexed. The names will be indexed anyway if indexallfilenames is set (default). MIME type names should be taken from the mimemap file (the values may be different from xdg-mime or file -i output in some cases). Can be redefined for subtrees. .TP .BI "excludedmimetypes = "string List of excluded MIME types. Lets you exclude some types from indexing. MIME type names should be taken from the mimemap file (the values may be different from xdg-mime or file -i output in some cases) Can be redefined for subtrees. .TP .BI "nomd5types = "string Don't compute md5 for these types. md5 checksums are used only for deduplicating results, and can be very expensive to compute on multimedia or other big files. This list lets you turn off md5 computation for selected types. It is global (no redefinition for subtrees). At the moment, it only has an effect for external handlers (exec and execm). The file types can be specified by listing either MIME types (e.g. audio/mpeg) or handler names (e.g. rclaudio.py). .TP .BI "compressedfilemaxkbs = "int Size limit for compressed files. We need to decompress these in a temporary directory for identification, which can be wasteful in some cases. Limit the waste. Negative means no limit. 0 results in no processing of any compressed file. Default 50 MB. .TP .BI "textfilemaxmbs = "int Size limit for text files. Mostly for skipping monster logs. Default 20 MB. .TP .BI "indexallfilenames = "bool Index the file names of unprocessed files Index the names of files the contents of which we don't index because of an excluded or unsupported MIME type. .TP .BI "usesystemfilecommand = "bool Use a system command for file MIME type guessing as a final step in file type identification This is generally useful, but will usually cause the indexing of many bogus 'text' files. See 'systemfilecommand' for the command used. .TP .BI "systemfilecommand = "string Command used to guess MIME types if the internal methods fails This should be a "file -i" workalike. The file path will be added as a last parameter to the command line. "xdg-mime" works better than the traditional "file" command, and is now the configured default (with a hard-coded fallback to "file") .TP .BI "processwebqueue = "bool Decide if we process the Web queue. The queue is a directory where the Recoll Web browser plugins create the copies of visited pages. .TP .BI "textfilepagekbs = "int Page size for text files. If this is set, text/plain files will be divided into documents of approximately this size. Will reduce memory usage at index time and help with loading data in the preview window at query time. Particularly useful with very big files, such as application or system logs. Also see textfilemaxmbs and compressedfilemaxkbs. .TP .BI "membermaxkbs = "int Size limit for archive members. This is passed to the filters in the environment as RECOLL_FILTER_MAXMEMBERKB. .TP .BI "indexStripChars = "bool Decide if we store character case and diacritics in the index. If we do, searches sensitive to case and diacritics can be performed, but the index will be bigger, and some marginal weirdness may sometimes occur. The default is a stripped index. When using multiple indexes for a search, this parameter must be defined identically for all. Changing the value implies an index reset. .TP .BI "indexStoreDocText = "bool Decide if we store the documents' text content in the index. Storing the text allows extracting snippets from it at query time, instead of building them from index position data. Newer Xapian index formats have rendered our use of positions list unacceptably slow in some cases. The last Xapian index format with good performance for the old method is Chert, which is default for 1.2, still supported but not default in 1.4 and will be dropped in 1.6. The stored document text is translated from its original format to UTF-8 plain text, but not stripped of upper-case, diacritics, or punctuation signs. Storing it increases the index size by 10-20% typically, but also allows for nicer snippets, so it may be worth enabling it even if not strictly needed for performance if you can afford the space. The variable only has an effect when creating an index, meaning that the xapiandb directory must not exist yet. Its exact effect depends on the Xapian version. For Xapian 1.4, if the variable is set to 0, the Chert format will be used, and the text will not be stored. If the variable is 1, Glass will be used, and the text stored. For Xapian 1.2, and for versions after 1.5 and newer, the index format is always the default, but the variable controls if the text is stored or not, and the abstract generation method. With Xapian 1.5 and later, and the variable set to 0, abstract generation may be very slow, but this setting may still be useful to save space if you do not use abstract generation at all. .TP .BI "nonumbers = "bool Decides if terms will be generated for numbers. For example "123", "1.5e6", 192.168.1.4, would not be indexed if nonumbers is set ("value123" would still be). Numbers are often quite interesting to search for, and this should probably not be set except for special situations, ie, scientific documents with huge amounts of numbers in them, where setting nonumbers will reduce the index size. This can only be set for a whole index, not for a subtree. .TP .BI "dehyphenate = "bool Determines if we index 'coworker' also when the input is 'co-worker'. This is new in version 1.22, and on by default. Setting the variable to off allows restoring the previous behaviour. .TP .BI "backslashasletter = "bool Process backslash as normal letter. This may make sense for people wanting to index TeX commands as such but is not of much general use. .TP .BI "underscoreasletter = "bool Process underscore as normal letter. This makes sense in so many cases that one wonders if it should not be the default. .TP .BI "maxtermlength = "int Maximum term length. Words longer than this will be discarded. The default is 40 and used to be hard-coded, but it can now be adjusted. You need an index reset if you change the value. .TP .BI "nocjk = "bool Decides if specific East Asian (Chinese Korean Japanese) characters/word splitting is turned off. This will save a small amount of CPU if you have no CJK documents. If your document base does include such text but you are not interested in searching it, setting nocjk may be a significant time and space saver. .TP .BI "cjkngramlen = "int This lets you adjust the size of n-grams used for indexing CJK text. The default value of 2 is probably appropriate in most cases. A value of 3 would allow more precision and efficiency on longer words, but the index will be approximately twice as large. .TP .BI "indexstemminglanguages = "string Languages for which to create stemming expansion data. Stemmer names can be found by executing 'recollindex -l', or this can also be set from a list in the GUI. The values are full language names, e.g. english, french... .TP .BI "defaultcharset = "string Default character set. This is used for files which do not contain a character set definition (e.g.: text/plain). Values found inside files, e.g. a 'charset' tag in HTML documents, will override it. If this is not set, the default character set is the one defined by the NLS environment ($LC_ALL, $LC_CTYPE, $LANG), or ultimately iso-8859-1 (cp-1252 in fact). If for some reason you want a general default which does not match your LANG and is not 8859-1, use this variable. This can be redefined for any sub-directory. .TP .BI "unac_except_trans = "string A list of characters, encoded in UTF-8, which should be handled specially when converting text to unaccented lowercase. For example, in Swedish, the letter a with diaeresis has full alphabet citizenship and should not be turned into an a. Each element in the space-separated list has the special character as first element and the translation following. The handling of both the lowercase and upper-case versions of a character should be specified, as appartenance to the list will turn-off both standard accent and case processing. The value is global and affects both indexing and querying. Examples: .br Swedish: .br unac_except_trans = ää Ää öö Öö üü Üü ßss œoe Œoe æae Æae ffff fifi flfl åå Åå .br German: .br unac_except_trans = ää Ää öö Öö üü Üü ßss œoe Œoe æae Æae ffff fifi flfl .br French: you probably want to decompose oe and ae and nobody would type a German ß .br unac_except_trans = ßss œoe Œoe æae Æae ffff fifi flfl .br The default for all until someone protests follows. These decompositions are not performed by unac, but it is unlikely that someone would type the composed forms in a search. .br unac_except_trans = ßss œoe Œoe æae Æae ffff fifi flfl .TP .BI "maildefcharset = "string Overrides the default character set for email messages which don't specify one. This is mainly useful for readpst (libpst) dumps, which are utf-8 but do not say so. .TP .BI "localfields = "string Set fields on all files (usually of a specific fs area). Syntax is the usual: name = value ; attr1 = val1 ; [...] value is empty so this needs an initial semi-colon. This is useful, e.g., for setting the rclaptg field for application selection inside mimeview. .TP .BI "testmodifusemtime = "bool Use mtime instead of ctime to test if a file has been modified. The time is used in addition to the size, which is always used. Setting this can reduce re-indexing on systems where extended attributes are used (by some other application), but not indexed, because changing extended attributes only affects ctime. Notes: - This may prevent detection of change in some marginal file rename cases (the target would need to have the same size and mtime). - You should probably also set noxattrfields to 1 in this case, except if you still prefer to perform xattr indexing, for example if the local file update pattern makes it of value (as in general, there is a risk for pure extended attributes updates without file modification to go undetected). Perform a full index reset after changing this. .TP .BI "noxattrfields = "bool Disable extended attributes conversion to metadata fields. This probably needs to be set if testmodifusemtime is set. .TP .BI "metadatacmds = "string Define commands to gather external metadata, e.g. tmsu tags. There can be several entries, separated by semi-colons, each defining which field name the data goes into and the command to use. Don't forget the initial semi-colon. All the field names must be different. You can use aliases in the "field" file if necessary. As a not too pretty hack conceded to convenience, any field name beginning with "rclmulti" will be taken as an indication that the command returns multiple field values inside a text blob formatted as a recoll configuration file ("fieldname = fieldvalue" lines). The rclmultixx name will be ignored, and field names and values will be parsed from the data. Example: metadatacmds = ; tags = tmsu tags %f; rclmulti1 = cmdOutputsConf %f .TP .BI "cachedir = "dfn Top directory for Recoll data. Recoll data directories are normally located relative to the configuration directory (e.g. ~/.recoll/xapiandb, ~/.recoll/mboxcache). If 'cachedir' is set, the directories are stored under the specified value instead (e.g. if cachedir is ~/.cache/recoll, the default dbdir would be ~/.cache/recoll/xapiandb). This affects dbdir, webcachedir, mboxcachedir, aspellDicDir, which can still be individually specified to override cachedir. Note that if you have multiple configurations, each must have a different cachedir, there is no automatic computation of a subpath under cachedir. .TP .BI "maxfsoccuppc = "int Maximum file system occupation over which we stop indexing. The value is a percentage, corresponding to what the "Capacity" df output column shows. The default value is 0, meaning no checking. .TP .BI "dbdir = "dfn Xapian database directory location. This will be created on first indexing. If the value is not an absolute path, it will be interpreted as relative to cachedir if set, or the configuration directory (-c argument or $RECOLL_CONFDIR). If nothing is specified, the default is then ~/.recoll/xapiandb/ .TP .BI "idxstatusfile = "fn Name of the scratch file where the indexer process updates its status. Default: idxstatus.txt inside the configuration directory. .TP .BI "mboxcachedir = "dfn Directory location for storing mbox message offsets cache files. This is normally 'mboxcache' under cachedir if set, or else under the configuration directory, but it may be useful to share a directory between different configurations. .TP .BI "mboxcacheminmbs = "int Minimum mbox file size over which we cache the offsets. There is really no sense in caching offsets for small files. The default is 5 MB. .TP .BI "mboxmaxmsgmbs = "int Maximum mbox member message size in megabytes. Size over which we assume that the mbox format is bad or we misinterpreted it, at which point we just stop processing the file. .TP .BI "webcachedir = "dfn Directory where we store the archived web pages. This is only used by the web history indexing code Default: cachedir/webcache if cachedir is set, else $RECOLL_CONFDIR/webcache .TP .BI "webcachemaxmbs = "int Maximum size in MB of the Web archive. This is only used by the web history indexing code. Default: 40 MB. Reducing the size will not physically truncate the file. .TP .BI "webqueuedir = "fn The path to the Web indexing queue. This used to be hard-coded in the old plugin as ~/.recollweb/ToIndex so there would be no need or possibility to change it, but the WebExtensions plugin now downloads the files to the user Downloads directory, and a script moves them to webqueuedir. The script reads this value from the config so it has become possible to change it. .TP .BI "webdownloadsdir = "fn The path to browser downloads directory. This is where the new browser add-on extension has to create the files. They are then moved by a script to webqueuedir. .TP .BI "aspellDicDir = "dfn Aspell dictionary storage directory location. The aspell dictionary (aspdict.(lang).rws) is normally stored in the directory specified by cachedir if set, or under the configuration directory. .TP .BI "filtersdir = "dfn Directory location for executable input handlers. If RECOLL_FILTERSDIR is set in the environment, we use it instead. Defaults to $prefix/share/recoll/filters. Can be redefined for subdirectories. .TP .BI "iconsdir = "dfn Directory location for icons. The only reason to change this would be if you want to change the icons displayed in the result list. Defaults to $prefix/share/recoll/images .TP .BI "idxflushmb = "int Threshold (megabytes of new data) where we flush from memory to disk index. Setting this allows some control over memory usage by the indexer process. A value of 0 means no explicit flushing, which lets Xapian perform its own thing, meaning flushing every $XAPIAN_FLUSH_THRESHOLD documents created, modified or deleted: as memory usage depends on average document size, not only document count, the Xapian approach is is not very useful, and you should let Recoll manage the flushes. The program compiled value is 0. The configured default value (from this file) is now 50 MB, and should be ok in many cases. You can set it as low as 10 to conserve memory, but if you are looking for maximum speed, you may want to experiment with values between 20 and 200. In my experience, values beyond this are always counterproductive. If you find otherwise, please drop me a note. .TP .BI "filtermaxseconds = "int Maximum external filter execution time in seconds. Default 1200 (20mn). Set to 0 for no limit. This is mainly to avoid infinite loops in postscript files (loop.ps) .TP .BI "filtermaxmbytes = "int Maximum virtual memory space for filter processes (setrlimit(RLIMIT_AS)), in megabytes. Note that this includes any mapped libs (there is no reliable Linux way to limit the data space only), so we need to be a bit generous here. Anything over 2000 will be ignored on 32 bits machines. The previous default value of 2000 would prevent java pdftk to work when executed from Python rclpdf.py. .TP .BI "thrQSizes = "string Stage input queues configuration. There are three internal queues in the indexing pipeline stages (file data extraction, terms generation, index update). This parameter defines the queue depths for each stage (three integer values). If a value of -1 is given for a given stage, no queue is used, and the thread will go on performing the next stage. In practise, deep queues have not been shown to increase performance. Default: a value of 0 for the first queue tells Recoll to perform autoconfiguration based on the detected number of CPUs (no need for the two other values in this case). Use thrQSizes = -1 -1 -1 to disable multithreading entirely. .TP .BI "thrTCounts = "string Number of threads used for each indexing stage. The three stages are: file data extraction, terms generation, index update). The use of the counts is also controlled by some special values in thrQSizes: if the first queue depth is 0, all counts are ignored (autoconfigured); if a value of -1 is used for a queue depth, the corresponding thread count is ignored. It makes no sense to use a value other than 1 for the last stage because updating the Xapian index is necessarily single-threaded (and protected by a mutex). .TP .BI "loglevel = "int Log file verbosity 1-6. A value of 2 will print only errors and warnings. 3 will print information like document updates, 4 is quite verbose and 6 very verbose. .TP .BI "logfilename = "fn Log file destination. Use 'stderr' (default) to write to the console. .TP .BI "idxloglevel = "int Override loglevel for the indexer. .TP .BI "idxlogfilename = "fn Override logfilename for the indexer. .TP .BI "daemloglevel = "int Override loglevel for the indexer in real time mode. The default is to use the idx... values if set, else the log... values. .TP .BI "daemlogfilename = "fn Override logfilename for the indexer in real time mode. The default is to use the idx... values if set, else the log... values. .TP .BI "pyloglevel = "int Override loglevel for the python module. .TP .BI "pylogfilename = "fn Override logfilename for the python module. .TP .BI "orgidxconfdir = "dfn Original location of the configuration directory. This is used exclusively for movable datasets. Locating the configuration directory inside the directory tree makes it possible to provide automatic query time path translations once the data set has moved (for example, because it has been mounted on another location). .TP .BI "curidxconfdir = "dfn Current location of the configuration directory. Complement orgidxconfdir for movable datasets. This should be used if the configuration directory has been copied from the dataset to another location, either because the dataset is readonly and an r/w copy is desired, or for performance reasons. This records the original moved location before copy, to allow path translation computations. For example if a dataset originally indexed as '/home/me/mydata/config' has been mounted to '/media/me/mydata', and the GUI is running from a copied configuration, orgidxconfdir would be '/home/me/mydata/config', and curidxconfdir (as set in the copied configuration) would be '/media/me/mydata/config'. .TP .BI "idxrundir = "dfn Indexing process current directory. The input handlers sometimes leave temporary files in the current directory, so it makes sense to have recollindex chdir to some temporary directory. If the value is empty, the current directory is not changed. If the value is (literal) tmp, we use the temporary directory as set by the environment (RECOLL_TMPDIR else TMPDIR else /tmp). If the value is an absolute path to a directory, we go there. .TP .BI "checkneedretryindexscript = "fn Script used to heuristically check if we need to retry indexing files which previously failed. The default script checks the modified dates on /usr/bin and /usr/local/bin. A relative path will be looked up in the filters dirs, then in the path. Use an absolute path to do otherwise. .TP .BI "recollhelperpath = "string Additional places to search for helper executables. This is only used on Windows for now. .TP .BI "idxabsmlen = "int Length of abstracts we store while indexing. Recoll stores an abstract for each indexed file. The text can come from an actual 'abstract' section in the document or will just be the beginning of the document. It is stored in the index so that it can be displayed inside the result lists without decoding the original file. The idxabsmlen parameter defines the size of the stored abstract. The default value is 250 bytes. The search interface gives you the choice to display this stored text or a synthetic abstract built by extracting text around the search terms. If you always prefer the synthetic abstract, you can reduce this value and save a little space. .TP .BI "idxmetastoredlen = "int Truncation length of stored metadata fields. This does not affect indexing (the whole field is processed anyway), just the amount of data stored in the index for the purpose of displaying fields inside result lists or previews. The default value is 150 bytes which may be too low if you have custom fields. .TP .BI "idxtexttruncatelen = "int Truncation length for all document texts. Only index the beginning of documents. This is not recommended except if you are sure that the interesting keywords are at the top and have severe disk space issues. .TP .BI "aspellLanguage = "string Language definitions to use when creating the aspell dictionary. The value must match a set of aspell language definition files. You can type "aspell dicts" to see a list The default if this is not set is to use the NLS environment to guess the value. The values are the 2-letter language codes (e.g. 'en', 'fr'...) .TP .BI "aspellAddCreateParam = "string Additional option and parameter to aspell dictionary creation command. Some aspell packages may need an additional option (e.g. on Debian Jessie: --local-data-dir=/usr/lib/aspell). See Debian bug 772415. .TP .BI "aspellKeepStderr = "bool Set this to have a look at aspell dictionary creation errors. There are always many, so this is mostly for debugging. .TP .BI "noaspell = "bool Disable aspell use. The aspell dictionary generation takes time, and some combinations of aspell version, language, and local terms, result in aspell crashing, so it sometimes makes sense to just disable the thing. .TP .BI "monauxinterval = "int Auxiliary database update interval. The real time indexer only updates the auxiliary databases (stemdb, aspell) periodically, because it would be too costly to do it for every document change. The default period is one hour. .TP .BI "monixinterval = "int Minimum interval (seconds) between processings of the indexing queue. The real time indexer does not process each event when it comes in, but lets the queue accumulate, to diminish overhead and to aggregate multiple events affecting the same file. Default 30 S. .TP .BI "mondelaypatterns = "string Timing parameters for the real time indexing. Definitions for files which get a longer delay before reindexing is allowed. This is for fast-changing files, that should only be reindexed once in a while. A list of wildcardPattern:seconds pairs. The patterns are matched with fnmatch(pattern, path, 0) You can quote entries containing white space with double quotes (quote the whole entry, not the pattern). The default is empty. Example: mondelaypatterns = *.log:20 "*with spaces.*:30" .TP .BI "idxniceprio = "int "nice" process priority for the indexing processes. Default: 19 (lowest) Appeared with 1.26.5. Prior versions were fixed at 19. .TP .BI "monioniceclass = "int ionice class for the indexing process. Despite the misleading name, and on platforms where this is supported, this affects all indexing processes, not only the real time/monitoring ones. The default value is 3 (use lowest "Idle" priority). .TP .BI "monioniceclassdata = "string ionice class level parameter if the class supports it. The default is empty, as the default "Idle" class has no levels. .TP .BI "autodiacsens = "bool auto-trigger diacritics sensitivity (raw index only). IF the index is not stripped, decide if we automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the "D" modifier to specify diacritics sensitivity. Default is no. .TP .BI "autocasesens = "bool auto-trigger case sensitivity (raw index only). IF the index is not stripped (see indexStripChars), decide if we automatically trigger character case sensitivity if the search term has upper-case characters in any but the first position. Else you need to use the query language and the "C" modifier to specify character-case sensitivity. Default is yes. .TP .BI "maxTermExpand = "int Maximum query expansion count for a single term (e.g.: when using wildcards). This only affects queries, not indexing. We used to not limit this at all (except for filenames where the limit was too low at 1000), but it is unreasonable with a big index. Default 10000. .TP .BI "maxXapianClauses = "int Maximum number of clauses we add to a single Xapian query. This only affects queries, not indexing. In some cases, the result of term expansion can be multiplicative, and we want to avoid eating all the memory. Default 50000. .TP .BI "snippetMaxPosWalk = "int Maximum number of positions we walk while populating a snippet for the result list. The default of 1,000,000 may be insufficient for very big documents, the consequence would be snippets with possibly meaning-altering missing words. .TP .BI "pdfocr = "bool Attempt OCR of PDF files with no text content. This can be defined in subdirectories. The default is off because OCR is so very slow. .TP .BI "pdfattach = "bool Enable PDF attachment extraction by executing pdftk (if available). This is normally disabled, because it does slow down PDF indexing a bit even if not one attachment is ever found. .TP .BI "pdfextrameta = "string Extract text from selected XMP metadata tags. This is a space-separated list of qualified XMP tag names. Each element can also include a translation to a Recoll field name, separated by a '|' character. If the second element is absent, the tag name is used as the Recoll field names. You will also need to add specifications to the "fields" file to direct processing of the extracted data. .TP .BI "pdfextrametafix = "fn Define name of XMP field editing script. This defines the name of a script to be loaded for editing XMP field values. The script should define a 'MetaFixer' class with a metafix() method which will be called with the qualified tag name and value of each selected field, for editing or erasing. A new instance is created for each document, so that the object can keep state for, e.g. eliminating duplicate values. .TP .BI "ocrprogs = "string OCR modules to try. The top OCR script will try to load the corresponding modules in order and use the first which reports being capable of performing OCR on the input file. Modules for tesseract (tesseract) and ABBYY FineReader (abbyy) are present in the standard distribution. For compatibility with the previous version, if this is not defined at all, the default value is "tesseract". Use an explicit empty value if needed. A value of "abbyy tesseract" will try everything. .TP .BI "ocrcachedir = "dfn Location for caching OCR data. The default if this is empty or undefined is to store the cached OCR data under $RECOLL_CONFDIR/ocrcache. .TP .BI "tesseractlang = "string Language to assume for tesseract OCR. Important for improving the OCR accuracy. This can also be set through the contents of a file in the currently processed directory. See the rclocrtesseract.py script. Example values: eng, fra... See the tesseract documentation. .TP .BI "tesseractcmd = "fn Path for the tesseract command. Do not quote. This is mostly useful on Windows, or for specifying a non-default tesseract command. E.g. on Windows. tesseractcmd = C:/Program Files (x86)/Tesseract-OCR/tesseract.exe .TP .BI "abbyylang = "string Language to assume for abbyy OCR. Important for improving the OCR accuracy. This can also be set through the contents of a file in the currently processed directory. See the rclocrabbyy.py script. Typical values: English, French... See the ABBYY documentation. .TP .BI "abbyycmd = "fn Path for the abbyy command The ABBY directory is usually not in the path, so you should set this. .TP .BI "mhmboxquirks = "string Enable thunderbird/mozilla-seamonkey mbox format quirks Set this for the directory where the email mbox files are stored. .SH SEE ALSO .PP recollindex(1) recoll(1) recoll-1.36.1/doc/man/rclgrep.10000644000175000017500000000031114410615043013034 00000000000000.TH RCLGREP 1 "20 September 2022" .SH NAME rclgrep \- grep-like program based on the recoll data extraction functions .SH SYNOPSIS .B rclgrep [ .B \--config ] .SH DESCRIPTION Some bla bla recoll-1.36.1/doc/man/recoll.10000644000175000017500000000440614410615043012667 00000000000000.\" $Id: recoll.1,v 1.3 2007-11-13 18:42:18 dockes Exp $ (C) 2005 J.F.Dockes\$ .TH RECOLL 1 "8 January 2006" .SH NAME recoll \- user interface for the Recoll full text search system .SH SYNOPSIS .B recoll [ .B \-c ] [ .B \-o | .B \-l | .B \-f | .B \-a ] [ .B \-t ] [ .B \-q ] .B recoll [ .B \-c ] .SH DESCRIPTION In the first form, the .B recoll command will start the graphical user interface for querying the .B Recoll database. .PP On the first run, .B recoll will create the user configuration which can be customized before starting the first indexation. .PP The .B \-c option specifies the configuration directory name, overriding the default or $RECOLL_CONFDIR. .PP The .B \-q option can be used to specify an initial query on the command line. This query will be interpreted by default as a query language string. If .B \-a is specified, the query string will be interpreted as an .I all words simple search query. If .B \-o is specified, the query string will be interpreted as an .I any word simple search query. If .B \-f is specified, the query string will be interpreted as a .I file name simple search query. If .B \-l is specified, the query string will be interpreted as a .I query language simple search query. .PP If .B \-t is specified, or if .B recoll is called as .B recollq (through a link), the Graphical User Interface will not be started, and results will be printed to the standard output. Additional options understood by the .B recollq command may be specified in this case. These can control the output format and the maximum number of results to be printed. .PP Please refer to online help for a full description. .PP In the second form, the .B recoll command can be used to start a native viewer for a document indexed by Recoll. It will understand a final URL fragment (separated by a '#' character) to indicate an .I ipath , the specifier for the part of the Recoll document access path which is is internal to a container such as a mbox folder or a zip archive, and will, if needed, create a temporary file to let a normal system utility display the document. .PP The second form is mostly used for opening embedded documents from the Ubuntu Unity Recoll lens. .SH SEE ALSO .PP recollindex(1) recollq(1) recoll.conf(5) recoll-1.36.1/rcldb/0000755000175000017500000000000014521161750011153 500000000000000recoll-1.36.1/rcldb/termproc.h0000644000175000017500000002675514520456422013120 00000000000000/* Copyright (C) 2011 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _TERMPROC_H_INCLUDED_ #define _TERMPROC_H_INCLUDED_ #include #include #include #include #include "textsplit.h" #include "stoplist.h" #include "smallut.h" #include "utf8iter.h" #include "unacpp.h" #include "syngroups.h" namespace Rcl { /** * Termproc objects take term tokens as input and do something * with them: transform to lowercase, filter out stop words, generate n-grams, * finally index or generate search clauses, etc. They are chained and can * be arranged to form different pipelines depending on the desired processing * steps: for example, optional stoplist or commongram processing. * * Shared processing steps are defined in this file. The first and last steps * are usually defined in the specific module. * - The front TermProc is typically chained from a TextSplit object * which generates the original terms, and calls takeword() from its * own takeword() method. * - The last TermProc does something with the finalized terms, e.g. adds * them to the index. */ /** * The base class takes care of chaining: all derived classes call its * takeword() and flush() methods to ensure that terms go through the pipe. */ class TermProc { public: TermProc(TermProc* next) : m_next(next) {} virtual ~TermProc() {} /* Copyconst and assignment forbidden */ TermProc(const TermProc &) = delete; TermProc& operator=(const TermProc &) = delete; virtual bool takeword(const std::string &term, int pos, int bs, int be) { if (m_next) return m_next->takeword(term, pos, bs, be); return true; } // newpage() is like takeword(), but for page breaks. virtual void newpage(int pos) { if (m_next) m_next->newpage(pos); } virtual bool flush() { if (m_next) return m_next->flush(); return true; } private: TermProc *m_next; }; /** * Helper specialized TextSplit class, feeds the pipeline: * - The takeword() method calls a TermProc->takeword(). * - The text_to_words() method also takes care of flushing. * Both methods can be further specialized by the user (they should then call * the base methods when they've done the local processing). */ class TextSplitP : public TextSplit { public: TextSplitP(TermProc *prc, Flags flags = Flags(TXTS_NONE)) : TextSplit(flags), m_prc(prc) {} virtual bool text_to_words(const std::string &in) { bool ret = TextSplit::text_to_words(in); if (m_prc && !m_prc->flush()) return false; return ret; } virtual bool takeword(const std::string& term, int pos, int bs, int be) { if (m_prc) return m_prc->takeword(term, pos, bs, be); return true; } virtual void newpage(int pos) { if (m_prc) return m_prc->newpage(pos); } private: TermProc *m_prc; }; /** Unaccent and lowercase term. If the index is * not case/diac-sensitive, this is usually the first step in the pipeline */ class TermProcPrep : public TermProc { public: TermProcPrep(TermProc *nxt) : TermProc(nxt) {} virtual bool takeword(const std::string& itrm, int pos, int bs, int be) { m_totalterms++; std::string otrm; if (!unacmaybefold(itrm, otrm, "UTF-8", UNACOP_UNACFOLD)) { LOGDEB("splitter::takeword: unac [" << itrm << "] failed\n"); m_unacerrors++; // We don't generate a fatal error because of a bad term, // but one has to put the limit somewhere if (m_unacerrors > 500 && (double(m_totalterms) / double(m_unacerrors)) < 2.0) { // More than 1 error for every other term LOGERR("splitter::takeword: too many unac errors " << m_unacerrors << "/" << m_totalterms << "\n"); return false; } return true; } if (otrm.empty()) { // It may happen in some weird cases that the output from // unac is empty (if the word actually consisted entirely // of diacritics ...) The consequence is that a phrase // search won't work without additional slack. return true; } // We should have a Japanese stemmer to handle this, but for // experimenting, let's do it here: remove 'prolounged sound // mark' and its halfwidth variant from the end of terms. if ((unsigned int)otrm[0] > 127) { Utf8Iter it(otrm); if (TextSplit::isKATAKANA(*it)) { Utf8Iter itprev = it; while (*it != (unsigned int)-1) { itprev = it; it++; } if (*itprev == 0x30fc || *itprev == 0xff70) { otrm = otrm.substr(0, itprev.getBpos()); } } } if (otrm.empty()) { return true; } // It may also occur that unac introduces spaces in the string // (when removing isolated accents, may happen for Greek // for example). This is a pathological situation. We // index all the resulting terms at the same pos because // the surrounding code is not designed to handle a pos // change in here. This means that phrase searches and // snippets will be wrong, but at least searching for the // terms will work. bool hasspace = otrm.find(' ') != std::string::npos; if (hasspace) { std::vector terms; stringToTokens(otrm, terms, " ", true); for (const auto& term : terms) { if (!TermProc::takeword(term, pos, bs, be)) { return false; } } return true; } return TermProc::takeword(otrm, pos, bs, be); } virtual bool flush() { m_totalterms = m_unacerrors = 0; return TermProc::flush(); } private: int m_totalterms{0}; int m_unacerrors{0}; }; /** Compare to stop words list and discard if match found */ class TermProcStop : public TermProc { public: TermProcStop(TermProc *nxt, const Rcl::StopList& stops) : TermProc(nxt), m_stops(stops) {} virtual bool takeword(const std::string& term, int pos, int bs, int be) { if (m_stops.isStop(term)) { return true; } return TermProc::takeword(term, pos, bs, be); } private: const Rcl::StopList& m_stops; }; /** Generate multiword terms for multiword synonyms. This allows * NEAR/PHRASE searches for multiword synonyms. */ class TermProcMulti : public TermProc { public: TermProcMulti(TermProc *nxt, const SynGroups& sg) : TermProc(nxt), m_groups(sg.getmultiwords()), m_maxl(sg.getmultiwordsmaxlength()) {} virtual bool takeword(const std::string& term, int pos, int bs, int be) { LOGDEB1("TermProcMulti::takeword[" << term << "] at pos " << pos <<"\n"); if (m_maxl < 2) { // Should not have been pushed?? return TermProc::takeword(term, pos, bs, be); } m_terms.push_back(term); if (m_terms.size() > m_maxl) { m_terms.pop_front(); } std::string comp; int gsz{1}; for (const auto& gterm : m_terms) { if (comp.empty()) { comp = gterm; continue; } else { comp += " "; comp += gterm; gsz++; // We could optimize by not testing m_groups for sizes // which do not exist. // if not gsz in sizes continue; } if (m_groups.find(comp) != m_groups.end()) { LOGDEB1("Emitting multiword synonym: [" << comp << "] at pos " << pos-gsz+1 << "\n"); // TBD bs-be correct computation. Need to store the // values in a parallel list TermProc::takeword(comp, pos-gsz+1, bs-comp.size(), be); } } return TermProc::takeword(term, pos, bs, be); } private: const std::set& m_groups; size_t m_maxl{0}; std::list m_terms; }; /** Handle common-gram generation: combine frequent terms with neighbours to * shorten the positions lists for phrase searches. * NOTE: This does not currently work because of bad interaction with the * spans (ie john@domain.com) generation in textsplit. Not used, kept for * testing only */ class TermProcCommongrams : public TermProc { public: TermProcCommongrams(TermProc *nxt, const Rcl::StopList& stops) : TermProc(nxt), m_stops(stops), m_onlygrams(false) {} virtual bool takeword(const std::string& term, int pos, int bs, int be) { LOGDEB1("TermProcCom::takeword: pos " << pos << " " << bs << " " << be << " [" << term << "]\n"); bool isstop = m_stops.isStop(term); bool twogramemit = false; if (!m_prevterm.empty() && (m_prevstop || isstop)) { // create 2-gram. space unnecessary but improves // the readability of queries std::string twogram; twogram.swap(m_prevterm); twogram.append(1, ' '); twogram += term; // When emitting a complex term we set the bps to 0. This may // be used by our clients if (!TermProc::takeword(twogram, m_prevpos, 0, 0)) return false; twogramemit = true; #if 0 if (m_stops.isStop(twogram)) { firstword = twogram; isstop = false; } #endif } m_prevterm = term; m_prevstop = isstop; m_prevpos = pos; m_prevsent = false; m_prevbs = bs; m_prevbe = be; // If flags allow, emit the bare term at the current pos. if (!m_onlygrams || (!isstop && !twogramemit)) { if (!TermProc::takeword(term, pos, bs, be)) return false; m_prevsent = true; } return true; } virtual bool flush() { if (!m_prevsent && !m_prevterm.empty()) if (!TermProc::takeword(m_prevterm, m_prevpos, m_prevbs, m_prevbe)) return false; m_prevterm.clear(); m_prevsent = true; return TermProc::flush(); } void onlygrams(bool on) { m_onlygrams = on; } private: // The stoplist we're using const Rcl::StopList& m_stops; // Remembered data for the last processed term std::string m_prevterm; bool m_prevstop; int m_prevpos; int m_prevbs; int m_prevbe; bool m_prevsent; // If this is set, we only emit longest grams bool m_onlygrams; }; } // End namespace Rcl #endif /* _TERMPROC_H_INCLUDED_ */ recoll-1.36.1/rcldb/stoplist.h0000644000175000017500000000277014427373216013142 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _STOPLIST_H_INCLUDED_ #define _STOPLIST_H_INCLUDED_ #include #include namespace Rcl { /** * A StopList is just a bunch of strings read from a file. * * Some of the string may contain whitespace (that's for experimentation with * stop n-grams), so we take care of dquotes while reading the file. We also * lowercase and remove accents. The source file should be utf-8. */ class StopList { public: StopList() {} StopList(const std::string &filename) {setFile(filename);} bool setFile(const std::string &filename); bool isStop(const std::string &term) const; bool hasStops() const {return !m_stops.empty();} private: std::set m_stops; }; } #endif /* _STOPLIST_H_INCLUDED_ */ recoll-1.36.1/rcldb/daterange.h0000644000175000017500000000121314427373216013202 00000000000000#ifndef _DATERANGE_H_INCLUDED_ #define _DATERANGE_H_INCLUDED_ #include using namespace std; static const string xapday_prefix = "D"; static const string xapmonth_prefix = "M"; static const string xapyear_prefix = "Y"; namespace Rcl { extern Xapian::Query date_range_filter(int y1, int m1, int d1, int y2, int m2, int d2); } #ifdef EXT4_BIRTH_TIME static const string xapbriday_prefix = "BD"; static const string xapbrimonth_prefix = "BM"; static const string xapbriyear_prefix = "BY"; namespace Rcl { extern Xapian::Query brdate_range_filter(int y1, int m1, int d1, int y2, int m2, int d2); } #endif #endif /* _DATERANGE_H_INCLUDED_ */ recoll-1.36.1/rcldb/rcldb.cpp0000644000175000017500000027746314520410776012714 00000000000000/* Copyright (C) 2004-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include "safeunistd.h" #include #include #include #include #include #include #include using namespace std; #include "xapian.h" #include "rclconfig.h" #include "log.h" #include "rcldb.h" #include "rcldb_p.h" #include "stemdb.h" #include "textsplit.h" #include "transcode.h" #include "unacpp.h" #include "conftree.h" #include "pathut.h" #include "rclutil.h" #include "smallut.h" #include "chrono.h" #include "searchdata.h" #include "rclquery.h" #include "rclquery_p.h" #include "rclvalues.h" #include "md5ut.h" #include "cancelcheck.h" #include "termproc.h" #include "expansiondbs.h" #include "rclinit.h" #include "internfile.h" #include "utf8fn.h" #include "wipedir.h" #ifdef RCL_USE_ASPELL #include "rclaspell.h" #endif #include "zlibut.h" #include "idxstatus.h" #include "rcldoc.h" #include "stoplist.h" #include "daterange.h" #ifndef XAPIAN_AT_LEAST // Added in Xapian 1.4.2. Define it here for older versions #define XAPIAN_AT_LEAST(A,B,C) \ (XAPIAN_MAJOR_VERSION > (A) || \ (XAPIAN_MAJOR_VERSION == (A) && \ (XAPIAN_MINOR_VERSION > (B) || \ (XAPIAN_MINOR_VERSION == (B) && XAPIAN_REVISION >= (C))))) #endif // Recoll index format version is stored in user metadata. When this change, // we can't open the db and will have to reindex. static const string cstr_RCL_IDX_VERSION_KEY("RCL_IDX_VERSION_KEY"); static const string cstr_RCL_IDX_VERSION("1"); static const string cstr_RCL_IDX_DESCRIPTOR_KEY("RCL_IDX_DESCRIPTOR_KEY"); static const string cstr_mbreaks("rclmbreaks"); namespace Rcl { // Some prefixes that we could get from the fields file, but are not going // to ever change. static const string fileext_prefix = "XE"; const string mimetype_prefix = "T"; const string pathelt_prefix = "XP"; static const string udi_prefix("Q"); static const string parent_prefix("F"); // Special terms to mark begin/end of field (for anchored searches). string start_of_field_term; string end_of_field_term; // Special term for page breaks. Note that we use a complicated mechanism for multiple page // breaks at the same position, when it would have been probably simpler to use XXPG/n terms // instead (did not try to implement though). A change would force users to reindex. const string page_break_term = "XXPG/"; // Special term to mark documents with children. const string has_children_term("XXC/"); // Field name for the unsplit file name. Has to exist in the field file // because of usage in termmatch() const string unsplitFilenameFieldName = "rclUnsplitFN"; static const string unsplitfilename_prefix = "XSFS"; // Empty string md5s static const string cstr_md5empty("d41d8cd98f00b204e9800998ecf8427e"); static const int MB = 1024 * 1024; string version_string(){ return string("Recoll ") + string(PACKAGE_VERSION) + string(" + Xapian ") + string(Xapian::version_string()); } // Synthetic abstract marker (to discriminate from abstract actually // found in document) static const string cstr_syntAbs("?!#@"); // Compute the unique term used to link documents to their origin. // "Q" + external udi static inline string make_uniterm(const string& udi) { string uniterm(wrap_prefix(udi_prefix)); uniterm.append(udi); return uniterm; } // Compute parent term used to link documents to their parent document (if any) // "F" + parent external udi static inline string make_parentterm(const string& udi) { // I prefer to be in possible conflict with omega than with // user-defined fields (Xxxx) that we also allow. "F" is currently // not used by omega (2008-07) string pterm(wrap_prefix(parent_prefix)); pterm.append(udi); return pterm; } Db::Native::Native(Db *db) : m_rcldb(db) #ifdef IDX_THREADS , m_wqueue("DbUpd", m_rcldb->m_config->getThrConf(RclConfig::ThrDbWrite).first) #endif // IDX_THREADS { LOGDEB1("Native::Native: me " << this << "\n"); } Db::Native::~Native() { LOGDEB1("Native::~Native: me " << this << "\n"); #ifdef IDX_THREADS if (m_havewriteq) { void *status = m_wqueue.setTerminateAndWait(); if (status) { LOGDEB1("Native::~Native: worker status " << status << "\n"); } } #endif // IDX_THREADS } #ifdef IDX_THREADS void *DbUpdWorker(void* vdbp) { recoll_threadinit(); Db::Native *ndbp = (Db::Native *)vdbp; WorkQueue *tqp = &(ndbp->m_wqueue); DbUpdTask *tsk = nullptr; for (;;) { size_t qsz = -1; if (!tqp->take(&tsk, &qsz)) { tqp->workerExit(); return (void*)1; } bool status = false; switch (tsk->op) { case DbUpdTask::AddOrUpdate: LOGDEB("DbUpdWorker: got add/update task, ql " << qsz << "\n"); status = ndbp->addOrUpdateWrite( tsk->udi, tsk->uniterm, tsk->doc, tsk->txtlen, tsk->rawztext); break; case DbUpdTask::Delete: LOGDEB("DbUpdWorker: got delete task, ql " << qsz << "\n"); status = ndbp->purgeFileWrite(false, tsk->udi, tsk->uniterm); break; case DbUpdTask::PurgeOrphans: LOGDEB("DbUpdWorker: got orphans purge task, ql " << qsz << "\n"); status = ndbp->purgeFileWrite(true, tsk->udi, tsk->uniterm); break; default: LOGERR("DbUpdWorker: unknown op " << tsk->op << " !!\n"); break; } if (!status) { LOGERR("DbUpdWorker: xxWrite failed\n"); tqp->workerExit(); delete tsk; return (void*)0; } delete tsk; } } void Db::Native::maybeStartThreads() { m_havewriteq = false; const RclConfig *cnf = m_rcldb->m_config; int writeqlen = cnf->getThrConf(RclConfig::ThrDbWrite).first; int writethreads = cnf->getThrConf(RclConfig::ThrDbWrite).second; if (writethreads > 1) { LOGINFO("RclDb: write threads count was forced down to 1\n"); writethreads = 1; } if (writeqlen >= 0 && writethreads > 0) { if (!m_wqueue.start(writethreads, DbUpdWorker, this)) { LOGERR("Db::Db: Worker start failed\n"); return; } m_havewriteq = true; } LOGDEB("RclDb:: threads: haveWriteQ " << m_havewriteq << ", wqlen " << writeqlen << " wqts " << writethreads << "\n"); } #endif // IDX_THREADS void Db::Native::openWrite(const string& dir, Db::OpenMode mode) { int action = (mode == Db::DbUpd) ? Xapian::DB_CREATE_OR_OPEN : Xapian::DB_CREATE_OR_OVERWRITE; #ifdef _WIN32 // On Windows, Xapian is quite bad at erasing partial db which can // occur because of open file deletion errors. if (mode == DbTrunc) { if (path_exists(path_cat(dir, "iamchert"))) { wipedir(dir); path_unlink(dir); } } #endif if (path_exists(dir)) { // Existing index. xwdb = Xapian::WritableDatabase(dir, action); if (action == Xapian::DB_CREATE_OR_OVERWRITE || xwdb.get_doccount() == 0) { // New or empty index. Set the "store text" option // according to configuration. The metadata record will be // written further down. m_storetext = o_index_storedoctext; LOGDEB("Db:: index " << (m_storetext?"stores":"does not store") << " document text\n"); } else { // Existing non empty. Get the option from the index. storesDocText(xwdb); } } else { // New index. If possible, and depending on config, use a stub // to force using Chert. No sense in doing this if we are // storing the text anyway. #if XAPIAN_AT_LEAST(1,3,0) && XAPIAN_HAS_CHERT_BACKEND // Xapian with Glass and Chert support. If storedoctext is // specified in the configuration, use the default backend // (Glass), else force Chert. There might be reasons why // someone would want to use Chert and store text anyway, but // it's an exotic case, and things are complicated enough // already. if (o_index_storedoctext) { xwdb = Xapian::WritableDatabase(dir, action); m_storetext = true; } else { // Force Chert format, don't store the text. string stub = path_cat(m_rcldb->m_config->getConfDir(), "xapian.stub"); std::fstream fp; if (!path_streamopen(stub, std::ios::out|std::ios::trunc, fp)) { throw(string("Can't create ") + stub); } fp << "chert " << dir << "\n"; fp.close(); xwdb = Xapian::WritableDatabase(stub, action); m_storetext = false; } LOGINF("Rcl::Db::openWrite: new index will " << (m_storetext?"":"not ") << "store document text\n"); #else // Old Xapian (chert only) or much newer (no chert). Use the // default index backend and let the user decide of the // abstract generation method. The configured default is to // store the text. xwdb = Xapian::WritableDatabase(dir, action); m_storetext = o_index_storedoctext; #endif } // If the index is empty, write the data format version, // and the storetext option value inside the index descriptor (new // with recoll 1.24, maybe we'll have other stuff to store in // there in the future). if (xwdb.get_doccount() == 0) { string desc = string("storetext=") + (m_storetext ? "1" : "0") + "\n"; xwdb.set_metadata(cstr_RCL_IDX_DESCRIPTOR_KEY, desc); xwdb.set_metadata(cstr_RCL_IDX_VERSION_KEY, cstr_RCL_IDX_VERSION); } m_iswritable = true; #ifdef IDX_THREADS maybeStartThreads(); #endif } void Db::Native::storesDocText(Xapian::Database& db) { string desc = db.get_metadata(cstr_RCL_IDX_DESCRIPTOR_KEY); ConfSimple cf(desc, 1); string val; m_storetext = false; if (cf.get("storetext", val) && stringToBool(val)) { m_storetext = true; } LOGDEB("Db:: index " << (m_storetext?"stores":"does not store") << " document text\n"); } void Db::Native::openRead(const string& dir) { m_iswritable = false; xrdb = Xapian::Database(dir); storesDocText(xrdb); } /* See comment in class declaration: return all subdocuments of a * document given by its unique id. */ bool Db::Native::subDocs(const string &udi, int idxi, vector& docids) { LOGDEB2("subDocs: [" << udi << "]\n"); string pterm = make_parentterm(udi); vector candidates; XAPTRY(docids.clear(); candidates.insert(candidates.begin(), xrdb.postlist_begin(pterm), xrdb.postlist_end(pterm)), xrdb, m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { LOGERR("Rcl::Db::subDocs: " << m_rcldb->m_reason << "\n"); return false; } else { for (unsigned int i = 0; i < candidates.size(); i++) { if (whatDbIdx(candidates[i]) == (size_t)idxi) { docids.push_back(candidates[i]); } } LOGDEB0("Db::Native::subDocs: returning " << docids.size() << " ids\n"); return true; } } bool Db::Native::xdocToUdi(Xapian::Document& xdoc, string &udi) { Xapian::TermIterator xit; XAPTRY(xit = xdoc.termlist_begin(); xit.skip_to(wrap_prefix(udi_prefix)), xrdb, m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { LOGERR("xdocToUdi: xapian error: " << m_rcldb->m_reason << "\n"); return false; } if (xit != xdoc.termlist_end()) { udi = *xit; if (!udi.empty()) { udi = udi.substr(wrap_prefix(udi_prefix).size()); return true; } } return false; } // Clear term from document if its frequency is 0. This should // probably be done by Xapian when the freq goes to 0 when removing a // posting, but we have to do it ourselves bool Db::Native::clearDocTermIfWdf0(Xapian::Document& xdoc, const string& term) { LOGDEB1("Db::clearDocTermIfWdf0: [" << term << "]\n"); // Find the term Xapian::TermIterator xit; XAPTRY(xit = xdoc.termlist_begin(); xit.skip_to(term);, xrdb, m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { LOGERR("Db::clearDocTerm...: [" << term << "] skip failed: " << m_rcldb->m_reason << "\n"); return false; } if (xit == xdoc.termlist_end() || term.compare(*xit)) { LOGDEB0("Db::clearDocTermIFWdf0: term [" << term << "] not found. xit: [" << (xit == xdoc.termlist_end() ? "EOL": *xit) << "]\n"); return false; } // Clear the term if its frequency is 0 if (xit.get_wdf() == 0) { LOGDEB1("Db::clearDocTermIfWdf0: clearing [" << term << "]\n"); XAPTRY(xdoc.remove_term(term), xwdb, m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { LOGDEB0("Db::clearDocTermIfWdf0: failed [" << term << "]: " << m_rcldb->m_reason << "\n"); } } return true; } // Holder for term + pos struct DocPosting { DocPosting(string t, Xapian::termpos ps) : term(t), pos(ps) {} string term; Xapian::termpos pos; }; // Clear all terms for given field for given document. // The terms to be cleared are all those with the appropriate // prefix. We also remove the postings for the unprefixed terms (that // is, we undo what we did when indexing). bool Db::Native::clearField(Xapian::Document& xdoc, const string& pfx, Xapian::termcount wdfdec) { LOGDEB1("Db::clearField: clearing prefix [" << pfx << "] for docid " << xdoc.get_docid() << "\n"); vector eraselist; string wrapd = wrap_prefix(pfx); m_rcldb->m_reason.clear(); for (int tries = 0; tries < 2; tries++) { try { Xapian::TermIterator xit; xit = xdoc.termlist_begin(); xit.skip_to(wrapd); while (xit != xdoc.termlist_end() && !(*xit).compare(0, wrapd.size(), wrapd)) { LOGDEB1("Db::clearfield: erasing for [" << *xit << "]\n"); Xapian::PositionIterator posit; for (posit = xit.positionlist_begin(); posit != xit.positionlist_end(); posit++) { eraselist.push_back(DocPosting(*xit, *posit)); eraselist.push_back(DocPosting(strip_prefix(*xit), *posit)); } xit++; } } catch (const Xapian::DatabaseModifiedError &e) { m_rcldb->m_reason = e.get_msg(); xrdb.reopen(); continue; } XCATCHERROR(m_rcldb->m_reason); break; } if (!m_rcldb->m_reason.empty()) { LOGERR("Db::clearField: failed building erase list: " << m_rcldb->m_reason << "\n"); return false; } // Now remove the found positions, and the terms if the wdf is 0 for (vector::const_iterator it = eraselist.begin(); it != eraselist.end(); it++) { LOGDEB1("Db::clearField: remove posting: [" << it->term << "] pos [" << it->pos << "]\n"); XAPTRY(xdoc.remove_posting(it->term, it->pos, wdfdec);, xwdb,m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { // Not that this normally fails for non-prefixed XXST and // ND, don't make a fuss LOGDEB1("Db::clearFiedl: remove_posting failed for [" << it->term << "]," << it->pos << ": " << m_rcldb->m_reason << "\n"); } clearDocTermIfWdf0(xdoc, it->term); } return true; } // Check if doc given by udi is indexed by term bool Db::Native::hasTerm(const string& udi, int idxi, const string& term) { LOGDEB2("Native::hasTerm: udi [" << udi << "] term [" << term << "]\n"); Xapian::Document xdoc; if (getDoc(udi, idxi, xdoc)) { Xapian::TermIterator xit; XAPTRY(xit = xdoc.termlist_begin(); xit.skip_to(term);, xrdb, m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { LOGERR("Rcl::Native::hasTerm: " << m_rcldb->m_reason << "\n"); return false; } if (xit != xdoc.termlist_end() && !term.compare(*xit)) { return true; } } return false; } // Retrieve Xapian document, given udi. There may be several identical udis // if we are using multiple indexes. Xapian::docid Db::Native::getDoc(const string& udi, int idxi, Xapian::Document& xdoc) { string uniterm = make_uniterm(udi); for (int tries = 0; tries < 2; tries++) { try { Xapian::PostingIterator docid; for (docid = xrdb.postlist_begin(uniterm); docid != xrdb.postlist_end(uniterm); docid++) { xdoc = xrdb.get_document(*docid); if (whatDbIdx(*docid) == (size_t)idxi) return *docid; } // Udi not in Db. return 0; } catch (const Xapian::DatabaseModifiedError &e) { m_rcldb->m_reason = e.get_msg(); xrdb.reopen(); continue; } XCATCHERROR(m_rcldb->m_reason); break; } LOGERR("Db::Native::getDoc: Xapian error: " << m_rcldb->m_reason << "\n"); return 0; } // Turn data record from db into document fields bool Db::Native::dbDataToRclDoc(Xapian::docid docid, std::string &data, Doc &doc, bool fetchtext) { LOGDEB2("Db::dbDataToRclDoc: data:\n" << data << "\n"); ConfSimple parms(data, 1, false, false); if (!parms.ok()) return false; doc.xdocid = docid; doc.haspages = hasPages(docid); // Compute what index this comes from, and check for path translations string dbdir = m_rcldb->m_basedir; doc.idxi = 0; if (!m_rcldb->m_extraDbs.empty()) { int idxi = int(whatDbIdx(docid)); // idxi is in [0, extraDbs.size()]. 0 is for the main index, // idxi-1 indexes into the additional dbs array. if (idxi) { dbdir = m_rcldb->m_extraDbs[idxi - 1]; doc.idxi = idxi; } } parms.get(Doc::keyurl, doc.idxurl); doc.url = doc.idxurl; m_rcldb->m_config->urlrewrite(dbdir, doc.url); if (!doc.url.compare(doc.idxurl)) doc.idxurl.clear(); // Special cases: parms.get(Doc::keytp, doc.mimetype); parms.get(Doc::keyfmt, doc.fmtime); parms.get(Doc::keydmt, doc.dmtime); parms.get(Doc::keyoc, doc.origcharset); parms.get(cstr_caption, doc.meta[Doc::keytt]); parms.get(Doc::keyabs, doc.meta[Doc::keyabs]); // Possibly remove synthetic abstract indicator (if it's there, we // used to index the beginning of the text as abstract). doc.syntabs = false; if (doc.meta[Doc::keyabs].find(cstr_syntAbs) == 0) { doc.meta[Doc::keyabs] = doc.meta[Doc::keyabs].substr(cstr_syntAbs.length()); doc.syntabs = true; } parms.get(Doc::keyipt, doc.ipath); parms.get(Doc::keypcs, doc.pcbytes); parms.get(Doc::keyfs, doc.fbytes); parms.get(Doc::keyds, doc.dbytes); parms.get(Doc::keysig, doc.sig); // Normal key/value pairs: vector keys = parms.getNames(string()); for (vector::const_iterator it = keys.begin(); it != keys.end(); it++) { if (doc.meta.find(*it) == doc.meta.end()) parms.get(*it, doc.meta[*it]); } doc.meta[Doc::keyurl] = doc.url; doc.meta[Doc::keymt] = doc.dmtime.empty() ? doc.fmtime : doc.dmtime; if (fetchtext) { getRawText(docid, doc.text); } return true; } bool Db::Native::hasPages(Xapian::docid docid) { string ermsg; Xapian::PositionIterator pos; XAPTRY(pos = xrdb.positionlist_begin(docid, page_break_term); if (pos != xrdb.positionlist_end(docid, page_break_term)) { return true; }, xrdb, ermsg); if (!ermsg.empty()) { LOGERR("Db::Native::hasPages: xapian error: " << ermsg << "\n"); } return false; } // Return the positions list for the page break term bool Db::Native::getPagePositions(Xapian::docid docid, vector& vpos) { vpos.clear(); // Need to retrieve the document record to check for multiple page breaks // that we store there for lack of better place map mbreaksmap; try { Xapian::Document xdoc = xrdb.get_document(docid); string data = xdoc.get_data(); Doc doc; string mbreaks; if (dbDataToRclDoc(docid, data, doc) && doc.getmeta(cstr_mbreaks, &mbreaks)) { vector values; stringToTokens(mbreaks, values, ","); for (unsigned int i = 0; i < values.size() - 1; i += 2) { int pos = atoi(values[i].c_str()) + baseTextPosition; int incr = atoi(values[i+1].c_str()); mbreaksmap[pos] = incr; } } } catch (...) { } string qterm = page_break_term; Xapian::PositionIterator pos; try { for (pos = xrdb.positionlist_begin(docid, qterm); pos != xrdb.positionlist_end(docid, qterm); pos++) { int ipos = *pos; if (ipos < int(baseTextPosition)) { LOGDEB("getPagePositions: got page position " << ipos << " not in body\n"); // Not in text body. Strange... continue; } map::iterator it = mbreaksmap.find(ipos); if (it != mbreaksmap.end()) { LOGDEB1("getPagePositions: found multibreak at " << ipos << " incr " << it->second << "\n"); for (int i = 0 ; i < it->second; i++) vpos.push_back(ipos); } vpos.push_back(ipos); } } catch (...) { // Term does not occur. No problem. } return true; } int Db::Native::getPageNumberForPosition(const vector& pbreaks, int pos) { if (pos < int(baseTextPosition)) // Not in text body return -1; vector::const_iterator it = upper_bound(pbreaks.begin(), pbreaks.end(), pos); return int(it - pbreaks.begin() + 1); } bool Db::Native::getRawText(Xapian::docid docid_combined, string& rawtext) { if (!m_storetext) { LOGDEB("Db::Native::getRawText: document text not stored in index\n"); return false; } // Xapian get_metadata only works on a single index (else of // course, unicity of keys can't be ensured). When using multiple // indexes, we need to open the right one. size_t dbidx = whatDbIdx(docid_combined); Xapian::docid docid = whatDbDocid(docid_combined); string reason; if (dbidx != 0) { Xapian::Database db(m_rcldb->m_extraDbs[dbidx-1]); XAPTRY(rawtext = db.get_metadata(rawtextMetaKey(docid)), db, reason); } else { XAPTRY(rawtext = xrdb.get_metadata(rawtextMetaKey(docid)), xrdb, reason); } if (!reason.empty()) { LOGERR("Rcl::Db::getRawText: could not get value: " << reason << endl); return false; } if (rawtext.empty()) { return true; } ZLibUtBuf cbuf; inflateToBuf(rawtext.c_str(), rawtext.size(), cbuf); rawtext.assign(cbuf.getBuf(), cbuf.getCnt()); return true; } // Note: we're passed a Xapian::Document* because Xapian // reference-counting is not mt-safe. We take ownership and need // to delete it before returning. bool Db::Native::addOrUpdateWrite( const string& udi, const string& uniterm, Xapian::Document *newdocument_ptr, size_t textlen, const string& rawztext) { #ifdef IDX_THREADS Chrono chron; std::unique_lock lock(m_mutex); #endif std::unique_ptr doc_cleaner(newdocument_ptr); // Check file system full every mbyte of indexed text. It's a bit wasteful // to do this after having prepared the document, but it needs to be in // the single-threaded section. if (m_rcldb->m_maxFsOccupPc > 0 && (m_rcldb->m_occFirstCheck || (m_rcldb->m_curtxtsz - m_rcldb->m_occtxtsz) / MB >= 1)) { LOGDEB("Db::add: checking file system usage\n"); int pc; m_rcldb->m_occFirstCheck = 0; if (fsocc(m_rcldb->m_basedir, &pc) && pc >= m_rcldb->m_maxFsOccupPc) { LOGERR("Db::add: stop indexing: file system " << pc << " %" << " full > max " << m_rcldb->m_maxFsOccupPc << " %" << "\n"); return false; } m_rcldb->m_occtxtsz = m_rcldb->m_curtxtsz; } const char *fnc = udi.c_str(); string ermsg; // Add db entry or update existing entry: Xapian::docid did = 0; try { did = xwdb.replace_document(uniterm, *newdocument_ptr); if (did < m_rcldb->updated.size()) { // This is necessary because only the file-level docs are tested // by needUpdate(), so the subdocs existence flags are only set // here. m_rcldb->updated[did] = true; LOGINFO("Db::add: docid " << did << " updated [" << fnc << "]\n"); } else { LOGINFO("Db::add: docid " << did << " added [" << fnc << "]\n"); } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db::add: replace_document failed: " << ermsg << "\n"); ermsg.erase(); // FIXME: is this ever actually needed? try { xwdb.add_document(*newdocument_ptr); LOGDEB("Db::add: " << fnc << " added (failed re-seek for duplicate)\n"); } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db::add: add_document failed: " << ermsg << "\n"); return false; } } XAPTRY(xwdb.set_metadata(rawtextMetaKey(did), rawztext), xwdb, m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { LOGERR("Db::addOrUpdate: set_metadata error: " << m_rcldb->m_reason << "\n"); // This only affects snippets, so let's say not fatal } // Test if we're over the flush threshold (limit memory usage): bool ret = m_rcldb->maybeflush(textlen); #ifdef IDX_THREADS m_totalworkns += chron.nanos(); #endif return ret; } bool Db::Native::purgeFileWrite(bool orphansOnly, const string& udi, const string& uniterm) { #if defined(IDX_THREADS) // We need a mutex even if we have a write queue (so we can only // be called by a single thread) to protect about multiple acces // to xrdb from subDocs() which is also called from needupdate() // (called from outside the write thread ! std::unique_lock lock(m_mutex); #endif // IDX_THREADS string ermsg; try { Xapian::PostingIterator docid = xwdb.postlist_begin(uniterm); if (docid == xwdb.postlist_end(uniterm)) { return true; } if (m_rcldb->m_flushMb > 0) { Xapian::termcount trms = xwdb.get_doclength(*docid); m_rcldb->maybeflush(trms * 5); } string sig; if (orphansOnly) { Xapian::Document doc = xwdb.get_document(*docid); sig = doc.get_value(VALUE_SIG); if (sig.empty()) { LOGINFO("purgeFileWrite: got empty sig\n"); return false; } } else { LOGDEB("purgeFile: delete docid " << *docid << "\n"); deleteDocument(*docid); } vector docids; subDocs(udi, 0, docids); LOGDEB("purgeFile: subdocs cnt " << docids.size() << "\n"); for (vector::iterator it = docids.begin(); it != docids.end(); it++) { if (m_rcldb->m_flushMb > 0) { Xapian::termcount trms = xwdb.get_doclength(*it); m_rcldb->maybeflush(trms * 5); } string subdocsig; if (orphansOnly) { Xapian::Document doc = xwdb.get_document(*it); subdocsig = doc.get_value(VALUE_SIG); if (subdocsig.empty()) { LOGINFO("purgeFileWrite: got empty sig for subdoc??\n"); continue; } } if (!orphansOnly || sig != subdocsig) { LOGDEB("Db::purgeFile: delete subdoc " << *it << "\n"); deleteDocument(*it); } } return true; } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db::purgeFileWrite: " << ermsg << "\n"); } return false; } /* Rcl::Db methods ///////////////////////////////// */ bool Db::o_nospell_chars[256]; Db::Db(const RclConfig *cfp) { m_config = new RclConfig(*cfp); m_config->getConfParam("maxfsoccuppc", &m_maxFsOccupPc); m_config->getConfParam("idxflushmb", &m_flushMb); m_config->getConfParam("idxmetastoredlen", &m_idxMetaStoredLen); m_config->getConfParam("idxtexttruncatelen", &m_idxTextTruncateLen); m_config->getConfParam("autoSpellRarityThreshold", &m_autoSpellRarityThreshold); m_config->getConfParam("autoSpellSelectionThreshold", &m_autoSpellSelectionThreshold); if (start_of_field_term.empty()) { if (o_index_stripchars) { start_of_field_term = "XXST"; end_of_field_term = "XXND"; } else { start_of_field_term = "XXST/"; end_of_field_term = "XXND/"; } memset(o_nospell_chars, 0, sizeof(o_nospell_chars)); for (unsigned char c : " !\"#$%&()*+,-./0123456789:;<=>?@[\\]^_`{|}~") { o_nospell_chars[(unsigned int)c] = 1; } } m_ndb = new Native(this); m_syngroups = std::make_unique(); m_stops = std::make_unique(); } Db::~Db() { LOGDEB2("Db::~Db\n"); if (nullptr == m_ndb) return; LOGDEB("Db::~Db: isopen " << m_ndb->m_isopen << " m_iswritable " << m_ndb->m_iswritable << "\n"); this->close(); delete m_ndb; #ifdef RCL_USE_ASPELL delete m_aspell; #endif delete m_config; } vector Db::getStemmerNames() { vector res; stringToStrings(Xapian::Stem::get_available_languages(), res); return res; } bool Db::open(OpenMode mode, OpenError *error) { if (error) *error = DbOpenMainDb; if (nullptr == m_ndb || m_config == nullptr) { m_reason = "Null configuration or Xapian Db"; return false; } LOGDEB("Db::open: m_isopen " << m_ndb->m_isopen << " m_iswritable " << m_ndb->m_iswritable << " mode " << mode << "\n"); m_inPlaceReset = false; if (m_ndb->m_isopen) { // We used to return an error here but I see no reason to if (!close()) return false; } if (!m_config->getStopfile().empty()) m_stops->setFile(m_config->getStopfile()); if (isWriteMode(mode)) { // Check for an index-time synonyms file. We use this to // generate multiword terms for multiword synonyms string synfile = m_config->getIdxSynGroupsFile(); if (path_exists(synfile)) { setSynGroupsFile(synfile); } } string dir = m_config->getDbDir(); string ermsg; try { if (isWriteMode(mode)) { m_ndb->openWrite(dir, mode); updated = vector(m_ndb->xwdb.get_lastdocid() + 1, false); // We used to open a readonly object in addition to the // r/w one because some operations were faster when // performed through a Database: no forced flushes on // allterms_begin(), used in subDocs(). This issue has // been gone for a long time (now: Xapian 1.2) and the // separate objects seem to trigger other Xapian issues, // so the query db is now a clone of the update one. m_ndb->xrdb = m_ndb->xwdb; LOGDEB("Db::open: lastdocid: " <xwdb.get_lastdocid()<<"\n"); } else { m_ndb->openRead(dir); for (auto& db : m_extraDbs) { if (error) *error = DbOpenExtraDb; LOGDEB("Db::Open: adding query db [" << &db << "]\n"); // An error here used to be non-fatal (1.13 and older) // but I can't see why m_ndb->xrdb.add_database(Xapian::Database(db)); } } if (error) *error = DbOpenMainDb; // Check index format version. Must not try to check a just created or // truncated db if (mode != DbTrunc && m_ndb->xrdb.get_doccount() > 0) { string version = m_ndb->xrdb.get_metadata(cstr_RCL_IDX_VERSION_KEY); if (version.compare(cstr_RCL_IDX_VERSION)) { m_ndb->m_noversionwrite = true; LOGERR("Rcl::Db::open: file index [" << version << "], software [" << cstr_RCL_IDX_VERSION << "]\n"); throw Xapian::DatabaseError("Recoll index version mismatch", "", ""); } } m_mode = mode; m_ndb->m_isopen = true; m_basedir = dir; if (error) *error = DbOpenNoError; return true; } XCATCHERROR(ermsg); m_reason = ermsg; LOGERR("Db::open: exception while opening [" <m_isopen) { LOGERR("Db::storesDocText: called on non-opened db\n"); return false; } return m_ndb->m_storetext; } bool Db::getDocRawText(Doc& doc) { if (!m_ndb || !m_ndb->m_isopen) { LOGERR("Db::getDocRawText: called on non-opened db\n"); return false; } return m_ndb->getRawText(doc.xdocid, doc.text); } // Note: xapian has no close call, we delete and recreate the db bool Db::close() { if (nullptr == m_ndb) return false; LOGDEB("Db::close: isopen " << m_ndb->m_isopen << " iswritable " << m_ndb->m_iswritable << "\n"); if (m_ndb->m_isopen == false) { return true; } string ermsg; try { bool w = m_ndb->m_iswritable; if (w) { #ifdef IDX_THREADS m_ndb->m_wqueue.closeShop(); waitUpdIdle(); #endif if (!m_ndb->m_noversionwrite) m_ndb->xwdb.set_metadata(cstr_RCL_IDX_VERSION_KEY, cstr_RCL_IDX_VERSION); LOGDEB("Rcl::Db:close: xapian will close. May take some time\n"); } deleteZ(m_ndb); if (w) LOGDEB("Rcl::Db:close() xapian close done.\n"); m_ndb = new Native(this); return true; } XCATCHERROR(ermsg); LOGERR("Db:close: exception while deleting/recreating db object: " << ermsg << "\n"); return false; } int Db::docCnt() { int res = -1; if (!m_ndb || !m_ndb->m_isopen) return -1; XAPTRY(res = m_ndb->xrdb.get_doccount(), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::docCnt: got error: " << m_reason << "\n"); return -1; } return res; } int Db::termDocCnt(const string& _term) { int res = -1; if (!m_ndb || !m_ndb->m_isopen) return -1; string term = _term; if (o_index_stripchars) if (!unacmaybefold(_term, term, "UTF-8", UNACOP_UNACFOLD)) { LOGINFO("Db::termDocCnt: unac failed for [" << _term << "]\n"); return 0; } if (m_stops->isStop(term)) { LOGDEB1("Db::termDocCnt [" << term << "] in stop list\n"); return 0; } XAPTRY(res = m_ndb->xrdb.get_termfreq(term), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::termDocCnt: got error: " << m_reason << "\n"); return -1; } return res; } // Reopen the db with a changed list of additional dbs bool Db::adjustdbs() { if (m_mode != DbRO) { LOGERR("Db::adjustdbs: mode not RO\n"); return false; } if (m_ndb && m_ndb->m_isopen) { if (!close()) return false; if (!open(m_mode)) { return false; } } return true; } // Set the extra indexes to the input list. bool Db::setExtraQueryDbs(const std::vector& dbs) { LOGDEB0("Db::setExtraQueryDbs: ndb " << m_ndb << " iswritable " << ((m_ndb)?m_ndb->m_iswritable:0) << " dbs [" << stringsToString(dbs) << "]\n"); if (!m_ndb) { return false; } if (m_ndb->m_iswritable) { return false; } m_extraDbs.clear(); for (const auto& dir : dbs) { m_extraDbs.push_back(path_canon(dir)); } return adjustdbs(); } bool Db::addQueryDb(const string &_dir) { string dir = _dir; LOGDEB0("Db::addQueryDb: ndb " << m_ndb << " iswritable " << ((m_ndb)?m_ndb->m_iswritable:0) << " db [" << dir << "]\n"); if (!m_ndb) return false; if (m_ndb->m_iswritable) return false; dir = path_canon(dir); if (find(m_extraDbs.begin(), m_extraDbs.end(), dir) == m_extraDbs.end()) { m_extraDbs.push_back(dir); } return adjustdbs(); } bool Db::rmQueryDb(const string &dir) { if (!m_ndb) return false; if (m_ndb->m_iswritable) return false; if (dir.empty()) { m_extraDbs.clear(); } else { vector::iterator it = find(m_extraDbs.begin(), m_extraDbs.end(), dir); if (it != m_extraDbs.end()) { m_extraDbs.erase(it); } } return adjustdbs(); } // Determining what index a doc result comes from is based on the // modulo of the docid against the db count. Ref: // http://trac.xapian.org/wiki/FAQ/MultiDatabaseDocumentID bool Db::fromMainIndex(const Doc& doc) { return m_ndb->whatDbIdx(doc.xdocid) == 0; } std::string Db::whatIndexForResultDoc(const Doc& doc) { size_t idx = m_ndb->whatDbIdx(doc.xdocid); if (idx == (size_t)-1) { LOGERR("whatIndexForResultDoc: whatDbIdx returned -1 for " << doc.xdocid << endl); return string(); } // idx is [0..m_extraDbs.size()] 0 is for the main index, else // idx-1 indexes into m_extraDbs if (idx == 0) { return m_basedir; } else { return m_extraDbs[idx-1]; } } size_t Db::Native::whatDbIdx(Xapian::docid id) { LOGDEB1("Db::whatDbIdx: xdocid " << id << ", " << m_rcldb->m_extraDbs.size() << " extraDbs\n"); if (id == 0) return (size_t)-1; if (m_rcldb->m_extraDbs.size() == 0) return 0; return (id - 1) % (m_rcldb->m_extraDbs.size() + 1); } // Return the docid inside the non-combined index Xapian::docid Db::Native::whatDbDocid(Xapian::docid docid_combined) { if (m_rcldb->m_extraDbs.size() == 0) return docid_combined; return (docid_combined - 1) / (m_rcldb->m_extraDbs.size() + 1) + 1; } bool Db::testDbDir(const string &dir, bool *stripped_p) { string aerr; bool mstripped = true; LOGDEB("Db::testDbDir: [" << dir << "]\n"); try { Xapian::Database db(dir); // If the prefix for mimetype is wrapped, it's an unstripped // index. T has been in use in recoll since the beginning and // all documents have a T field (possibly empty). Xapian::TermIterator term = db.allterms_begin(":T:"); if (term == db.allterms_end()) { mstripped = true; } else { mstripped = false; } LOGDEB("testDbDir: " << dir << " is a " << (mstripped ? "stripped" : "raw") << " index\n"); } XCATCHERROR(aerr); if (!aerr.empty()) { LOGERR("Db::Open: error while trying to open database from [" << dir << "]: " << aerr << "\n"); return false; } if (stripped_p) *stripped_p = mstripped; return true; } bool Db::isopen() { if (nullptr == m_ndb) return false; return m_ndb->m_isopen; } // Try to translate field specification into field prefix. bool Db::fieldToTraits(const string& fld, const FieldTraits **ftpp, bool isquery) { if (m_config && m_config->getFieldTraits(fld, ftpp, isquery)) return true; *ftpp = nullptr; return false; } // The splitter breaks text into words and adds postings to the Xapian // document. We use a single object to split all of the document // fields and position jumps to separate fields class TextSplitDb : public TextSplitP { public: Xapian::Document &doc; // Xapian document // Base for document section. Gets large increment when we change // sections, to avoid cross-section proximity matches. Xapian::termpos basepos; // Current relative position. This is the remembered value from // the splitter callback. The term position is reset for each call // to text_to_words(), so that the last value of curpos is the // section size (last relative term position), and this is what // gets added to basepos in addition to the inter-section increment // to compute the first position of the next section. Xapian::termpos curpos; Xapian::WritableDatabase& wdb; TextSplitDb(Xapian::WritableDatabase& _wdb, Xapian::Document &d, TermProc *prc) : TextSplitP(prc), doc(d), basepos(1), curpos(0), wdb(_wdb) {} // Reimplement text_to_words to insert the begin and end anchor terms. virtual bool text_to_words(const string &in) { string ermsg; try { // Index the possibly prefixed start term. doc.add_posting(ft.pfx + start_of_field_term, basepos, ft.wdfinc); ++basepos; } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db: xapian add_posting error " << ermsg << "\n"); goto out; } if (!TextSplitP::text_to_words(in)) { LOGDEB("TextSplitDb: TextSplit::text_to_words failed\n"); goto out; } try { // Index the possibly prefixed end term. doc.add_posting(ft.pfx + end_of_field_term, basepos + curpos + 1, ft.wdfinc); ++basepos; } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db: xapian add_posting error " << ermsg << "\n"); goto out; } out: basepos += curpos + 100; return true; } void setTraits(const FieldTraits& ftp) { ft = ftp; if (!ft.pfx.empty()) ft.pfx = wrap_prefix(ft.pfx); } friend class TermProcIdx; private: FieldTraits ft; }; class TermProcIdx : public TermProc { public: TermProcIdx() : TermProc(nullptr), m_ts(nullptr), m_lastpagepos(0), m_pageincr(0) {} void setTSD(TextSplitDb *ts) {m_ts = ts;} bool takeword(const std::string &term, int pos, int, int) { // Compute absolute position (pos is relative to current segment), // and remember relative. m_ts->curpos = pos; pos += m_ts->basepos; // Don't try to add empty term Xapian doesnt like it... Safety check // this should not happen. if (term.empty()) return true; string ermsg; try { // Index without prefix, using the field-specific weighting LOGDEB1("Emitting term at " << pos << " : [" << term << "]\n"); if (!m_ts->ft.pfxonly) m_ts->doc.add_posting(term, pos, m_ts->ft.wdfinc); #ifdef TESTING_XAPIAN_SPELL if (Db::isSpellingCandidate(term, false)) { m_ts->wdb.add_spelling(term); } #endif // Index the prefixed term. if (!m_ts->ft.pfx.empty()) { m_ts->doc.add_posting(m_ts->ft.pfx + term, pos, m_ts->ft.wdfinc); } return true; } XCATCHERROR(ermsg); LOGERR("Db: xapian add_posting error " << ermsg << "\n"); return false; } void newpage(int pos) { pos += m_ts->basepos; if (pos < int(baseTextPosition)) { LOGDEB("newpage: not in body: " << pos << "\n"); return; } m_ts->doc.add_posting(m_ts->ft.pfx + page_break_term, pos); if (pos == m_lastpagepos) { m_pageincr++; LOGDEB2("newpage: same pos, pageincr " << m_pageincr << " lastpagepos " << m_lastpagepos << "\n"); } else { LOGDEB2("newpage: pos change, pageincr " << m_pageincr << " lastpagepos " << m_lastpagepos << "\n"); if (m_pageincr > 0) { // Remember the multiple page break at this position unsigned int relpos = m_lastpagepos - baseTextPosition; LOGDEB2("Remembering multiple page break. Relpos " << relpos << " cnt " << m_pageincr << "\n"); m_pageincrvec.push_back(pair(relpos, m_pageincr)); } m_pageincr = 0; } m_lastpagepos = pos; } virtual bool flush() { if (m_pageincr > 0) { unsigned int relpos = m_lastpagepos - baseTextPosition; LOGDEB2("Remembering multiple page break. Position " << relpos << " cnt " << m_pageincr << "\n"); m_pageincrvec.push_back(pair(relpos, m_pageincr)); m_pageincr = 0; } return TermProc::flush(); } TextSplitDb *m_ts; // Auxiliary page breaks data for positions with multiple page breaks. int m_lastpagepos; // increment of page breaks at same pos. Normally 0, 1.. when several // breaks at the same pos int m_pageincr; vector > m_pageincrvec; }; bool Db::isSpellingCandidate(const std::string& term, bool with_aspell) { if (term.empty() || term.length() > 50 || has_prefix(term)) return false; Utf8Iter u8i(term); if (with_aspell) { // If spelling with aspell, CJK scripts are not candidates if (TextSplit::isCJK(*u8i)) return false; } else { #ifdef TESTING_XAPIAN_SPELL // The Xapian speller (purely proximity-based) can be used // for Katakana (when split as words which is not always // completely feasible because of separator-less // compounds). Currently we don't try to use the Xapian // speller with other scripts with which it would be usable // in the absence of aspell (it would indeed be better // than nothing with e.g. european languages). This would // require a few more config variables, maybe one day. if (!TextSplit::isKATAKANA(*u8i)) { return false; } #else return false; #endif } // Most punctuation chars inhibate stemming. We accept one dash. See o_nospell_chars init in // the rcldb constructor. int ccnt = 0; for (unsigned char c : term) { if (o_nospell_chars[(unsigned int)c] && (c != '-' || ++ccnt > 1)) return false; } return true; } // At the moment, we only use aspell. The xapian speller code part is only for testing and normally // not compiled in. bool Db::getSpellingSuggestions(const string& word, vector& suggs) { LOGDEB("Db::getSpellingSuggestions:[" << word << "]\n"); suggs.clear(); if (nullptr == m_ndb) { return false; } string term = word; if (isSpellingCandidate(term, true)) { // Term is candidate for aspell processing #ifdef RCL_USE_ASPELL bool noaspell = false; m_config->getConfParam("noaspell", &noaspell); if (noaspell) { return false; } if (nullptr == m_aspell) { m_aspell = new Aspell(m_config); if (m_aspell) { string reason; m_aspell->init(reason); if (!m_aspell->ok()) { LOGDEB("Aspell speller init failed: " << reason << endl); delete m_aspell; m_aspell = nullptr; } } } if (nullptr == m_aspell) { LOGERR("Db::getSpellingSuggestions: aspell not initialized\n"); return false; } string reason; if (!m_aspell->suggest(*this, term, suggs, reason)) { LOGERR("Db::getSpellingSuggestions: aspell failed: " << reason << "\n"); return false; } #endif } else { #ifdef TESTING_XAPIAN_SPELL // Was not aspell candidate (e.g.: katakana). Maybe use Xapian speller? if (isSpellingCandidate(term, false)) { if (!o_index_stripchars) { if (!unacmaybefold(word, term, "UTF-8", UNACOP_UNACFOLD)) { LOGINFO("Db::getSpelling: unac failed for [" << word << "]\n"); return false; } } string sugg = m_ndb->xrdb.get_spelling_suggestion(term); if (!sugg.empty()) { suggs.push_back(sugg); } } #endif } return true; } // Let our user set the parameters for abstract processing void Db::setAbstractParams(int idxtrunc, int syntlen, int syntctxlen) { LOGDEB1("Db::setAbstractParams: trunc " << idxtrunc << " syntlen " << syntlen << " ctxlen " << syntctxlen << "\n"); if (idxtrunc >= 0) m_idxAbsTruncLen = idxtrunc; if (syntlen > 0) m_synthAbsLen = syntlen; if (syntctxlen > 0) m_synthAbsWordCtxLen = syntctxlen; } bool Db::setSynGroupsFile(const string& fn) { return m_syngroups->setfile(fn); } static const string cstr_nc("\n\r\x0c\\"); #define RECORD_APPEND(R, NM, VAL) {R += NM + "=" + VAL + "\n";} // Add document in internal form to the database: index the terms in // the title abstract and body and add special terms for file name, // date, mime type etc. , create the document data record (more // metadata), and update database bool Db::addOrUpdate(const string &udi, const string &parent_udi, Doc &doc) { LOGDEB("Db::add: udi [" << udi << "] parent [" << parent_udi << "]\n"); if (nullptr == m_ndb) return false; // This document is potentially going to be passed to the index // update thread. The reference counters are not mt-safe, so we // need to do this through a pointer. The reference is just there // to avoid changing too much code (the previous version passed a copy). Xapian::Document *newdocument_ptr = new Xapian::Document; Xapian::Document &newdocument(*newdocument_ptr); // The term processing pipeline: TermProcIdx tpidx; TermProc *nxt = &tpidx; TermProcStop tpstop(nxt, *m_stops);nxt = &tpstop; //TermProcCommongrams tpcommon(nxt, m_stops); nxt = &tpcommon; TermProcMulti tpmulti(nxt, *m_syngroups); if (m_syngroups->getmultiwordsmaxlength() > 1) { nxt = &tpmulti; } TermProcPrep tpprep(nxt); if (o_index_stripchars) nxt = &tpprep; TextSplitDb splitter(m_ndb->xwdb, newdocument, nxt); tpidx.setTSD(&splitter); // Udi unique term: this is used for file existence/uptodate // checks, and unique id for the replace_document() call. string uniterm = make_uniterm(udi); string rawztext; // Doc compressed text if (doc.onlyxattr) { // Only updating an existing doc with new extended attributes // data. Need to read the old doc and its data record // first. This is so different from the normal processing that // it uses a fully separate code path (with some duplication // unfortunately) if (!m_ndb->docToXdocXattrOnly(&splitter, udi, doc, newdocument)) { delete newdocument_ptr; return false; } } else { if (m_idxTextTruncateLen > 0) { doc.text = truncate_to_word(doc.text, m_idxTextTruncateLen); } // If the ipath is like a path, index the last element. This is // for compound documents like zip and chm for which the filter // uses the file path as ipath. if (!doc.ipath.empty() && doc.ipath.find_first_not_of("0123456789") != string::npos) { string utf8ipathlast; // There is no way in hell we could have an idea of the // charset here, so let's hope it's ascii or utf-8. We call // transcode to strip the bad chars and pray if (transcode(path_getsimple(doc.ipath), utf8ipathlast, "UTF-8", "UTF-8")) { splitter.text_to_words(utf8ipathlast); } } // Split and index the path from the url for path-based filtering { string path = url_gpathS(doc.url); #ifdef _WIN32 // Windows file names are case-insensitive, so we // translate to UTF-8 and lowercase string upath = compute_utf8fn(m_config, path, false); unacmaybefold(upath, path, "UTF-8", UNACOP_FOLD); #endif vector vpath; stringToTokens(path, vpath, "/"); // If vpath is not /, the last elt is the file/dir name, not a // part of the path. if (vpath.size()) vpath.resize(vpath.size()-1); splitter.curpos = 0; newdocument.add_posting(wrap_prefix(pathelt_prefix), splitter.basepos + splitter.curpos++); for (auto& elt : vpath) { if (elt.length() > 230) { // Just truncate it. May still be useful because of wildcards elt = elt.substr(0, 230); } newdocument.add_posting(wrap_prefix(pathelt_prefix) + elt, splitter.basepos + splitter.curpos++); } splitter.basepos += splitter.curpos + 100; } // Index textual metadata. These are all indexed as text with // positions, as we may want to do phrase searches with them (this // makes no sense for keywords by the way). // // The order has no importance, and we set a position gap of 100 // between fields to avoid false proximity matches. for (const auto& entry: doc.meta) { if (entry.second.empty()) { continue; } const FieldTraits *ftp{nullptr}; fieldToTraits(entry.first, &ftp); if (ftp && ftp->valueslot) { LOGDEB("Adding value: for field " << entry.first << " slot " << ftp->valueslot << endl); add_field_value(newdocument, *ftp, entry.second); } // There was an old comment here about not testing for // empty prefix, and we indeed did not test. I don't think // that it makes sense any more (and was in disagreement // with the LOG message. Really now: no prefix: no // indexing. if (ftp && !ftp->pfx.empty()) { LOGDEB0("Db::add: field [" << entry.first << "] pfx [" << ftp->pfx << "] inc " << ftp->wdfinc << ": [" << entry.second << "]\n"); splitter.setTraits(*ftp); if (!splitter.text_to_words(entry.second)) { LOGDEB("Db::addOrUpdate: split failed for " << entry.first << "\n"); } } else { LOGDEB0("Db::add: no prefix for field [" << entry.first << "], no indexing\n"); } } // Reset to no prefix and default params splitter.setTraits(FieldTraits()); if (splitter.curpos < baseTextPosition) splitter.basepos = baseTextPosition; // Split and index body text LOGDEB2("Db::add: split body: [" << doc.text << "]\n"); #ifdef TEXTSPLIT_STATS splitter.resetStats(); #endif if (!splitter.text_to_words(doc.text)) { LOGDEB("Db::addOrUpdate: split failed for main text\n"); } else { if (m_ndb->m_storetext) { ZLibUtBuf buf; deflateToBuf(doc.text.c_str(), doc.text.size(), buf); rawztext.assign(buf.getBuf(), buf.getCnt()); } } #ifdef TEXTSPLIT_STATS // Reject bad data. unrecognized base64 text is characterized by // high avg word length and high variation (because there are // word-splitters like +/ inside the data). TextSplit::Stats::Values v = splitter.getStats(); // v.avglen > 15 && v.sigma > 12 if (v.count > 200 && (v.avglen > 10 && v.sigma / v.avglen > 0.8)) { LOGINFO("RclDb::addOrUpdate: rejecting doc for bad stats count " << v.count << " avglen " << v.avglen << " sigma " << v.sigma << " url [" << doc.url << "] ipath [" << doc.ipath << "] text " << doc.text << "\n"); delete newdocument_ptr; return true; } #endif ////// Special terms for other metadata. No positions for these. // Mime type newdocument.add_boolean_term(wrap_prefix(mimetype_prefix) + doc.mimetype); // Simple file name indexed unsplit for specific "file name" // searches. This is not the same as a filename: clause inside the // query language. // We also add a term for the filename extension if any. string utf8fn; if (doc.getmeta(Doc::keyfn, &utf8fn) && !utf8fn.empty()) { string fn; if (unacmaybefold(utf8fn, fn, "UTF-8", UNACOP_UNACFOLD)) { // We should truncate after extracting the extension, // but this is a pathological case anyway if (fn.size() > 230) utf8truncate(fn, 230); string::size_type pos = fn.rfind('.'); if (pos != string::npos && pos != fn.length() - 1) { newdocument.add_boolean_term(wrap_prefix(fileext_prefix) + fn.substr(pos + 1)); } newdocument.add_term(wrap_prefix(unsplitfilename_prefix) + fn,0); } } newdocument.add_boolean_term(uniterm); // Parent term. This is used to find all descendents, mostly // to delete them when the parent goes away if (!parent_udi.empty()) { newdocument.add_boolean_term(make_parentterm(parent_udi)); } // Fields used for selecting by date. Note that this only // works for years AD 0-9999 (no crash elsewhere, but things // won't work). time_t mtime = atoll(doc.dmtime.empty() ? doc.fmtime.c_str() : doc.dmtime.c_str()); struct tm tmb; localtime_r(&mtime, &tmb); char buf[50]; // It's actually 9, but use 50 to suppress warnings. snprintf(buf, 50, "%04d%02d%02d", tmb.tm_year+1900, tmb.tm_mon + 1, tmb.tm_mday); // Date (YYYYMMDD) newdocument.add_boolean_term(wrap_prefix(xapday_prefix) + string(buf)); // Month (YYYYMM) buf[6] = '\0'; newdocument.add_boolean_term(wrap_prefix(xapmonth_prefix) + string(buf)); // Year (YYYY) buf[4] = '\0'; newdocument.add_boolean_term(wrap_prefix(xapyear_prefix) + string(buf)); #ifdef EXT4_BIRTH_TIME // Fields used for selecting by birtime. Note that this only // works for years AD 0-9999 (no crash elsewhere, but things // won't work). std::string sbirtime; doc.getmeta(Doc::keybrt, &sbirtime); if (!sbirtime.empty()) { time_t birtime = atoll(sbirtime.c_str()); struct tm tmbr; localtime_r(&birtime, &tmbr); char brbuf[50]; // It's actually 9, but use 50 to suppress warnings. snprintf(brbuf, 50, "%04d%02d%02d", tmbr.tm_year+1900, tmbr.tm_mon + 1, tmbr.tm_mday); // Date (YYYYMMDD) newdocument.add_boolean_term(wrap_prefix(xapbriday_prefix) + string(brbuf)); // Month (YYYYMM) brbuf[6] = '\0'; newdocument.add_boolean_term(wrap_prefix(xapbrimonth_prefix) + string(brbuf)); // Year (YYYY) brbuf[4] = '\0'; newdocument.add_boolean_term(wrap_prefix(xapbriyear_prefix) + string(brbuf)); } #endif ////////////////////////////////////////////////////////////////// // Document data record. omindex has the following nl separated fields: // - url // - sample // - caption (title limited to 100 chars) // - mime type // // The title, author, abstract and keywords fields are special, // they always get stored in the document data // record. Configurable other fields can be, too. // // We truncate stored fields abstract, title and keywords to // reasonable lengths and suppress newlines (so that the data // record can keep a simple syntax) string record; RECORD_APPEND(record, Doc::keyurl, doc.url); RECORD_APPEND(record, Doc::keytp, doc.mimetype); // We left-zero-pad the times so that they are lexico-sortable leftzeropad(doc.fmtime, 11); RECORD_APPEND(record, Doc::keyfmt, doc.fmtime); #ifdef EXT4_BIRTH_TIME { std::string birtime; doc.getmeta(Doc::keybrt, &birtime); if (!birtime.empty()) { leftzeropad(birtime, 11); RECORD_APPEND(record, Doc::keybrt, birtime); } } #endif if (!doc.dmtime.empty()) { leftzeropad(doc.dmtime, 11); RECORD_APPEND(record, Doc::keydmt, doc.dmtime); } RECORD_APPEND(record, Doc::keyoc, doc.origcharset); if (doc.fbytes.empty()) doc.fbytes = doc.pcbytes; if (!doc.fbytes.empty()) { RECORD_APPEND(record, Doc::keyfs, doc.fbytes); leftzeropad(doc.fbytes, 12); newdocument.add_value(VALUE_SIZE, doc.fbytes); } if (doc.haschildren) { newdocument.add_boolean_term(has_children_term); } if (!doc.pcbytes.empty()) RECORD_APPEND(record, Doc::keypcs, doc.pcbytes); char sizebuf[30]; sprintf(sizebuf, "%u", (unsigned int)doc.text.length()); RECORD_APPEND(record, Doc::keyds, sizebuf); // Note that we add the signature both as a value and in the data record if (!doc.sig.empty()) { RECORD_APPEND(record, Doc::keysig, doc.sig); newdocument.add_value(VALUE_SIG, doc.sig); } if (!doc.ipath.empty()) RECORD_APPEND(record, Doc::keyipt, doc.ipath); // Fields from the Meta array. Handle title specially because it has a // different name inside the data record (history...) string& ttref = doc.meta[Doc::keytt]; ttref = neutchars(truncate_to_word(ttref, m_idxMetaStoredLen), cstr_nc); if (!ttref.empty()) { RECORD_APPEND(record, cstr_caption, ttref); ttref.clear(); } // If abstract is empty, we make up one with the beginning of the // document. This is then not indexed, but part of the doc data so // that we can return it to a query without having to decode the // original file. // Note that the map accesses by operator[] create empty entries if they // don't exist yet. if (m_idxAbsTruncLen > 0) { string& absref = doc.meta[Doc::keyabs]; trimstring(absref, " \t\r\n"); if (absref.empty()) { if (!doc.text.empty()) absref = cstr_syntAbs + neutchars(truncate_to_word(doc.text, m_idxAbsTruncLen), cstr_nc); } else { absref = neutchars(truncate_to_word(absref, m_idxAbsTruncLen), cstr_nc); } // Do the append here to avoid the different truncation done // in the regular "stored" loop if (!absref.empty()) { RECORD_APPEND(record, Doc::keyabs, absref); absref.clear(); } } // Append all regular "stored" meta fields const set& stored = m_config->getStoredFields(); for (set::const_iterator it = stored.begin(); it != stored.end(); it++) { string nm = m_config->fieldCanon(*it); if (!doc.meta[nm].empty()) { string value = neutchars(truncate_to_word(doc.meta[nm], m_idxMetaStoredLen), cstr_nc); RECORD_APPEND(record, nm, value); } } // At this point, if the document "filename" field was empty, // try to store the "container file name" value. This is done // after indexing because we don't want search matches on // this, but the filename is often useful for display // purposes. const string *fnp = nullptr; if (!doc.peekmeta(Rcl::Doc::keyfn, &fnp) || fnp->empty()) { if (doc.peekmeta(Rcl::Doc::keyctfn, &fnp) && !fnp->empty()) { string value = neutchars(truncate_to_word(*fnp, m_idxMetaStoredLen), cstr_nc); RECORD_APPEND(record, Rcl::Doc::keyfn, value); } } // If empty pages (multiple break at same pos) were recorded, save them (this is // because we have no way to record them in the Xapian list) if (!tpidx.m_pageincrvec.empty()) { ostringstream multibreaks; for (unsigned int i = 0; i < tpidx.m_pageincrvec.size(); i++) { if (i != 0) multibreaks << ","; multibreaks << tpidx.m_pageincrvec[i].first << "," << tpidx.m_pageincrvec[i].second; } RECORD_APPEND(record, string(cstr_mbreaks), multibreaks.str()); } // If the file's md5 was computed, add value and term. The // value is optionally used for query result duplicate // elimination, and the term to find the duplicates (XM is the // prefix for rclmd5 in fields) We don't do this for empty // docs. const string *md5; if (doc.peekmeta(Doc::keymd5, &md5) && !md5->empty() && md5->compare(cstr_md5empty)) { string digest; MD5HexScan(*md5, digest); newdocument.add_value(VALUE_MD5, digest); newdocument.add_boolean_term(wrap_prefix("XM") + *md5); } LOGDEB0("Rcl::Db::add: new doc record:\n" << record << "\n"); newdocument.set_data(record); } #ifdef IDX_THREADS if (m_ndb->m_havewriteq) { DbUpdTask *tp = new DbUpdTask( DbUpdTask::AddOrUpdate, udi, uniterm, newdocument_ptr, doc.text.length(), rawztext); if (!m_ndb->m_wqueue.put(tp)) { LOGERR("Db::addOrUpdate:Cant queue task\n"); delete newdocument_ptr; return false; } else { return true; } } #endif return m_ndb->addOrUpdateWrite(udi, uniterm, newdocument_ptr, doc.text.length(), rawztext); } bool Db::Native::docToXdocXattrOnly(TextSplitDb *splitter, const string &udi, Doc &doc, Xapian::Document& xdoc) { LOGDEB0("Db::docToXdocXattrOnly\n"); #ifdef IDX_THREADS std::unique_lock lock(m_mutex); #endif // Read existing document and its data record if (getDoc(udi, 0, xdoc) == 0) { LOGERR("docToXdocXattrOnly: existing doc not found\n"); return false; } string data; XAPTRY(data = xdoc.get_data(), xrdb, m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { LOGERR("Db::xattrOnly: got error: " << m_rcldb->m_reason << "\n"); return false; } // Clear the term lists for the incoming fields and index the new values map::iterator meta_it; for (const auto& ent : doc.meta) { const FieldTraits *ftp; if (!m_rcldb->fieldToTraits(ent.first, &ftp) || ftp->pfx.empty()) { LOGDEB0("Db::xattrOnly: no prefix for field [" << ent.first << "], skipped\n"); continue; } // Clear the previous terms for the field clearField(xdoc, ftp->pfx, ftp->wdfinc); LOGDEB0("Db::xattrOnly: field [" << ent.first << "] pfx [" << ftp->pfx << "] inc " << ftp->wdfinc << ": [" << ent.second << "]\n"); splitter->setTraits(*ftp); if (!splitter->text_to_words(ent.second)) { LOGDEB("Db::xattrOnly: split failed for " << ent.first << "\n"); } } xdoc.add_value(VALUE_SIG, doc.sig); // Parse current data record into a dict for ease of processing ConfSimple datadic(data); if (!datadic.ok()) { LOGERR("db::docToXdocXattrOnly: failed turning data rec to dict\n"); return false; } // For each "stored" field, check if set in doc metadata and // update the value if it is const set& stored = m_rcldb->m_config->getStoredFields(); for (set::const_iterator it = stored.begin(); it != stored.end(); it++) { string nm = m_rcldb->m_config->fieldCanon(*it); if (doc.getmeta(nm, nullptr)) { string value = neutchars( truncate_to_word(doc.meta[nm], m_rcldb->m_idxMetaStoredLen), cstr_nc); datadic.set(nm, value, ""); } } // Recreate the record. We want to do this with the local RECORD_APPEND // method for consistency in format, instead of using ConfSimple print vector names = datadic.getNames(""); data.clear(); for (vector::const_iterator it = names.begin(); it != names.end(); it++) { string value; datadic.get(*it, value, ""); RECORD_APPEND(data, *it, value); } RECORD_APPEND(data, Doc::keysig, doc.sig); xdoc.set_data(data); return true; } #ifdef IDX_THREADS void Db::closeQueue() { if (m_ndb->m_iswritable && m_ndb->m_havewriteq) { m_ndb->m_wqueue.closeShop(); } } void Db::waitUpdIdle() { if (m_ndb->m_iswritable && m_ndb->m_havewriteq) { Chrono chron; m_ndb->m_wqueue.waitIdle(); // We flush here just for correct measurement of the thread work time string ermsg; try { m_ndb->xwdb.commit(); } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db::waitUpdIdle: flush() failed: " << ermsg << "\n"); } m_ndb->m_totalworkns += chron.nanos(); LOGINFO("Db::waitUpdIdle: total xapian work " << lltodecstr(m_ndb->m_totalworkns/1000000) << " mS\n"); } } #endif // Flush when idxflushmbs is reached bool Db::maybeflush(int64_t moretext) { if (m_flushMb > 0) { m_curtxtsz += moretext; if ((m_curtxtsz - m_flushtxtsz) / MB >= m_flushMb) { LOGINF("Db::add/delete: txt size >= " << m_flushMb << " Mb, flushing\n"); return doFlush(); } } return true; } bool Db::doFlush() { if (!m_ndb) { LOGERR("Db::doFLush: no ndb??\n"); return false; } string ermsg; try { statusUpdater()->update(DbIxStatus::DBIXS_FLUSH, ""); m_ndb->xwdb.commit(); } XCATCHERROR(ermsg); statusUpdater()->update(DbIxStatus::DBIXS_NONE, ""); if (!ermsg.empty()) { LOGERR("Db::doFlush: flush() failed: " << ermsg << "\n"); return false; } m_flushtxtsz = m_curtxtsz; return true; } void Db::setExistingFlags(const string& udi, unsigned int docid) { if (m_mode == DbRO) return; if (docid == (unsigned int)-1) { LOGERR("Db::setExistingFlags: called with bogus docid !!\n"); return; } #ifdef IDX_THREADS std::unique_lock lock(m_ndb->m_mutex); #endif i_setExistingFlags(udi, docid); } void Db::i_setExistingFlags(const string& udi, unsigned int docid) { // Set the up to date flag for the document and its // subdocs. needUpdate() can also be called at query time (for // preview up to date check), so no error if the updated bitmap is // of size 0, and also this now happens when fsIndexer() calls // udiTreeMarkExisting() after an error, so the message level is // now debug if (docid >= updated.size()) { if (updated.size()) { LOGDEB("needUpdate: existing docid beyond updated.size() " "(probably ok). Udi [" << udi << "], docid " << docid << ", updated.size() " << updated.size() << "\n"); } return; } else { updated[docid] = true; } // Set the existence flag for all the subdocs (if any) vector docids; if (!m_ndb->subDocs(udi, 0, docids)) { LOGERR("Rcl::Db::needUpdate: can't get subdocs\n"); return; } for (auto docid : docids) { if (docid < updated.size()) { LOGDEB2("Db::needUpdate: docid " << docid << " set\n"); updated[docid] = true; } } } // Before running indexing for a specific backend: set the existence bits for all backends except // the specified one, so that the corresponding documents are not purged after the update. The "FS" // backend is special because the documents usually have no rclbes field (but we prepare for the // time when they might have one). // - If this is the FS backend: unset all the bits, set them for all backends but FS (docs without // a backend field will stay off). // - Else set all the bits, unset them for the specified backend (docs without a backend field // will stay on). bool Db::preparePurge(const std::string& _backend) { auto backend = stringtolower(_backend); TermMatchResult result; if (!idxTermMatch(ET_WILD, "*", result, -1, Doc::keybcknd)) { LOGERR("Rcl::Db:preparePurge: termMatch failed\n"); return false; } if ("fs" == backend) { updated = vector(m_ndb->xwdb.get_lastdocid() + 1, false); for (const auto& entry : result.entries) { auto stripped = strip_prefix(entry.term); if (stripped.empty() || "fs" == stripped) continue; Xapian::PostingIterator docid = m_ndb->xrdb.postlist_begin(entry.term); while (docid != m_ndb->xrdb.postlist_end(entry.term)) { if (*docid < updated.size()) { updated[*docid] = true; } docid++; } } } else { updated = vector(m_ndb->xwdb.get_lastdocid() + 1, true); for (const auto& entry : result.entries) { auto stripped = strip_prefix(entry.term); if (stripped.empty() || backend != strip_prefix(entry.term)) continue; Xapian::PostingIterator docid = m_ndb->xrdb.postlist_begin(entry.term); while (docid != m_ndb->xrdb.postlist_end(entry.term)) { if (*docid < updated.size()) { updated[*docid] = false; } docid++; } } } return true; } // Test if doc given by udi has changed since last indexed (test sigs) bool Db::needUpdate(const string &udi, const string& sig, unsigned int *docidp, string *osigp) { if (nullptr == m_ndb) return false; if (osigp) osigp->clear(); if (docidp) *docidp = 0; // If we are doing an in place or full reset, no need to test. if (m_inPlaceReset || m_mode == DbTrunc) { // For in place reset, pretend the doc existed, to enable // subdoc purge. The value is only used as a boolean in this case. if (docidp && m_inPlaceReset) { *docidp = -1; } return true; } string uniterm = make_uniterm(udi); string ermsg; #ifdef IDX_THREADS // Need to protect against interaction with the doc update/insert // thread which also updates the existence map, and even multiple // accesses to the readonly Xapian::Database are not allowed // anyway std::unique_lock lock(m_ndb->m_mutex); #endif // Try to find the document indexed by the uniterm. Xapian::PostingIterator docid; XAPTRY(docid = m_ndb->xrdb.postlist_begin(uniterm), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::needUpdate: xapian::postlist_begin failed: " << m_reason << "\n"); return false; } if (docid == m_ndb->xrdb.postlist_end(uniterm)) { // No document exists with this path: we do need update LOGDEB("Db::needUpdate:yes (new): [" << uniterm << "]\n"); return true; } Xapian::Document xdoc; XAPTRY(xdoc = m_ndb->xrdb.get_document(*docid), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::needUpdate: get_document error: " << m_reason << "\n"); return true; } if (docidp) { *docidp = *docid; } // Retrieve old file/doc signature from value string osig; XAPTRY(osig = xdoc.get_value(VALUE_SIG), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::needUpdate: get_value error: " << m_reason << "\n"); return true; } LOGDEB2("Db::needUpdate: oldsig [" << osig << "] new [" << sig << "]\n"); if (osigp) { *osigp = osig; } // Compare new/old sig if (sig != osig) { LOGDEB("Db::needUpdate:yes: olsig [" << osig << "] new [" << sig << "] [" << uniterm << "]\n"); // Db is not up to date. Let's index the file return true; } // Up to date. Set the existance flags in the map for the doc and // its subdocs. LOGDEB("Db::needUpdate:no: [" << uniterm << "]\n"); i_setExistingFlags(udi, *docid); return false; } // Return existing stem db languages vector Db::getStemLangs() { LOGDEB("Db::getStemLang\n"); vector langs; if (nullptr == m_ndb || m_ndb->m_isopen == false) return langs; StemDb db(m_ndb->xrdb); db.getMembers(langs); return langs; } /** * Delete stem db for given language */ bool Db::deleteStemDb(const string& lang) { LOGDEB("Db::deleteStemDb(" << lang << ")\n"); if (nullptr == m_ndb || m_ndb->m_isopen == false || !m_ndb->m_iswritable) return false; XapWritableSynFamily db(m_ndb->xwdb, synFamStem); return db.deleteMember(lang); } /** * Create database of stem to parents associations for a given language. * We walk the list of all terms, stem them, and create another Xapian db * with documents indexed by a single term (the stem), and with the list of * parent terms in the document data. */ bool Db::createStemDbs(const vector& langs) { LOGDEB("Db::createStemDbs\n"); if (nullptr == m_ndb || m_ndb->m_isopen == false || !m_ndb->m_iswritable) { LOGERR("createStemDb: db not open or not writable\n"); return false; } return createExpansionDbs(m_ndb->xwdb, langs); } /** * This is called at the end of an indexing session, to delete the documents for files that are no * longer there. It depends on the state of the 'updated' bitmap and will delete all documents * for which the existence bit is off. * This can ONLY be called after an appropriate call to unsetExistbits() and a full backend document * set walk, else the file existence flags will be wrong. */ bool Db::purge() { if (nullptr == m_ndb) { LOGERR("Db::purge: null m_ndb??\n"); return false; } LOGDEB("Db::purge: m_isopen "<m_isopen<<" m_iswritable "<m_iswritable<<"\n"); if (m_ndb->m_isopen == false || m_ndb->m_iswritable == false) return false; #ifdef IDX_THREADS // If we manage our own write queue, make sure it's drained and closed if (m_ndb->m_havewriteq) { m_ndb->m_wqueue.setTerminateAndWait(); // And restore state for a possible next backend. m_ndb->maybeStartThreads(); } // We need to lock out other top level threads. This is just a precaution as they should have // been waited for by the top level actor at this point. std::unique_lock lock(m_ndb->m_mutex); #endif // IDX_THREADS // For Xapian versions up to 1.0.1, deleting a non-existant document would trigger an exception // that would discard any pending update. This could lose both previous added documents or // deletions. Adding the flush before the delete pass ensured that any added document would go // to the index. Kept here because it doesn't really hurt. m_reason.clear(); try { statusUpdater()->update(DbIxStatus::DBIXS_FLUSH, ""); m_ndb->xwdb.commit(); } XCATCHERROR(m_reason); statusUpdater()->update(DbIxStatus::DBIXS_NONE, ""); if (!m_reason.empty()) { LOGERR("Db::purge: 1st flush failed: " << m_reason << "\n"); return false; } // Walk the 'updated' bitmap and delete any Xapian document whose flag is not set (we did not // see its source during indexing). int purgecount = 0; for (Xapian::docid docid = 1; docid < updated.size(); ++docid) { if (!updated[docid]) { if ((purgecount+1) % 100 == 0) { try { CancelCheck::instance().checkCancel(); } catch(CancelExcept) { LOGINFO("Db::purge: partially cancelled\n"); break; } } try { if (m_flushMb > 0) { // We use an average term length of 5 for estimating the doc sizes which is // probably not accurate but gives rough consistency with what we do for // add/update. I should fetch the doc size from the data record, but this would // be bad for performance. Xapian::termcount trms = m_ndb->xwdb.get_doclength(docid); maybeflush(trms * 5); } m_ndb->deleteDocument(docid); LOGDEB("Db::purge: deleted document #" << docid << "\n"); } catch (const Xapian::DocNotFoundError &) { LOGDEB0("Db::purge: document #" << docid << " not found\n"); } catch (const Xapian::Error &e) { LOGERR("Db::purge: document #" << docid << ": " << e.get_msg() << "\n"); } catch (...) { LOGERR("Db::purge: document #" << docid << ": unknown error\n"); } purgecount++; } } m_reason.clear(); try { statusUpdater()->update(DbIxStatus::DBIXS_FLUSH, ""); m_ndb->xwdb.commit(); } XCATCHERROR(m_reason); statusUpdater()->update(DbIxStatus::DBIXS_NONE, ""); if (!m_reason.empty()) { LOGERR("Db::purge: 2nd flush failed: " << m_reason << "\n"); return false; } return true; } // Test for doc existence. bool Db::docExists(const string& uniterm) { #ifdef IDX_THREADS // Need to protect read db against multiaccess. std::unique_lock lock(m_ndb->m_mutex); #endif string ermsg; try { Xapian::PostingIterator docid = m_ndb->xrdb.postlist_begin(uniterm); if (docid == m_ndb->xrdb.postlist_end(uniterm)) { return false; } else { return true; } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db::docExists(" << uniterm << ") " << ermsg << "\n"); } return false; } /* Delete document(s) for given unique identifier (doc and descendents) */ bool Db::purgeFile(const string &udi, bool *existed) { LOGDEB("Db:purgeFile: [" << udi << "]\n"); if (nullptr == m_ndb || !m_ndb->m_iswritable) return false; string uniterm = make_uniterm(udi); bool exists = docExists(uniterm); if (existed) *existed = exists; if (!exists) return true; #ifdef IDX_THREADS if (m_ndb->m_havewriteq) { string rztxt; DbUpdTask *tp = new DbUpdTask(DbUpdTask::Delete, udi, uniterm, nullptr, (size_t)-1, rztxt); if (!m_ndb->m_wqueue.put(tp)) { LOGERR("Db::purgeFile:Cant queue task\n"); return false; } else { return true; } } #endif /* We get there is IDX_THREADS is not defined or there is no queue */ return m_ndb->purgeFileWrite(false, udi, uniterm); } /* Delete subdocs with an out of date sig. We do this to purge obsolete subdocs during a partial update where no general purge will be done */ bool Db::purgeOrphans(const string &udi) { LOGDEB("Db:purgeOrphans: [" << udi << "]\n"); if (nullptr == m_ndb || !m_ndb->m_iswritable) return false; string uniterm = make_uniterm(udi); #ifdef IDX_THREADS if (m_ndb->m_havewriteq) { string rztxt; DbUpdTask *tp = new DbUpdTask(DbUpdTask::PurgeOrphans, udi, uniterm, nullptr, (size_t)-1, rztxt); if (!m_ndb->m_wqueue.put(tp)) { LOGERR("Db::purgeFile:Cant queue task\n"); return false; } else { return true; } } #endif /* We get there is IDX_THREADS is not defined or there is no queue */ return m_ndb->purgeFileWrite(true, udi, uniterm); } bool Db::dbStats(DbStats& res, bool listfailed) { if (!m_ndb || !m_ndb->m_isopen) return false; Xapian::Database xdb = m_ndb->xrdb; XAPTRY(res.dbdoccount = xdb.get_doccount(); res.dbavgdoclen = xdb.get_avlength(); res.mindoclen = xdb.get_doclength_lower_bound(); res.maxdoclen = xdb.get_doclength_upper_bound(); , xdb, m_reason); if (!m_reason.empty()) return false; if (!listfailed) { return true; } // listfailed is set : look for failed docs string ermsg; try { for (unsigned int docid = 1; docid < xdb.get_lastdocid(); docid++) { try { Xapian::Document doc = xdb.get_document(docid); string sig = doc.get_value(VALUE_SIG); if (sig.empty() || sig.back() != '+') { continue; } string data = doc.get_data(); ConfSimple parms(data); if (!parms.ok()) { } else { string url, ipath; parms.get(Doc::keyipt, ipath); parms.get(Doc::keyurl, url); // Turn to local url or not? It seems to make more // sense to keep the original urls as seen by the // indexer. // m_config->urlrewrite(dbdir, url); if (!ipath.empty()) { url += " | " + ipath; } res.failedurls.push_back(url); } } catch (Xapian::DocNotFoundError) { continue; } } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db::dbStats: " << ermsg << "\n"); return false; } return true; } // Retrieve document defined by Unique doc identifier. This is used // by the GUI history feature and by open parent/getenclosing // ! The return value is always true except for fatal errors. Document // existence should be tested by looking at doc.pc bool Db::getDoc(const string &udi, const Doc& idxdoc, Doc &doc) { LOGDEB1("Db:getDoc: [" << udi << "]\n"); int idxi = idxdoc.idxi; return getDoc(udi, idxi, doc); } bool Db::getDoc(const string &udi, const std::string& dbdir, Doc &doc, bool fetchtext) { LOGDEB1("Db::getDoc(udi, dbdir): (" << udi << ", " << dbdir << ")\n"); int idxi = -1; if (dbdir.empty() || dbdir == m_basedir) { idxi = 0; } else { for (unsigned int i = 0; i < m_extraDbs.size(); i++) { if (dbdir == m_extraDbs[i]) { idxi = int(i + 1); break; } } } LOGDEB1("Db::getDoc(udi, dbdir): idxi: " << idxi << endl); if (idxi < 0) { LOGERR("Db::getDoc(udi, dbdir): dbdir not in current extra dbs\n"); return false; } return getDoc(udi, idxi, doc, fetchtext); } bool Db::getDoc(const string& udi, int idxi, Doc& doc, bool fetchtext) { // Initialize what we can in any case. If this is history, caller // will make partial display in case of error if (nullptr == m_ndb) return false; doc.meta[Rcl::Doc::keyrr] = "100%"; doc.pc = 100; Xapian::Document xdoc; Xapian::docid docid; if (idxi >= 0 && (docid = m_ndb->getDoc(udi, idxi, xdoc))) { string data = xdoc.get_data(); doc.meta[Rcl::Doc::keyudi] = udi; return m_ndb->dbDataToRclDoc(docid, data, doc, fetchtext); } else { // Document found in history no longer in the // database. We return true (because their might be // other ok docs further) but indicate the error with // pc = -1 doc.pc = -1; LOGINFO("Db:getDoc: no such doc in current index: [" << udi << "]\n"); return true; } } bool Db::hasSubDocs(const Doc &idoc) { if (nullptr == m_ndb) return false; string inudi; if (!idoc.getmeta(Doc::keyudi, &inudi) || inudi.empty()) { LOGERR("Db::hasSubDocs: no input udi or empty\n"); return false; } LOGDEB1("Db::hasSubDocs: idxi " << idoc.idxi << " inudi [" < docids; if (!m_ndb->subDocs(inudi, idoc.idxi, docids)) { LOGDEB("Db::hasSubDocs: lower level subdocs failed\n"); return false; } if (!docids.empty()) return true; // Check if doc has an "has_children" term if (m_ndb->hasTerm(inudi, idoc.idxi, has_children_term)) return true; return false; } // Retrieve all subdocuments of a given one, which may not be a file-level // one (in which case, we have to retrieve this first, then filter the ipaths) bool Db::getSubDocs(const Doc &idoc, vector& subdocs) { if (nullptr == m_ndb) return false; string inudi; if (!idoc.getmeta(Doc::keyudi, &inudi) || inudi.empty()) { LOGERR("Db::getSubDocs: no input udi or empty\n"); return false; } string rootudi; string ipath = idoc.ipath; LOGDEB0("Db::getSubDocs: idxi " << idoc.idxi << " inudi [" << inudi << "] ipath [" << ipath << "]\n"); if (ipath.empty()) { // File-level doc. Use it as root rootudi = inudi; } else { // See if we have a parent term Xapian::Document xdoc; if (!m_ndb->getDoc(inudi, idoc.idxi, xdoc)) { LOGERR("Db::getSubDocs: can't get Xapian document\n"); return false; } Xapian::TermIterator xit; XAPTRY(xit = xdoc.termlist_begin(); xit.skip_to(wrap_prefix(parent_prefix)), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::getSubDocs: xapian error: " << m_reason << "\n"); return false; } if (xit == xdoc.termlist_end() || get_prefix(*xit) != parent_prefix) { LOGERR("Db::getSubDocs: parent term not found\n"); return false; } rootudi = strip_prefix(*xit); } LOGDEB("Db::getSubDocs: root: [" << rootudi << "]\n"); // Retrieve all subdoc xapian ids for the root vector docids; if (!m_ndb->subDocs(rootudi, idoc.idxi, docids)) { LOGDEB("Db::getSubDocs: lower level subdocs failed\n"); return false; } // Retrieve doc, filter, and build output list for (int tries = 0; tries < 2; tries++) { try { for (vector::const_iterator it = docids.begin(); it != docids.end(); it++) { Xapian::Document xdoc = m_ndb->xrdb.get_document(*it); string data = xdoc.get_data(); string docudi; m_ndb->xdocToUdi(xdoc, docudi); Doc doc; doc.meta[Doc::keyudi] = docudi; doc.meta[Doc::keyrr] = "100%"; doc.pc = 100; if (!m_ndb->dbDataToRclDoc(*it, data, doc)) { LOGERR("Db::getSubDocs: doc conversion error\n"); return false; } if (ipath.empty() || FileInterner::ipathContains(ipath, doc.ipath)) { subdocs.push_back(doc); } } return true; } catch (const Xapian::DatabaseModifiedError &e) { m_reason = e.get_msg(); m_ndb->xrdb.reopen(); continue; } XCATCHERROR(m_reason); break; } LOGERR("Db::getSubDocs: Xapian error: " << m_reason << "\n"); return false; } bool Db::getContainerDoc(const Doc &idoc, Doc& ctdoc) { if (nullptr == m_ndb) return false; string inudi; if (!idoc.getmeta(Doc::keyudi, &inudi) || inudi.empty()) { LOGERR("Db::getContainerDoc: no input udi or empty\n"); return false; } string rootudi; string ipath = idoc.ipath; LOGDEB0("Db::getContainerDoc: idxi " << idoc.idxi << " inudi [" << inudi << "] ipath [" << ipath << "]\n"); if (ipath.empty()) { // File-level doc ?? ctdoc = idoc; return true; } // See if we have a parent term Xapian::Document xdoc; if (!m_ndb->getDoc(inudi, idoc.idxi, xdoc)) { LOGERR("Db::getContainerDoc: can't get Xapian document\n"); return false; } Xapian::TermIterator xit; XAPTRY(xit = xdoc.termlist_begin(); xit.skip_to(wrap_prefix(parent_prefix)), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::getContainerDoc: xapian error: " << m_reason << "\n"); return false; } if (xit == xdoc.termlist_end() || get_prefix(*xit) != parent_prefix) { LOGERR("Db::getContainerDoc: parent term not found\n"); return false; } rootudi = strip_prefix(*xit); if (!getDoc(rootudi, idoc.idxi, ctdoc)) { LOGERR("Db::getContainerDoc: can't get container document\n"); return false; } return true; } // Walk an UDI section (all UDIs beginning with input prefix), and // mark all docs and subdocs as existing. Caller beware: Makes sense // or not depending on the UDI structure for the data store. In practise, // used for absent FS mountable volumes. bool Db::udiTreeMarkExisting(const string& udi) { LOGDEB("Db::udiTreeMarkExisting: " << udi << endl); string prefix = wrap_prefix(udi_prefix); string expr = udi + "*"; #ifdef IDX_THREADS std::unique_lock lock(m_ndb->m_mutex); #endif bool ret = m_ndb->idxTermMatch_p( int(ET_WILD), expr, prefix, [this, &udi](const string& term, Xapian::termcount, Xapian::doccount) { Xapian::PostingIterator docid; XAPTRY(docid = m_ndb->xrdb.postlist_begin(term), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::udiTreeWalk: xapian::postlist_begin failed: " << m_reason << "\n"); return false; } if (docid == m_ndb->xrdb.postlist_end(term)) { LOGDEB("Db::udiTreeWalk:no doc for " << term << " ??\n"); return false; } i_setExistingFlags(udi, *docid); LOGDEB0("Db::udiTreeWalk: uniterm: " << term << "\n"); return true; }); return ret; } } // End namespace Rcl recoll-1.36.1/rcldb/rcldb.h0000644000175000017500000005663214466636436012366 00000000000000/* Copyright (C) 2004-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _DB_H_INCLUDED_ #define _DB_H_INCLUDED_ #include #include #include #include // rcldb defines an interface for a 'real' text database. The current // implementation uses xapian only, and xapian-related code is in rcldb.cpp // If support was added for other backend, the xapian code would be moved in // rclxapian.cpp, another file would be created for the new backend, and the // configuration/compile/link code would be adjusted to allow choosing. There // is no plan for supporting multiple different backends. // // In no case does this try to implement a useful virtualized text-db interface // The main goal is simplicity and good matching to usage inside the recoll // user interface. In other words, this is not exhaustive or well-designed or // reusable. // // Unique Document Identifier: uniquely identifies a document in its // source storage (file system or other). Used for up to date checks // etc. "udi". Our user is responsible for making sure it's not too // big, cause it's stored as a Xapian term (< 150 bytes would be // reasonable) class RclConfig; class Aspell; struct FieldTraits; class SynGroups; namespace Rcl { class Doc; class StopList; // Omega compatible values. We leave a hole for future omega values. Not sure // it makes any sense to keep any level of omega compat given that the index // is incompatible anyway. enum value_slot { // Omega-compatible values: VALUE_LASTMOD = 0, // 4 byte big endian value - seconds since 1970. VALUE_MD5 = 1, // 16 byte MD5 checksum of original document. VALUE_SIZE = 2, // sortable_serialise() ////////// Recoll only: // Doc sig as chosen by app (ex: mtime+size VALUE_SIG = 10, }; class SearchData; class TermIter; class Query; /** Used for returning result lists for index terms matching some criteria */ class TermMatchEntry { public: TermMatchEntry() {} TermMatchEntry(const std::string& t, int f, int d) : term(t), wcf(f), docs(d) {} TermMatchEntry(const std::string& t) : term(t) {} bool operator==(const TermMatchEntry &o) const { return term == o.term; } bool operator<(const TermMatchEntry &o) const { return term < o.term; } std::string term; int wcf{0}; // Total count of occurrences within collection. int docs{0}; // Number of documents countaining term. }; /** Term match result list header: statistics and global info */ class TermMatchResult { public: TermMatchResult(bool strip_prefixes = false) : m_prefix_stripped(strip_prefixes) { clear(); } void clear() { entries.clear(); } bool m_prefix_stripped; // Term expansion std::vector entries; // If a field was specified, this is the corresponding index prefix std::string prefix; // Terms generated by spelling proximity: used to inform the user (transmitted through // Highlightdata) std::vector fromspelling; }; class DbStats { public: DbStats() {} // Index-wide stats unsigned int dbdoccount{0}; double dbavgdoclen{0}; size_t mindoclen{0}; size_t maxdoclen{0}; std::vector failedurls; /* Only set if requested */ }; /** * Wrapper class for the native database. */ class Db { public: // A place for things we don't want visible here. class Native; friend class Native; /* General stuff (valid for query or update) ****************************/ Db(const RclConfig *cfp); ~Db(); Db(const Db&) = delete; Db& operator=(const Db&) = delete; enum OpenMode {DbRO, DbUpd, DbTrunc}; bool isWriteMode(OpenMode mode) { return mode == DbUpd || mode == DbTrunc; } enum OpenError {DbOpenNoError, DbOpenMainDb, DbOpenExtraDb}; bool open(OpenMode mode, OpenError *error = nullptr); bool close(); bool isopen(); /** Get explanation about last error */ std::string getReason() const {return m_reason;} /** Return all possible stemmer names */ static std::vector getStemmerNames(); /** Return existing stemming databases */ std::vector getStemLangs(); /** Check if index stores the documents' texts. Only valid after open */ bool storesDocText(); /** Test word for spelling correction candidate: not too long, no special chars... * @param with_aspell test for use with aspell, else for xapian speller */ static bool isSpellingCandidate(const std::string& term, bool with_aspell=true); /** Return spelling suggestion */ bool getSpellingSuggestions(const std::string& word, std::vector& suggs); /* The next two, only for searchdata, should be somehow hidden */ /* Return configured stop words */ const StopList& getStopList() const {return *m_stops;} /* Field name to prefix translation (ie: author -> 'A') */ bool fieldToTraits(const std::string& fldname, const FieldTraits **ftpp, bool isquery = false); /* Update-related methods ******************************************/ /** Test if the db entry for the given udi is up to date. * * This is done by comparing the input and stored sigs. This is * used both when indexing and querying (before opening a document * using stale info). * * **This assumes that the udi pertains to the main index (idxi==0).** * * Side-effect when the db is writeable and the document up to * date: set the existence flag for the file document and all * subdocs if any (for later use by 'purge()') * * @param udi Unique Document Identifier (as chosen by indexer). * @param sig New signature (as computed by indexer). * @param xdocid[output] Non-zero if doc existed. Should be considered * as opaque, to be used for a possible later call to setExistingFlags() * Note that if inplaceReset is set, the return value is non-zero but not * an actual docid, it's only used as a flag in this case. * @param osig[output] old signature. */ bool needUpdate(const std::string &udi, const std::string& sig, unsigned int *xdocid = nullptr, std::string *osig = nullptr); /** * Unset the existence bits for the documents from a specific backend, and set them for all the * others. This must be called before indexing the backend, so that purge() can be called after * indexing and only affect the documents for the specified backend. * * @param backend the backend name (e.g. "FS", "BGL", ...) */ bool preparePurge(const std::string& backend); /** Set the existence flags for the document and its eventual subdocuments * * This can be called by the indexer after needUpdate() has returned true, * if the indexer does not wish to actually re-index (e.g.: the doc is * known to cause errors). */ void setExistingFlags(const std::string& udi, unsigned int docid); /** Indicate if we are doing a systematic reindex. This complements needUpdate() return */ bool inFullReset() {return m_inPlaceReset || m_mode == DbTrunc;} /** Add or update document identified by unique identifier. * @param config Config object to use. Can be the same as the member config * or a clone, to avoid sharing when called in multithread context. * @param udi the Unique Document Identifier is opaque to us. * Maximum size 150 bytes. * @param parent_udi the UDI for the container document. In case of complex * embedding, this is not always the immediate parent but the UDI for * the container file (which may be a farther ancestor). It is * used for purging subdocuments when a file ceases to exist and * to set the existence flags of all subdocuments of a container * that is found to be up to date. In other words, the * parent_udi is the UDI for the ancestor of the document which * is subject to needUpdate() and physical existence tests (some * kind of file equivalent). Empty for top-level docs. Should * probably be renamed container_udi. * @param doc container for document data. Should have been filled as * much as possible depending on the document type. * ** doc will be modified in a destructive way ** */ bool addOrUpdate(const std::string &udi, const std::string &parent_udi, Doc &doc); #ifdef IDX_THREADS void closeQueue(); void waitUpdIdle(); #endif /** Delete document(s) for given UDI, including subdocs */ bool purgeFile(const std::string &udi, bool *existed = nullptr); /** Delete subdocs with an out of date sig. We do this to purge obsolete subdocs during a partial update where no general purge will be done */ bool purgeOrphans(const std::string &udi); /** Remove documents that no longer exist in the file system. This * depends on the update map, which is built during * indexing (needUpdate() / addOrUpdate()). * * This should only be called after a full walk of * the file system, else the update map will not be complete, and * many documents will be deleted that shouldn't, which is why this * has to be called externally, rcldb can't know if the indexing * pass was complete or partial. */ bool purge(); /** Create stem expansion database for given languages. */ bool createStemDbs(const std::vector &langs); /** Delete stem expansion database for given language. */ bool deleteStemDb(const std::string &lang); /* Query-related methods ************************************/ /** Return total docs in db */ int docCnt(); /** Return count of docs which have an occurrence of term */ int termDocCnt(const std::string& term); /** Add extra Xapian database for querying. * @param dir must point to something which can be passed as parameter * to a Xapian::Database constructor (directory or stub). */ bool addQueryDb(const std::string &dir); /** Remove extra database. if dir == "", remove all. */ bool rmQueryDb(const std::string &dir); /** Set the extra indexes to the input list. */ bool setExtraQueryDbs(const std::vector& dbs); /** Check if document comes from the main index (this is used to decide if we can update the index for it */ bool fromMainIndex(const Doc& doc); /** Retrieve the stored doc text. This returns false if the index does not store raw text or other problems (discriminate with storesDocText(). On success, the data is stored in doc.text */ bool getDocRawText(Doc& doc); /** Retrieve an index designator for the document result. This is used * by the GUI document history feature for remembering where a * doc comes from and allowing later retrieval (if the ext index * is still active...). */ std::string whatIndexForResultDoc(const Doc& doc); /** Tell if directory seems to hold xapian db */ static bool testDbDir(const std::string &dir, bool *stripped = nullptr); /** Return the index terms that match the input string * Expansion is performed either with either wildcard or regexp processing * Stem expansion is performed if lang is not empty * * @param typ_sens defines the kind of expansion: none, wildcard, * regexp or stemming. "none" may still expand case, * diacritics and synonyms, depending on the casesens, diacsens and * synexp flags. * @param lang sets the stemming language(s). Can be a space-separated list * @param term is the term to expand * @param result is the main output * @param max defines the maximum result count * @param field if set, defines the field within with the expansion should * be performed. Only used for wildcards and regexps, stemming is * always global. If this is set, the resulting output terms * will be appropriately prefixed and the prefix value will be set * in the TermMatchResult header */ enum MatchType {ET_NONE=0, ET_WILD=1, ET_REGEXP=2, ET_STEM=3, ET_DIACSENS=8, ET_CASESENS=16, ET_SYNEXP=32, ET_PATHELT=64}; int matchTypeTp(int tp) { return tp & 7; } bool termMatch(int typ_sens, const std::string &lang, const std::string &term, TermMatchResult& result, int max = -1, const std::string& field = "", std::vector *multiwords = nullptr); bool dbStats(DbStats& stats, bool listFailed); /** Return min and max years for doc mod times in db */ bool maxYearSpan(int *minyear, int *maxyear); /** Return all mime types in index. This can be different from the ones defined in the config because of 'file' command usage. Inserts the types at the end of the parameter */ bool getAllDbMimeTypes(std::vector&); /** Compute a list of all the directories containing indexed documents, down to a given depth @param depth depth belowr a possible computed common prefix, that is, if all directories are relative to /home/you, a depth of 2 would get you /home/you/1/2 but not /home/you/1/2/3 @param[out] commonprefix common prefix path for the list. May be "/". @param[out] dirs the computed list (full paths including the prefix). */ bool dirlist(int depth, std::string& commonprefix, std::vector& dirs); /** Wildcard expansion specific to file names. Internal/sdata use only */ bool filenameWildExp(const std::string& exp, std::vector& names, int max); /** Set parameters for synthetic abstract generation */ void setAbstractParams(int idxTrunc, int synthLen, int syntCtxLen); int getAbsCtxLen() const { return m_synthAbsWordCtxLen; } int getAbsLen() const { return m_synthAbsLen; } /** Decide if we expand queries to more common terms which spell almost like the user term */ void setUseSpellFuzz(bool onoff) { m_usingSpellFuzz = onoff; } /** Set maximum spelling distance when considering terms for spelling expansion */ void setMaxSpellDist(int max) { m_maxSpellDistance = max; } int getMaxSpellDist() { return m_maxSpellDistance; } /** Get document for given udi and db index * * Used to retrieve ancestor documents. * @param udi The unique document identifier. * @param idxdoc A document from the same database as an opaque way to pass * the database id (e.g.: when looking for parent in a multi-database * context). * @param[out] doc The output Recoll document. * @return True for success. */ bool getDoc(const std::string &udi, const Doc& idxdoc, Doc &doc); /** Get document for given udi and index directory. * * Used by the 'history' feature. This supposes that the extra db is still active. * @param udi The unique document identifier. * @param dbdir The index directory, from storage, as returned by * whatIndexForResultDoc() at the time of the query. Can be * empty to mean "main index" (allows the history to avoid * storing the main dbdir value). * @param[out] doc The output Recoll document. * @return True for success. */ bool getDoc(const std::string &udi, const std::string& dbdir, Doc &doc, bool fetchtext=false); /** Same as from dbdir but use index integer index (main==0). Mostly for the python module */ bool getDoc(const std::string& udi, int idxi, Doc& doc, bool fetchtext=false); /** Test if documents has sub-documents. * * This can always be detected for file-level documents, using the * postlist for the parent term constructed with udi. * * For non file-level documents (e.g.: does an email inside an * mbox have attachments ?), detection is dependant on the filter * having set an appropriate flag at index time. Higher level code * can't detect it because the doc for the parent may have been * seen before any children. The flag is stored as a value in the * index. */ bool hasSubDocs(const Doc &idoc); /** Get subdocuments of given document. * * For file-level documents, these are all docs indexed by the * parent term built on idoc.udi. For embedded documents, the * parent doc is looked for, then its subdocs list is * filtered using the idoc ipath as a prefix. */ bool getSubDocs(const Doc& idoc, std::vector& subdocs); /** Get container (top level file) document. * * If the input is not a subdocument, this returns a copy of the input. */ bool getContainerDoc(const Doc &idoc, Doc& ctdoc); /** Get duplicates (md5) of document */ bool docDups(const Doc& idoc, std::vector& odocs); /* The following are mainly for the aspell module */ /** Whole term list walking. */ TermIter *termWalkOpen(); bool termWalkNext(TermIter *, std::string &term); void termWalkClose(TermIter *); /** Test term existence */ bool termExists(const std::string& term); /** Test if terms stem to different roots. */ bool stemDiffers(const std::string& lang, const std::string& term, const std::string& base); const RclConfig *getConf() {return m_config;} /** * Activate the "in place reset" mode where all documents are * considered as needing update. It should be set at the start of * the indexing pass. */ void setInPlaceReset() {m_inPlaceReset = true;} /** Flush interval get/set. This is used by the first indexing pass to override the config value and flush more rapidly initially so that the user can quickly play with queries */ int getFlushMb() { return m_flushMb; } void setFlushMb(int mb) { m_flushMb = mb; } bool doFlush(); // Use empty fn for no synonyms bool setSynGroupsFile(const std::string& fn); const SynGroups& getSynGroups() {return *m_syngroups;} // Mark all documents with an UDI having @param udi as prefix as // existing. Only works if the UDIs for the store are // hierarchical of course. Used by FsIndexer to avoid purging // files for a topdir which is on a removable file system and // currently unmounted (topdir does not exist or is empty. bool udiTreeMarkExisting(const std::string& udi); /* This has to be public for access by embedded Query::Native */ Native *m_ndb{nullptr}; private: const RclConfig *m_config; std::string m_reason; // Error explanation // Xapian directories for additional databases to query std::vector m_extraDbs; OpenMode m_mode{Db::DbRO}; // File existence vector: this is filled during the indexing pass. Any // document whose bit is not set at the end is purged std::vector updated; // Text bytes indexed since beginning long long m_curtxtsz{0}; // Text bytes at last flush long long m_flushtxtsz{0}; // Text bytes at last fsoccup check long long m_occtxtsz{0}; // First fs occup check ? int m_occFirstCheck{1}; // Synonym groups. There is no strict reason that this has to be // an Rcl::Db member, as it is only used when building each query. It // could be a SearchData member, or even a parameter to // Query::setQuery(). Otoh, building the syngroups structure from // a file may be expensive and it's unlikely to change with every // query, so it makes sense to cache it, and Rcl::Db is not a bad // place for this. std::unique_ptr m_syngroups; // Aspell object if needed Aspell *m_aspell{nullptr}; /*************** * Parameters cached out of the configuration files. Logically const * after init */ // Stop terms: those don't get indexed. std::unique_ptr m_stops; // Truncation length for stored meta fields int m_idxMetaStoredLen{150}; // This is how long an abstract we keep or build from beginning of // text when indexing. It only has an influence on the size of the // db as we are free to shorten it again when displaying int m_idxAbsTruncLen{250}; // Document text truncation length int m_idxTextTruncateLen{0}; // This is the size of the abstract that we synthetize out of query // term contexts at *query time* int m_synthAbsLen{250}; // This is how many words (context size) we keep around query terms // when building the abstract int m_synthAbsWordCtxLen{4}; // Flush threshold. Megabytes of text indexed before we flush. int m_flushMb{-1}; // Maximum file system occupation percentage int m_maxFsOccupPc{0}; // Using spelling approximation? bool m_usingSpellFuzz{true}; // Maximum dam-lev distance when considering terms for spelling fuzz int m_maxSpellDistance{1}; int m_autoSpellRarityThreshold{200000}; int m_autoSpellSelectionThreshold{20}; // Database directory std::string m_basedir; // When this is set, all documents are considered as needing a reindex. // This implements an alternative to just erasing the index before // beginning, with the advantage that, for small index formats updates, // between releases the index remains available while being recreated. bool m_inPlaceReset{false}; static bool o_nospell_chars[256]; /******* End logical constnesss */ #ifdef IDX_THREADS friend void *DbUpdWorker(void*); #endif // IDX_THREADS // Internal form of setExistingFlags: no locking void i_setExistingFlags(const std::string& udi, unsigned int docid); // Reinitialize when adding/removing additional dbs bool adjustdbs(); // Match the input term (which must be in index form, diacase stripped as needed) and actual // index contents, possibly using wildcard or regexp matching. If typ_sens is ET_NONE, this is // just an index lookup. Accumulate the results in the TermMatchResult mutable input. bool idxTermMatch(int typ_sens, const std::string &term, TermMatchResult& result, int max = -1, const std::string& field = std::string()); // Flush when idxflushmb is reached bool maybeflush(int64_t moretext); bool docExists(const std::string& uniterm); void spellExpand(const std::string& term, const std::string& field, std::vector& expansion); }; // This has to go somewhere, and as it needs the Xapian version, this is // the most reasonable place. std::string version_string(); extern const std::string pathelt_prefix; extern const std::string mimetype_prefix; extern const std::string unsplitFilenameFieldName; extern std::string start_of_field_term; extern std::string end_of_field_term; } #endif /* _DB_H_INCLUDED_ */ recoll-1.36.1/rcldb/synfamily.h0000644000175000017500000001660014426501047013263 00000000000000/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SYNFAMILY_H_INCLUDED_ #define _SYNFAMILY_H_INCLUDED_ /** * The Xapian synonyms mechanism can be used for many things beyond actual * synonyms, anything that would turn a string into a group of equivalents. * Unfortunately, it has only one keyspace. * This class partitions the Xapian synonyms keyspace by using prefixes and * can provide different applications each with a family of keyspaces. * Two characters are reserved by the class and should not be used inside * either family or member names: ':' and ';' * A synonym key for family "stemdb", member "french", key "somestem" * looks like: * :stemdb:french:somestem -> somestem expansions * A special entry is used to list all the members for a family, e.g.: * :stemdb;members -> french, english ... */ #include #include #include #include "log.h" #include "xmacros.h" #include "strmatcher.h" namespace Rcl { class XapSynFamily { public: /** * Construct from readable xapian database and family name (ie: Stm) */ XapSynFamily(Xapian::Database xdb, const std::string& familyname) : m_rdb(xdb) { m_prefix1 = std::string(":") + familyname; } virtual ~XapSynFamily() = default; /** Retrieve all members of this family (e.g: french english german...) */ virtual bool getMembers(std::vector&); /** Debug: list map for one member to stdout */ virtual bool listMap(const std::string& membername); /** Expand term to list of synonyms for given member */ bool synExpand(const std::string& membername, const std::string& term, std::vector& result); // The prefix shared by all synonym entries inside a family member virtual std::string entryprefix(const std::string& member) { return m_prefix1 + ":" + member + ":"; } // The key for the "list of members" entry virtual std::string memberskey() { return m_prefix1 + ";" + "members"; } Xapian::Database& getdb() { return m_rdb; } protected: Xapian::Database m_rdb; std::string m_prefix1; }; /** Modify ops for a synonyms family * * A method to add a synonym entry inside a given member would make sense, * but would not be used presently as all these ops go through * ComputableSynFamMember objects */ class XapWritableSynFamily : public XapSynFamily { public: /** Construct with Xapian db open for r/w */ XapWritableSynFamily(Xapian::WritableDatabase db, const std::string& familyname) : XapSynFamily(db, familyname), m_wdb(db) {} virtual ~XapWritableSynFamily() = default; /** Delete all entries for one member (e.g. french), and remove from list * of members */ virtual bool deleteMember(const std::string& membername); /** Add to list of members. Idempotent, does not affect actual expansions */ virtual bool createMember(const std::string& membername); Xapian::WritableDatabase getdb() {return m_wdb;} protected: Xapian::WritableDatabase m_wdb; }; /** A functor which transforms a string */ class SynTermTrans { public: virtual std::string operator()(const std::string&) = 0; virtual std::string name() {return "SynTermTrans: unknown";} }; /** A member (set of root-synonyms associations) of a SynFamily for * which the root is computable from the input term. * The objects use a functor member to compute the term root on input * (e.g. compute the term sterm or casefold it */ class XapComputableSynFamMember { public: XapComputableSynFamMember(Xapian::Database xdb, std::string familyname, std::string membername, SynTermTrans* trans) : m_family(xdb, familyname), m_membername(membername), m_trans(trans), m_prefix(m_family.entryprefix(m_membername)) {} virtual ~XapComputableSynFamMember() = default; /** Expand a term to its list of synonyms. If filtertrans is set we * keep only the results which transform to the same value as the input * This is used for example for filtering the result of case+diac * expansion when only either case or diac expansion is desired. */ bool synExpand(const std::string& term, std::vector& result, SynTermTrans *filtertrans = nullptr); /** Same with also wildcard/regexp expansion of entry against the keys. * The input matcher will be modified to fit our key format. */ bool synKeyExpand(StrMatcher* in, std::vector& result, SynTermTrans *filtertrans = nullptr); private: XapSynFamily m_family; std::string m_membername; SynTermTrans *m_trans; std::string m_prefix; }; /** Computable term root SynFamily member, modify ops */ class XapWritableComputableSynFamMember { public: XapWritableComputableSynFamMember( Xapian::WritableDatabase xdb, std::string familyname, std::string membername, SynTermTrans* trans) : m_family(xdb, familyname), m_membername(membername), m_trans(trans), m_prefix(m_family.entryprefix(m_membername)) {} virtual ~XapWritableComputableSynFamMember() = default; virtual bool addSynonym(const std::string& term) { LOGDEB2("addSynonym:me " << this << " term [" << term << "] m_trans " << m_trans << "\n"); std::string transformed = (*m_trans)(term); LOGDEB2("addSynonym: transformed [" << transformed << "]\n"); if (transformed == term) return true; std::string ermsg; try { m_family.getdb().add_synonym(m_prefix + transformed, term); } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("XapWritableComputableSynFamMember::addSynonym: xapian error " << ermsg << "\n"); return false; } return true; } void clear() { m_family.deleteMember(m_membername); } void recreate() { clear(); m_family.createMember(m_membername); } private: XapWritableSynFamily m_family; std::string m_membername; SynTermTrans *m_trans; std::string m_prefix; }; // // Prefixes are centrally defined here to avoid collisions // // Lowercase accented stem to expansion. Family member name: language static const std::string synFamStem("Stm"); // Lowercase unaccented stem to expansion. Family member name: language static const std::string synFamStemUnac("StU"); // Lowercase unaccented term to case and accent variations. Only one // member, named "all". This set is used for separate case/diac // expansion by post-filtering the results of dual expansion. static const std::string synFamDiCa("DCa"); } // end namespace Rcl #endif /* _SYNFAMILY_H_INCLUDED_ */ recoll-1.36.1/rcldb/synfamily.cpp0000644000175000017500000002120214410615043013603 00000000000000/* Copyright (C) 2012-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include "log.h" #include "cstr.h" #include "xmacros.h" #include "synfamily.h" #include "smallut.h" using namespace std; namespace Rcl { bool XapWritableSynFamily::createMember(const string& membername) { string ermsg; try { m_wdb.add_synonym(memberskey(), membername); } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("XapSynFamily::createMember: error: " << ermsg << "\n"); return false; } return true; } bool XapWritableSynFamily::deleteMember(const string& membername) { string key = entryprefix(membername); for (Xapian::TermIterator xit = m_wdb.synonym_keys_begin(key); xit != m_wdb.synonym_keys_end(key); xit++) { m_wdb.clear_synonyms(*xit); } m_wdb.remove_synonym(memberskey(), membername); return true; } bool XapSynFamily::getMembers(vector& members) { string key = memberskey(); string ermsg; try { for (Xapian::TermIterator xit = m_rdb.synonyms_begin(key); xit != m_rdb.synonyms_end(key); xit++) { members.push_back(*xit); } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("XapSynFamily::getMembers: xapian error " << ermsg << "\n"); return false; } return true; } bool XapSynFamily::listMap(const string& membername) { string key = entryprefix(membername); string ermsg; try { for (Xapian::TermIterator kit = m_rdb.synonym_keys_begin(key); kit != m_rdb.synonym_keys_end(key); kit++) { cout << "[" << *kit << "] -> "; for (Xapian::TermIterator xit = m_rdb.synonyms_begin(*kit); xit != m_rdb.synonyms_end(*kit); xit++) { cout << *xit << " "; } cout << endl; } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("XapSynFamily::listMap: xapian error " << ermsg << "\n"); return false; } vectormembers; getMembers(members); cout << "All family members: "; for (vector::const_iterator it = members.begin(); it != members.end(); it++) { cout << *it << " "; } cout << endl; return true; } bool XapSynFamily::synExpand(const string& member, const string& term, vector& result) { LOGDEB("XapSynFamily::synExpand:(" << m_prefix1 << ") " << term << " for " << member << "\n"); string key = entryprefix(member) + term; string ermsg; try { for (Xapian::TermIterator xit = m_rdb.synonyms_begin(key); xit != m_rdb.synonyms_end(key); xit++) { LOGDEB2(" Pushing " << *xit << "\n"); result.push_back(*xit); } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("synFamily::synExpand: error for member [" << member << "] term [" << term << "]\n"); result.push_back(term); return false; } // If the input term is not in the list, add it if (find(result.begin(), result.end(), term) == result.end()) { result.push_back(term); } return true; } bool XapComputableSynFamMember::synExpand(const string& term, vector& result, SynTermTrans *filtertrans) { string root = (*m_trans)(term); string filter_root; if (filtertrans) filter_root = (*filtertrans)(term); string key = m_prefix + root; LOGDEB("XapCompSynFamMbr::synExpand([" << m_prefix << "]): term [" << term << "] root [" << root << "] m_trans: " << m_trans->name() << " filter: " << (filtertrans ? filtertrans->name() : "none") << "\n"); string ermsg; try { for (Xapian::TermIterator xit = m_family.getdb().synonyms_begin(key); xit != m_family.getdb().synonyms_end(key); xit++) { LOGDEB("XapCompSynFamMbr::synExpand: testing " << *xit << endl); if (!filtertrans || (*filtertrans)(*xit) == filter_root) { LOGDEB2(" Pushing " << *xit << "\n"); result.push_back(*xit); } } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("XapSynDb::synExpand: error for term [" << term << "] (key " << key << ")\n"); result.push_back(term); return false; } // If the input term and root are not in the list, add them if (find(result.begin(), result.end(), term) == result.end()) { LOGDEB2(" Pushing " << term << "\n"); result.push_back(term); } if (root != term && find(result.begin(), result.end(), root) == result.end()) { if (!filtertrans || (*filtertrans)(root) == filter_root) { LOGDEB2(" Pushing " << root << "\n"); result.push_back(root); } } LOGDEB("XapCompSynFamMbr::synExpand([" << m_prefix << "]): term [" << term << "] -> [" << stringsToString(result) << "]\n"); return true; } bool XapComputableSynFamMember::synKeyExpand(StrMatcher* inexp, vector& result, SynTermTrans *filtertrans) { LOGDEB("XapCompSynFam::synKeyExpand: [" << inexp->exp() << "]\n"); // If set, compute filtering term (e.g.: only case-folded) std::shared_ptr filter_exp; if (filtertrans) { filter_exp = std::shared_ptr(inexp->clone()); filter_exp->setExp((*filtertrans)(inexp->exp())); } // Transform input into our key format (e.g.: case-folded + diac-stripped), // and prepend prefix inexp->setExp(m_prefix + (*m_trans)(inexp->exp())); // Find the initial section before any special chars for skipping the keys string::size_type es = inexp->baseprefixlen(); string is = inexp->exp().substr(0, es); string::size_type preflen = m_prefix.size(); LOGDEB2("XapCompSynFam::synKeyExpand: init section: [" << is << "]\n"); string ermsg; try { for (Xapian::TermIterator xit = m_family.getdb().synonym_keys_begin(is); xit != m_family.getdb().synonym_keys_end(is); xit++) { LOGDEB2(" Checking1 [" << *xit << "] against [" << inexp->exp() << "]\n"); if (!inexp->match(*xit)) continue; // Push all the synonyms if they match the secondary filter for (Xapian::TermIterator xit1 = m_family.getdb().synonyms_begin(*xit); xit1 != m_family.getdb().synonyms_end(*xit); xit1++) { string term = *xit1; if (filter_exp) { string term1 = (*filtertrans)(term); LOGDEB2(" Testing [" << term1 << "] against [" << filter_exp->exp() << "]\n"); if (!filter_exp->match(term1)) { continue; } } LOGDEB2("XapCompSynFam::keyWildExpand: [" << *xit1 << "]\n"); result.push_back(*xit1); } // Same with key itself string term = (*xit).substr(preflen); if (filter_exp) { string term1 = (*filtertrans)(term); LOGDEB2(" Testing [" << term1 << "] against [" << filter_exp->exp() << "]\n"); if (!filter_exp->match(term1)) { continue; } } LOGDEB2("XapCompSynFam::keyWildExpand: [" << term << "]\n"); result.push_back(term); } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("XapCompSynFam::synKeyExpand: xapian: [" << ermsg << "]\n"); return false; } LOGDEB1("XapCompSynFam::synKeyExpand: final: [" << stringsToString(result) << "]\n"); return true; } } // Namespace Rcl recoll-1.36.1/rcldb/searchdatatox.cpp0000644000175000017500000011763114427373216014451 00000000000000/* Copyright (C) 2006-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Handle translation from rcl's SearchData structures to Xapian Queries #include "autoconfig.h" #include #include #include #include #include #include #include "xapian.h" #include "cstr.h" #include "rcldb.h" #include "rcldb_p.h" #include "searchdata.h" #include "log.h" #include "smallut.h" #include "textsplit.h" #include "unacpp.h" #include "utf8iter.h" #include "stoplist.h" #include "rclconfig.h" #include "termproc.h" #include "synfamily.h" #include "stemdb.h" #include "expansiondbs.h" #include "base64.h" #include "daterange.h" #include "rclvalues.h" #include "pathut.h" using namespace std; namespace Rcl { static const int original_term_wqf_booster = 10; // Expand doc categories and mime type wild card expressions // // Categories are expanded against the configuration, mimetypes // against the index. bool SearchData::expandFileTypes(Db &db, vector& tps) { const RclConfig *cfg = db.getConf(); if (!cfg) { LOGFATAL("Db::expandFileTypes: null configuration!!\n"); return false; } vector exptps; for (const auto& mtype : tps) { if (cfg->isMimeCategory(mtype)) { vector ctps; cfg->getMimeCatTypes(mtype, ctps); exptps.insert(exptps.end(), ctps.begin(), ctps.end()); } else { TermMatchResult res; string mt = stringtolower(mtype); // Expand possible wildcard in mime type, e.g. text/* // We set casesens|diacsens to get an equivalent of ixTermMatch() db.termMatch( Db::ET_WILD|Db::ET_CASESENS|Db::ET_DIACSENS, string(), mt, res, -1, "mtype"); if (res.entries.empty()) { exptps.push_back(mtype); } else { for (const auto& entry : res.entries) { exptps.push_back(strip_prefix(entry.term)); } } } } sort(exptps.begin(), exptps.end()); exptps.erase(unique(exptps.begin(), exptps.end()), exptps.end()); tps = exptps; return true; } static const char *maxXapClauseMsg = "Maximum Xapian query size exceeded. Increase maxXapianClauses in the configuration. "; static const char *maxXapClauseCaseDiacMsg = "Or try to use case (C) or diacritics (D) sensitivity qualifiers, or less wildcards ?"; // Walk the clauses list, translate each and add to top Xapian Query bool SearchData::clausesToQuery( Rcl::Db &db, SClType tp, vector& query, string& reason, void *d) { Xapian::Query xq; for (auto& clausep : query) { #if 0 string txt; auto clp = dynamic_cast(clausep); if (clp) txt = clp->gettext(); LOGINF("Clause: tp: " << clausep->getTp() << " txt: [" << txt << "] mods: " << std::hex << clausep->getModifiers() << std::dec << "\n"); #endif Xapian::Query nq; if (!clausep->toNativeQuery(db, &nq)) { LOGERR("SearchData::clausesToQuery: toNativeQuery failed: " << clausep->getReason() << "\n"); reason += clausep->getReason() + " "; return false; } if (nq.empty()) { LOGDEB0("SearchData::clausesToQuery: skipping empty clause\n"); continue; } // If this structure is an AND list, must use AND_NOT for excl clauses. // Else this is an OR list, and there can't be excl clauses (checked by // addClause()) Xapian::Query::op op; if (tp == SCLT_AND) { if (clausep->getexclude()) { op = Xapian::Query::OP_AND_NOT; } else { if (clausep->getModifiers() & SearchDataClause::SDCM_FILTER) { op = Xapian::Query::OP_FILTER; } else { op = Xapian::Query::OP_AND; } } } else { op = Xapian::Query::OP_OR; } if (xq.empty()) { if (op == Xapian::Query::OP_AND_NOT) xq = Xapian::Query(op, Xapian::Query::MatchAll, nq); else xq = nq; } else { xq = Xapian::Query(op, xq, nq); } if (int(xq.get_length()) >= getMaxCl()) { LOGERR("" << maxXapClauseMsg << "\n"); m_reason += maxXapClauseMsg; if (!o_index_stripchars) m_reason += maxXapClauseCaseDiacMsg; return false; } } LOGDEB0("SearchData::clausesToQuery: got " << xq.get_length()<<" clauses\n"); if (xq.empty()) xq = Xapian::Query::MatchAll; *((Xapian::Query *)d) = xq; return true; } static void processdaterange(Rcl::Db& db, Xapian::Query& xq, DateInterval& dates, bool isbr = false) { // If one of the extremities is unset, compute db extremas if (dates.y1 == 0 || dates.y2 == 0) { int minyear = 1970, maxyear = 2100; if (!db.maxYearSpan(&minyear, &maxyear)) { LOGERR("Can't retrieve index min/max dates\n"); //whatever, go on. } if (dates.y1 == 0) { dates.y1 = minyear; dates.m1 = 1; dates.d1 = 1; } if (dates.y2 == 0) { dates.y2 = maxyear; dates.m2 = 12; dates.d2 = 31; } } LOGDEB("Db::toNativeQuery: " << (isbr?"birtime":"date") << " interval: " << dates.y1 << "-" << dates.m1 << "-" << dates.d1 << "/" << dates.y2 << "-" << dates.m2 << "-" << dates.d2 << "\n"); Xapian::Query dq; #ifdef EXT4_BIRTH_TIME if (isbr) { dq = brdate_range_filter(dates.y1, dates.m1, dates.d1, dates.y2, dates.m2, dates.d2); } else #endif { dq = date_range_filter(dates.y1, dates.m1, dates.d1, dates.y2, dates.m2, dates.d2); } if (dq.empty()) { LOGINFO("Db::toNativeQuery: date filter is empty\n"); } // If no probabilistic query is provided then promote the daterange // filter to be THE query instead of filtering an empty query. if (xq.empty()) { LOGINFO("Db::toNativeQuery: proba query is empty\n"); xq = dq; } else { xq = Xapian::Query(Xapian::Query::OP_FILTER, xq, dq); } } bool SearchData::toNativeQuery(Rcl::Db &db, void *d) { LOGDEB("SearchData::toNativeQuery: stemlang [" << m_stemlang << "]\n"); m_reason.erase(); db.getConf()->getConfParam("maxTermExpand", &m_maxexp); db.getConf()->getConfParam("maxXapianClauses", &m_maxcl); m_autocasesens = true; db.getConf()->getConfParam("autocasesens", &m_autocasesens); m_autodiacsens = false; db.getConf()->getConfParam("autodiacsens", &m_autodiacsens); simplify(); // Walk the clause list translating each in turn and building the // Xapian query tree Xapian::Query xq; if (!clausesToQuery(db, m_tp, m_query, m_reason, &xq)) { LOGERR("SearchData::toNativeQuery: clausesToQuery failed. reason: " << m_reason << "\n"); return false; } if (m_haveDates) { processdaterange(db, xq, m_dates); } #ifdef EXT4_BIRTH_TIME //handle birtime if (m_haveBrDates) { processdaterange(db, xq, m_brdates, true); } #endif if (m_minSize != -1 || m_maxSize != -1) { Xapian::Query sq; string min = lltodecstr(m_minSize); string max = lltodecstr(m_maxSize); if (m_minSize == -1) { string value(max); leftzeropad(value, 12); sq = Xapian::Query(Xapian::Query::OP_VALUE_LE, VALUE_SIZE, value); } else if (m_maxSize == -1) { string value(min); leftzeropad(value, 12); sq = Xapian::Query(Xapian::Query::OP_VALUE_GE, VALUE_SIZE, value); } else { string minvalue(min); leftzeropad(minvalue, 12); string maxvalue(max); leftzeropad(maxvalue, 12); sq = Xapian::Query(Xapian::Query::OP_VALUE_RANGE, VALUE_SIZE, minvalue, maxvalue); } // If no probabilistic query is provided then promote the // filter to be THE query instead of filtering an empty query. if (xq.empty()) { LOGINFO("Db::toNativeQuery: proba query is empty\n"); xq = sq; } else { xq = Xapian::Query(Xapian::Query::OP_FILTER, xq, sq); } } // Add the autophrase if any if (m_autophrase) { Xapian::Query apq; if (m_autophrase->toNativeQuery(db, &apq)) { xq = xq.empty() ? apq : Xapian::Query(Xapian::Query::OP_AND_MAYBE, xq, apq); } } // Add the file type filtering clause if any if (!m_filetypes.empty()) { expandFileTypes(db, m_filetypes); Xapian::Query tq; for (const auto& ft : m_filetypes) { string term = wrap_prefix(mimetype_prefix) + ft; LOGDEB0("Adding file type term: [" << term << "]\n"); tq = tq.empty() ? Xapian::Query(term) : Xapian::Query(Xapian::Query::OP_OR, tq, Xapian::Query(term)); } xq = xq.empty() ? tq : Xapian::Query(Xapian::Query::OP_FILTER, xq, tq); } // Add the neg file type filtering clause if any if (!m_nfiletypes.empty()) { expandFileTypes(db, m_nfiletypes); Xapian::Query tq; for (const auto& ft : m_nfiletypes) { string term = wrap_prefix(mimetype_prefix) + ft; LOGDEB0("Adding negative file type term: [" << term << "]\n"); tq = tq.empty() ? Xapian::Query(term) : Xapian::Query(Xapian::Query::OP_OR, tq, Xapian::Query(term)); } xq = xq.empty() ? tq : Xapian::Query(Xapian::Query::OP_AND_NOT, xq, tq); } *((Xapian::Query *)d) = xq; return true; } // Splitter for breaking a user string into simple terms and // phrases. This is for parts of the user entry which would appear as // a single word because there is no white space inside, but are // actually multiple terms to rcldb (ie term1,term2). Still, most of // the time, the result of our splitting will be a single term. class TextSplitQ : public TextSplitP { public: TextSplitQ(Flags flags, TermProc *prc) : TextSplitP(prc, flags), m_nostemexp(false) { } bool takeword(const std::string &term, int pos, int bs, int be) { // Check if the first letter is a majuscule in which // case we do not want to do stem expansion. Need to do this // before unac of course... m_nostemexp = unaciscapital(term); return TextSplitP::takeword(term, pos, bs, be); } bool nostemexp() const { return m_nostemexp; } private: bool m_nostemexp; }; class TermProcQ : public TermProc { public: TermProcQ() : TermProc(nullptr), m_alltermcount(0), m_lastpos(0), m_ts(nullptr) {} // We need a ref to the splitter (only it knows about orig term // capitalization for controlling stemming. The ref can't be set // in the constructor because the splitter is not built yet when // we are born (chicken and egg). void setTSQ(const TextSplitQ *ts) { m_ts = ts; } bool takeword(const std::string &term, int pos, int, int be) { m_alltermcount++; if (m_lastpos < pos) m_lastpos = pos; bool noexpand = be ? m_ts->nostemexp() : true; LOGDEB1("TermProcQ::takeword: pushing [" << term << "] pos " << pos << " noexp " << noexpand << "\n"); if (m_terms[pos].size() < term.size()) { m_terms[pos] = term; m_nste[pos] = noexpand; } return true; } bool flush() { for (const auto& entry : m_terms) { m_vterms.push_back(entry.second); m_vnostemexps.push_back(m_nste[entry.first]); } return true; } int alltermcount() const { return m_alltermcount; } int lastpos() const { return m_lastpos; } const vector& terms() { return m_vterms; } const vector& nostemexps() { return m_vnostemexps; } private: // Count of terms including stopwords: this is for adjusting // phrase/near slack int m_alltermcount; int m_lastpos; const TextSplitQ *m_ts; vector m_vterms; vector m_vnostemexps; map m_terms; map m_nste; }; static const vector expandModStrings{ {SearchDataClause::SDCM_NOSTEMMING, "nostemming"}, {SearchDataClause::SDCM_ANCHORSTART, "anchorstart"}, {SearchDataClause::SDCM_ANCHOREND, "anchorend"}, {SearchDataClause::SDCM_CASESENS, "casesens"}, {SearchDataClause::SDCM_DIACSENS, "diacsens"}, {SearchDataClause::SDCM_NOTERMS, "noterms"}, {SearchDataClause::SDCM_NOSYNS, "nosyns"}, {SearchDataClause::SDCM_PATHELT, "pathelt"}, }; /** Expand term into term list, using appropriate mode: stem, wildcards, * diacritics... * * @param mods stem expansion, case and diacritics sensitivity control. * @param term input single word * @param oexp output expansion list * @param sterm output original input term if there were no wildcards * @param prefix field prefix in index. We could recompute it, but the caller * has it already. Used in the simple case where there is nothing to expand, * and we just return the prefixed term (else Db::termMatch deals with it). * @param multiwords it may happen that synonym processing results in multi-word * expansions which should be processed as phrases. */ bool SearchDataClauseSimple::expandTerm(Rcl::Db &db, string& ermsg, int mods, const string& term, vector& oexp, string &sterm, const string& prefix, vector* multiwords ) { LOGDEB0("expandTerm: mods: [" << flagsToString(expandModStrings, mods) << "] fld [" << m_field << "] trm [" << term << "] lang [" << getStemLang() << "]\n"); sterm.clear(); oexp.clear(); if (term.empty()) return true; if (mods & SDCM_PATHELT) { // Path element are so special. Only wildcards, and they are // case-sensitive. mods |= SDCM_NOSTEMMING|SDCM_CASESENS|SDCM_DIACSENS|SDCM_NOSYNS; } bool maxexpissoft = false; int maxexpand = getSoftMaxExp(); if (maxexpand != -1) { maxexpissoft = true; } else { maxexpand = getMaxExp(); } bool haswild = term.find_first_of(cstr_minwilds) != string::npos; // If there are no wildcards, add term to the list of user-entered terms if (!haswild) { m_hldata.uterms.insert(term); sterm = term; } // No stem expansion if there are wildcards or if prevented by caller bool nostemexp = (mods & SDCM_NOSTEMMING) != 0; if (haswild || getStemLang().empty()) { LOGDEB2("expandTerm: found wildcards or stemlang empty: no exp\n"); nostemexp = true; } bool diac_sensitive = (mods & SDCM_DIACSENS) != 0; bool case_sensitive = (mods & SDCM_CASESENS) != 0; bool synonyms = (mods & SDCM_NOSYNS) == 0; bool pathelt = (mods & SDCM_PATHELT) != 0; // noexpansion can be modified further down by possible case/diac expansion bool noexpansion = nostemexp && !haswild && !synonyms; if (o_index_stripchars) { diac_sensitive = case_sensitive = false; } else { // If we are working with a raw index, apply the rules for case and // diacritics sensitivity. // If any character has a diacritic, we become // diacritic-sensitive. Note that the way that the test is // performed (conversion+comparison) will automatically ignore // accented characters which are actually a separate letter if (getAutoDiac() && unachasaccents(term)) { LOGDEB0("expandTerm: term has accents -> diac-sensitive\n"); diac_sensitive = true; } // If any character apart the first is uppercase, we become // case-sensitive. The first character is reserved for // turning off stemming. You need to use a query language // modifier to search for Floor in a case-sensitive way. Utf8Iter it(term); it++; if (getAutoCase() && unachasuppercase(term.substr(it.getBpos()))) { LOGDEB0("expandTerm: term has uppercase -> case-sensitive\n"); case_sensitive = true; } // If we are sensitive to case or diacritics turn stemming off if (diac_sensitive || case_sensitive) { LOGDEB0("expandTerm: diac or case sens set -> stemexpand and synonyms off\n"); nostemexp = true; synonyms = false; } if (!case_sensitive || !diac_sensitive) noexpansion = false; } if (!m_exclude && noexpansion) { oexp.push_back(prefix + term); m_hldata.terms[term] = term; LOGDEB("ExpandTerm: noexpansion: final: "<= maxexpand && !maxexpissoft) { ermsg = "Maximum term expansion size exceeded." " Maybe use case/diacritics sensitivity or increase maxTermExpand."; return false; } for (const auto& entry : res.entries) { oexp.push_back(entry.term); } // If the term does not exist at all in the db, the return from // termMatch() is going to be empty, which is not what we want (we // would then compute an empty Xapian query) if (oexp.empty()) oexp.push_back(prefix + term); // Remember the uterm-to-expansion links if (!m_exclude) { for (const auto& entry : oexp) { m_hldata.terms[strip_prefix(entry)] = term; } } // Remember the terms generated trough spelling approximation m_hldata.spellexpands.insert(m_hldata.spellexpands.end(), res.fromspelling.begin(), res.fromspelling.end()); LOGDEB("ExpandTerm: final: " << stringsToString(oexp) << "\n"); return true; } static void prefix_vector(vector& v, const string& prefix) { for (auto& elt : v) { elt = prefix + elt; } } void SearchDataClauseSimple::processSimpleSpan( Rcl::Db &db, string& ermsg, const string& span, int mods, void *pq) { vector& pqueries(*(vector*)pq); LOGDEB0("StringToXapianQ::processSimpleSpan: [" << span << "] mods 0x" << (unsigned int)mods << "\n"); vector exp; string sterm; // dumb version of user term string prefix; const FieldTraits *ftp; if (!m_field.empty() && db.fieldToTraits(m_field, &ftp, true)) { if (ftp->noterms) addModifier(SDCM_NOTERMS); // Don't add terms to highlight data prefix = wrap_prefix(ftp->pfx); } vector multiwords; if (!expandTerm(db, ermsg, mods, span, exp, sterm, prefix, &multiwords)) return; // Set up the highlight data. No prefix should go in there if (!m_exclude) { for (const auto& term : exp) { HighlightData::TermGroup tg; tg.term = term.substr(prefix.size()); tg.grpsugidx = m_hldata.ugroups.size() - 1; m_hldata.index_term_groups.push_back(tg); } } // Push either term or OR of stem-expanded set Xapian::Query xq(Xapian::Query::OP_OR, exp.begin(), exp.end()); m_curcl += exp.size(); // If sterm (simplified original user term) is not null, give it a // relevance boost. We do this even if no expansion occurred (else // the non-expanded terms in a term list would end-up with even // less wqf). This does not happen if there are wildcards anywhere // in the search. // We normally boost the original term in the stem expansion list. Don't // do it if there are wildcards anywhere, this would skew the results. Also // no need to do it if there was no expansion. bool doBoostUserTerm = (m_parentSearch && !m_parentSearch->haveWildCards()) || (nullptr == m_parentSearch && !m_haveWildCards); if (exp.size() > 1 && doBoostUserTerm && !sterm.empty()) { xq = Xapian::Query(Xapian::Query::OP_OR, xq, Xapian::Query(prefix+sterm, original_term_wqf_booster)); } // Push phrases for the multi-word expansions for (const auto& mw : multiwords) { vector phr; // We just do a basic split to keep things a bit simpler here // (no textsplit). This means though that no punctuation is // allowed in multi-word synonyms. stringToTokens(mw, phr); if (!prefix.empty()) prefix_vector(phr, prefix); xq = Xapian::Query(Xapian::Query::OP_OR, xq, Xapian::Query(Xapian::Query::OP_PHRASE, phr.begin(), phr.end())); m_curcl++; } pqueries.push_back(xq); } // User entry element had several terms: transform into a PHRASE or // NEAR xapian query, the elements of which can themselves be OR // queries if the terms get expanded by stemming or wildcards (we // don't do stemming for PHRASE though) void SearchDataClauseSimple::processPhraseOrNear( Rcl::Db &db, string& ermsg, TermProcQ *splitData, int mods, void *pq, bool useNear, int slack) { vector &pqueries(*(vector*)pq); Xapian::Query::op op = useNear ? Xapian::Query::OP_NEAR : Xapian::Query::OP_PHRASE; vector orqueries; vector >groups; bool useidxsynonyms = db.getSynGroups().getpath() == db.getConf()->getIdxSynGroupsFile(); string prefix; const FieldTraits *ftp; if (!m_field.empty() && db.fieldToTraits(m_field, &ftp, true)) { prefix = wrap_prefix(ftp->pfx); } if (mods & Rcl::SearchDataClause::SDCM_ANCHORSTART) { orqueries.push_back(Xapian::Query(prefix + start_of_field_term)); } // Go through the list and perform stem/wildcard expansion for each element auto nxit = splitData->nostemexps().begin(); for (auto it = splitData->terms().begin(); it != splitData->terms().end(); it++, nxit++) { LOGDEB0("ProcessPhrase: processing [" << *it << "]\n"); // Adjust when we do stem expansion. Not if disabled by caller, not inside phrases. bool nostemexp = *nxit || (op == Xapian::Query::OP_PHRASE && !o_expand_phrases && !(mods & Rcl::SearchDataClause::SDCM_EXPANDPHRASE)); int lmods = mods; if (nostemexp) lmods |= SearchDataClause::SDCM_NOSTEMMING; string sterm; vector exp; vector multiwords; if (!expandTerm(db, ermsg, lmods, *it, exp, sterm, prefix, &multiwords)) return; // Note: because of how expandTerm works, the multiwords can // only come from the synonyms expansion, which means that, if // idxsynonyms is set, they have each been indexed as a single // term. So, if idxsynonyms is set, and is the current active // synonyms file, we just add them to the expansion. if (!multiwords.empty() && useidxsynonyms) { exp.insert(exp.end(), multiwords.begin(), multiwords.end()); } LOGDEB0("ProcessPhraseOrNear: exp size " << exp.size() << ", exp: " << stringsToString(exp) << "\n"); // groups is used for highlighting, we don't want prefixes in there. vector noprefs; for (const auto& prefterm : exp) { noprefs.push_back(prefterm.substr(prefix.size())); } groups.push_back(noprefs); orqueries.push_back(Xapian::Query(Xapian::Query::OP_OR, exp.begin(), exp.end())); m_curcl += exp.size(); if (m_curcl >= getMaxCl()) return; } if (mods & Rcl::SearchDataClause::SDCM_ANCHOREND) { orqueries.push_back(Xapian::Query(prefix + end_of_field_term)); } // Generate an appropriate PHRASE/NEAR query with adjusted slack // For phrases, give a relevance boost like we do for original terms LOGDEB2("PHRASE/NEAR: alltermcount " << splitData->alltermcount() << " lastpos " << splitData->lastpos() << "\n"); Xapian::Query xq(op, orqueries.begin(), orqueries.end(), orqueries.size() + slack); if (op == Xapian::Query::OP_PHRASE) xq = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT, xq, original_term_wqf_booster); pqueries.push_back(xq); // Insert the search groups and slacks in the highlight data, with // a reference to the user entry that generated them: if (!m_exclude) { HighlightData::TermGroup tg; tg.orgroups = groups; tg.slack = slack; tg.grpsugidx = m_hldata.ugroups.size() - 1; tg.kind = (op == Xapian::Query::OP_PHRASE) ? HighlightData::TermGroup::TGK_PHRASE : HighlightData::TermGroup::TGK_NEAR; m_hldata.index_term_groups.push_back(tg); } } // Trim string beginning with ^ or ending with $ and convert to flags static int stringToMods(string& s) { int mods = 0; // Check for an anchored search trimstring(s); if (s.length() > 0 && s[0] == '^') { mods |= Rcl::SearchDataClause::SDCM_ANCHORSTART; s.erase(0, 1); } if (s.length() > 0 && s[s.length()-1] == '$') { mods |= Rcl::SearchDataClause::SDCM_ANCHOREND; s.erase(s.length()-1); } return mods; } /** * Turn user entry string (NOT raw query language, but possibly the contents of a phrase/near * clause out of the parser) into a list of Xapian queries. * We just separate words and phrases, and do wildcard and stem expansion, * * This is used to process data entered into an OR/AND/NEAR/PHRASE field of * the GUI (in the case of NEAR/PHRASE, clausedist adds dquotes to the user * entry). * * This appears awful, and it would seem that the split into * terms/phrases should be performed in the upper layer so that we * only receive pure term or near/phrase pure elements here, but in * fact there are things that would appear like terms to naive code, * and which will actually may be turned into phrases (ie: tom-jerry), * in a manner which intimately depends on the index implementation, * so that it makes sense to process this here. * * The final list contains one query for each term or phrase * - Elements corresponding to a stem-expanded part are an OP_OR * composition of the stem-expanded terms (or a single term query). * - Elements corresponding to phrase/near are an OP_PHRASE/NEAR * composition of the phrase terms (no stem expansion in this case) * @return the subquery count (either or'd stem-expanded terms or phrase word * count) */ bool SearchDataClauseSimple::processUserString( Rcl::Db &db, const string &iq, string &ermsg, void *pq, int slack0, bool useNear) { vector &pqueries(*(vector*)pq); int mods = m_modifiers; LOGDEB("StringToXapianQ:pUS:: qstr [" << iq << "] fld [" << m_field << "] mods 0x"< phrases; TextSplit::stringToStrings(iq, phrases); // Process each element: textsplit into terms, handle stem/wildcard // expansion and transform into an appropriate Xapian::Query try { for (auto& wordorphrase : phrases) { LOGDEB0("strToXapianQ: phrase/word: [" << wordorphrase << "]\n"); int slack = slack0; // Anchoring modifiers int amods = stringToMods(wordorphrase); int terminc = amods != 0 ? 1 : 0; mods |= amods; // If there are multiple spans in this element, including // at least one composite, we have to increase the slack // else a phrase query including a span would fail. // Ex: "term0@term1 term2" is onlyspans-split as: // 0 term0@term1 0 12 // 2 term2 13 18 // The position of term2 is 2, not 1, so a phrase search // would fail. // We used to do word split, searching for // "term0 term1 term2" instead, which may have worse // performance, but will succeed. // We now adjust the phrase/near slack by comparing the term count // and the last position // The term processing pipeline: // split -> [unac/case ->] stops -> store terms TermProcQ tpq; TermProc *nxt = &tpq; TermProcStop tpstop(nxt, stops); nxt = &tpstop; //TermProcCommongrams tpcommon(nxt, stops); nxt = &tpcommon; //tpcommon.onlygrams(true); TermProcPrep tpprep(nxt); if (o_index_stripchars) nxt = &tpprep; TextSplitQ splitter(TextSplit::Flags( TextSplit::TXTS_ONLYSPANS | TextSplit::TXTS_KEEPWILD), nxt); tpq.setTSQ(&splitter); splitter.text_to_words(wordorphrase); slack += tpq.lastpos() - int(tpq.terms().size()) + 1; LOGDEB0("strToXapianQ: termcount: " << tpq.terms().size() << "\n"); switch (tpq.terms().size() + terminc) { case 0: continue;// ?? case 1: { int lmods = mods; if (tpq.nostemexps().front()) lmods |= SearchDataClause::SDCM_NOSTEMMING; if (!m_exclude) { m_hldata.ugroups.push_back(tpq.terms()); } processSimpleSpan(db, ermsg, tpq.terms().front(), lmods, &pqueries); } break; default: if (!m_exclude) { m_hldata.ugroups.push_back(tpq.terms()); } processPhraseOrNear(db, ermsg, &tpq, mods, &pqueries, useNear, slack); } if (m_curcl >= getMaxCl()) { ermsg = maxXapClauseMsg; if (!o_index_stripchars) ermsg += maxXapClauseCaseDiacMsg; break; } } } catch (const Xapian::Error &e) { ermsg = e.get_msg(); } catch (const string &s) { ermsg = s; } catch (const char *s) { ermsg = s; } catch (...) { ermsg = "Caught unknown exception"; } if (!ermsg.empty()) { LOGERR("stringToXapianQueries: " << ermsg << "\n"); return false; } return true; } // Translate a simple OR or AND search clause. bool SearchDataClauseSimple::toNativeQuery(Rcl::Db &db, void *p) { LOGDEB("SearchDataClauseSimple::toNativeQuery: fld [" << m_field << "] val [" << m_text << "] stemlang [" << getStemLang() << "]\n"); // Transform (in)equalities into a range query switch (getrel()) { case REL_EQUALS: { SearchDataClauseRange cl(*this, gettext(), gettext()); bool ret = cl.toNativeQuery(db, p); m_reason = cl.getReason(); return ret; } case REL_LT: case REL_LTE: { SearchDataClauseRange cl(*this, "", gettext()); bool ret = cl.toNativeQuery(db, p); m_reason = cl.getReason(); return ret; } case REL_GT: case REL_GTE: { SearchDataClauseRange cl(*this, gettext(), ""); bool ret = cl.toNativeQuery(db, p); m_reason = cl.getReason(); return ret; } default: break; } Xapian::Query *qp = (Xapian::Query *)p; *qp = Xapian::Query(); Xapian::Query::op op; switch (m_tp) { case SCLT_AND: op = Xapian::Query::OP_AND; break; case SCLT_OR: op = Xapian::Query::OP_OR; break; default: LOGERR("SearchDataClauseSimple: bad m_tp " << m_tp << "\n"); m_reason = "Internal error"; return false; } vector pqueries; if (!processUserString(db, m_text, m_reason, &pqueries)) return false; if (pqueries.empty()) { LOGERR("SearchDataClauseSimple: resolved to null query\n"); m_reason = string("Resolved to null query. Term too long ? : [" + m_text + string("]")); return false; } *qp = Xapian::Query(op, pqueries.begin(), pqueries.end()); if (m_weight != 1.0) { *qp = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT, *qp, m_weight); } return true; } // Translate a range clause. This only works if a Xapian value slot // was attributed to the field. bool SearchDataClauseRange::toNativeQuery(Rcl::Db &db, void *p) { LOGDEB("SearchDataClauseRange::toNativeQuery: " << m_field << " :[" << m_text << ".." << m_t2 << "]\n"); Xapian::Query *qp = (Xapian::Query *)p; *qp = Xapian::Query(); if (m_field.empty() || (m_text.empty() && m_t2.empty())) { m_reason = "Range clause needs a field and a value"; return false; } // Get the value number for the field from the configuration const FieldTraits *ftp; if (!db.fieldToTraits(m_field, &ftp, true)) { m_reason = string("field ") + m_field + " not found in configuration"; return false; } if (ftp->valueslot == 0) { m_reason = string("No value slot specified in configuration for field ") + m_field; return false; } LOGDEB("SearchDataClauseRange: value slot " << ftp->valueslot << endl); // Build Xapian VALUE query. string errstr; try { if (m_text.empty()) { *qp = Xapian::Query(Xapian::Query::OP_VALUE_LE, ftp->valueslot, convert_field_value(*ftp, m_t2)); } else if (m_t2.empty()) { *qp = Xapian::Query(Xapian::Query::OP_VALUE_GE, ftp->valueslot, convert_field_value(*ftp, m_text)); } else { *qp = Xapian::Query(Xapian::Query::OP_VALUE_RANGE, ftp->valueslot, convert_field_value(*ftp, m_text), convert_field_value(*ftp, m_t2)); } } XCATCHERROR(errstr); if (!errstr.empty()) { LOGERR("SearchDataClauseRange: range query creation failed for slot "<valueslot<<"\n"); m_reason = "Range query creation failed\n"; *qp = Xapian::Query(); return false; } return true; } // Translate a FILENAME search clause. This always comes // from a "filename" search from the gui or recollq. A query language // "filename:"-prefixed field will not go through here, but through // the generic field-processing code. // // We do not split the entry any more (used to do some crazy thing // about expanding multiple fragments in the past). We just take the // value blanks and all and expand this against the indexed unsplit // file names bool SearchDataClauseFilename::toNativeQuery(Rcl::Db &db, void *p) { Xapian::Query *qp = (Xapian::Query *)p; *qp = Xapian::Query(); int maxexp = getSoftMaxExp(); if (maxexp == -1) maxexp = getMaxExp(); vector names; db.filenameWildExp(m_text, names, maxexp); *qp = Xapian::Query(Xapian::Query::OP_OR, names.begin(), names.end()); if (m_weight != 1.0) { *qp = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT, *qp, m_weight); } return true; } // Translate a dir: path filtering clause. See comments in .h bool SearchDataClausePath::toNativeQuery(Rcl::Db &db, void *p) { LOGDEB("SearchDataClausePath::toNativeQuery: [" << m_text << "]\n"); Xapian::Query *qp = (Xapian::Query *)p; *qp = Xapian::Query(); string ltext; #ifdef _WIN32 // Windows file names are case-insensitive, so we lowercase (same as when indexing) unacmaybefold(m_text, ltext, "UTF-8", UNACOP_FOLD); #else ltext = m_text; #endif if (ltext.empty()) { LOGERR("SearchDataClausePath: empty path??\n"); m_reason = "Empty path ?"; return false; } vector orqueries; if (path_isabsolute(ltext)) orqueries.push_back(Xapian::Query(wrap_prefix(pathelt_prefix))); else ltext = path_tildexpand(ltext); vector vpath; stringToTokens(ltext, vpath, "/"); for (const auto& pathelt : vpath) { string sterm; vector exp; if (!expandTerm( db, m_reason, SDCM_PATHELT, pathelt, exp, sterm, wrap_prefix(pathelt_prefix))) { return false; } LOGDEB0("SDataPath::toNative: exp size " << exp.size() << ". Exp: " << stringsToString(exp) << "\n"); if (exp.size() == 1) orqueries.push_back(Xapian::Query(exp[0])); else orqueries.push_back(Xapian::Query(Xapian::Query::OP_OR, exp.begin(), exp.end())); m_curcl += exp.size(); if (m_curcl >= getMaxCl()) return false; } *qp = Xapian::Query(Xapian::Query::OP_PHRASE, orqueries.begin(), orqueries.end()); if (m_weight != 1.0) { *qp = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT, *qp, m_weight); } return true; } // Translate NEAR or PHRASE clause. bool SearchDataClauseDist::toNativeQuery(Rcl::Db &db, void *p) { LOGDEB("SearchDataClauseDist::toNativeQuery\n"); Xapian::Query *qp = (Xapian::Query *)p; *qp = Xapian::Query(); vector pqueries; // We produce a single phrase out of the user entry then use processUserString() to lowercase // and simplify the phrase terms etc. This will result into a single (complex) Xapian::Query. if (m_text.find('\"') != string::npos) { m_text = neutchars(m_text, "\""); } string s = cstr_dquote + m_text + cstr_dquote; bool useNear = (m_tp == SCLT_NEAR); if (!useNear && !o_expand_phrases && !(m_modifiers & SDCM_EXPANDPHRASE)) { // We are a phrase query. Make sure to disable stemming explicitely in case this is a single // quoted word because processUserString won't see it as a phrase by itself. m_modifiers |= SDCM_NOSTEMMING; } if (!processUserString(db, s, m_reason, &pqueries, m_slack, useNear)) return false; if (pqueries.empty()) { LOGERR("SearchDataClauseDist: resolved to null query\n"); m_reason = string("Resolved to null query. Term too long ? : [" + m_text + string("]")); return false; } *qp = *pqueries.begin(); if (m_weight != 1.0) { *qp = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT, *qp, m_weight); } return true; } } // Namespace Rcl recoll-1.36.1/rcldb/rcldoc.cpp0000644000175000017500000001131514477552536013066 00000000000000/* Copyright (C) 2007-2018 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "rcldoc.h" #include "log.h" #include "rclutil.h" #include "cstr.h" using namespace std; namespace Rcl { const string Doc::keyabs("abstract"); const string Doc::keyapptg("rclaptg"); const string Doc::keyau("author"); const string Doc::keybcknd("rclbes"); const string Doc::keybght("beagleHitType"); const string Doc::keycc("collapsecount"); const string Doc::keychildurl("childurl"); const string Doc::keydmt("dmtime"); const string Doc::keyds("dbytes"); const string Doc::keyfmt("fmtime"); const string Doc::keyfn("filename"); const string Doc::keyctfn("containerfilename"); const string Doc::keyfs("fbytes"); const string Doc::keyipt("ipath"); const string Doc::keykw("keywords"); const string Doc::keymd5("md5"); const string Doc::keymt("mtime"); const string Doc::keyoc("origcharset"); const string Doc::keypcs("pcbytes"); const string Doc::keyrr("relevancyrating"); const string Doc::keysig("sig"); const string Doc::keysz("size"); const string Doc::keytp("mtype"); const string Doc::keytt("title"); const string Doc::keyudi("rcludi"); const string Doc::keyurl("url"); #ifdef EXT4_BIRTH_TIME const string Doc::keybrt("birtime"); #endif void Doc::dump(bool dotext) const { LOGDEB("Rcl::Doc::dump: url: [" << url << "]\n"); LOGDEB("Rcl::Doc::dump: idxurl: [" << idxurl << "]\n"); LOGDEB("Rcl::Doc::dump: ipath: [" << ipath << "]\n"); LOGDEB("Rcl::Doc::dump: mimetype: [" << mimetype << "]\n"); LOGDEB("Rcl::Doc::dump: fmtime: [" << fmtime << "]\n"); LOGDEB("Rcl::Doc::dump: dmtime: [" << dmtime << "]\n"); LOGDEB("Rcl::Doc::dump: origcharset: [" << origcharset << "]\n"); LOGDEB("Rcl::Doc::dump: syntabs: [" << syntabs << "]\n"); LOGDEB("Rcl::Doc::dump: pcbytes: [" << pcbytes << "]\n"); LOGDEB("Rcl::Doc::dump: fbytes: [" << fbytes << "]\n"); LOGDEB("Rcl::Doc::dump: dbytes: [" << dbytes << "]\n"); LOGDEB("Rcl::Doc::dump: sig: [" << sig << "]\n"); LOGDEB("Rcl::Doc::dump: pc: [" << pc << "]\n"); LOGDEB("Rcl::Doc::dump: xdocid: [" << (unsigned long)xdocid << "]\n"); for (const auto& e : meta) { LOGDEB("Rcl::Doc::dump: meta[" << e.first <<"]->["<< e.second << "]\n"); } if (dotext) LOGDEB("Rcl::Doc::dump: text: \n[" << text << "]\n"); } // Copy ensuring no shared string data, for threading issues. void Doc::copyto(Doc *d) const { d->url.assign(url.begin(), url.end()); d->idxurl.assign(idxurl.begin(), idxurl.end()); d->idxi = idxi; d->ipath.assign(ipath.begin(), ipath.end()); d->mimetype.assign(mimetype.begin(), mimetype.end()); d->fmtime.assign(fmtime.begin(), fmtime.end()); d->dmtime.assign(dmtime.begin(), dmtime.end()); d->origcharset.assign(origcharset.begin(), origcharset.end()); map_ss_cp_noshr(meta, &d->meta); d->syntabs = syntabs; d->pcbytes.assign(pcbytes.begin(), pcbytes.end()); d->fbytes.assign(fbytes.begin(), fbytes.end()); d->dbytes.assign(dbytes.begin(), dbytes.end()); d->sig.assign(sig.begin(), sig.end()); d->text.assign(text.begin(), text.end()); d->pc = pc; d->xdocid = xdocid; d->haspages = haspages; d->haschildren = haschildren; d->onlyxattr = onlyxattr; } bool docsToPaths(const vector &docs, vector &paths) { for (const auto& idoc : docs) { string backend; idoc.getmeta(Rcl::Doc::keybcknd, &backend); // This only makes sense for file system files: beagle docs are // always up to date because they can't be updated in the cache, // only added/removed. Same remark as made inside internfile, we // need a generic way to handle backends. if (!backend.empty() && backend.compare("FS")) continue; // Filesystem document. The url has to be like file:// if (!urlisfileurl(idoc.url)) { LOGERR("idx::docsToPaths: FS backend and non fs url: [" << idoc.url << "]\n"); continue; } paths.push_back(url_gpath(idoc.url)); } return true; } } recoll-1.36.1/rcldb/daterange.cpp0000644000175000017500000001057214427373216013545 00000000000000/* The dates-to-query routine is is lifted quasi-verbatim but * modified from xapian-omega:date.cc. Copyright info: * * Copyright 1999,2000,2001 BrightStation PLC * Copyright 2001 James Aylett * Copyright 2001,2002 Ananova Ltd * Copyright 2002 Intercede 1749 Ltd * Copyright 2002,2003,2006 Olly Betts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include "autoconfig.h" #include #include #include #include "log.h" #include "rclconfig.h" #include "smallut.h" #include "daterange.h" using namespace std; namespace Rcl { static inline void bufprefix(char *buf, string pre) { const char* prechar = pre.c_str(); if (o_index_stripchars) { for (size_t i= 0; i< pre.length(); i++) { buf[i] = prechar[i]; } } else { buf[0] = ':'; for (size_t i= 1; i< pre.length()+1; i++) { buf[i] = prechar[i]; } buf[pre.length()+1] = ':'; } } static inline int bpoffs(string pre) { return o_index_stripchars ? pre.length() : pre.length()+2; } static Xapian::Query anydate_range_filter( const std::string& daypref, const std::string& monpref, const std::string& yearpref, int y1, int m1, int d1, int y2, int m2, int d2) { // Xapian uses a smallbuf and snprintf. Can't be bothered, we're // doing at most 3 %d's ! char buf[200]; vector v; // Deal with days till the end of the first month if any bufprefix(buf, daypref); sprintf(buf + bpoffs(daypref), "%04d%02d", y1, m1); int d_last = monthdays(m1, y1); int d_end = d_last; if (y1 == y2 && m1 == m2 && d2 < d_last) { d_end = d2; } if (d1 > 1 || d_end < d_last) { for ( ; d1 <= d_end ; d1++) { sprintf(buf + 6 + bpoffs(daypref), "%02d", d1); v.push_back(Xapian::Query(buf)); } } else { bufprefix(buf, monpref); v.push_back(Xapian::Query(buf)); } if (y1 == y2 && m1 == m2) { return Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end()); } // Months till the end of first year int m_last = (y1 < y2) ? 12 : m2 - 1; bufprefix(buf, monpref); while (++m1 <= m_last) { sprintf(buf + 4 + bpoffs(monpref), "%02d", m1); v.push_back(Xapian::Query(buf)); } // Years inbetween and first months of the last year if (y1 < y2) { bufprefix(buf, yearpref); while (++y1 < y2) { sprintf(buf + bpoffs(yearpref), "%04d", y1); v.push_back(Xapian::Query(buf)); } bufprefix(buf, monpref); sprintf(buf + bpoffs(monpref), "%04d", y2); for (m1 = 1; m1 < m2; m1++) { sprintf(buf + 4 + bpoffs(monpref), "%02d", m1); v.push_back(Xapian::Query(buf)); } } // Last month sprintf(buf + 4 + bpoffs(monpref), "%02d", m2); // Deal with any final partial month if (d2 < monthdays(m2, y2)) { bufprefix(buf, daypref); for (d1 = 1 ; d1 <= d2; d1++) { sprintf(buf + 6 + bpoffs(daypref), "%02d", d1); v.push_back(Xapian::Query(buf)); } } else { bufprefix(buf, monpref); v.push_back(Xapian::Query(buf)); } return Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end()); } Xapian::Query date_range_filter(int y1, int m1, int d1, int y2, int m2, int d2) { return anydate_range_filter(xapday_prefix, xapmonth_prefix, xapyear_prefix, y1, m1, d1, y2, m2, d2); } #ifdef EXT4_BIRTH_TIME Xapian::Query brdate_range_filter(int y1, int m1, int d1, int y2, int m2, int d2) { return anydate_range_filter(xapbriday_prefix, xapbrimonth_prefix, xapbriyear_prefix, y1, m1, d1, y2, m2, d2); } #endif } recoll-1.36.1/rcldb/rclabstract.cpp0000644000175000017500000006016614427373216014123 00000000000000/* Copyright (C) 2004-2017 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include "log.h" #include "rcldb.h" #include "rcldb_p.h" #include "rclquery.h" #include "rclquery_p.h" #include "textsplit.h" #include "searchdata.h" #include "utf8iter.h" #include "hldata.h" #include "chrono.h" using namespace std; namespace Rcl { static Chrono chron; // This is used as a marker inside the abstract frag lists, but // normally doesn't remain in final output (which is built with a // custom sep. by our caller). static const string cstr_ellipsis("..."); static const string emptys; // This is used to mark positions overlapped by a multi-word match term static const string occupiedmarker("?"); #define DEBUGABSTRACT #ifdef DEBUGABSTRACT #define LOGABS LOGDEB #else #define LOGABS LOGDEB2 #endif // Unprefix terms. Actually it's not completely clear if we should // remove prefixes and keep all terms or prune the prefixed // ones. There is no good way to be sure what will provide the best // result in general. static const bool prune_prefixed_terms = true; static void noPrefixList(const vector& in, vector& out) { for (const auto& term : in) { if (prune_prefixed_terms) { if (has_prefix(term)) continue; } out.push_back(strip_prefix(term)); } sort(out.begin(), out.end()); vector::iterator it = unique(out.begin(), out.end()); out.resize(it - out.begin()); } bool Query::Native::getMatchTerms(unsigned long xdocid, vector& terms) { if (!xenquire) { LOGERR("Query::getMatchTerms: no query opened\n"); return false; } terms.clear(); Xapian::TermIterator it; Xapian::docid id = Xapian::docid(xdocid); vector iterms; XAPTRY(iterms.insert(iterms.begin(), xenquire->get_matching_terms_begin(id), xenquire->get_matching_terms_end(id)), m_q->m_db->m_ndb->xrdb, m_q->m_reason); if (!m_q->m_reason.empty()) { LOGERR("getMatchTerms: xapian error: " << m_q->m_reason << "\n"); return false; } noPrefixList(iterms, terms); return true; } // Retrieve db-wide frequencies for the query terms and store them in // the query object. This is done at most once for a query, and the data is used // while computing abstracts for the different result documents. void Query::Native::setDbWideQTermsFreqs() { // Do it once only for a given query. if (!termfreqs.empty()) return; vector qterms; { vector iqterms; m_q->getQueryTerms(iqterms); noPrefixList(iqterms, qterms); } LOGDEB("Query terms: " << stringsToString(qterms) << endl); Xapian::Database &xrdb = m_q->m_db->m_ndb->xrdb; double doccnt = xrdb.get_doccount(); if (doccnt == 0) doccnt = 1; for (const auto& term : qterms) { termfreqs[term] = xrdb.get_termfreq(term) / doccnt; LOGABS("setDbWideQTermFreqs: [" << term << "] db freq " << termfreqs[term] << "\n"); } } // Compute matched terms quality coefficients for a matched document by // retrieving the Within Document Frequencies and multiplying by // overal term frequency, then using log-based thresholds. // 2012: it's not too clear to me why exactly we do the log thresholds thing. // Preferring terms which are rare either or both in the db and the document // seems reasonable though // To avoid setting a high quality for a low frequency expansion of a // common stem, which seems wrong, we group the terms by // root, compute a frequency for the group from the sum of member // occurrences, and let the frequency for each group member be the // aggregated frequency. double Query::Native::qualityTerms(Xapian::docid docid, const vector& terms, multimap >& byQ) { LOGABS("qualityTerms: entry " << chron.millis() << "mS\n"); setDbWideQTermsFreqs(); LOGABS("qualityTerms: setDbWide..: " << chron.millis() << "mS\n"); map termQcoefs; double totalweight = 0; Xapian::Database &xrdb = m_q->m_db->m_ndb->xrdb; double doclen = xrdb.get_doclength(docid); if (doclen == 0) doclen = 1; HighlightData hld; if (m_q->m_sd) { m_q->m_sd->getTerms(hld); } LOGABS("qualityTerms: m_sd->getTerms(): " << chron.millis() << "mS\n"); // Group the input terms by the user term they were possibly // expanded from (by stemming) map > byRoot; for (const auto& term: terms) { const auto eit = hld.terms.find(term); if (eit != hld.terms.end()) { byRoot[eit->second].push_back(term); } else { LOGDEB0("qualityTerms: [" << term << "] not found in hld\n"); byRoot[term].push_back(term); } } #ifdef DEBUGABSTRACT { LOGABS("qualityTerms: hld: " << hld.toString() << "\n"); string byRootstr; for (const auto& entry : byRoot) { byRootstr.append("[").append(entry.first).append("]->"); for (const auto& term : entry.second) { byRootstr.append("[").append(term).append("] "); } byRootstr.append("\n"); } LOGABS("qualityTerms: uterms to terms: " << chron.millis() << "mS " << byRootstr << endl); } #endif // Compute in-document and global frequencies for the groups. We // used to call termlist_begin() for each term. This was very slow // on big documents and long term lists. We now compute a sorted // list of terms (with pointers back to their root through a map), // and just call skip_to repeatedly vector allterms; unordered_map toRoot; for (const auto& group : byRoot) { for (const auto& term : group.second) { allterms.push_back(term); toRoot[term] = group.first; } } sort(allterms.begin(), allterms.end()); allterms.erase(unique(allterms.begin(), allterms.end()), allterms.end()); map grpwdfs; map grptfreqs; Xapian::TermIterator xtermit = xrdb.termlist_begin(docid); for (const auto& term : allterms) { const string& root = toRoot[term]; xtermit.skip_to(term); if (xtermit != xrdb.termlist_end(docid) && *xtermit == term) { if (grpwdfs.find(root) != grpwdfs.end()) { grpwdfs[root] = xtermit.get_wdf() / doclen; grptfreqs[root] = termfreqs[term]; } else { grpwdfs[root] += xtermit.get_wdf() / doclen; grptfreqs[root] += termfreqs[term]; } } else { LOGDEB("qualityTerms: term not found in doc term list: " << term << endl); } } LOGABS("qualityTerms: freqs compute: " << chron.millis() << "mS\n"); // Build a sorted by quality container for the groups for (const auto& group : byRoot) { double q = (grpwdfs[group.first]) * grptfreqs[group.first]; q = -log10(q); if (q < 3) { q = 0.05; } else if (q < 4) { q = 0.3; } else if (q < 5) { q = 0.7; } else if (q < 6) { q = 0.8; } else { q = 1; } totalweight += q; byQ.insert(pair >(q, group.second)); } #ifdef DEBUGABSTRACT for (auto mit= byQ.rbegin(); mit != byQ.rend(); mit++) { LOGABS("qualityTerms: coef: " << mit->first << " group: " << stringsToString(mit->second) << endl); } #endif return totalweight; } // Choose most interesting term and return the page number for its first match int Query::Native::getFirstMatchPage(Xapian::docid docid, string& term) { LOGDEB("Query::Native::getFirstMatchPage\n"); chron.restart(); if (!m_q|| !m_q->m_db || !m_q->m_db->m_ndb || !m_q->m_db->m_ndb->m_isopen) { LOGERR("Query::getFirstMatchPage: no db\n"); return -1; } Rcl::Db::Native *ndb(m_q->m_db->m_ndb); Xapian::Database& xrdb(ndb->xrdb); vector terms; getMatchTerms(docid, terms); if (terms.empty()) { LOGDEB("getFirstMatchPage: empty match term list (field match?)\n"); return -1; } vector pagepos; ndb->getPagePositions(docid, pagepos); if (pagepos.empty()) return -1; setDbWideQTermsFreqs(); // We try to use a page which matches the "best" term. Get a sorted list multimap > byQ; qualityTerms(docid, terms, byQ); for (auto mit = byQ.rbegin(); mit != byQ.rend(); mit++) { for (const auto& qterm : mit->second) { Xapian::PositionIterator pos; string emptys; try { for (pos = xrdb.positionlist_begin(docid, qterm); pos != xrdb.positionlist_end(docid, qterm); pos++) { int pagenum = ndb->getPageNumberForPosition(pagepos, *pos); if (pagenum > 0) { term = qterm; return pagenum; } } } catch (...) { // Term does not occur. No problem. } } } return -1; } // Creating the abstract from index position data: populate the sparse // array with the positions for a given query term, and mark the // neighboring positions. void Query::Native::abstractPopulateQTerm( Xapian::Database& xrdb, Xapian::docid docid, const string& qterm, int qtrmwrdcnt, int ctxwords, unsigned int maxgrpoccs, unsigned int maxtotaloccs, map& sparseDoc, unordered_set& searchTermPositions, unsigned int& maxpos, unsigned int& totaloccs, unsigned int& grpoccs, int& ret ) { Xapian::PositionIterator pos; // Walk the position list for this term. for (pos = xrdb.positionlist_begin(docid, qterm); pos != xrdb.positionlist_end(docid, qterm); pos++) { int ipos = *pos; if (ipos < int(baseTextPosition)) // Not in text body continue; LOGABS("makeAbstract: [" << qterm << "] at pos " << ipos << " grpoccs " << grpoccs << " maxgrpoccs " << maxgrpoccs << "\n"); totaloccs++; grpoccs++; // Add adjacent slots to the set to populate at next // step by inserting empty strings. Special provisions // for adding ellipsis and for positions overlapped by // the match term. unsigned int sta = MAX(int(baseTextPosition), ipos - ctxwords); unsigned int sto = ipos + qtrmwrdcnt-1 + m_q->m_db->getAbsCtxLen(); for (unsigned int ii = sta; ii <= sto; ii++) { if (ii == (unsigned int)ipos) { sparseDoc[ii] = qterm; searchTermPositions.insert(ii); if (ii > maxpos) maxpos = ii; } else if (ii > (unsigned int)ipos && ii < (unsigned int)ipos + qtrmwrdcnt) { // Position for another word of the multi-word term sparseDoc[ii] = occupiedmarker; } else if (!sparseDoc[ii].compare(cstr_ellipsis)) { // For an empty slot, the test above has a side // effect of inserting an empty string which // is what we want. Do it also if it was an ellipsis sparseDoc[ii] = emptys; } } // Add ellipsis at the end. This may be replaced later by // an overlapping extract. Take care not to replace an // empty string here, we really want an empty slot, // use find() if (sparseDoc.find(sto+1) == sparseDoc.end()) { sparseDoc[sto+1] = cstr_ellipsis; } // Group done ? if (grpoccs >= maxgrpoccs) { ret |= ABSRES_TRUNC; LOGABS("Db::makeAbstract: max group occs cutoff\n"); break; } // Global done ? if (totaloccs >= maxtotaloccs) { ret |= ABSRES_TRUNC; LOGABS("Db::makeAbstract: max occurrences cutoff\n"); break; } } } // Creating the abstract from index position data: after the query // terms have been inserted at their place in the sparse array, and // the neighboring positions marked, populate the neighbours: for each // term in the document, walk its position list and populate slots // around the query terms. We arbitrarily truncate the list to avoid // taking forever. If we do cutoff, the abstract may be inconsistent // (missing words, potentially altering meaning), which is bad. void Query::Native::abstractPopulateContextTerms( Xapian::Database& xrdb, Xapian::docid docid, unsigned int maxpos, map& sparseDoc, int& ret ) { Xapian::TermIterator term; int cutoff = m_q->m_snipMaxPosWalk; for (term = xrdb.termlist_begin(docid); term != xrdb.termlist_end(docid); term++) { // Ignore prefixed terms if (has_prefix(*term)) continue; if (m_q->m_snipMaxPosWalk > 0 && cutoff-- < 0) { ret |= ABSRES_TERMMISS; LOGDEB0("makeAbstract: max term count cutoff " << m_q->m_snipMaxPosWalk << "\n"); break; } map::iterator vit; Xapian::PositionIterator pos; for (pos = xrdb.positionlist_begin(docid, *term); pos != xrdb.positionlist_end(docid, *term); pos++) { if (m_q->m_snipMaxPosWalk > 0 && cutoff-- < 0) { ret |= ABSRES_TERMMISS; LOGDEB0("makeAbstract: max term count cutoff " << m_q->m_snipMaxPosWalk << "\n"); break; } // If we are beyond the max possible position, stop // for this term if (*pos > maxpos) { break; } if ((vit = sparseDoc.find(*pos)) != sparseDoc.end()) { // Don't replace a term: the terms list is in // alphabetic order, and we may have several terms // at the same position, we want to keep only the // first one (ie: dockes and dockes@wanadoo.fr) if (vit->second.empty()) { LOGDEB2("makeAbstract: populating: [" << *term << "] at " << *pos << "\n"); sparseDoc[*pos] = *term; } } } } } // Creating the abstract from position data: final phase: extract the // snippets from the sparse array. void Query::Native::abstractCreateSnippetsVector( Rcl::Db::Native *ndb, map& sparseDoc, unordered_set& searchTermPositions, vector& vpbreaks, vector& vabs) { vabs.clear(); string chunk; bool incjk = false; int page = 0; string term; for (const auto& ent : sparseDoc) { LOGDEB2("Abtract:output "<< ent.first <<" -> [" <getPageNumberForPosition(vpbreaks, ent.first); if (page < 0) page = 0; term.clear(); } Utf8Iter uit(ent.second); bool newcjk = false; if (TextSplit::isNGRAMMED(*uit)) newcjk = true; if (!incjk || (incjk && !newcjk)) chunk += " "; incjk = newcjk; if (searchTermPositions.find(ent.first) != searchTermPositions.end()) term = ent.second; if (ent.second == cstr_ellipsis) { vabs.push_back(Snippet(page, chunk).setTerm(term)); chunk.clear(); } else { if (ent.second.compare(end_of_field_term) && ent.second.compare(start_of_field_term)) chunk += ent.second; } } if (!chunk.empty()) vabs.push_back(Snippet(page, chunk).setTerm(term)); } // Creating the abstract from index position data: top level routine int Query::Native::abstractFromIndex( Rcl::Db::Native *ndb, Xapian::docid docid, const vector&, const multimap> byQ, double totalweight, int ctxwords, unsigned int maxtotaloccs, vector& vabs, Chrono& chron ) { Xapian::Database& xrdb(ndb->xrdb); int ret = ABSRES_OK; // The terms 'array' that we partially populate with the document // terms, at their positions around the search terms positions: map sparseDoc; // Also remember apart the search term positions so that we can list // them with their snippets. std::unordered_set searchTermPositions; // Remember max position. Used to stop walking positions lists while // populating the adjacent slots. unsigned int maxpos = 0; // Total number of occurrences for all terms. We stop when we have too much unsigned int totaloccs = 0; // First pass to populate the sparse document: we walk the term // groups, beginning with the better ones, and insert each term at // its position. We also insert empty strings at the surrounding // positions. These are markers showing where we should insert // data during the next pass. for (auto mit = byQ.rbegin(); mit != byQ.rend(); mit++) { unsigned int maxgrpoccs; double q; if (byQ.size() == 1) { maxgrpoccs = maxtotaloccs; q = 1.0; } else { // We give more slots to the better term groups q = mit->first / totalweight; maxgrpoccs = int(std::ceil(maxtotaloccs * q)); } unsigned int grpoccs = 0; // For each term in user term expansion group for (const auto& qterm : mit->second) { // Enough for this group ? if (grpoccs >= maxgrpoccs) break; LOGABS("makeAbstract: [" << qterm << "] " << maxgrpoccs << " max grp occs (coef " << q << ")\n"); // The match term may span several words (more than one position) int qtrmwrdcnt = TextSplit::countWords(qterm, TextSplit::TXTS_NOSPANS); // Populate positions for this query term. // There may be query terms not in this doc. This raises an // exception when requesting the position list, we catch it ?? // Not clear how this can happen because we are walking the // match list returned by Xapian. Maybe something with the // fields? try { abstractPopulateQTerm(xrdb, docid, qterm, qtrmwrdcnt, ctxwords, maxgrpoccs,maxtotaloccs, sparseDoc, searchTermPositions, maxpos, totaloccs, grpoccs, ret); } catch (...) { // Term does not occur. No problem. } if (totaloccs >= maxtotaloccs) { ret |= ABSRES_TRUNC; LOGABS("Db::makeAbstract: max1 occurrences cutoff\n"); break; } } } maxpos += ctxwords + 1; LOGABS("makeAbstract:" << chron.millis() << "mS:chosen number of positions " << totaloccs << "\n"); // This can happen if there are term occurrences in the keywords // etc. but not elsewhere ? if (totaloccs == 0) { LOGDEB("makeAbstract: no occurrences\n"); return ABSRES_OK; } abstractPopulateContextTerms(xrdb, docid, maxpos, sparseDoc, ret); LOGABS("makeAbstract:" << chron.millis() << "mS: all term poslist read\n"); vector vpbreaks; ndb->getPagePositions(docid, vpbreaks); LOGABS("makeAbstract:" << chron.millis() << "mS: extracting. Got " << vpbreaks.size() << " pages\n"); // Finally build the abstract by walking the map (in order of position) abstractCreateSnippetsVector(ndb, sparseDoc, searchTermPositions, vpbreaks, vabs); LOGABS("makeAbtract: done in " << chron.millis() << " mS\n"); return ret; } // Build a document abstract by extracting text chunks around the // query terms. This can either uses the index position lists, or the // stored document text, with very different implementations. // // DatabaseModified and other general exceptions are caught and // possibly retried by our caller. // // @param[out] vabs the abstract is returned as a vector of snippets. int Query::Native::makeAbstract( Xapian::docid docid, vector& vabs, int imaxoccs, int ictxwords, bool sortbypage) { chron.restart(); LOGDEB("makeAbstract: docid " << docid << " imaxoccs " << imaxoccs << " ictxwords " << ictxwords << " sort by page " << sortbypage << "\n"); // The (unprefixed) terms matched by this document vector matchedTerms; getMatchTerms(docid, matchedTerms); if (matchedTerms.empty()) { LOGDEB("makeAbstract:" << chron.millis() << "mS:Empty term list\n"); return ABSRES_ERROR; } LOGDEB("Match terms: " << stringsToString(matchedTerms) << endl); // Retrieve the term frequencies for the query terms. This is // actually computed only once for a query, and for all terms in // the query (not only the matches for this doc) setDbWideQTermsFreqs(); // Build a sorted by quality container for the match terms We are // going to try and show text around the less common search terms. // Terms issued from an original one by stem expansion are // aggregated by the qualityTerms() routine (this is what we call // 'term groups' in the following: index terms expanded from the // same user term). multimap> byQ; double totalweight = qualityTerms(docid, matchedTerms, byQ); LOGABS("makeAbstract:" << chron.millis() << "mS: computed Qcoefs.\n"); // This can't happen, but would crash us if (totalweight == 0.0) { LOGERR("makeAbstract:"<m_db->m_ndb); // Total number of slots we populate. The 7 is taken as // average word size. It was a mistake to have the user max // abstract size parameter in characters, we basically only deal // with words. We used to limit the character size at the end, but // this damaged our careful selection of terms const unsigned int maxtotaloccs = imaxoccs > 0 ? imaxoccs : m_q->m_db->getAbsLen() /(7 * (m_q->m_db->getAbsCtxLen() + 1)); int ctxwords = ictxwords == -1 ? m_q->m_db->getAbsCtxLen() : ictxwords; LOGABS("makeAbstract:" << chron.millis() << "mS: mxttloccs " << maxtotaloccs << " ctxwords " << ctxwords << "\n"); if (ndb->m_storetext) { return abstractFromText(ndb, docid, matchedTerms, byQ, totalweight, ctxwords, maxtotaloccs, vabs, chron, sortbypage); } else { return abstractFromIndex(ndb, docid, matchedTerms, byQ, totalweight, ctxwords, maxtotaloccs, vabs, chron); } } } recoll-1.36.1/rcldb/stemdb.h0000644000175000017500000000707414427373216012541 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _STEMDB_H_INCLUDED_ #define _STEMDB_H_INCLUDED_ /** Stem database code * * Stem databases are key-data stores where the keys are the word stems and the data records are the * sets of index terms they expand to. * * Stem databases are stored as prefixed subtrees inside the Xapian synonyms table (other segments * in the table are used for separate term expansions, e.g. accenting and case). See synfamily.h * * It would probably be possible to store the expansions in the document term list instead (using a * prefix to distinguish the stem term). I tried this (chert, 08-2012) and the stem db creation is * very slightly slower than with the record approach, and the result is 50% bigger. * * For future reference, I also tried to store the map in a gdbm file and the result is bigger and * takes more time to create than the Xapian version. * * Stem databases are generated at the end of an indexing session by walking the whole index term * list, computing the stem for each term, and building a stem->terms map. * * Another possible approach would be to update the stem map as we index. This would probably be be * less efficient for a full index pass because each term would be seen and stemmed many times, but * it might be more efficient for an incremental pass with a limited number of updated * documents. For a small update, the stem building part often dominates the indexing time. * */ #include #include #include #include "synfamily.h" namespace Rcl { /* A stemming functor for using with XapComputableSynFamMember. * We could perform the change to lowercase in there too, as stemdb keys * must be lower case, but then the input conversion would be repeated for each * stemming language, which would be inefficient. So we let our caller make sure * that the input is lower-case */ class SynTermTransStem : public SynTermTrans { public: SynTermTransStem(const std::string& lang) : m_stemmer(lang), m_lang(lang) {} virtual ~SynTermTransStem() = default; virtual std::string operator()(const std::string& in) { std::string out = m_stemmer(in); LOGDEB2("SynTermTransStem(" << m_lang << "): in [" << in << "] out [" << out << "]\n"); return out; } Xapian::Stem m_stemmer; std::string m_lang; }; /** Stemdb is a bit special as a SynFamily as we may want to expand for one * or several members (languages) */ class StemDb : public XapSynFamily { public: StemDb(Xapian::Database& xdb) : XapSynFamily(xdb, synFamStem) {} /** Expand for a number of languages * @param langs space-separated set of languages * @param term term to expand */ bool stemExpand( const std::string& langs, const std::string& term, std::vector& result); }; } #endif /* _STEMDB_H_INCLUDED_ */ recoll-1.36.1/rcldb/stoplist.cpp0000644000175000017500000000343714427373216013476 00000000000000/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "log.h" #include "readfile.h" #include "unacpp.h" #include "smallut.h" #include "stoplist.h" using std::string; using std::set; namespace Rcl { bool StopList::setFile(const string &filename) { m_stops.clear(); string stoptext, reason; if (!file_to_string(filename, stoptext, &reason)) { LOGDEB0("StopList::StopList: file_to_string(" << filename << ") failed: " << reason << "\n"); return false; } set stops; stringToStrings(stoptext, stops); for (set::iterator it = stops.begin(); it != stops.end(); it++) { string dterm; unacmaybefold(*it, dterm, "UTF-8", UNACOP_UNACFOLD); m_stops.insert(dterm); } return true; } // Most sites will have an empty stop list. We try to optimize the // empty set case as much as possible. empty() is probably sligtly // faster than find() in this case. bool StopList::isStop(const string &term) const { return !m_stops.empty() && m_stops.find(term) != m_stops.end(); } } recoll-1.36.1/rcldb/rclquery_p.h0000644000175000017500000001002714427373216013440 00000000000000/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _rclquery_p_h_included_ #define _rclquery_p_h_included_ #include #include #include #include #include #include "rclquery.h" #include "smallut.h" class Chrono; namespace Rcl { class Query::Native { public: // The query I belong to Query *m_q{nullptr}; // query descriptor: terms and subqueries joined by operators // (or/and etc...) Xapian::Query xquery; // Open query descriptor. Xapian::Enquire *xenquire{nullptr}; // Partial result set Xapian::MSet xmset; // Term frequencies for current query. See makeAbstract, setQuery std::map termfreqs; Xapian::MatchDecider *subdecider{nullptr}; Native(Query *q) : m_q(q), xenquire(nullptr) {} ~Native() { clear(); } Native(const Native &) = delete; Native& operator=(const Native &) = delete; void clear() { deleteZ(xenquire); deleteZ(subdecider); termfreqs.clear(); } /** Return a list of terms which matched for a specific result document */ bool getMatchTerms(unsigned long xdocid, std::vector& terms); int makeAbstract(Xapian::docid id, std::vector&, int maxoccs, int ctxwords, bool sortbypage); int getFirstMatchPage(Xapian::docid docid, std::string& term); void setDbWideQTermsFreqs(); double qualityTerms(Xapian::docid docid, const std::vector& terms, std::multimap >& byQ); void abstractPopulateQTerm( Xapian::Database& xrdb, Xapian::docid docid, const std::string& qterm, int qtrmwrdcnt, int ctxwords, unsigned int maxgrpoccs, unsigned int maxtotaloccs, std::map& sparseDoc, std::unordered_set& searchTermPositions, unsigned int& maxpos, unsigned int& totaloccs, unsigned int& grpoccs, int& ret ); void abstractPopulateContextTerms( Xapian::Database& xrdb, Xapian::docid docid, unsigned int maxpos, std::map& sparseDoc, int& ret ); void abstractCreateSnippetsVector( Db::Native *ndb, std::map& sparseDoc, std::unordered_set& searchTermPositions, std::vector& vpbreaks, std::vector& vabs); int abstractFromIndex( Rcl::Db::Native *ndb, Xapian::docid docid, const std::vector& matchTerms, const std::multimap> byQ, double totalweight, int ctxwords, unsigned int maxtotaloccs, std::vector& vabs, Chrono& chron ); int abstractFromText( Rcl::Db::Native *ndb, Xapian::docid docid, const std::vector& matchTerms, const std::multimap> byQ, double totalweight, int ctxwords, unsigned int maxtotaloccs, std::vector& vabs, Chrono& chron, bool sortbypage ); }; } #endif /* _rclquery_p_h_included_ */ recoll-1.36.1/rcldb/expansiondbs.cpp0000644000175000017500000001325214427373216014306 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "expansiondbs.h" #include #include #include "log.h" #include "utf8iter.h" #include "smallut.h" #include "chrono.h" #include "textsplit.h" #include "xmacros.h" #include "rcldb.h" #include "rcldb_p.h" #include "stemdb.h" using namespace std; namespace Rcl { /** * Create all expansion dbs used to transform user input term to widen a query * We use Xapian synonyms subsets to store the expansions. */ bool createExpansionDbs(Xapian::WritableDatabase& wdb, const vector& langs) { LOGDEB("StemDb::createExpansionDbs: languages: " < stemdbs; // Note: tried to make this to work with stack-allocated objects, couldn't. // Looks like a bug in copy constructors somewhere, can't guess where vector > stemmers; for (unsigned int i = 0; i < langs.size(); i++) { stemmers.push_back(std::shared_ptr (new SynTermTransStem(langs[i]))); stemdbs.push_back( XapWritableComputableSynFamMember(wdb, synFamStem, langs[i], stemmers.back().get())); stemdbs.back().recreate(); } // Unaccented stem dbs vector unacstemdbs; // We can reuse the same stemmer pointers, the objects are stateless. if (!o_index_stripchars) { for (unsigned int i = 0; i < langs.size(); i++) { unacstemdbs.push_back( XapWritableComputableSynFamMember( wdb, synFamStemUnac, langs[i], stemmers.back().get())); unacstemdbs.back().recreate(); } } SynTermTransUnac transunac(UNACOP_UNACFOLD); XapWritableComputableSynFamMember diacasedb(wdb, synFamDiCa, "all", &transunac); if (!o_index_stripchars) diacasedb.recreate(); Xapian::TermIterator it = wdb.allterms_begin(); // We'd want to skip to the first non-prefixed term, but this is a bit // complicated, so we just jump over most of the prefixed term and then // skip the rest one by one. it.skip_to(wrap_prefix("Z")); for ( ;it != wdb.allterms_end(); it++) { const string term{*it}; if (has_prefix(term)) continue; // Detect and skip CJK terms. Utf8Iter utfit(term); if (utfit.eof()) // Empty term?? Seems to happen. continue; if (TextSplit::isCJK(*utfit)) { // LOGDEB("stemskipped: Skipping CJK\n"); continue; } string lower = term; // If the index is raw, compute the case-folded term which // is the input to the stem db, and add a synonym from the // stripped term to the cased and accented one, for accent // and case expansion at query time if (!o_index_stripchars) { unacmaybefold(term, lower, "UTF-8", UNACOP_FOLD); diacasedb.addSynonym(term); } // Dont' apply stemming to terms which don't look like // natural language words. if (!Db::isSpellingCandidate(term)) { LOGDEB1("createExpansionDbs: skipped: [" << term << "]\n"); continue; } // Create stemming synonym for every language. The input is the // lowercase accented term for (unsigned int i = 0; i < langs.size(); i++) { stemdbs[i].addSynonym(lower); } // For a raw index, also maybe create a stem expansion for // the unaccented term. While this may be incorrect, it is // also necessary for searching in a diacritic-unsensitive // way on a raw index if (!o_index_stripchars) { string unac; unacmaybefold(lower, unac, "UTF-8", UNACOP_UNAC); if (unac != lower) { for (unsigned int i = 0; i < langs.size(); i++) { unacstemdbs[i].addSynonym(unac); } } } } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("Db::createStemDb: map build failed: " << ermsg << "\n"); return false; } LOGDEB("StemDb::createExpansionDbs: done: " << cron.secs() << " S\n"); return true; } } recoll-1.36.1/rcldb/rclvalues.h0000644000175000017500000000223114410615043013236 00000000000000#ifndef _RCLVALUES_H_INCLUDED_ #define _RCLVALUES_H_INCLUDED_ /* Copyright (C) 2004-2018 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include namespace Rcl { extern void add_field_value(Xapian::Document& xdoc, const FieldTraits& ft, const std::string& data); extern std::string convert_field_value(const FieldTraits& ft, const std::string& data); } #endif /* _RCLVALUES_H_INCLUDED_ */ recoll-1.36.1/rcldb/stemdb.cpp0000644000175000017500000000535414410615043013060 00000000000000/* Copyright (C) 2005-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** * Management of the auxiliary databases listing stems and their expansion terms */ #include "autoconfig.h" #include "safeunistd.h" #include #include #include #include using namespace std; #include #include "stemdb.h" #include "log.h" #include "smallut.h" #include "synfamily.h" #include "unacpp.h" #include "rclconfig.h" namespace Rcl { /** * Expand for one or several languages */ bool StemDb::stemExpand(const std::string& langs, const std::string& _term, vector& result) { vector llangs; stringToStrings(langs, llangs); // The stemdb keys may have kept their diacritics or not but they are always lower-case. It // would be more logical for the term transformers to perform before doing the stemming, but // this would be inefficient when there are several stemming languages string term; unacmaybefold(_term, term, "UTF-8", UNACOP_FOLD); for (const auto& lang : llangs) { SynTermTransStem stemmer(lang); XapComputableSynFamMember expander(getdb(), synFamStem, lang, &stemmer); (void)expander.synExpand(term, result); } if (!o_index_stripchars) { string unac; unacmaybefold(term, unac, "UTF-8", UNACOP_UNAC); // Expand the unaccented stem, using the unaccented stem db. Because it's a different db, We // need to do it even if the input has no accent (unac == term) for (const auto& lang : llangs) { SynTermTransStem stemmer(lang); XapComputableSynFamMember expander(getdb(), synFamStemUnac, lang, &stemmer); (void)expander.synExpand(unac, result); } } if (result.empty()) result.push_back(term); sort(result.begin(), result.end()); auto uit = unique(result.begin(), result.end()); result.resize(uit - result.begin()); LOGDEB1("stemExpand:" << langs << ": " << term << " -> " << stringsToString(result) << "\n"); return true; } } recoll-1.36.1/rcldb/xmacros.h0000644000175000017500000000531414426500174012724 00000000000000/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _xmacros_h_included_ #define _xmacros_h_included_ // Generic Xapian exception catching code. We do this quite often, // and I have no idea how to do this except for a macro #define XCATCHERROR(MSG) \ catch (const Xapian::Error &e) { \ MSG = e.get_msg(); \ if (MSG.empty()) MSG = "Empty error message"; \ } catch (const std::string &s) { \ MSG = s; \ if (MSG.empty()) MSG = "Empty error message"; \ } catch (const char *s) { \ MSG = s; \ if (MSG.empty()) MSG = "Empty error message"; \ } catch (std::exception& ex) { \ MSG = std::string("Caught std::exception: ") + ex.what(); \ } catch (...) { \ MSG = std::string("Caught unknown exception??"); \ } #define XAPTRY(STMTTOTRY, XAPDB, ERSTR) \ for (int tries = 0; tries < 2; tries++) { \ try { \ STMTTOTRY; \ ERSTR.erase(); \ break; \ } catch (const Xapian::DatabaseModifiedError &e) { \ ERSTR = e.get_msg(); \ XAPDB.reopen(); \ continue; \ } XCATCHERROR(ERSTR); \ break; \ } #endif recoll-1.36.1/rcldb/rclterms.cpp0000644000175000017500000006427414444307651013455 00000000000000/* Copyright (C) 2004-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ //////////////////////////////////////////////////////////////////// /** Things dealing with walking the terms lists and expansion dbs */ #include "autoconfig.h" #include #include #include "log.h" #include "rcldb.h" #include "rcldb_p.h" #include "stemdb.h" #include "expansiondbs.h" #include "strmatcher.h" #include "pathut.h" #include "rcldoc.h" #include "syngroups.h" using namespace std; namespace Rcl { // File name wild card expansion. This is a specialisation ot termMatch bool Db::filenameWildExp(const string& fnexp, vector& names, int max) { string pattern = fnexp; names.clear(); // If pattern is not capitalized, not quoted (quoted pattern can't // get here currently anyway), and has no wildcards, we add * at // each end: match any substring if (pattern[0] == '"' && pattern[pattern.size()-1] == '"') { pattern = pattern.substr(1, pattern.size() -2); } else if (pattern.find_first_of(cstr_minwilds) == string::npos && !unaciscapital(pattern)) { pattern = "*" + pattern + "*"; } // else let it be LOGDEB("Rcl::Db::filenameWildExp: pattern: [" << pattern << "]\n"); // We inconditionnally lowercase and strip the pattern, as is done // during indexing. This seems to be the only sane possible // approach with file names and wild cards. termMatch does // stripping conditionally on indexstripchars. string pat1; if (unacmaybefold(pattern, pat1, "UTF-8", UNACOP_UNACFOLD)) { pattern.swap(pat1); } TermMatchResult result; if (!idxTermMatch(ET_WILD, pattern, result, max, unsplitFilenameFieldName)) return false; for (const auto& entry : result.entries) { names.push_back(entry.term); } if (names.empty()) { // Build an impossible query: we know its impossible because we // control the prefixes! names.push_back(wrap_prefix("XNONE") + "NoMatchingTerms"); } return true; } // Walk the Y terms and return min/max bool Db::maxYearSpan(int *minyear, int *maxyear) { LOGDEB("Rcl::Db:maxYearSpan\n"); *minyear = 1000000; *maxyear = -1000000; TermMatchResult result; if (!idxTermMatch(ET_WILD, "*", result, -1, "xapyear")) { LOGINFO("Rcl::Db:maxYearSpan: termMatch failed\n"); return false; } for (const auto& entry : result.entries) { if (!entry.term.empty()) { int year = atoi(strip_prefix(entry.term).c_str()); if (year < *minyear) *minyear = year; if (year > *maxyear) *maxyear = year; } } return true; } bool Db::getAllDbMimeTypes(std::vector& exp) { Rcl::TermMatchResult res; if (!idxTermMatch(ET_WILD, "*", res, -1, "mtype")) { return false; } for (const auto& entry : res.entries) { exp.push_back(Rcl::strip_prefix(entry.term)); } return true; } class TermMatchCmpByWcf { public: int operator()(const TermMatchEntry& l, const TermMatchEntry& r) { return r.wcf - l.wcf < 0; } }; class TermMatchCmpByTerm { public: int operator()(const TermMatchEntry& l, const TermMatchEntry& r) { return l.term.compare(r.term) > 0; } }; class TermMatchTermEqual { public: int operator()(const TermMatchEntry& l, const TermMatchEntry& r) { return !l.term.compare(r.term); } }; static const char *tmtptostr(int typ) { switch (typ) { case Db::ET_WILD: return "wildcard"; case Db::ET_REGEXP: return "regexp"; case Db::ET_STEM: return "stem"; case Db::ET_NONE: default: return "none"; } } // Find all index terms that match an input along different expansion modes: // wildcard, regular expression, or stemming. Depending on flags we perform // case and/or diacritics expansion (this can be the only thing requested). // If the "field" parameter is set, we return a list of appropriately // prefixed terms (which are going to be used to build a Xapian // query). // This routine performs case/diacritics/stemming expansion against // the auxiliary tables, and possibly calls idxTermMatch() for work // using the main index terms (filtering, retrieving stats, expansion // in some cases). bool Db::termMatch(int typ_sens, const string &lang, const string &_term, TermMatchResult& res, int max, const string& field, vector* multiwords) { int matchtyp = matchTypeTp(typ_sens); if (!m_ndb || !m_ndb->m_isopen) return false; Xapian::Database xrdb = m_ndb->xrdb; bool diac_sensitive = (typ_sens & ET_DIACSENS) != 0; bool case_sensitive = (typ_sens & ET_CASESENS) != 0; // Path elements (used for dir: filtering) are special because // they are not unaccented or lowercased even if the index is // otherwise stripped. bool pathelt = (typ_sens & ET_PATHELT) != 0; LOGDEB0("Db::TermMatch: typ " << tmtptostr(matchtyp) << " diacsens " << diac_sensitive << " casesens " << case_sensitive << " pathelt " << pathelt << " lang [" << lang << "] term [" << _term << "] max " << max << " field [" << field << "] stripped " << o_index_stripchars << " init res.size " << res.entries.size() << "\n"); // If the index is stripped, no case or diac expansion can be needed: // for the processing inside this routine, everything looks like // we're all-sensitive: no use of expansion db. // Also, convert input to lowercase and strip its accents. string term = _term; if (o_index_stripchars) { diac_sensitive = case_sensitive = true; if (!pathelt && !unacmaybefold(_term, term, "UTF-8", UNACOP_UNACFOLD)) { LOGERR("Db::termMatch: unac failed for [" << _term << "]\n"); return false; } } // The case/diac expansion db SynTermTransUnac unacfoldtrans(UNACOP_UNACFOLD); XapComputableSynFamMember synac(xrdb, synFamDiCa, "all", &unacfoldtrans); if (matchtyp == ET_WILD || matchtyp == ET_REGEXP) { std::unique_ptr matcher; if (matchtyp == ET_REGEXP) { matcher = std::make_unique(term); } else { matcher = std::make_unique(term); } if (!diac_sensitive || !case_sensitive) { // Perform case/diac expansion on the exp as appropriate and // expand the result. vector exp; if (diac_sensitive) { // Expand for diacritics and case, filtering for same diacritics SynTermTransUnac foldtrans(UNACOP_FOLD); synac.synKeyExpand(matcher.get(), exp, &foldtrans); } else if (case_sensitive) { // Expand for diacritics and case, filtering for same case SynTermTransUnac unactrans(UNACOP_UNAC); synac.synKeyExpand(matcher.get(), exp, &unactrans); } else { // Expand for diacritics and case, no filtering synac.synKeyExpand(matcher.get(), exp); } // Retrieve additional info and filter against the index itself for (const auto& term : exp) { idxTermMatch(ET_NONE, term, res, max, field); } // And also expand the original expression against the // main index: for the common case where the expression // had no case/diac expansion (no entry in the exp db if // the original term is lowercase and without accents). idxTermMatch(typ_sens, term, res, max, field); } else { idxTermMatch(typ_sens, term, res, max, field); } } else { // match_typ is either ET_STEM or ET_NONE (which may still need synonyms and case/diac exp) vector lexp; if (diac_sensitive && case_sensitive) { // No case/diac expansion lexp.push_back(term); } else if (diac_sensitive) { // Expand for accents and case, filtering for same accents, SynTermTransUnac foldtrans(UNACOP_FOLD); synac.synExpand(term, lexp, &foldtrans); } else if (case_sensitive) { // Expand for accents and case, filtering for same case SynTermTransUnac unactrans(UNACOP_UNAC); synac.synExpand(term, lexp, &unactrans); } else { // We are neither accent- nor case- sensitive and may need stem // expansion or not. Expand for accents and case synac.synExpand(term, lexp); } if (matchtyp == ET_STEM || (typ_sens & ET_SYNEXP)) { // Note: if any of the above conds is true, we are insensitive to // diacs and case (enforced in searchdatatox:termexpand // Need stem expansion. Lowercase the result of accent and case // expansion for input to stemdb. for (auto& term : lexp) { string lower; unacmaybefold(term, lower, "UTF-8", UNACOP_FOLD); term.swap(lower); } sort(lexp.begin(), lexp.end()); lexp.erase(unique(lexp.begin(), lexp.end()), lexp.end()); if (matchtyp == ET_STEM) { vector exp1; if (m_usingSpellFuzz) { // Apply spell expansion to the input term spellExpand(term, field, exp1); // Remember the generated terms. This is for informing the user. res.fromspelling.insert(res.fromspelling.end(), exp1.begin(), exp1.end()); lexp.insert(lexp.end(), exp1.begin(), exp1.end()); sort(lexp.begin(), lexp.end()); lexp.erase(unique(lexp.begin(), lexp.end()), lexp.end()); exp1.clear(); } StemDb sdb(xrdb); for (const auto& term : lexp) { sdb.stemExpand(lang, term, exp1); } exp1.swap(lexp); sort(lexp.begin(), lexp.end()); lexp.erase(unique(lexp.begin(), lexp.end()), lexp.end()); LOGDEB("Db::TermMatch: stemexp: " << stringsToString(lexp) << "\n"); } if (m_syngroups->ok() && (typ_sens & ET_SYNEXP)) { LOGDEB("Db::TermMatch: got syngroups\n"); vector exp1(lexp); for (const auto& term : lexp) { vector sg = m_syngroups->getgroup(term); if (!sg.empty()) { LOGDEB("Db::TermMatch: syngroups out: " << term << " -> " << stringsToString(sg) << "\n"); for (const auto& synonym : sg) { if (synonym.find(' ') != string::npos) { if (multiwords) { multiwords->push_back(synonym); } } else { exp1.push_back(synonym); } } } } lexp.swap(exp1); sort(lexp.begin(), lexp.end()); lexp.erase(unique(lexp.begin(), lexp.end()), lexp.end()); } // Expand the resulting list for case and diacritics (all // stemdb content is case-folded) vector exp1; for (const auto& term: lexp) { synac.synExpand(term, exp1); } exp1.swap(lexp); sort(lexp.begin(), lexp.end()); lexp.erase(unique(lexp.begin(), lexp.end()), lexp.end()); } // Filter the result against the index and get the stats, possibly add prefixes. LOGDEB0("Db::TermMatch: final lexp before idx filter: " << stringsToString(lexp) << "\n"); for (const auto& term : lexp) { idxTermMatch(Rcl::Db::ET_NONE, term, res, max, field); } } TermMatchCmpByTerm tcmp; sort(res.entries.begin(), res.entries.end(), tcmp); TermMatchTermEqual teq; vector::iterator uit = unique(res.entries.begin(), res.entries.end(), teq); res.entries.resize(uit - res.entries.begin()); TermMatchCmpByWcf wcmp; sort(res.entries.begin(), res.entries.end(), wcmp); if (max > 0) { // Would need a small max and big stem expansion... res.entries.resize(MIN(res.entries.size(), (unsigned int)max)); } return true; } void Db::spellExpand( const std::string& term, const std::string& field, std::vector& neighbours) { TermMatchResult matchResult; idxTermMatch(Rcl::Db::ET_NONE, term, matchResult, 1); if (!matchResult.entries.empty()) { // Term exists. If it is very rare, try to expand query. auto totlen = m_ndb->xrdb.get_total_length(); auto wcf = matchResult.entries[0].wcf; if (wcf == 0) // ?? wcf += 1; auto rarity = int(totlen / wcf); LOGDEB1("Db::spellExpand: rarity for [" << term << "] " << rarity << "\n"); if (rarity < m_autoSpellRarityThreshold) { LOGDEB0("Db::spellExpand: [" << term << "] is not rare: " << rarity << "\n"); return; } vector suggs; TermMatchResult locres1; if (getSpellingSuggestions(term, suggs) && !suggs.empty()) { LOGDEB0("Db::spellExpand: spelling suggestions for [" << term << "] : [" << stringsToString(suggs) << "]\n"); // Only use spelling suggestions up to the chosen maximum distance for (int i = 0; i < int(suggs.size()) && i < 300;i++) { auto d = u8DLDistance(suggs[i], term); LOGDEB0("Db::spellExpand: spell: " << term << " -> " << suggs[i] << " distance " << d << " (max " << m_maxSpellDistance << ")\n"); if (d <= m_maxSpellDistance) { idxTermMatch(Rcl::Db::ET_NONE, suggs[i], locres1, 1); } } // Only use spelling suggestions which are more frequent than the term by a set ratio if (!locres1.entries.empty()) { TermMatchCmpByWcf wcmp; sort(locres1.entries.begin(), locres1.entries.end(), wcmp); for (int i = 0; i < int(locres1.entries.size()); i++) { double freqratio = locres1.entries[i].wcf / wcf; LOGDEB0("Db::spellExpand: freqratio for [" << locres1.entries[i].term << "] : " << freqratio <<"\n"); if (locres1.entries[i].wcf > m_autoSpellSelectionThreshold * wcf) { LOGDEB0("Db::spellExpand: [" << locres1.entries[i].term << "] selected (frequent enough)\n"); neighbours.push_back(locres1.entries[i].term); } else { LOGDEB0("Db::spellExpand: [" << locres1.entries[i].term << "] rejected (not frequent enough)\n"); break; } } } } else { LOGDEB("Db::spellExpand: no spelling suggestions for [" << term << "]\n"); } } else { // If the expansion list is empty, the term is not in the index. Maybe try to use aspell // for a close one ? vector suggs; if (getSpellingSuggestions(term, suggs) && !suggs.empty()) { LOGDEB0("Db::spellExpand: spelling suggestions for [" << term << "] : [" << stringsToString(suggs) << "]\n"); for (int i = 0; i < int(suggs.size()) && i < 300;i++) { auto d = u8DLDistance(suggs[i], term); LOGDEB0("Db::spellExpand: spell: " << term << " -> " << suggs[i] << " distance " << d << " (max " << m_maxSpellDistance << ")\n"); if (d <= m_maxSpellDistance) { neighbours.push_back(suggs[i]); } } } else { LOGDEB0("Db::spellExpand: no spelling suggestions for [" << term << "]\n"); } } } bool Db::Native::idxTermMatch_p(int typ, const string& expr, const std::string& prefix, std::function client) { LOGDEB1("idxTermMatch_p: input expr: [" << expr << "] prefix [" << prefix << "]\n"); Xapian::Database xdb = xrdb; std::unique_ptr matcher; if (typ == ET_REGEXP) { matcher = std::make_unique(expr); if (!matcher->ok()) { LOGERR("termMatch: regcomp failed: " << matcher->getreason()); return false; } } else if (typ == ET_WILD) { matcher = std::make_unique(expr); } // Initial section: the part of the prefix+expr before the // first wildcard character. We only scan the part of the // index where this matches string is; if (matcher) { string::size_type es = matcher->baseprefixlen(); is = prefix + expr.substr(0, es); } else { is = prefix + expr; } LOGDEB1("idxTermMatch_p: initial section: [" << is << "]\n"); XAPTRY( Xapian::TermIterator it = xdb.allterms_begin(is); for (; it != xdb.allterms_end(); it++) { const string ixterm{*it}; LOGDEB1("idxTermMatch_p: term at skip [" << ixterm << "]\n"); // If we're beyond the terms matching the initial section, end. if (!is.empty() && ixterm.find(is) != 0) { LOGDEB1("idxTermMatch_p: initial section mismatch: stop\n"); break; } // Else try to match the term. The matcher content is prefix-less. string term; if (!prefix.empty()) { term = ixterm.substr(prefix.length()); } else { if (has_prefix(ixterm)) { // This is possible with a left-side wildcard which would have an empty // initial section so that we're scanning the whole term list. continue; } term = ixterm; } // If matching an expanding expression, a mismatch does not stop us. Else we want // equality if (matcher) { if (!matcher->match(term)) { continue; } } else if (term != expr) { break; } if (!client(ixterm, xdb.get_collection_freq(ixterm), it.get_termfreq()) || !matcher) { // If the client tells us or this is an exact search, stop. break; } }, xdb, m_rcldb->m_reason); if (!m_rcldb->m_reason.empty()) { LOGERR("termMatch: " << m_rcldb->m_reason << "\n"); return false; } return true; } // Second phase of wildcard/regexp term expansion after case/diac // expansion: expand against main index terms bool Db::idxTermMatch(int typ_sens, const string &expr, TermMatchResult& res, int max, const string& field) { int typ = matchTypeTp(typ_sens); LOGDEB1("Db::idxTermMatch: typ " << tmtptostr(typ) << "] expr [" << expr << "] max " << max << " field [" << field << "] init res.size " << res.entries.size() << "\n"); if (typ == ET_STEM) { LOGFATAL("RCLDB: internal error: idxTermMatch called with ET_STEM\n"); abort(); } string prefix; if (!field.empty()) { const FieldTraits *ftp = nullptr; if (!fieldToTraits(field, &ftp, true) || ftp->pfx.empty()) { LOGDEB("Db::termMatch: field is not indexed (no prefix): [" << field << "]\n"); } else { prefix = wrap_prefix(ftp->pfx); } } res.prefix = prefix; int rcnt = 0; bool dostrip = res.m_prefix_stripped; bool ret = m_ndb->idxTermMatch_p( typ, expr, prefix, [&res, &rcnt, max, dostrip](const string& term, Xapian::termcount cf, Xapian::doccount tf) { res.entries.push_back(TermMatchEntry((dostrip?strip_prefix(term):term), cf, tf)); // The problem with truncating here is that this is done alphabetically and we may not // keep the most frequent terms. OTOH, not doing it may stall the program if we are // walking the whole term list. We compromise by cutting at 2*max if (max > 0 && ++rcnt >= 2*max) return false; return true; }); return ret; } // Compute list of directories in index at given depth under the common root (which we also compute) // This is used for the GUI directory side filter tree. // // This is more complicated than it seems because there could be extra_dbs so we can't use topdirs // (which we did with an fstreewalk() initially), and also inode/directory might be excluded from // the index (for example by an onlymimetypes parameter). // // We look at all the paths, compute a common prefix, and truncate at the given depth under the // prefix and insert into an std::unordered_set for deduplication. // // unordered_set was the (slightly) fastest of "insert all then sort and truncate", std::set, // std::unordered_set. Other approaches may exist, for example, by skipping same prefix in the list // (which is sorted). Did not try, as the current approach is reasonably fast. // // This is admittedly horrible, and might be too slow on very big indexes, or actually fail if the // requested depth is such that we reach the max term length and the terms are // truncated-hashed. We'd need to look at the doc data for the full URLs, but this would be much // slower. // // Still I have no other idea of how to do this, other than disable the side filter if directories // are not indexed? // // We could use less memory by not computing a full list and walking the index twice instead (we // need two passes in any case because of the common root computation). // bool Db::dirlist(int depth, std::string& root, std::vector& dirs) { // Build a full list of filesystem paths. Xapian::Database xdb = m_ndb->xrdb; auto prefix = wrap_prefix("Q"); std::vector listall; for (int tries = 0; tries < 2; tries++) { try { Xapian::TermIterator it = xdb.allterms_begin(); it.skip_to(prefix.c_str()); for (; it != xdb.allterms_end(); it++) { string ixterm{*it}; // If we're beyond the Q terms end if (ixterm.find(prefix) != 0) break; ixterm = strip_prefix(ixterm); // Skip non-paths like Web entries etc. if (!path_isabsolute(ixterm)) continue; // Skip subdocs auto pos = ixterm.find_first_of('|'); if (pos < ixterm.size() - 1) continue; listall.push_back(ixterm); } break; } catch (const Xapian::DatabaseModifiedError &e) { m_reason = e.get_msg(); xdb.reopen(); continue; } XCATCHERROR(m_reason); break; } if (!m_reason.empty()) { LOGERR("Db::dirlist: exception while accessing index: " << m_reason << "\n"); return false; } root = commonprefix(listall); std::unordered_set unics; for (auto& entry : listall) { string::size_type pos = root.size(); for (int i = 0; i < depth; i++) { auto npos = entry.find("/", pos+1); if (npos == std::string::npos) { break; } pos = npos; } entry.erase(pos); unics.insert(entry); } dirs.clear(); dirs.insert(dirs.begin(), unics.begin(), unics.end()); sort(dirs.begin(), dirs.end()); return true; } /** Term list walking. */ class TermIter { public: Xapian::TermIterator it; Xapian::Database db; }; TermIter *Db::termWalkOpen() { if (!m_ndb || !m_ndb->m_isopen) return nullptr; TermIter *tit = new TermIter; if (tit) { tit->db = m_ndb->xrdb; XAPTRY(tit->it = tit->db.allterms_begin(), tit->db, m_reason); if (!m_reason.empty()) { LOGERR("Db::termWalkOpen: xapian error: " << m_reason << "\n"); return nullptr; } } return tit; } bool Db::termWalkNext(TermIter *tit, string &term) { XAPTRY( if (tit && tit->it != tit->db.allterms_end()) { term = *(tit->it)++; return true; } , tit->db, m_reason); if (!m_reason.empty()) { LOGERR("Db::termWalkOpen: xapian error: " << m_reason << "\n"); } return false; } void Db::termWalkClose(TermIter *tit) { try { delete tit; } catch (...) {} } bool Db::termExists(const string& word) { if (!m_ndb || !m_ndb->m_isopen) return 0; XAPTRY(if (!m_ndb->xrdb.term_exists(word)) return false, m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::termWalkOpen: xapian error: " << m_reason << "\n"); return false; } return true; } bool Db::stemDiffers(const string& lang, const string& word, const string& base) { Xapian::Stem stemmer(lang); if (!stemmer(word).compare(stemmer(base))) { LOGDEB2("Rcl::Db::stemDiffers: same for " << word << " and " << base << "\n"); return false; } return true; } } // End namespace Rcl recoll-1.36.1/rcldb/searchdataxml.cpp0000644000175000017500000001166114410615043014420 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Handle translation from rcl's SearchData structures to XML. Used for // complex search history storage in the GUI #include "autoconfig.h" #include #include #include #include #include "searchdata.h" #include "log.h" #include "base64.h" using namespace std; namespace Rcl { static string tpToString(SClType tp) { switch (tp) { case SCLT_AND: return "AND"; case SCLT_OR: return "OR"; case SCLT_FILENAME: return "FN"; case SCLT_PHRASE: return "PH"; case SCLT_NEAR: return "NE"; case SCLT_RANGE: return "RG"; case SCLT_SUB: return "SU"; // Unsupported actually default: return "UN"; } } string SearchData::asXML() { LOGDEB("SearchData::asXML\n" ); ostringstream os; // Searchdata os << "" << endl; // Clause list os << "" << endl; // List conjunction: default is AND, else print it. if (m_tp != SCLT_AND) os << "" << tpToString(m_tp) << "" << endl; for (unsigned int i = 0; i < m_query.size(); i++) { SearchDataClause *c = m_query[i]; if (c->getTp() == SCLT_SUB) { LOGERR("SearchData::asXML: can't do subclauses !\n" ); continue; } if (c->getTp() == SCLT_PATH) { // Keep these apart, for compat with the older history format. NEG // is ignored here, we have 2 different tags instead. SearchDataClausePath *cl = dynamic_cast(c); if (cl->getexclude()) { os << "" << base64_encode(cl->gettext()) << "" << endl; } else { os << "" << base64_encode(cl->gettext()) << "" << endl; } continue; } else { os << "" << endl; if (c->getexclude()) os << "" << endl; if (c->getTp() != SCLT_AND) { os << "" << tpToString(c->getTp()) << "" << endl; } if (c->getTp() == SCLT_FILENAME) { SearchDataClauseFilename *cl = dynamic_cast(c); os << "" << base64_encode(cl->gettext()) << "" << endl; } else { SearchDataClauseSimple *cl = dynamic_cast(c); if (!cl->getfield().empty()) { os << "" << base64_encode(cl->getfield()) << "" << endl; } os << "" << base64_encode(cl->gettext()) << "" << endl; if (cl->getTp() == SCLT_RANGE) { SearchDataClauseRange *clr = dynamic_cast(cl); const string& t = clr->gettext2(); if (!t.empty()) { os << "" << base64_encode(clr->gettext2()) << "" << endl; } } if (cl->getTp() == SCLT_NEAR || cl->getTp() == SCLT_PHRASE) { SearchDataClauseDist *cld = dynamic_cast(cl); os << "" << cld->getslack() << "" << endl; } } os << "" << endl; } } os << "" << endl; if (m_haveDates) { if (m_dates.y1 > 0) { os << "" << "" << m_dates.d1 << "" << "" << m_dates.m1 << "" << "" << m_dates.y1 << "" << "" << endl; } if (m_dates.y2 > 0) { os << "" << "" << m_dates.d2 << "" << "" << m_dates.m2 << "" << "" << m_dates.y2 << "" << "" << endl; } } if (m_minSize != -1) { os << "" << m_minSize << "" << endl; } if (m_maxSize != -1) { os << "" << m_maxSize << "" << endl; } if (!m_filetypes.empty()) { os << ""; for (vector::iterator it = m_filetypes.begin(); it != m_filetypes.end(); it++) { os << *it << " "; } os << "" << endl; } if (!m_nfiletypes.empty()) { os << ""; for (vector::iterator it = m_nfiletypes.begin(); it != m_nfiletypes.end(); it++) { os << *it << " "; } os << "" << endl; } os << ""; return os.str(); } } recoll-1.36.1/rcldb/rclquery.h0000644000175000017500000001245214427373216013125 00000000000000/* Copyright (C) 2008 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _rclquery_h_included_ #define _rclquery_h_included_ #include #include #include class PlainToRich; namespace Rcl { class Db; class Doc; class SearchData; enum abstract_result { ABSRES_ERROR = 0, ABSRES_OK = 1, ABSRES_TRUNC = 2, ABSRES_TERMMISS = 4 }; // Snippet data out of makeDocAbstract class Snippet { public: Snippet(int page, const std::string& snip, int ln = 0) : page(page), snippet(snip), line(ln) {} Snippet& setTerm(const std::string& trm) { term = trm; return *this; } int page{0}; std::string snippet; int line{0}; // This is the "best term" in the fragment, as determined by the qualityTerms() coef. It's only // used as a search term when launching an external app. We don't even try to use NEAR/PHRASE // groups for this, there are many cases where this would fail. std::string term; }; /** * An Rcl::Query is a question (SearchData) applied to a * database. Handles access to the results. Somewhat equivalent to a * cursor in an rdb. * */ class Query { public: Query(Db *db); ~Query(); Query(const Query &) = delete; Query& operator=(const Query &) = delete; /** Get explanation about last error */ std::string getReason() const { return m_reason; } /** Choose sort order. Must be called before setQuery */ void setSortBy(const std::string& fld, bool ascending = true); const std::string& getSortBy() const { return m_sortField; } bool getSortAscending() const { return m_sortAscending; } /** Return or filter results with identical content checksum */ void setCollapseDuplicates(bool on) { m_collapseDuplicates = on; } /** Accept data describing the search and query the index. This can * be called repeatedly on the same object which gets reinitialized each * time. */ bool setQuery(std::shared_ptr q); /** Get results count for current query. * * @param useestimate Use get_matches_estimated() if true, else * get_matches_lower_bound() * @param checkatleast checkatleast parameter to get_mset(). Use -1 for * full scan. */ int getResCnt(int checkatleast=1000, bool useestimate=false); /** Get document at rank i in current query results. */ bool getDoc(int i, Doc &doc, bool fetchtext = false); /** Get possibly expanded list of query terms */ bool getQueryTerms(std::vector& terms); /** Build synthetic abstract for document, extracting chunks relevant for * the input query. This uses index data only (no access to the file) */ // Returned as a vector of Snippet objects int makeDocAbstract(const Doc &doc, PlainToRich *plaintorich, std::vector& abstract, int maxoccs= -1, int ctxwords= -1,bool sortbypage=false); // Returned as a vector of text snippets. This just calls the above with default parameters and // does a bit of formatting (page/line numbers if applicable). bool makeDocAbstract(const Doc &doc, PlainToRich *plaintorich, std::vector& abstract); /** Choose most interesting term and return the page number for its first match * @param term returns the chosen term * @return page number or -1 if term not found or other issue */ int getFirstMatchPage(const Doc &doc, std::string& term); /** Compute line number for first match of term. Only works if doc.text has text. * This uses a text split. Both this and the above getFirstMaxPage() could be done and saved * while we compute the abstracts, quite a lot of waste here. */ int getFirstMatchLine(const Doc &doc, const std::string& term); /** Retrieve a reference to the searchData we are using */ std::shared_ptr getSD() { return m_sd; } /** Expand query to look for documents like the one passed in */ std::vector expand(const Doc &doc); /** Return the Db we're set for */ Db *whatDb() const { return m_db; } /* make this public for access from embedded Db::Native */ class Native; Native *m_nq; private: std::string m_reason; // Error explanation Db *m_db; void *m_sorter{nullptr}; std::string m_sortField; bool m_sortAscending{true}; bool m_collapseDuplicates{false}; int m_resCnt{-1}; std::shared_ptr m_sd; int m_snipMaxPosWalk{1000000}; }; } #endif /* _rclquery_h_included_ */ recoll-1.36.1/rcldb/rclvalues.cpp0000644000175000017500000000506314410615043013577 00000000000000/* Copyright (C) 2004-2018 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include "xapian.h" #include "rclconfig.h" #include "smallut.h" #include "log.h" #include "unacpp.h" using namespace std; namespace Rcl { void add_field_value(Xapian::Document& xdoc, const FieldTraits& ft, const string& data) { string ndata; switch (ft.valuetype) { case FieldTraits::STR: if (o_index_stripchars) { if (!unacmaybefold(data, ndata, "UTF-8", UNACOP_UNACFOLD)) { LOGDEB("Rcl::add_field_value: unac failed for ["< #include #include #include #include #ifdef IDX_THREADS #include "workqueue.h" #endif // IDX_THREADS #include "xmacros.h" #include "log.h" #include "rclconfig.h" #include "cstr.h" namespace Rcl { class Query; inline bool has_prefix(const std::string& trm) { if (o_index_stripchars) { return !trm.empty() && 'A' <= trm[0] && trm[0] <= 'Z'; } else { return !trm.empty() && trm[0] == ':'; } } inline std::string strip_prefix(const std::string& trm) { if (!has_prefix(trm)) return trm; std::string::size_type st = 0; if (o_index_stripchars) { st = trm.find_first_not_of("ABCDEFIJKLMNOPQRSTUVWXYZ"); #ifdef _WIN32 // We have a problem there because we forgot to lowercase the drive // name. So if the found character is a colon consider the drive name as // the first non capital even if it is uppercase if (st != std::string::npos && st >= 2 && trm[st] == ':') { st -= 1; } #endif } else { st = trm.find_first_of(":", 1) + 1; } if (st == std::string::npos) { return std::string(); // ?? } return trm.substr(st); } inline std::string get_prefix(const std::string& trm) { if (!has_prefix(trm)) return std::string(); std::string::size_type st = 0; if (o_index_stripchars) { st = trm.find_first_not_of("ABCDEFIJKLMNOPQRSTUVWXYZ"); if (st == std::string::npos) { return std::string(); // ?? } #ifdef _WIN32 // We have a problem there because we forgot to lowercase the drive // name. So if the found character is a colon consider the drive name as // the first non capital even if it is uppercase if (st >= 2 && trm[st] == ':') { st -= 1; } #endif return trm.substr(0, st); } else { st = trm.find_first_of(":", 1) + 1; if (st == std::string::npos) { return std::string(); // ?? } return trm.substr(1, st-2); } } inline std::string wrap_prefix(const std::string& pfx) { if (o_index_stripchars) { return pfx; } else { return cstr_colon + pfx + cstr_colon; } } #ifdef IDX_THREADS // Task for the index update thread. This can be // - add/update for a new / update document // - delete for a deleted document // - purgeOrphans when a multidoc file is updated during a partial pass (no // general purge). We want to remove subDocs that possibly don't // exist anymore. We find them by their different sig // txtlen and doc are only valid for add/update else, len is (size_t)-1 and doc // is empty class DbUpdTask { public: enum Op {AddOrUpdate, Delete, PurgeOrphans}; // Note that udi and uniterm are strictly equivalent and are // passed both just to avoid recomputing uniterm which is // available on the caller site. // Take some care to avoid sharing string data (if string impl is cow) DbUpdTask(Op _op, const std::string& ud, const std::string& un, Xapian::Document *d, size_t tl, std::string& rztxt) : op(_op), udi(ud.begin(), ud.end()), uniterm(un.begin(), un.end()), doc(d), txtlen(tl) { rawztext.swap(rztxt); } // Udi and uniterm equivalently designate the doc Op op; std::string udi; std::string uniterm; Xapian::Document *doc; // txtlen is used to update the flush interval. It's -1 for a // purge because we actually don't know it, and the code fakes a // text length based on the term count. size_t txtlen; std::string rawztext; // Compressed doc text }; #endif // IDX_THREADS class TextSplitDb; // A class for data and methods that would have to expose // Xapian-specific stuff if they were in Rcl::Db. There could actually be // 2 different ones for indexing or query as there is not much in // common. class Db::Native { public: Db *m_rcldb; // Parent bool m_isopen{false}; bool m_iswritable{false}; bool m_noversionwrite{false}; //Set if open failed because of version mismatch! bool m_storetext{false}; #ifdef IDX_THREADS WorkQueue m_wqueue; std::mutex m_mutex; long long m_totalworkns{0}; bool m_havewriteq{false}; void maybeStartThreads(); #endif // IDX_THREADS // Indexing Xapian::WritableDatabase xwdb; // Querying (active even if the wdb is too) Xapian::Database xrdb; Native(Db *db); ~Native(); Native(const Native &) = delete; Native& operator=(const Native &) = delete; #ifdef IDX_THREADS friend void *DbUpdWorker(void*); #endif // IDX_THREADS void openWrite(const std::string& dir, Db::OpenMode mode); void openRead(const std::string& dir); // Determine if an existing index is of the full-text-storing kind // by looking at the index metadata. Stores the result in m_storetext void storesDocText(Xapian::Database&); // Final steps of doc update, part which need to be single-threaded bool addOrUpdateWrite(const std::string& udi, const std::string& uniterm, Xapian::Document *doc, size_t txtlen , const std::string& rawztext); /** Delete all documents which are contained in the input document, * which must be a file-level one. * * @param onlyOrphans if true, only delete documents which have * not the same signature as the input. This is used to delete docs * which do not exist any more in the file after an update, for * example the tail messages after a folder truncation). If false, * delete all. * @param udi the parent document identifier. * @param uniterm equivalent to udi, passed just to avoid recomputing. */ bool purgeFileWrite(bool onlyOrphans, const std::string& udi, const std::string& uniterm); bool getPagePositions(Xapian::docid docid, std::vector& vpos); int getPageNumberForPosition(const std::vector& pbreaks, int pos); bool dbDataToRclDoc(Xapian::docid docid, std::string &data, Doc &doc, bool fetchtext = false); size_t whatDbIdx(Xapian::docid id); Xapian::docid whatDbDocid(Xapian::docid); /** Retrieve Xapian::docid, given unique document identifier, * using the posting list for the derived term. * * @param udi the unique document identifier (opaque hashed path+ipath). * @param idxi the database index, at query time, when using external * databases. * @param[out] xdoc the xapian document. * @return 0 if not found */ Xapian::docid getDoc(const std::string& udi, int idxi, Xapian::Document& xdoc); /** Retrieve unique document identifier for given Xapian document, * using the document termlist */ bool xdocToUdi(Xapian::Document& xdoc, std::string &udi); /** Check if doc is indexed by term */ bool hasTerm(const std::string& udi, int idxi, const std::string& term); /** Update existing Xapian document for pure extended attrs change */ bool docToXdocXattrOnly(TextSplitDb *splitter, const std::string &udi, Doc &doc, Xapian::Document& xdoc); /** Remove all terms currently indexed for field defined by idx prefix */ bool clearField(Xapian::Document& xdoc, const std::string& pfx, Xapian::termcount wdfdec); /** Check if term wdf is 0 and remove term if so */ bool clearDocTermIfWdf0(Xapian::Document& xdoc, const std::string& term); /** Compute list of subdocuments for a given udi. We look for documents * indexed by a parent term matching the udi, the posting list for the * parentterm(udi) (As suggested by James Aylett) * * Note that this is not currently recursive: all subdocs are supposed * to be children of the file doc. * Ie: in a mail folder, all messages, attachments, attachments of * attached messages etc. must have the folder file document as * parent. * * Finer grain parent-child relationships are defined by the * indexer (rcldb user), using the ipath. * */ bool subDocs(const std::string &udi, int idxi, std::vector& docids); /** Final matcher. All term transformations are done, we are just matching the input * expression against index stored terms. * @param matchtyp match type: can be ET_NONE, ET_WILDCARD or ET_REGEXP. * @param expr prefix-less expression to be matched against. * @param client function to be called when a matching term is found. The term parameter * will have no prefix. * @return false for error (Xapian issue mostly). */ bool idxTermMatch_p(int matchtyp, const std::string &expr, const std::string& prefix, std::function client); /** Check if a page position list is defined */ bool hasPages(Xapian::docid id); std::string rawtextMetaKey(Xapian::docid did) { // Xapian's Olly Betts avises to use a key which will // sort the same as the docid (which we do), and to // use Xapian's pack.h:pack_uint_preserving_sort() which is // efficient but hard to read. I'd wager that this // does not make much of a difference. 10 ascii bytes // gives us 10 billion docs, which is enough (says I). char buf[30]; sprintf(buf, "%010d", did); return buf; } bool getRawText(Xapian::docid docid, std::string& rawtext); void deleteDocument(Xapian::docid docid) { std::string metareason; XAPTRY(xwdb.set_metadata(rawtextMetaKey(docid), std::string()), xwdb, metareason); if (!metareason.empty()) { LOGERR("deleteDocument: set_metadata error: " << metareason << "\n"); // not fatal } xwdb.delete_document(docid); } }; // This is the word position offset at which we index the body text // (abstract, keywords, etc.. are stored before this) static const unsigned int baseTextPosition = 100000; } #endif /* _rcldb_p_h_included_ */ recoll-1.36.1/rcldb/rclabsfromtext.cpp0000644000175000017500000004635314427373216014660 00000000000000/* Copyright (C) 2004-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include "log.h" #include "rcldb.h" #include "rcldb_p.h" #include "rclquery.h" #include "rclquery_p.h" #include "textsplit.h" #include "hldata.h" #include "chrono.h" #include "unacpp.h" #include "zlibut.h" #include "rcldoc.h" #include "searchdata.h" using namespace std; #undef DEBUGABSTRACT #ifdef DEBUGABSTRACT #define LOGABS LOGDEB #else #define LOGABS LOGDEB2 #endif // We now let plaintorich do the highlight tags insertions which is // wasteful because we have most of the information (but the perf hit // is small because it's only called on the output fragments, not on // the whole text). The highlight zone computation code has been left // around just in case I change my mind. #undef COMPUTE_HLZONES namespace Rcl { #ifndef NO_STD_REGEX //// Fragment cleanup // Chars we turn to spaces in the Snippets static const string cstr_nc("\n\r\x0c\\"); // Things that we don't want to repeat in a displayed snippet. // e.g. > > > > > > static const string punctcls("[-<>._+,#*=|]"); static const string punctRE = "(" + punctcls + " *)(" + punctcls + " *)+"; static std::regex fixfrag_re(punctRE); static const string punctRep{"$2"}; static string fixfrag(const string& infrag) { return std::regex_replace(neutchars(infrag, cstr_nc), fixfrag_re, punctRep); } #else static string fixfrag(const string& infrag) { return infrag; } #endif // Fragment descriptor. A fragment is a text area with one or several // matched terms and some context. It is ranked according to the // matched term weights and the near/phrase matches get a boost. struct MatchFragment { // Start/End byte offsets of fragment in the document text int start; int stop; // Weight for this fragment (bigger better) double coef; #ifdef COMPUTE_HLZONES // Highlight areas (each is one or several contiguous match // terms). Because a fragment extends around a match, there // can be several contiguous or separate matches in a given // fragment. vector> hlzones; #endif // Position of the first matched term (for page number computations) unsigned int hitpos; // "best term" for this match (e.g. for use as ext app search term) string term; int line; MatchFragment(int sta, int sto, double c, #ifdef COMPUTE_HLZONES vector>& hl, #endif unsigned int pos, string& trm, int ln) : start(sta), stop(sto), coef(c), hitpos(pos), line(ln) { #ifdef COMPUTE_HLZONES hlzones.swap(hl); #endif term.swap(trm); } }; // Text splitter for finding the match areas in the document text. class TextSplitABS : public TextSplit { public: TextSplitABS(const string& rawtext, const vector& matchTerms, const HighlightData& hdata, unordered_map& wordcoefs, unsigned int ctxwords, Flags flags, unsigned int maxterms) : TextSplit(flags), m_rawtext(rawtext), m_terms(matchTerms.begin(), matchTerms.end()), m_hdata(hdata), m_wordcoefs(wordcoefs), m_ctxwords(ctxwords), maxtermcount(maxterms) { // Take note of the group (phrase/near) terms because we need // to compute the position lists for them. for (const auto& tg : hdata.index_term_groups) { if (tg.kind != HighlightData::TermGroup::TGK_TERM) { for (const auto& group : tg.orgroups) { for (const auto& term: group) { m_gterms.insert(term); } } } } } virtual void newline(int) override { m_line++; } // Accept a word and its position. If the word is a matched term, // add/update fragment definition. virtual bool takeword(const std::string& term, int pos, int bts, int bte) override { LOGDEB1("takeword: [" << term << "] bytepos: "<< bts << ":" << bte << "\n"); // Limit time taken with monster documents. The resulting abstract will be incorrect or // inexistent, but this is better than taking forever (the default cutoff value comes from // the snippetMaxPosWalk configuration parameter, and is 10E6) if (maxtermcount && termcount++ > maxtermcount) { LOGINF("Rclabsfromtext: stopping because maxtermcount reached: "< maxtermcount / 100) { LOGINF("Rclabsfromtext: stopping: max fragments count: " << maxtermcount/100 << "\n"); retflags |= ABSRES_TRUNC; return false; } // Remember recent past m_prevterms.push_back(pair(bts,bte)); if (m_prevterms.size() > m_ctxwords+1) { m_prevterms.pop_front(); } string dumb; if (o_index_stripchars) { if (!unacmaybefold(term, dumb, "UTF-8", UNACOP_UNACFOLD)) { LOGINFO("abstract: unac failed for [" << term << "]\n"); return true; } } else { dumb = term; } if (m_terms.find(dumb) != m_terms.end()) { PRETEND_USE(m_rawtext); // This word is a search term. Extend or create fragment LOGDEB1("match: [" << dumb << "] pos " << pos << " bpos " << bts << ":" << bte << " remainingWords " << m_remainingWords << "\n"); LOGDEB1("Match text " << m_rawtext.substr(bts, bte - bts) << "\n"); double coef = m_wordcoefs[dumb]; if (!m_remainingWords) { // No current fragment. Start one m_curhitpos = baseTextPosition + pos; m_curfrag.first = m_prevterms.front().first; m_curfrag.second = m_prevterms.back().second; #ifdef COMPUTE_HLZONES m_curhlzones.push_back(pair(bts, bte)); #endif m_curterm = term; m_curtermcoef = coef; m_curfragline = m_line; } else { LOGDEB2("Extending current fragment: "< "<(bts, bte)); } #endif if (coef > m_curtermcoef) { m_curterm = term; m_curtermcoef = coef; } } #ifdef COMPUTE_HLZONES m_prevwordhit = true; #endif m_curfragcoef += coef; m_remainingWords = m_ctxwords + 1; if (m_extcount > 5) { // Limit expansion of contiguous fragments (this is to avoid common terms in search // causing long heavyweight meaningless fragments. Also, limit length). m_remainingWords = 1; m_extcount = 0; } // If the term is part of a near/phrase group, update its positions list if (m_gterms.find(dumb) != m_gterms.end()) { // Term group (phrase/near) handling m_plists[dumb].push_back(pos); m_gpostobytes[pos] = pair(bts, bte); LOGDEB1("Recorded bpos for pos " << pos << ": " << bts << " " << bte << "\n"); } } #ifdef COMPUTE_HLZONES else { // Not a matched term m_prevwordhit = false; } #endif if (m_remainingWords) { // Fragment currently open. Time to close ? m_remainingWords--; m_curfrag.second = bte; if (m_remainingWords == 0) { LOGDEB1("FRAGMENT: from byte " << m_curfrag.first << " to byte " << m_curfrag.second << "\n"); LOGDEB1("FRAGMENT TEXT [" << m_rawtext.substr( m_curfrag.first, m_curfrag.second-m_curfrag.first) << "]\n"); // We used to not push weak fragments if we had a lot already. This can cause // problems if the fragments we drop are actually group fragments (which have not // got their boost yet). The right cut value is difficult to determine, because the // absolute values of the coefs depend on many things (index size, etc.) The old // test was if (m_totalcoef < 5.0 || m_curfragcoef >= 1.0) We now just avoid // creating a monster by testing the current fragments count at the top of the // function m_fragments.push_back(MatchFragment(m_curfrag.first, m_curfrag.second, m_curfragcoef, #ifdef COMPUTE_HLZONES m_curhlzones, #endif m_curhitpos, m_curterm, m_curfragline)); m_totalcoef += m_curfragcoef; m_curfragcoef = 0.0; m_curtermcoef = 0.0; } } return true; } const vector& getFragments() { return m_fragments; } // After the text is split: use the group terms positions lists to // find the group matches. void updgroups() { // Possibly store current incomplete fragment (if match was // close to the end of the text, so we did not close it): if (m_curtermcoef != 0.0) { m_fragments.push_back(MatchFragment(m_curfrag.first, m_curfrag.second, m_curfragcoef, #ifdef COMPUTE_HLZONES m_curhlzones, #endif m_curhitpos, m_curterm, m_curfragline)); m_totalcoef += m_curfragcoef; m_curfragcoef = 0.0; m_curtermcoef = 0.0; } LOGDEB("TextSplitABS: stored total " << m_fragments.size() << " fragments" << "\n"); vector tboffs; // Look for matches to PHRASE and NEAR term groups and finalize the matched regions list // (sort it by increasing start then decreasing length). for (unsigned int i = 0; i < m_hdata.index_term_groups.size(); i++) { if (m_hdata.index_term_groups[i].kind != HighlightData::TermGroup::TGK_TERM) { matchGroup(m_hdata, i, m_plists, m_gpostobytes, tboffs); } } // Sort the fragments by increasing start and decreasing width std::sort(m_fragments.begin(), m_fragments.end(), [](const MatchFragment& a, const MatchFragment& b) -> bool { if (a.start != b.start) return a.start < b.start; return a.stop - a.start > b.stop - a.stop; } ); // Sort the group regions by increasing start and decreasing width. std::sort(tboffs.begin(), tboffs.end(), [](const GroupMatchEntry& a, const GroupMatchEntry& b) -> bool { if (a.offs.first != b.offs.first) return a.offs.first < b.offs.first; return a.offs.second > b.offs.second; } ); // Give a boost to fragments which contain a group match (phrase/near), they are dear to the // user's heart. Lists are sorted, so we never go back in the fragment list (can always // start the search where we previously stopped). if (m_fragments.empty()) { return; } auto fragit = m_fragments.begin(); for (const auto& grpmatch : tboffs) { LOGDEB2("LOOKING FOR FRAGMENT: group: " << grpmatch.offs.first << "-" << grpmatch.offs.second<<" curfrag "<start<<"-"<stop<<"\n"); while (fragit->stop < grpmatch.offs.first) { fragit++; if (fragit == m_fragments.end()) { return; } } if (fragit->start <= grpmatch.offs.first && fragit->stop >= grpmatch.offs.second) { // grp in frag fragit->coef += 10.0; } } return; } int getretflags() { return retflags; } private: const string& m_rawtext; // Past terms because we need to go back for context before a hit deque> m_prevterms; // Data about the fragment we are building pair m_curfrag{0,0}; int m_curfragline{0}; double m_curfragcoef{0.0}; unsigned int m_remainingWords{0}; unsigned int m_extcount{0}; #ifdef COMPUTE_HLZONES vector> m_curhlzones; bool m_prevwordhit{false}; #endif // Current sum of fragment weights double m_totalcoef{0.0}; // Position of 1st term match (for page number computations) unsigned int m_curhitpos{0}; // "best" term string m_curterm; double m_curtermcoef{0.0}; int m_line{1}; // Group terms, extracted from m_hdata unordered_set m_gterms; // group/near terms word positions. unordered_map > m_plists; unordered_map > m_gpostobytes; // Input unordered_set m_terms; const HighlightData& m_hdata; unordered_map& m_wordcoefs; unsigned int m_ctxwords; // Result: begin and end byte positions of query terms/groups in text vector m_fragments; unsigned int termcount{0}; unsigned int maxtermcount{0}; int retflags{0}; }; int Query::Native::abstractFromText( Rcl::Db::Native *ndb, Xapian::docid docid, const vector& matchTerms, const multimap> byQ, double, int ctxwords, unsigned int maxtotaloccs, vector& vabs, Chrono& chron, bool sortbypage ) { PRETEND_USE(chron); LOGABS("abstractFromText: entry: " << chron.millis() << "mS\n"); string rawtext; if (!ndb->getRawText(docid, rawtext)) { LOGDEB0("abstractFromText: can't fetch text\n"); return ABSRES_ERROR; } LOGABS("abstractFromText: got raw text: size "< wordcoefs; for (const auto& mment : byQ) { for (const auto& word : mment.second) { wordcoefs[word] = mment.first; } } // Note: getTerms() was already called by qualityTerms, so this is // a bit wasteful. I guess that the performance impact is // negligible though. To be checked ? We need the highlightdata for the // phrase/near groups. HighlightData hld; if (m_q->m_sd) { m_q->m_sd->getTerms(hld); } LOGABS("abstractFromText: getterms: " << chron.millis() << "mS\n"); TextSplitABS splitter(rawtext, matchTerms, hld, wordcoefs, ctxwords, TextSplit::TXTS_NONE, m_q->m_snipMaxPosWalk); splitter.text_to_words(rawtext); LOGABS("abstractFromText: text_to_words: " << chron.millis() << "mS\n"); splitter.updgroups(); // Sort the fragments by decreasing weight const vector& res1 = splitter.getFragments(); vector result(res1.begin(), res1.end()); if (sortbypage) { std::sort(result.begin(), result.end(), [](const MatchFragment& a, const MatchFragment& b) -> bool { return a.hitpos < b.hitpos; } ); } else { std::sort(result.begin(), result.end(), [](const MatchFragment& a, const MatchFragment& b) -> bool { return a.coef > b.coef; } ); } vector vpbreaks; ndb->getPagePositions(docid, vpbreaks); // Build the output snippets array by merging the fragments, their // main term and the page positions. unsigned int count = 0; for (const auto& entry : result) { string frag(fixfrag(rawtext.substr(entry.start, entry.stop - entry.start))); #ifdef COMPUTE_HLZONES // This would need to be modified to take tag parameters // instead of the const strings static const string starthit(""); static const string endhit(""); size_t inslen = 0; for (const auto& hlzone: entry.hlzones) { frag.replace(hlzone.first - entry.start + inslen, 0, starthit); inslen += starthit.size(); frag.replace(hlzone.second - entry.start + inslen, 0, endhit); inslen += endhit.size(); } #endif int page = 0; if (vpbreaks.size() > 1) { page = ndb->getPageNumberForPosition(vpbreaks, entry.hitpos); if (page < 0) page = 0; } LOGDEB0("=== FRAGMENT: p. " << page << " Coef: " << entry.coef << ": " << frag << "\n"); vabs.push_back(Snippet(page, frag, entry.line).setTerm(entry.term)); if (count++ >= maxtotaloccs) break; } return ABSRES_OK | splitter.getretflags(); } class TermLineSplitter : public TextSplit { public: TermLineSplitter(const std::string& term) : TextSplit(TextSplit::TXTS_NOSPANS), m_term(term) { } bool takeword(const std::string& _term, int, int, int) override { std::string term; if (o_index_stripchars) { if (!unacmaybefold(_term, term, "UTF-8", UNACOP_UNACFOLD)) { LOGINFO("PlainToRich::takeword: unac failed for [" << term << "]\n"); return true; } } if (term == m_term) { return false; } return true; } void newline(int) override { m_line++; } int getline() { return m_line; } private: int m_line{1}; std::string m_term; }; int Query::getFirstMatchLine(const Doc &doc, const std::string& term) { int line = 1; TermLineSplitter splitter(term); bool ret = splitter.text_to_words(doc.text); // The splitter takeword() breaks by returning false as soon as the term is found if (ret == false) { line = splitter.getline(); } return line; } } recoll-1.36.1/rcldb/rcldups.cpp0000644000175000017500000000570314427373216013267 00000000000000/* Copyright (C) 2013-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ //////////////////////////////////////////////////////////////////// #include "autoconfig.h" #include using namespace std; #include #include "log.h" #include "rcldb.h" #include "rcldb_p.h" #include "xmacros.h" #include "md5ut.h" #include "searchdata.h" #include "rclquery.h" #include "rcldoc.h" namespace Rcl { /** Retrieve the dups of a given document. The input has to be a query result * because we use the xdocid. We get the md5 from this, then the dups */ bool Db::docDups(const Doc& idoc, vector& odocs) { if (nullptr == m_ndb) { LOGERR("Db::docDups: no db\n"); return false; } if (idoc.xdocid == 0) { LOGERR("Db::docDups: null xdocid in input doc\n"); return false; } // Get the xapian doc Xapian::Document xdoc; XAPTRY(xdoc = m_ndb->xrdb.get_document(Xapian::docid(idoc.xdocid)), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::docDups: xapian error: " << m_reason << "\n"); return false; } // Get the md5 string digest; XAPTRY(digest = xdoc.get_value(VALUE_MD5), m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("Db::docDups: xapian error: " << m_reason << "\n"); return false; } if (digest.empty()) { LOGDEB("Db::docDups: doc has no md5\n"); return false; } string md5; MD5HexPrint(digest, md5); SearchData *sdp = new SearchData(); std::shared_ptr sd(sdp); SearchDataClauseSimple *sdc = new SearchDataClauseSimple(SCLT_AND, md5, "rclmd5"); sdc->addModifier(SearchDataClause::SDCM_CASESENS); sdc->addModifier(SearchDataClause::SDCM_DIACSENS); sd->addClause(sdc); Query query(this); query.setCollapseDuplicates(0); if (!query.setQuery(sd)) { LOGERR("Db::docDups: setQuery failed\n"); return false; } int cnt = query.getResCnt(); for (int i = 0; i < cnt; i++) { Doc doc; if (!query.getDoc(i, doc)) { LOGERR("Db::docDups: getDoc failed at " << i << " (cnt " << cnt << ")\n"); return false; } odocs.push_back(doc); } return true; } } recoll-1.36.1/rcldb/rclquery.cpp0000644000175000017500000004150714427373216013463 00000000000000/* Copyright (C) 2008 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include "xapian.h" #include "cstr.h" #include "rclconfig.h" #include "log.h" #include "rcldb.h" #include "rcldb_p.h" #include "rclquery.h" #include "rclquery_p.h" #include "smallut.h" #include "chrono.h" #include "searchdata.h" #include "unacpp.h" #include "rcldoc.h" #include "plaintorich.h" using namespace std; namespace Rcl { // This is used as a marker inside the abstract frag lists, but // normally doesn't remain in final output (which is built with a // custom sep. by our caller). static const string cstr_ellipsis("..."); // Field names inside the index data record may differ from the rcldoc ones // (esp.: caption / title) static const string& docfToDatf(const string& df) { if (!df.compare(Doc::keytt)) { return cstr_caption; } else if (!df.compare(Doc::keymt)) { return cstr_dmtime; } else { return df; } } // Sort helper class. As Xapian sorting is lexicographic, we do some // special processing for special fields like dates and sizes. User // custom field data will have to be processed before insertion to // achieve equivalent results. #if XAPIAN_MAJOR_VERSION == 1 && XAPIAN_MINOR_VERSION < 2 class QSorter : public Xapian::Sorter #else class QSorter : public Xapian::KeyMaker #endif { public: QSorter(const string& f) : m_fld(docfToDatf(f) + "=") { if (m_fld == "dmtime=") { m_ismtime = true; } else if (m_fld == "fbytes=" || m_fld == "dbytes=" || m_fld == "pcbytes=") { m_issize = true; } else if (m_fld == "mtype=") { m_ismtype = true; } } virtual std::string operator()(const Xapian::Document& xdoc) const { string data = xdoc.get_data(); // It would be simpler to do the record->Rcl::Doc thing, but // hand-doing this will be faster. It makes more assumptions // about the format than a ConfTree though: string::size_type i1, i2; i1 = data.find(m_fld); if (i1 == string::npos) { if (m_ismtime) { // Ugly: specialcase mtime as it's either dmtime or fmtime i1 = data.find("fmtime="); if (i1 == string::npos) { return string(); } } else { return string(); } } i1 += m_fld.length(); if (i1 >= data.length()) return string(); i2 = data.find_first_of("\n\r", i1); if (i2 == string::npos) return string(); string term = data.substr(i1, i2-i1); if (m_ismtime) { return term; } else if (m_issize) { // Left zeropad values for appropriate numeric sorting leftzeropad(term, 12); return term; } else if (m_ismtype) { // Arrange for directories to always sort first if (term == "inode/directory" || term == "application/x-fsdirectory") { term.insert(0, 1, ' '); } // No further processing needed for mtype return term; } // Process data for better sorting. We should actually do the // unicode thing // (http://unicode.org/reports/tr10/#Introduction), but just // removing accents and majuscules will remove the most // glaring weirdnesses (or not, depending on your national // approach to collating...) string sortterm; // We're not even sure the term is utf8 here (ie: url) if (!unacmaybefold(term, sortterm, "UTF-8", UNACOP_UNACFOLD)) { sortterm = term; } // Also remove some common uninteresting starting characters i1 = sortterm.find_first_not_of(" \t\\\"'([*+,.#/"); if (i1 != 0 && i1 != string::npos) { sortterm = sortterm.substr(i1, sortterm.size()-i1); } LOGDEB2("QSorter: [" << term << "] -> [" << sortterm << "]\n"); return sortterm; } private: string m_fld; bool m_ismtime{false}; bool m_issize{false}; bool m_ismtype{false}; }; Query::Query(Db *db) : m_nq(new Native(this)), m_db(db) { if (db) db->getConf()->getConfParam("snippetMaxPosWalk", &m_snipMaxPosWalk); } Query::~Query() { deleteZ(m_nq); if (m_sorter) { delete (QSorter*)m_sorter; m_sorter = nullptr; } } void Query::setSortBy(const string& fld, bool ascending) { if (fld.empty()) { m_sortField.erase(); } else { m_sortField = m_db->getConf()->fieldQCanon(fld); m_sortAscending = ascending; } LOGDEB0("RclQuery::setSortBy: [" << m_sortField << "] " << (m_sortAscending ? "ascending" : "descending") << "\n"); } static const string parent_prefix{"F"}; class SubdocDecider : public Xapian::MatchDecider { public: SubdocDecider(bool sel) : MatchDecider(), m_select(sel) {} virtual ~SubdocDecider() {} virtual bool operator()(const Xapian::Document &doc) const { bool hasparent{false}; try { Xapian::TermIterator xit = doc.termlist_begin(); xit.skip_to(wrap_prefix(parent_prefix)); hasparent = (xit != doc.termlist_end()) && (get_prefix(*xit) == parent_prefix); } catch (...) { } return hasparent == m_select; } bool m_select; }; // Prepare query out of user search data bool Query::setQuery(std::shared_ptr sdata) { LOGDEB("Query::setQuery:\n"); if (!m_db || !m_nq) { LOGERR("Query::setQuery: not initialised!\n"); return false; } m_resCnt = -1; m_reason.erase(); m_nq->clear(); m_sd = sdata; Xapian::Query xq; if (!sdata->toNativeQuery(*m_db, &xq)) { m_reason += sdata->getReason(); return false; } m_nq->xquery = xq; if (sdata->getSubSpec() == SearchData::SUBDOC_NO) { m_nq->subdecider = new SubdocDecider(false); } else if (sdata->getSubSpec() == SearchData::SUBDOC_YES) { m_nq->subdecider = new SubdocDecider(true); } string d; for (int tries = 0; tries < 2; tries++) { try { m_nq->xenquire = new Xapian::Enquire(m_db->m_ndb->xrdb); if (m_collapseDuplicates) { m_nq->xenquire->set_collapse_key(Rcl::VALUE_MD5); } else { m_nq->xenquire->set_collapse_key(Xapian::BAD_VALUENO); } m_nq->xenquire->set_docid_order(Xapian::Enquire::DONT_CARE); if (!m_sortField.empty() && stringlowercmp("relevancyrating", m_sortField)) { if (m_sorter) { delete (QSorter*)m_sorter; m_sorter = nullptr; } m_sorter = new QSorter(m_sortField); // It really seems there is a xapian bug about sort order, we // invert here. m_nq->xenquire->set_sort_by_key((QSorter*)m_sorter, !m_sortAscending); } m_nq->xenquire->set_query(m_nq->xquery); m_nq->xmset = Xapian::MSet(); // Get the query description and trim the "Xapian::Query" d = m_nq->xquery.get_description(); m_reason.erase(); break; } catch (const Xapian::DatabaseModifiedError &e) { m_reason = e.get_msg(); m_db->m_ndb->xrdb.reopen(); continue; } XCATCHERROR(m_reason); break; } if (!m_reason.empty()) { LOGDEB("Query::SetQuery: xapian error " << m_reason << "\n"); return false; } if (d.find("Xapian::Query") == 0) d.erase(0, strlen("Xapian::Query")); sdata->setDescription(d); m_sd = sdata; LOGDEB("Query::SetQuery: Q: " << sdata->getDescription() << "\n"); return true; } bool Query::getQueryTerms(vector& terms) { if (!m_nq) return false; terms.clear(); Xapian::TermIterator it; string ermsg; try { for (it = m_nq->xquery.get_terms_begin(); it != m_nq->xquery.get_terms_end(); it++) { terms.push_back(*it); } } XCATCHERROR(ermsg); if (!ermsg.empty()) { LOGERR("getQueryTerms: xapian error: " << ermsg << "\n"); return false; } return true; } int Query::makeDocAbstract(const Doc &doc, PlainToRich *plaintorich, vector& abstract, int maxoccs, int ctxwords, bool sortbypage) { LOGDEB("makeDocAbstract: maxoccs " << maxoccs << " ctxwords " << ctxwords << "\n"); if (!m_db || !m_db->m_ndb || !m_db->m_ndb->m_isopen || !m_nq) { LOGERR("Query::makeDocAbstract: no db or no nq\n"); return ABSRES_ERROR; } int ret = ABSRES_ERROR; vector abs1; XAPTRY(ret = m_nq->makeAbstract(doc.xdocid, abs1, maxoccs, ctxwords, sortbypage), m_db->m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGDEB("makeDocAbstract: makeAbstract: reason: " << m_reason << "\n"); return ABSRES_ERROR; } HighlightData hldata; auto sd = getSD(); sd->getTerms(hldata); for (auto& snip : abs1) { list ls; if (plaintorich->plaintorich(snip.snippet, ls, hldata)) { snip.snippet = ls.front(); abstract.push_back(snip); } } return ret; } bool Query::makeDocAbstract(const Doc &doc, PlainToRich *plaintorich, vector& abstract) { vector vpabs; if (!makeDocAbstract(doc, plaintorich, vpabs)) return false; for (const auto& snippet : vpabs) { string chunk; if (snippet.page > 0) { ostringstream ss; ss << snippet.page; chunk += string(" [P. ") + ss.str() + "] "; } else if (snippet.line > 0) { ostringstream ss; ss << snippet.line; chunk += string(" [L. ") + ss.str() + "] "; } chunk += snippet.snippet; abstract.push_back(chunk); } return true; } int Query::getFirstMatchPage(const Doc &doc, string& term) { LOGDEB1("Db::getFirstMatchPage\n");; if (!m_nq) { LOGERR("Query::getFirstMatchPage: no nq\n"); return false; } int pagenum = -1; XAPTRY(pagenum = m_nq->getFirstMatchPage(Xapian::docid(doc.xdocid), term), m_db->m_ndb->xrdb, m_reason); return m_reason.empty() ? pagenum : -1; } // Mset size // Note: times for retrieving (multiple times)all docs from a sample // 25k docs db (q: mime:*) // qqantum: 10 50 100 150 200 1000 // Seconds: 21 8.5 6.7 6.4 5.8 6.0 static const int qquantum = 100; // Get estimated result count for query. Xapian actually does most of // the search job in there, this can be long int Query::getResCnt(int checkatleast, bool useestimate) { if (!m_db || !m_nq || !m_nq->xenquire) { LOGERR("Query::getResCnt: no query opened\n"); return -1; } LOGDEB0("Query::getResCnt: checkatleast " << checkatleast << " estimate " << useestimate << "\n"); if (m_resCnt >= 0) return m_resCnt; if (m_nq->xmset.size() <= 0) { Chrono chron; XAPTRY(if (checkatleast == -1) checkatleast = m_db->docCnt(); m_nq->xmset = m_nq->xenquire->get_mset( 0, qquantum, checkatleast, nullptr, m_nq->subdecider), m_db->m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("xenquire->get_mset: exception: " << m_reason << "\n"); return -1; } LOGDEB("Query::getResCnt: get_mset: " << chron.millis() << " mS\n"); } if (useestimate) { m_resCnt = m_nq->xmset.get_matches_estimated(); } else { m_resCnt = m_nq->xmset.get_matches_lower_bound(); } LOGDEB("Query::getResCnt: " << m_resCnt << "\n"); return m_resCnt; } // Get document at rank xapi in query results. We check if the // current mset has the doc, else ask for an other one. We use msets // of qquantum documents. // // Note that as stated by a Xapian developer, Enquire searches from // scratch each time get_mset() is called. So the better performance // on subsequent calls is probably only due to disk caching. bool Query::getDoc(int xapi, Doc &doc, bool fetchtext) { LOGDEB1("Query::getDoc: xapian enquire index " << xapi << "\n"); if (!m_nq || !m_nq->xenquire) { LOGERR("Query::getDoc: no query opened\n"); return false; } int first = m_nq->xmset.get_firstitem(); int last = first + m_nq->xmset.size() -1; if (!(xapi >= first && xapi <= last)) { LOGDEB("Fetching for first " << xapi << ", count " << qquantum << "\n"); XAPTRY(m_nq->xmset = m_nq->xenquire->get_mset( xapi, qquantum, nullptr, m_nq->subdecider), m_db->m_ndb->xrdb, m_reason); if (!m_reason.empty()) { LOGERR("enquire->get_mset: exception: " << m_reason << "\n"); return false; } if (m_nq->xmset.empty()) { LOGDEB("enquire->get_mset: got empty result\n"); return false; } first = m_nq->xmset.get_firstitem(); last = first + m_nq->xmset.size() -1; } Xapian::Document xdoc; Xapian::docid docid = 0; int pc = 0; int collapsecount = 0; string data; string udi; m_reason.erase(); for (int xaptries=0; xaptries < 2; xaptries++) { try { xdoc = m_nq->xmset[xapi-first].get_document(); collapsecount = m_nq->xmset[xapi-first].get_collapse_count(); docid = *(m_nq->xmset[xapi-first]); pc = m_nq->xmset.convert_to_percent(m_nq->xmset[xapi-first]); data = xdoc.get_data(); m_reason.erase(); Chrono chron; m_db->m_ndb->xdocToUdi(xdoc, udi); LOGDEB2("Query::getDoc: " << chron.millis() << " ms for udi [" << udi << "], collapse count " << collapsecount << "\n"); break; } catch (Xapian::DatabaseModifiedError &error) { // retry or end of loop m_reason = error.get_msg(); continue; } XCATCHERROR(m_reason); break; } if (!m_reason.empty()) { LOGERR("Query::getDoc: " << m_reason << "\n"); return false; } doc.meta[Rcl::Doc::keyudi] = udi; doc.pc = pc; char buf[200]; if (collapsecount > 0) { sprintf(buf,"%3d%% (%d)", pc, collapsecount + 1); } else { sprintf(buf,"%3d%%", pc); } doc.meta[Doc::keyrr] = buf; if (collapsecount > 0) { sprintf(buf, "%d", collapsecount); doc.meta[Rcl::Doc::keycc] = buf; } // Parse xapian document's data and populate doc fields return m_db->m_ndb->dbDataToRclDoc(docid, data, doc, fetchtext); } vector Query::expand(const Doc &doc) { LOGDEB("Rcl::Query::expand()\n"); vector res; if (!m_nq || !m_nq->xenquire) { LOGERR("Query::expand: no query opened\n"); return res; } for (int tries = 0; tries < 2; tries++) { try { Xapian::RSet rset; rset.add_document(Xapian::docid(doc.xdocid)); // We don't exclude the original query terms. Xapian::ESet eset = m_nq->xenquire->get_eset(20, rset, false); LOGDEB("ESet terms:\n"); // We filter out the special terms for (Xapian::ESetIterator it = eset.begin(); it != eset.end(); it++) { LOGDEB(" [" << (*it) << "]\n"); if ((*it).empty() || has_prefix(*it)) continue; res.push_back(*it); if (res.size() >= 10) break; } m_reason.erase(); break; } catch (const Xapian::DatabaseModifiedError &e) { m_reason = e.get_msg(); m_db->m_ndb->xrdb.reopen(); continue; } XCATCHERROR(m_reason); break; } if (!m_reason.empty()) { LOGERR("Query::expand: xapian error " << m_reason << "\n"); res.clear(); } return res; } } recoll-1.36.1/rcldb/searchdata.cpp0000644000175000017500000002635714427373216013722 00000000000000/* Copyright (C) 2006-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Handle translation from rcl's SearchData structures to Xapian Queries #include "autoconfig.h" #include #include #include #include #include #include using namespace std; #include "xapian.h" #include "cstr.h" #include "rcldb.h" #include "rcldb_p.h" #include "searchdata.h" #include "log.h" #include "smallut.h" #include "textsplit.h" #include "unacpp.h" #include "utf8iter.h" #include "stoplist.h" #include "rclconfig.h" #include "termproc.h" #include "synfamily.h" #include "stemdb.h" #include "expansiondbs.h" #include "base64.h" #include "daterange.h" namespace Rcl { SearchData::~SearchData() { LOGDEB0("SearchData::~SearchData\n"); for (auto& clausep : m_query) delete clausep; } // This is called by the GUI simple search if the option is set: add // (OR) phrase to a query (if it is simple enough) so that results // where the search terms are close and in order will come up on top. // We remove very common terms from the query to avoid performance issues. bool SearchData::maybeAddAutoPhrase(Rcl::Db& db, double freqThreshold) { LOGDEB0("SearchData::maybeAddAutoPhrase()\n"); simplify(); if (m_query.empty()) { LOGDEB2("SearchData::maybeAddAutoPhrase: empty query\n"); return false; } string field; auto clp0 = dynamic_cast(*m_query.begin()); if (clp0) field = clp0->getfield(); vector words; // Walk the clause list. If this is not an AND list, we find any // non simple clause or different field names, bail out. for (auto& clausep : m_query) { SClType tp = clausep->m_tp; if (tp != SCLT_AND) { LOGDEB2("SearchData::maybeAddAutoPhrase: wrong tp " << tp << "\n"); return false; } auto clp = dynamic_cast(clausep); if (clp == nullptr) { LOGDEB2("SearchData::maybeAddAutoPhrase: other than clauseSimple in query.\n"); return false; } if (clp->getfield().compare(field)) { LOGDEB2("SearchData::maybeAddAutoPhrase: diff. fields\n"); return false; } // If there are wildcards or quotes in there, bail out if (clp->gettext().find_first_of("\"*[?") != string::npos) { LOGDEB2("SearchData::maybeAddAutoPhrase: wildcards\n"); return false; } // Do a simple word-split here, not the full-blown // textsplit. Spans of stopwords should not be trimmed later // in this function, they will be properly split when the // phrase gets processed by toNativeQuery() later on. vector wl; stringToStrings(clp->gettext(), wl); words.insert(words.end(), wl.begin(), wl.end()); } // Trim the word list by eliminating very frequent terms // (increasing the slack as we do it): int slack = 0; int doccnt = db.docCnt(); if (!doccnt) doccnt = 1; string swords; for (const auto& word : words) { double freq = double(db.termDocCnt(word)) / doccnt; if (freq < freqThreshold) { if (!swords.empty()) swords.append(1, ' '); swords += word; } else { LOGDEB0("SearchData::Autophrase: [" << word << "] too frequent (" << (100 * freq) << " %" << ")\n"); slack++; } } // We can't make a phrase with a single word :) int nwords = TextSplit::countWords(swords); if (nwords <= 1) { LOGDEB2("SearchData::maybeAddAutoPhrase: ended with 1 word\n"); return false; } // Increase the slack: we want to be a little more laxist than for // an actual user-entered phrase slack += 1 + nwords / 3; m_autophrase = make_shared(SCLT_PHRASE, swords, slack, field); return true; } // Add clause to current list. OR lists cant have EXCL clauses. bool SearchData::addClause(SearchDataClause* cl) { if (m_tp == SCLT_OR && cl->getexclude()) { LOGERR("SearchData::addClause: cant add EXCL to OR list\n"); m_reason = "No Negative (AND_NOT) clauses allowed in OR queries"; return false; } cl->setParent(this); m_haveWildCards = m_haveWildCards || cl->m_haveWildCards; m_query.push_back(cl); return true; } // Am I a file name only search ? This is to turn off term highlighting. // There can't be a subclause in a filename search: no possible need to recurse bool SearchData::fileNameOnly() { for (const auto& clausep : m_query) { if (!clausep->isFileName()) return false; } return true; } // The query language creates a lot of subqueries. See if we can merge them. void SearchData::simplify() { LOGDEB0("SearchData::simplify()\n"); //std::cerr << "SIMPLIFY BEFORE: "; dump(std::cerr); for (unsigned int i = 0; i < m_query.size(); i++) { if (m_query[i]->m_tp != SCLT_SUB) continue; auto clsubp = dynamic_cast(m_query[i]); if (nullptr == clsubp) { // ?? continue; } if (clsubp->getSub()->m_tp != m_tp) { LOGDEB0("Not simplifying because sub has differing m_tp\n"); continue; } clsubp->getSub()->simplify(); // If this subquery has special attributes, it's not a // candidate for collapsing, except if it has no clauses, because // then, we just pick the attributes. if (!clsubp->getSub()->m_filetypes.empty() || !clsubp->getSub()->m_nfiletypes.empty() || clsubp->getSub()->m_haveDates || #ifdef EXT4_BIRTH_TIME clsubp->getSub()->m_haveBrDates || #endif clsubp->getSub()->m_maxSize != -1 || clsubp->getSub()->m_minSize != -1 || clsubp->getSub()->m_subspec != SUBDOC_ANY || clsubp->getSub()->m_haveWildCards) { if (!clsubp->getSub()->m_query.empty()) { LOGDEB0("Not simplifying because sub has special attributes and non-empty query\n"); continue; } m_filetypes.insert(m_filetypes.end(), clsubp->getSub()->m_filetypes.begin(), clsubp->getSub()->m_filetypes.end()); m_nfiletypes.insert(m_nfiletypes.end(), clsubp->getSub()->m_nfiletypes.begin(), clsubp->getSub()->m_nfiletypes.end()); if (clsubp->getSub()->m_haveDates && !m_haveDates) { m_dates = clsubp->getSub()->m_dates; m_haveDates = 1; } #ifdef EXT4_BIRTH_TIME if (clsubp->getSub()->m_haveBrDates && !m_haveBrDates) { m_brdates = clsubp->getSub()->m_brdates; m_haveBrDates = 1; } #endif if (m_maxSize == -1) m_maxSize = clsubp->getSub()->m_maxSize; if (m_minSize == -1) m_minSize = clsubp->getSub()->m_minSize; if (m_subspec == SUBDOC_ANY) m_subspec = clsubp->getSub()->m_subspec; m_haveWildCards = m_haveWildCards || clsubp->getSub()->m_haveWildCards; // And then let the clauses processing go on, there are // none anyway, we will just delete the subquery. } // Delete the clause_sub, and insert the queries from its searchdata in its place m_query.erase(m_query.begin() + i); for (unsigned int j = 0; j < clsubp->getSub()->m_query.size(); j++) { m_query.insert(m_query.begin() + i + j, clsubp->getSub()->m_query[j]->clone()); m_query[i+j]->setParent(this); } i += int(clsubp->getSub()->m_query.size()) - 1; } //std::cerr << "SIMPLIFY AFTER: "; dump(std::cerr); } // Extract terms and groups for highlighting void SearchData::getTerms(HighlightData &hld) const { for (const auto& clausep : m_query) { if (!(clausep->getModifiers() & SearchDataClause::SDCM_NOTERMS) && !clausep->getexclude()) { clausep->getTerms(hld); } } sort(hld.spellexpands.begin(), hld.spellexpands.end()); hld.spellexpands.erase( unique(hld.spellexpands.begin(), hld.spellexpands.end()), hld.spellexpands.end()); return; } static const char * tpToString(SClType t) { switch (t) { case SCLT_AND: return "AND"; case SCLT_OR: return "OR"; case SCLT_FILENAME: return "FILENAME"; case SCLT_PHRASE: return "PHRASE"; case SCLT_NEAR: return "NEAR"; case SCLT_PATH: return "PATH"; case SCLT_SUB: return "SUB"; default: return "UNKNOWN"; } } static string dumptabs; void SearchData::dump(ostream& o) const { o << dumptabs << "SearchData: " << tpToString(m_tp) << " qs " << int(m_query.size()) << " ft " << m_filetypes.size() << " nft " << m_nfiletypes.size() << " hd " << m_haveDates << #ifdef EXT4_BIRTH_TIME " hbrd " << m_haveBrDates << #endif " maxs " << m_maxSize << " mins " << m_minSize << " wc " << m_haveWildCards << " subsp " << m_subspec << "\n"; for (const auto& clausep : m_query) { o << dumptabs; clausep->dump(o); o << "\n"; } // o << dumptabs << "\n"; } void SearchDataClause::dump(ostream& o) const { o << "SearchDataClause??"; } void SearchDataClauseSimple::dump(ostream& o) const { o << "ClauseSimple: " << tpToString(m_tp) << " "; if (m_exclude) o << "- "; o << "[" ; if (!m_field.empty()) o << m_field << " : "; o << m_text << "]"; } void SearchDataClauseFilename::dump(ostream& o) const { o << "ClauseFN: "; if (m_exclude) o << " - "; o << "[" << m_text << "]"; } void SearchDataClausePath::dump(ostream& o) const { o << "ClausePath: "; if (m_exclude) o << " - "; o << "[" << m_text << "]"; } void SearchDataClauseRange::dump(ostream& o) const { o << "ClauseRange: "; if (m_exclude) o << " - "; o << "[" << gettext() << "]"; } void SearchDataClauseDist::dump(ostream& o) const { if (m_tp == SCLT_NEAR) o << "ClauseDist: NEAR "; else o << "ClauseDist: PHRA "; if (m_exclude) o << " - "; o << "["; if (!m_field.empty()) o << m_field << " : "; o << m_text << "]"; } void SearchDataClauseSub::dump(ostream& o) const { o << "ClauseSub {\n"; dumptabs += '\t'; m_sub->dump(o); dumptabs.erase(dumptabs.size()- 1); o << dumptabs << "}"; } } // Namespace Rcl recoll-1.36.1/rcldb/searchdata.h0000644000175000017500000004627514427373216013370 00000000000000/* Copyright (C) 2004-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SEARCHDATA_H_INCLUDED_ #define _SEARCHDATA_H_INCLUDED_ /** * Structures to hold data coming almost directly from the GUI * and handle its translation to Xapian queries. * This is not generic code, it reflects the choices made for the user * interface, and it also knows some specific of recoll's usage of Xapian * (ie: term prefixes) */ #include #include #include #include #include "rcldb.h" #include "smallut.h" #include "hldata.h" class RclConfig; class AdvSearch; extern const std::string cstr_minwilds; namespace Rcl { /** Search clause types */ enum SClType { SCLT_AND, SCLT_OR, SCLT_FILENAME, SCLT_PHRASE, SCLT_NEAR, SCLT_PATH, SCLT_RANGE, SCLT_SUB, }; class SearchDataClause; class SearchDataClauseDist; /** A SearchData object represents a Recoll user query, for translation into a Xapian query tree. This could probably better called a 'question'. This is a list of SearchDataClause objects combined through either OR or AND. Clauses either reflect user entry in a query field: some text, a clause type (AND/OR/NEAR etc.), possibly a distance, or are the result of parsing query language input. A clause can also point to another SearchData representing a subquery. The content of each clause when added may not be fully parsed yet (may come directly from a gui field). It will be parsed and may be translated to several queries in the Xapian sense, for example several terms and phrases as would result from ["this is a phrase" term1 term2] . This is why the clauses also have an AND/OR/... type. They are an intermediate form between the primary user input and the final Xapian::Query tree. For example, a phrase clause could be added either explicitly or using double quotes: {SCLT_PHRASE, [this is a phrase]} or as {SCLT_XXX, ["this is a phrase"]} */ class SearchData { public: SearchData(SClType tp, const std::string& stemlang) : m_tp(tp), m_stemlang(stemlang) { if (m_tp != SCLT_OR && m_tp != SCLT_AND) m_tp = SCLT_OR; } SearchData() : m_tp(SCLT_AND) { } ~SearchData(); SearchData(const SearchData &) = delete; SearchData& operator=(const SearchData&) = delete; /** Is there anything but a file name search in here ? */ bool fileNameOnly(); /** Do we have wildcards anywhere apart from filename searches ? */ bool haveWildCards() {return m_haveWildCards;} /** Translate to Xapian query. rcldb knows about the void* */ bool toNativeQuery(Rcl::Db &db, void *); /** We become the owner of cl and will delete it */ bool addClause(SearchDataClause *cl); /** If this is a simple query (one field only, no distance clauses), * add phrase made of query terms to query, so that docs containing the * user terms in order will have higher relevance. This must be called * before toNativeQuery(). * @param threshold: don't use terms more frequent than the value * (proportion of docs where they occur) */ bool maybeAddAutoPhrase(Rcl::Db &db, double threshold); const std::string& getStemLang() {return m_stemlang;} void setMinSize(int64_t size) {m_minSize = size;} void setMaxSize(int64_t size) {m_maxSize = size;} enum SubdocSpec {SUBDOC_ANY = -1, SUBDOC_NO = 0, SUBDOC_YES = 1}; void setSubSpec(int spec) { switch (spec) { case SUBDOC_ANY: case SUBDOC_NO: case SUBDOC_YES: m_subspec = spec; } } int getSubSpec() {return m_subspec;} /** Set date span for filtering results */ void setDateSpan(DateInterval *dip) {m_dates = *dip; m_haveDates = true;} #ifdef EXT4_BIRTH_TIME /** Set brtime span for filtering results */ void setBrDateSpan(DateInterval *dip) {m_brdates = *dip; m_haveBrDates = true;} #endif /** Add file type for filtering results */ void addFiletype(const std::string& ft) {m_filetypes.push_back(ft);} /** Add file type to not wanted list */ void remFiletype(const std::string& ft) {m_nfiletypes.push_back(ft);} /** Retrieve error description */ std::string getReason() {return m_reason;} /** Return term expansion data. Mostly used by caller for highlighting */ void getTerms(HighlightData& hldata) const; /** * Get/set the description field which is retrieved from xapian after * initializing the query. It is stored here for usage in the GUI. */ std::string getDescription() {return m_description;} void setDescription(const std::string& d) {m_description = d;} /** Return an XML version of the contents, for storage in search history by the GUI */ std::string asXML(); void setTp(SClType tp) { m_tp = tp; } SClType getTp() { return m_tp; } void setMaxExpand(int max) { m_softmaxexpand = max; } bool getAutoDiac() {return m_autodiacsens;} bool getAutoCase() {return m_autocasesens;} int getMaxExp() {return m_maxexp;} int getMaxCl() {return m_maxcl;} int getSoftMaxExp() {return m_softmaxexpand;} void dump(std::ostream& o) const; friend class ::AdvSearch; private: // Combine type. Only SCLT_AND or SCLT_OR here SClType m_tp; // The clauses std::vector m_query; // Restricted set of filetypes if not empty. std::vector m_filetypes; // Excluded set of file types if not empty std::vector m_nfiletypes; // Autophrase if set. Can't be part of the normal chain because // it uses OP_AND_MAYBE std::shared_ptr m_autophrase; // Special stuff produced by input which looks like a clause but means // something else (date, size specs, etc.) bool m_haveDates{false}; DateInterval m_dates; // Restrict to date interval // Note we don't use ifdef EXT4_BIRTH_TIME here because it would affect the unofficial // librecollABI and force configuring the krunner and KIO worker in consequence. bool m_haveBrDates{false}; DateInterval m_brdates; // Restrict to date interval int64_t m_maxSize{-1}; int64_t m_minSize{-1}; // Filtering for subdocs: -1:any, 0: only free-standing, 1: only subdocs int m_subspec{SUBDOC_ANY}; // Printable expanded version of the complete query, retrieved/set // from rcldb after the Xapian::setQuery() call std::string m_description; // Error diag std::string m_reason; bool m_haveWildCards{false}; std::string m_stemlang; // Parameters set at the start of ToNativeQuery because they need // an rclconfig. Actually this does not make sense and it would be // simpler to just pass an rclconfig to the constructor; bool m_autodiacsens{false}; bool m_autocasesens{true}; int m_maxexp{10000}; int m_maxcl{100000}; // Parameters which are not part of the main query data but may influence // translation in special cases. // Maximum TermMatch (e.g. wildcard) expansion. This is normally set // from the configuration with a high default, but may be set to a lower // value during "find-as-you-type" operations from the GUI int m_softmaxexpand{-1}; // Collapse bogus subqueries generated by the query parser, mostly // so that we can check if this is an autophrase candidate (else // Xapian will do it anyway) void simplify(); bool expandFileTypes(Rcl::Db &db, std::vector& exptps); bool clausesToQuery(Rcl::Db &db, SClType tp, std::vector& query, std::string& reason, void *d); }; class SearchDataClause { public: enum Modifier {SDCM_NONE=0, SDCM_NOSTEMMING=0x1, SDCM_ANCHORSTART=0x2, SDCM_ANCHOREND=0x4, SDCM_CASESENS=0x8, SDCM_DIACSENS=0x10, SDCM_NOTERMS=0x20, // Don't include terms for highlighting SDCM_NOSYNS = 0x40, // Don't perform synonym expansion // Aargh special case. pathelts are case/diac-sensitive // even in a stripped index SDCM_PATHELT = 0x80, SDCM_FILTER = 0x100, // Terms inside phrases are not expanded if this is not set (by 'x' modifier) SDCM_EXPANDPHRASE = 0x200, }; enum Relation {REL_CONTAINS, REL_EQUALS, REL_LT, REL_LTE, REL_GT, REL_GTE}; SearchDataClause(SClType tp) : m_tp(tp), m_parentSearch(nullptr), m_haveWildCards(0), m_modifiers(SDCM_NONE), m_weight(1.0), m_exclude(false), m_rel(REL_CONTAINS) {} virtual ~SearchDataClause() = default; SearchDataClause(const SearchDataClause &) = default; SearchDataClause& operator=(const SearchDataClause&) = default; virtual SearchDataClause* clone() = 0; virtual bool toNativeQuery(Rcl::Db &db, void *) = 0; bool isFileName() const {return m_tp == SCLT_FILENAME ? true: false;} virtual std::string getReason() const {return m_reason;} virtual void getTerms(HighlightData&) const {} SClType getTp() const { return m_tp; } void setTp(SClType tp) { m_tp = tp; } void setParent(SearchData *p) { m_parentSearch = p; } std::string getStemLang() { return (m_modifiers & SDCM_NOSTEMMING) || nullptr == m_parentSearch ? std::string() : m_parentSearch->getStemLang(); } bool getAutoDiac() { return m_parentSearch ? m_parentSearch->getAutoDiac() : false; } bool getAutoCase() { return m_parentSearch ? m_parentSearch->getAutoCase() : true; } int getMaxExp() { return m_parentSearch ? m_parentSearch->getMaxExp() : 10000; } size_t getMaxCl() { return m_parentSearch ? m_parentSearch->getMaxCl() : 100000; } int getSoftMaxExp() { return m_parentSearch ? m_parentSearch->getSoftMaxExp() : -1; } virtual void addModifier(Modifier mod) { m_modifiers = m_modifiers | mod; } virtual unsigned int getModifiers() { return m_modifiers; } virtual void setWeight(float w) { m_weight = w; } virtual bool getexclude() const { return m_exclude; } virtual void setexclude(bool onoff) { m_exclude = onoff; } virtual void setrel(Relation rel) { m_rel = rel; } virtual Relation getrel() { return m_rel; } virtual void dump(std::ostream& o) const; friend class SearchData; protected: std::string m_reason; SClType m_tp; SearchData *m_parentSearch; bool m_haveWildCards; unsigned int m_modifiers; float m_weight; bool m_exclude; Relation m_rel; }; /** * "Simple" data clause with user-entered query text. This can include * multiple phrases and words, but no specified distance. */ class TermProcQ; class SearchDataClauseSimple : public SearchDataClause { public: SearchDataClauseSimple(SClType tp, const std::string& txt, const std::string& fld = std::string()) : SearchDataClause(tp), m_text(txt), m_field(fld), m_curcl(0) { m_haveWildCards = (txt.find_first_of(cstr_minwilds) != std::string::npos); } SearchDataClauseSimple(const std::string& txt, SClType tp) : SearchDataClause(tp), m_text(txt), m_curcl(0) { m_haveWildCards = (txt.find_first_of(cstr_minwilds) != std::string::npos); } virtual ~SearchDataClauseSimple() = default; virtual SearchDataClauseSimple *clone() override{ return new SearchDataClauseSimple(*this); } /** Translate to Xapian query */ virtual bool toNativeQuery(Rcl::Db &, void *) override; virtual void getTerms(HighlightData& hldata) const override { hldata.append(m_hldata); } virtual const std::string& gettext() const { return m_text; } virtual const std::string& getfield() const { return m_field; } virtual void setfield(const std::string& field) { m_field = field; } virtual void dump(std::ostream& o) const override; protected: std::string m_text; // Raw user entry text. std::string m_field; // Field specification if any HighlightData m_hldata; // Current count of Xapian clauses, to check against expansion limit size_t m_curcl; bool processUserString(Rcl::Db &db, const std::string &iq, std::string &ermsg, void* pq, int slack = 0, bool useNear = false); bool expandTerm(Rcl::Db &db, std::string& ermsg, int mods, const std::string& term, std::vector& exp, std::string& sterm, const std::string& prefix, std::vector* multiwords = nullptr); // After splitting entry on whitespace: process non-phrase element void processSimpleSpan(Rcl::Db &db, std::string& ermsg, const std::string& span, int mods, void *pq); // Process phrase/near element void processPhraseOrNear(Rcl::Db &db, std::string& ermsg, TermProcQ *splitData, int mods, void *pq, bool useNear, int slack); }; class SearchDataClauseRange : public SearchDataClauseSimple { public: SearchDataClauseRange(const std::string& t1, const std::string& t2, const std::string& fld = std::string()) : SearchDataClauseSimple(SCLT_RANGE, t1, fld), m_t2(t2) {} // This is for 'upgrading' a clauseSimple with eq/gt/lt... rel to // a range. Either of t1 or t2 or both can be set to the original // text, which is why they are passed as separate parameters SearchDataClauseRange(const SearchDataClauseSimple& cl, const std::string& t1, const std::string& t2) : SearchDataClauseSimple(cl) { m_text = t1; m_t2 = t2; } virtual ~SearchDataClauseRange() = default; virtual SearchDataClauseRange *clone() override { return new SearchDataClauseRange(*this); } virtual void dump(std::ostream& o) const override; virtual const std::string& gettext2() const { return m_t2; } virtual bool toNativeQuery(Rcl::Db &db, void *) override; protected: std::string m_t2; }; /** * Filename search clause. This is special because term expansion is only * performed against the unsplit file name terms. * * There is a big advantage in expanding only against the * field, especially for file names, because this makes searches for * "*xx" much faster (no need to scan the whole main index). */ class SearchDataClauseFilename : public SearchDataClauseSimple { public: SearchDataClauseFilename(const std::string& txt) : SearchDataClauseSimple(txt, SCLT_FILENAME) { // File name searches don't count when looking for wild cards. m_haveWildCards = false; addModifier(SDCM_FILTER); } virtual ~SearchDataClauseFilename() = default; virtual SearchDataClauseFilename *clone() override { return new SearchDataClauseFilename(*this); } virtual bool toNativeQuery(Rcl::Db &, void *) override; virtual void dump(std::ostream& o) const override; }; /** * Pathname filtering clause. This is special because of history: * - Pathname filtering used to be performed as a post-processing step * done with the url fields of doc data records. * - Then it was done as special phrase searchs on path elements prefixed * with XP. * Up to this point dir filtering data was stored as part of the searchdata * object, not in the SearchDataClause tree. Only one, then a list, * of clauses where stored, and they were always ANDed together. * * In order to allow for OR searching, dir clauses are now stored in a * specific SearchDataClause, but this is still special because the field has * non-standard phrase-like processing, reflected in index storage by * an empty element representing / (as "XP"). * * A future version should use a standard phrase with an anchor to the * start if the path starts with /. As this implies an index format * change but is no important enough to warrant it, this has to wait for * the next format change. */ class SearchDataClausePath : public SearchDataClauseSimple { public: SearchDataClausePath(const std::string& txt, bool excl = false) : SearchDataClauseSimple(SCLT_PATH, txt, "dir") { m_exclude = excl; m_haveWildCards = false; addModifier(SDCM_FILTER); } virtual ~SearchDataClausePath() = default; virtual SearchDataClausePath *clone() override { return new SearchDataClausePath(*this); } virtual bool toNativeQuery(Rcl::Db &, void *) override; virtual void dump(std::ostream& o) const override; }; /** * A clause coming from a NEAR or PHRASE entry field. There is only one * std::string group, and a specified distance, which applies to it. */ class SearchDataClauseDist : public SearchDataClauseSimple { public: SearchDataClauseDist(SClType tp, const std::string& txt, int slack, const std::string& fld = std::string()) : SearchDataClauseSimple(tp, txt, fld), m_slack(slack) {} virtual ~SearchDataClauseDist() = default; virtual SearchDataClauseDist *clone() override { return new SearchDataClauseDist(*this); } virtual bool toNativeQuery(Rcl::Db &, void *) override; virtual int getslack() const { return m_slack; } virtual void setslack(int slack) { m_slack = slack; } virtual void dump(std::ostream& o) const override; private: int m_slack; }; /** Subquery */ class SearchDataClauseSub : public SearchDataClause { public: SearchDataClauseSub(std::shared_ptr sub) : SearchDataClause(SCLT_SUB), m_sub(sub) {} virtual ~SearchDataClauseSub() = default; virtual SearchDataClauseSub *clone() override { return new SearchDataClauseSub(*this); } virtual bool toNativeQuery(Rcl::Db &db, void *p) override { bool ret = m_sub->toNativeQuery(db, p); if (!ret) m_reason = m_sub->getReason(); return ret; } virtual void getTerms(HighlightData& hldata) const override { m_sub.get()->getTerms(hldata); } virtual std::shared_ptr getSub() { return m_sub; } virtual void dump(std::ostream& o) const override; protected: std::shared_ptr m_sub; }; } // Namespace Rcl #endif /* _SEARCHDATA_H_INCLUDED_ */ recoll-1.36.1/rcldb/rcldoc.h0000644000175000017500000002471114444307651012525 00000000000000/* Copyright (C) 2006-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RCLDOC_H_INCLUDED_ #define _RCLDOC_H_INCLUDED_ #include #include #include #include "smallut.h" #include "rclutil.h" namespace Rcl { /** * Dumb holder for document attributes and data. * * This is used both for indexing, where fields are filled-up by the * indexer prior to adding to the index, and for querying, where * fields are filled from data stored in the index. Not all fields are * in use at both index and query times, and not all field data is * stored at index time. */ class Doc { public: //////////////////////////////////////////////////////////// // The following fields are stored into the document data record (so they // can be accessed after a query without fetching the actual document). // We indicate the routine that sets them up during indexing // Binary or url-encoded url. No transcoding: this is used to access files // Index: computed by Db::add caller. // Query: from doc data. std::string url; // When we do path translation for documents from external indexes, we // save the original path: std::string idxurl; // And the originating db. 0 is base, 1 first external etc. int idxi{0}; // Internal path for multi-doc files. // Set by FsIndexer::processone // Note: 2023-06: could be binary but all filters currently output UTF-8. This is not // enforced though, and, for example, the tar handler, which is not enabled by default // can output binary values (for binary Posix paths). // This is an issue for the python module, because, at the moment, only the URL can be retrieved // as as bytes. Maybe change doc.get() to return bytes if the key is bytes? std::string ipath; // Mime type. Set by FileInterner::internfile std::string mimetype; // File modification time as decimal ascii unix time // Set by FsIndexer::processone std::string fmtime; // Data reference date (same format). Ie: mail date // Possibly set by mimetype-specific handler // Filter::metaData["modificationdate"] std::string dmtime; // Charset we transcoded the 'text' field from (in case we want back) // Possibly set by handler std::string origcharset; // A map for textual metadata like, author, keywords, abstract, // title. The entries are possibly set by the mimetype-specific // handler. If a fieldname-to-prefix translation exists, the // terms in the value will be indexed with a prefix. // Only some predefined fields are stored in the data record: // "title", "keywords", "abstract", "author", but if a field name is // in the "stored" configuration list, it will be stored too. std::unordered_map meta; // Attribute for the "abstract" entry. true if it is just the top // of doc, not a native document attribute. Not stored directly, but // as an indicative prefix at the beginning of the abstract (ugly hack) bool syntabs{false}; // File size. This is the size of the compressed file or of the // external containing archive. // Index: Set by caller prior to Db::Add. // Query: Set from data record std::string pcbytes; // Document size, ie, size of the .odt or .xls. // Index: Set in internfile from the filter stack // Query: set from data record std::string fbytes; // Doc text size. // Index: from text.length(). // Query: set by rcldb from index data record std::string dbytes; // Doc signature. Used for up to date checks. // Index: set by Db::Add caller. Query: set from doc data. // This is opaque to rcldb, and could just as well be ctime, size, // ctime+size, md5, whatever. std::string sig; ///////////////////////////////////////////////// // The following fields don't go to the db record, so they can't // be retrieved at query time // Main document text. This is plaintext utf-8 text to be split // and indexed std::string text; ///////////////////////////////////////////////// // Misc stuff int pc{0}; // relevancy percentage, used by sortseq, convenience unsigned long xdocid{0}; // Opaque: rcldb doc identifier. // Page breaks were stored during indexing. bool haspages{false}; // Has children, either as content of file-level container or // ipath descendants. bool haschildren{false}; // During indexing: only fields from extended attributes were set, no // doc content. Allows for faster reindexing of existing doc bool onlyxattr{false}; /////////////////////////////////////////////////////////////////// void erase() { url.erase(); idxurl.erase(); idxi = 0; ipath.erase(); mimetype.erase(); fmtime.erase(); dmtime.erase(); origcharset.erase(); meta.clear(); syntabs = false; pcbytes.erase(); fbytes.erase(); dbytes.erase(); sig.erase(); text.erase(); pc = 0; xdocid = 0; haspages = false; haschildren = false; onlyxattr = false; } // Copy ensuring no shared string data, for threading issues. void copyto(Doc *d) const; Doc() { } /** Get value for named field. If value pointer is 0, just test existence */ bool getmeta(const std::string& nm, std::string *value = nullptr) const { const auto it = meta.find(nm); if (it != meta.end()) { if (value) *value = it->second; return true; } else { return false; } } /** Nocopy getvalue. sets pointer to entry value if exists */ bool peekmeta(const std::string& nm, const std::string **value = nullptr) const { const auto it = meta.find(nm); if (it != meta.end()) { if (value) *value = &(it->second); return true; } return false; } /** Check if metadata is set and not empty */ bool hasmetavalue(const std::string& nm) { const std::string *vp; if (peekmeta(nm, &vp) && !vp->empty()) { return true; } return false; } // Create entry or append text to existing entry. bool addmeta(const std::string& nm, const std::string& value) { ::addmeta(meta, nm, value); return true; } /* Is this document stored in a regular filesystem file ? * (as opposed to e.g. a webcache file). */ bool isFsFile() { std::string backend; getmeta(keybcknd, &backend); if (!backend.empty() && backend.compare("FS")) return false; return true; } void dump(bool dotext=false) const; //////////////////////////////////////////////////////////////// // The official names for recoll native fields when used in a text // context (ie: the python interface duplicates some of the fixed // fields in the meta array, these are the names used). Defined in // rcldoc.cpp. Fields stored in the meta[] array (ie, title, // author), _must_ use these canonical values, not aliases. This is // enforced in internfile.cpp and misc other bits of metadata-gathering // code static const std::string keyurl; // url // childurl. This is set when working with the parent of the result, to hold // the child of interest url, typically to highlight a directory entry static const std::string keychildurl; // file name. This is set for filesystem-level containers or // documents, and not inherited by subdocuments (which can get a // keyfn anyway from, e.g, an attachment filename value). Subdocs // used to inherit the file name, but this was undesirable (you // usually don't want to see all subdocs when searching for the // file name). Instead the container file name is now set in the // document record but not indexed (see next entry). static const std::string keyfn; // Container file name. This is set for all subdocuments of a // given top level container. It is not indexed by default but // stored in the document record keyfn field if this is still // empty when we create it, for display purposes. static const std::string keyctfn; static const std::string keyipt; // ipath static const std::string keytp; // mime type static const std::string keyfmt; // file mtime static const std::string keydmt; // document mtime #ifdef EXT4_BIRTH_TIME static const std::string keybrt; // file birthtime #endif static const std::string keymt; // mtime dmtime if set else fmtime static const std::string keyoc; // original charset static const std::string keypcs; // document outer container size static const std::string keyfs; // document size static const std::string keyds; // document text size static const std::string keysz; // dbytes if set else fbytes else pcbytes static const std::string keysig; // sig static const std::string keyrr; // relevancy rating static const std::string keycc; // Collapse count static const std::string keyabs; // abstract static const std::string keyau; // author static const std::string keytt; // title static const std::string keykw; // keywords static const std::string keymd5; // file md5 checksum static const std::string keybcknd; // backend type when not from the fs // udi back from index. Only set by Rcl::Query::getdoc(). static const std::string keyudi; static const std::string keyapptg; // apptag. Set from localfields (fs only) static const std::string keybght; // beagle hit type ("beagleHitType") }; extern bool docsToPaths(const std::vector &docs, std::vector &paths); } #endif /* _RCLDOC_H_INCLUDED_ */ recoll-1.36.1/rcldb/expansiondbs.h0000644000175000017500000000432514426501047013746 00000000000000/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _EXPANSIONDBS_H_INCLUDED_ #define _EXPANSIONDBS_H_INCLUDED_ #include #include #include #include "unacpp.h" #include "synfamily.h" /** Specialization and overall creation code for the term expansion mechanism * defined in synfamily.h */ namespace Rcl { /** A Capitals/Diacritics removal functor for using with * XapComputableSynFamMember. The input term transformation always uses * UNACFOLD. Post-expansion filtering uses either UNAC or FOLD */ class SynTermTransUnac : public SynTermTrans { public: /** Constructor * @param op defines if we remove diacritics, case or both */ SynTermTransUnac(UnacOp op) : m_op(op) {} virtual ~SynTermTransUnac() = default; virtual std::string name() { std::string nm("Unac: "); if (m_op & UNACOP_UNAC) nm += "UNAC "; if (m_op & UNACOP_FOLD) nm += "FOLD "; return nm; } virtual std::string operator()(const std::string& in) { std::string out; unacmaybefold(in, out, "UTF-8", m_op); LOGDEB2("SynTermTransUnac(" << m_op << "): in [" << in << "] out [" << out << "]\n"); return out; } UnacOp m_op; }; /** Walk the Xapian term list and create all the expansion dbs in one go. */ extern bool createExpansionDbs(Xapian::WritableDatabase& wdb, const std::vector& langs); } #endif /* _EXPANSIONDBS_H_INCLUDED_ */ recoll-1.36.1/Makefile.in0000644000175000017500000034411614521160720012057 00000000000000# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @COND_BTIME_TRUE@am__append_1 = -DEXT4_BIRTH_TIME @MAKE_RECOLL_LIB_TRUE@@PUBLIC_LIB_TRUE@am__append_2 = librecoll.la @MAKE_RECOLL_LIB_TRUE@@PUBLIC_LIB_FALSE@am__append_3 = librecoll.la bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) @MAKEINDEXER_TRUE@am__append_4 = recollindex @MAKECMDLINE_TRUE@am__append_5 = recollq @MAKEXADUMP_TRUE@am__append_6 = xadump subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(dist_filter_DATA) \ $(am__inc_HEADERS_DIST) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/common/autoconfig.h CONFIG_CLEAN_FILES = python/recoll/setup.py python/recoll/setup.cfg \ python/pychm/setup.py python/pychm/setup.cfg \ python/pyaspell/setup.py CONFIG_CLEAN_VPATH_FILES = @MAKEINDEXER_TRUE@am__EXEEXT_1 = recollindex$(EXEEXT) @MAKECMDLINE_TRUE@am__EXEEXT_2 = recollq$(EXEEXT) @MAKEXADUMP_TRUE@am__EXEEXT_3 = xadump$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \ "$(DESTDIR)$(librcldir)" "$(DESTDIR)$(man1dir)" \ "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(defconfdir)" \ "$(DESTDIR)$(filterdir)" "$(DESTDIR)$(rdocdir)" \ "$(DESTDIR)$(systemd_system_unitdir)" \ "$(DESTDIR)$(systemd_user_unitdir)" "$(DESTDIR)$(incdir)" PROGRAMS = $(bin_PROGRAMS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } LTLIBRARIES = $(lib_LTLIBRARIES) $(librcl_LTLIBRARIES) am__DEPENDENCIES_1 = librecoll_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__dirstamp = $(am__leading_dot)dirstamp am_librecoll_la_OBJECTS = aspell/rclaspell.lo bincimapmime/convert.lo \ bincimapmime/mime-parsefull.lo \ bincimapmime/mime-parseonlyheader.lo \ bincimapmime/mime-printbody.lo bincimapmime/mime.lo \ common/webstore.lo common/cstr.lo common/rclconfig.lo \ common/rclinit.lo common/syngroups.lo common/textsplit.lo \ common/textsplitko.lo common/unacpp.lo common/utf8fn.lo \ index/webqueuefetcher.lo index/checkretryfailed.lo \ index/exefetcher.lo index/fetcher.lo index/fsfetcher.lo \ index/idxdiags.lo index/idxstatus.lo index/mimetype.lo \ index/subtreelist.lo internfile/extrameta.lo \ internfile/htmlparse.lo internfile/internfile.lo \ internfile/mh_exec.lo internfile/mh_execm.lo \ internfile/mh_html.lo internfile/mh_mail.lo \ internfile/mh_mbox.lo internfile/mh_text.lo \ internfile/mh_xslt.lo internfile/mimehandler.lo \ internfile/myhtmlparse.lo internfile/txtdcode.lo \ internfile/uncomp.lo query/docseq.lo query/docseqdb.lo \ query/docseqhist.lo query/dynconf.lo query/filtseq.lo \ common/plaintorich.lo query/qresultstore.lo query/recollq.lo \ query/reslistpager.lo query/sortseq.lo query/wasaparse.lo \ query/wasaparseaux.lo rcldb/daterange.lo rcldb/expansiondbs.lo \ rcldb/rclabstract.lo rcldb/rclabsfromtext.lo rcldb/rcldb.lo \ rcldb/rcldoc.lo rcldb/rcldups.lo rcldb/rclquery.lo \ rcldb/rclterms.lo rcldb/rclvalues.lo rcldb/searchdata.lo \ rcldb/searchdatatox.lo rcldb/searchdataxml.lo rcldb/stemdb.lo \ rcldb/stoplist.lo rcldb/synfamily.lo unac/unac.lo \ utils/appformime.lo utils/base64.lo utils/cancelcheck.lo \ utils/chrono.lo utils/circache.lo utils/closefrom.lo \ utils/cmdtalk.lo utils/conftree.lo utils/copyfile.lo \ utils/cpuconf.lo utils/dlib.lo utils/ecrontab.lo \ utils/execmd.lo utils/fileudi.lo utils/fstreewalk.lo \ utils/hldata.lo utils/idfile.lo utils/listmem.lo utils/log.lo \ utils/md5.lo utils/md5ut.lo utils/mimeparse.lo utils/miniz.lo \ utils/netcon.lo utils/pathut.lo utils/pxattr.lo \ utils/rclionice.lo utils/rclutil.lo utils/readfile.lo \ utils/smallut.lo utils/strmatcher.lo utils/transcode.lo \ utils/utf8iter.lo utils/wipedir.lo utils/x11mon.lo \ utils/zlibut.lo librecoll_la_OBJECTS = $(am_librecoll_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = librecoll_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(librecoll_la_LDFLAGS) $(LDFLAGS) -o $@ @MAKE_RECOLL_LIB_TRUE@@PUBLIC_LIB_FALSE@am_librecoll_la_rpath = \ @MAKE_RECOLL_LIB_TRUE@@PUBLIC_LIB_FALSE@ -rpath $(librcldir) @MAKE_RECOLL_LIB_TRUE@@PUBLIC_LIB_TRUE@am_librecoll_la_rpath = -rpath \ @MAKE_RECOLL_LIB_TRUE@@PUBLIC_LIB_TRUE@ $(libdir) am_recollindex_OBJECTS = index/checkindexed.$(OBJEXT) \ index/fsindexer.$(OBJEXT) index/indexer.$(OBJEXT) \ index/rclmonprc.$(OBJEXT) index/rclmonrcv.$(OBJEXT) \ index/recollindex.$(OBJEXT) index/webqueue.$(OBJEXT) recollindex_OBJECTS = $(am_recollindex_OBJECTS) recollindex_DEPENDENCIES = librecoll.la am_recollq_OBJECTS = query/recollqmain.$(OBJEXT) recollq_OBJECTS = $(am_recollq_OBJECTS) recollq_DEPENDENCIES = librecoll.la am_xadump_OBJECTS = query/xadump.$(OBJEXT) xadump_OBJECTS = $(am_xadump_OBJECTS) xadump_DEPENDENCIES = librecoll.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/common depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = aspell/$(DEPDIR)/rclaspell.Plo \ bincimapmime/$(DEPDIR)/convert.Plo \ bincimapmime/$(DEPDIR)/mime-parsefull.Plo \ bincimapmime/$(DEPDIR)/mime-parseonlyheader.Plo \ bincimapmime/$(DEPDIR)/mime-printbody.Plo \ bincimapmime/$(DEPDIR)/mime.Plo common/$(DEPDIR)/cstr.Plo \ common/$(DEPDIR)/plaintorich.Plo \ common/$(DEPDIR)/rclconfig.Plo common/$(DEPDIR)/rclinit.Plo \ common/$(DEPDIR)/syngroups.Plo common/$(DEPDIR)/textsplit.Plo \ common/$(DEPDIR)/textsplitko.Plo common/$(DEPDIR)/unacpp.Plo \ common/$(DEPDIR)/utf8fn.Plo common/$(DEPDIR)/webstore.Plo \ index/$(DEPDIR)/checkindexed.Po \ index/$(DEPDIR)/checkretryfailed.Plo \ index/$(DEPDIR)/exefetcher.Plo index/$(DEPDIR)/fetcher.Plo \ index/$(DEPDIR)/fsfetcher.Plo index/$(DEPDIR)/fsindexer.Po \ index/$(DEPDIR)/idxdiags.Plo index/$(DEPDIR)/idxstatus.Plo \ index/$(DEPDIR)/indexer.Po index/$(DEPDIR)/mimetype.Plo \ index/$(DEPDIR)/rclmonprc.Po index/$(DEPDIR)/rclmonrcv.Po \ index/$(DEPDIR)/recollindex.Po index/$(DEPDIR)/subtreelist.Plo \ index/$(DEPDIR)/webqueue.Po \ index/$(DEPDIR)/webqueuefetcher.Plo \ internfile/$(DEPDIR)/extrameta.Plo \ internfile/$(DEPDIR)/htmlparse.Plo \ internfile/$(DEPDIR)/internfile.Plo \ internfile/$(DEPDIR)/mh_exec.Plo \ internfile/$(DEPDIR)/mh_execm.Plo \ internfile/$(DEPDIR)/mh_html.Plo \ internfile/$(DEPDIR)/mh_mail.Plo \ internfile/$(DEPDIR)/mh_mbox.Plo \ internfile/$(DEPDIR)/mh_text.Plo \ internfile/$(DEPDIR)/mh_xslt.Plo \ internfile/$(DEPDIR)/mimehandler.Plo \ internfile/$(DEPDIR)/myhtmlparse.Plo \ internfile/$(DEPDIR)/txtdcode.Plo \ internfile/$(DEPDIR)/uncomp.Plo query/$(DEPDIR)/docseq.Plo \ query/$(DEPDIR)/docseqdb.Plo query/$(DEPDIR)/docseqhist.Plo \ query/$(DEPDIR)/dynconf.Plo query/$(DEPDIR)/filtseq.Plo \ query/$(DEPDIR)/qresultstore.Plo query/$(DEPDIR)/recollq.Plo \ query/$(DEPDIR)/recollqmain.Po \ query/$(DEPDIR)/reslistpager.Plo query/$(DEPDIR)/sortseq.Plo \ query/$(DEPDIR)/wasaparse.Plo query/$(DEPDIR)/wasaparseaux.Plo \ query/$(DEPDIR)/xadump.Po rcldb/$(DEPDIR)/daterange.Plo \ rcldb/$(DEPDIR)/expansiondbs.Plo \ rcldb/$(DEPDIR)/rclabsfromtext.Plo \ rcldb/$(DEPDIR)/rclabstract.Plo rcldb/$(DEPDIR)/rcldb.Plo \ rcldb/$(DEPDIR)/rcldoc.Plo rcldb/$(DEPDIR)/rcldups.Plo \ rcldb/$(DEPDIR)/rclquery.Plo rcldb/$(DEPDIR)/rclterms.Plo \ rcldb/$(DEPDIR)/rclvalues.Plo rcldb/$(DEPDIR)/searchdata.Plo \ rcldb/$(DEPDIR)/searchdatatox.Plo \ rcldb/$(DEPDIR)/searchdataxml.Plo rcldb/$(DEPDIR)/stemdb.Plo \ rcldb/$(DEPDIR)/stoplist.Plo rcldb/$(DEPDIR)/synfamily.Plo \ unac/$(DEPDIR)/unac.Plo utils/$(DEPDIR)/appformime.Plo \ utils/$(DEPDIR)/base64.Plo utils/$(DEPDIR)/cancelcheck.Plo \ utils/$(DEPDIR)/chrono.Plo utils/$(DEPDIR)/circache.Plo \ utils/$(DEPDIR)/closefrom.Plo utils/$(DEPDIR)/cmdtalk.Plo \ utils/$(DEPDIR)/conftree.Plo utils/$(DEPDIR)/copyfile.Plo \ utils/$(DEPDIR)/cpuconf.Plo utils/$(DEPDIR)/dlib.Plo \ utils/$(DEPDIR)/ecrontab.Plo utils/$(DEPDIR)/execmd.Plo \ utils/$(DEPDIR)/fileudi.Plo utils/$(DEPDIR)/fstreewalk.Plo \ utils/$(DEPDIR)/hldata.Plo utils/$(DEPDIR)/idfile.Plo \ utils/$(DEPDIR)/listmem.Plo utils/$(DEPDIR)/log.Plo \ utils/$(DEPDIR)/md5.Plo utils/$(DEPDIR)/md5ut.Plo \ utils/$(DEPDIR)/mimeparse.Plo utils/$(DEPDIR)/miniz.Plo \ utils/$(DEPDIR)/netcon.Plo utils/$(DEPDIR)/pathut.Plo \ utils/$(DEPDIR)/pxattr.Plo utils/$(DEPDIR)/rclionice.Plo \ utils/$(DEPDIR)/rclutil.Plo utils/$(DEPDIR)/readfile.Plo \ utils/$(DEPDIR)/smallut.Plo utils/$(DEPDIR)/strmatcher.Plo \ utils/$(DEPDIR)/transcode.Plo utils/$(DEPDIR)/utf8iter.Plo \ utils/$(DEPDIR)/wipedir.Plo utils/$(DEPDIR)/x11mon.Plo \ utils/$(DEPDIR)/zlibut.Plo am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ -e s/c++$$/h++/ -e s/c$$/h/ YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) AM_V_YACC = $(am__v_YACC_@AM_V@) am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) am__v_YACC_0 = @echo " YACC " $@; am__v_YACC_1 = YLWRAP = $(top_srcdir)/ylwrap COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(librecoll_la_SOURCES) $(recollindex_SOURCES) \ $(recollq_SOURCES) $(xadump_SOURCES) DIST_SOURCES = $(librecoll_la_SOURCES) $(recollindex_SOURCES) \ $(recollq_SOURCES) $(xadump_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac man1dir = $(mandir)/man1 man5dir = $(mandir)/man5 NROFF = nroff MANS = $(dist_man1_MANS) $(dist_man5_MANS) DATA = $(defconf_DATA) $(dist_filter_DATA) $(rdoc_DATA) \ $(systemd_system_unit_DATA) $(systemd_user_unit_DATA) am__inc_HEADERS_DIST = common/plaintorich.h common/rclconfig.h \ common/rclinit.h index/idxstatus.h internfile/Filter.h \ internfile/internfile.h internfile/mimehandler.h \ query/docseq.h query/docseqdb.h query/qresultstore.h \ query/reslistpager.h query/wasatorcl.h rcldb/rcldb.h \ rcldb/rcldoc.h rcldb/rclquery.h rcldb/searchdata.h \ utils/hldata.h utils/log.h utils/pathut.h utils/rclutil.h \ utils/readfile.h utils/smallut.h HEADERS = $(inc_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir distdir-am dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(dist_man1_MANS) $(dist_man5_MANS) \ $(srcdir)/Makefile.in $(top_srcdir)/common/autoconfig.h.in \ $(top_srcdir)/python/pyaspell/setup.py.in \ $(top_srcdir)/python/pychm/setup.cfg.in \ $(top_srcdir)/python/pychm/setup.py.in \ $(top_srcdir)/python/recoll/setup.cfg.in \ $(top_srcdir)/python/recoll/setup.py.in AUTHORS COPYING \ ChangeLog INSTALL README compile config.guess config.rpath \ config.sub depcomp install-sh ltmain.sh missing \ query/wasaparse.cpp query/wasaparse.hpp ylwrap DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip # Exists only to be overridden by the user if desired. AM_DISTCHECK_DVI_TARGET = dvi distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ $(am__append_1) CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEF_EXT4_BIRTH_TIME = @DEF_EXT4_BIRTH_TIME@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INCICONV = @INCICONV@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBFAM = @LIBFAM@ LIBICONV = @LIBICONV@ LIBOBJS = @LIBOBJS@ LIBQZEITGEIST = @LIBQZEITGEIST@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ NO_UNDEF_LINK_FLAG = @NO_UNDEF_LINK_FLAG@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QMAKE = @QMAKE@ QMAKEPATH = @QMAKEPATH@ QMAKE_DISABLE_GUIDEBUG = @QMAKE_DISABLE_GUIDEBUG@ QMAKE_DISABLE_WEBENGINE = @QMAKE_DISABLE_WEBENGINE@ QMAKE_DISABLE_WEBKIT = @QMAKE_DISABLE_WEBKIT@ QMAKE_DISABLE_ZEITGEIST = @QMAKE_DISABLE_ZEITGEIST@ QMAKE_ENABLE_GUIDEBUG = @QMAKE_ENABLE_GUIDEBUG@ QMAKE_ENABLE_WEBENGINE = @QMAKE_ENABLE_WEBENGINE@ QMAKE_ENABLE_WEBKIT = @QMAKE_ENABLE_WEBKIT@ QMAKE_ENABLE_ZEITGEIST = @QMAKE_ENABLE_ZEITGEIST@ QTGUI = @QTGUI@ RANLIB = @RANLIB@ RCLLIBVERSION = @RCLLIBVERSION@ RCLVERSION = @RCLVERSION@ RECOLL_DATADIR = @RECOLL_DATADIR@ RECOLL_PUBLIC_LIB = @RECOLL_PUBLIC_LIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_SYSTEM_UNIT_DIR = @SYSTEMD_SYSTEM_UNIT_DIR@ SYSTEMD_USER_UNIT_DIR = @SYSTEMD_USER_UNIT_DIR@ VERSION = @VERSION@ XAPIAN_CFLAGS = @XAPIAN_CFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ XMKMF = @XMKMF@ XSLT_CFLAGS = @XSLT_CFLAGS@ XSLT_LIBS = @XSLT_LIBS@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_LIBX11 = @X_LIBX11@ X_PRE_LIBS = @X_PRE_LIBS@ YACC = @YACC@ YFLAGS = @YFLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ aspellProg = @aspellProg@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ fileProg = @fileProg@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # Conditionally enable building the small test drivers, but don't # distribute them, they are not generally useful @COND_TESTMAINS_TRUE@MAYBE_TESTMAINS = testmains @COND_RCLGREP_TRUE@MAYBE_RCLGREP = rclgrep @COND_RCLGREP_TRUE@rclgrep_man1_MANS = doc/man/rclgrep.1 SUBDIRS = . $(MAYBE_TESTMAINS) $(MAYBE_RCLGREP) DIST_SUBDIRS = . COMMONCPPFLAGS = -I. \ -I$(top_srcdir)/aspell \ -I$(top_srcdir)/bincimapmime \ -I$(top_srcdir)/common \ -I$(top_srcdir)/index \ -I$(top_srcdir)/internfile \ -I$(top_srcdir)/rcldb \ -I$(top_srcdir)/unac \ -I$(top_srcdir)/utils \ -I$(top_srcdir)/xaposix \ -DBUILDING_RECOLL AM_CPPFLAGS = -Wall -Wno-unused -std=c++14 \ $(COMMONCPPFLAGS) \ $(INCICONV) \ $(XAPIAN_CFLAGS) \ $(XSLT_CFLAGS) \ $(X_CFLAGS) \ -DRECOLL_DATADIR=\"${pkgdatadir}\" \ -DREADFILE_ENABLE_ZLIB -DREADFILE_ENABLE_MINIZ -DREADFILE_ENABLE_MD5 \ -D_GNU_SOURCE \ $(DEFS) @COND_BTIME_TRUE@ext4_birth_time = 1 ACLOCAL_AMFLAGS = -I m4 @NOTHREADS_FALSE@LIBTHREADS = $(LIBSYSTHREADS) @NOTHREADS_TRUE@LIBTHREADS = @PUBLIC_LIB_TRUE@lib_LTLIBRARIES = $(am__append_2) @PUBLIC_LIB_FALSE@librcldir = $(libdir)/recoll @PUBLIC_LIB_FALSE@librcl_LTLIBRARIES = $(am__append_3) librecoll_la_SOURCES = \ aspell/rclaspell.cpp \ aspell/rclaspell.h \ bincimapmime/convert.cc \ bincimapmime/convert.h \ bincimapmime/mime-inputsource.h \ bincimapmime/mime-parsefull.cc \ bincimapmime/mime-parseonlyheader.cc \ bincimapmime/mime-printbody.cc \ bincimapmime/mime-utils.h \ bincimapmime/mime.cc \ bincimapmime/mime.h \ common/webstore.cpp \ common/webstore.h \ common/conf_post.h \ common/cstr.cpp \ common/cstr.h \ common/rclconfig.cpp \ common/rclconfig.h \ common/rclinit.cpp \ common/rclinit.h \ common/syngroups.cpp \ common/syngroups.h \ common/textsplit.cpp \ common/textsplitko.cpp \ common/textsplit.h \ common/unacpp.cpp \ common/unacpp.h \ common/uproplist.h \ common/utf8fn.cpp \ common/utf8fn.h \ index/webqueuefetcher.cpp \ index/webqueuefetcher.h \ index/checkretryfailed.cpp \ index/checkretryfailed.h \ index/exefetcher.cpp \ index/exefetcher.h \ index/fetcher.cpp \ index/fetcher.h \ index/fsfetcher.cpp \ index/fsfetcher.h \ index/idxdiags.h \ index/idxdiags.cpp \ index/idxstatus.h \ index/idxstatus.cpp \ index/mimetype.cpp \ index/mimetype.h \ index/rclmon.h \ index/recollindex.h \ index/subtreelist.cpp \ index/subtreelist.h \ internfile/Filter.h \ internfile/extrameta.cpp \ internfile/extrameta.h \ internfile/htmlparse.cpp \ internfile/htmlparse.h \ internfile/indextext.h \ internfile/internfile.cpp \ internfile/internfile.h \ internfile/mh_exec.cpp \ internfile/mh_exec.h \ internfile/mh_execm.cpp \ internfile/mh_execm.h \ internfile/mh_html.cpp \ internfile/mh_html.h \ internfile/mh_mail.cpp \ internfile/mh_mail.h \ internfile/mh_mbox.cpp \ internfile/mh_mbox.h \ internfile/mh_null.h \ internfile/mh_symlink.h \ internfile/mh_text.cpp \ internfile/mh_text.h \ internfile/mh_unknown.h \ internfile/mh_xslt.cpp \ internfile/mh_xslt.h \ internfile/mimehandler.cpp \ internfile/mimehandler.h \ internfile/myhtmlparse.cpp \ internfile/myhtmlparse.h \ internfile/txtdcode.cpp \ internfile/uncomp.cpp \ internfile/uncomp.h \ query/docseq.cpp \ query/docseq.h \ query/docseqdb.cpp \ query/docseqdb.h \ query/docseqdocs.h \ query/docseqhist.cpp \ query/docseqhist.h \ query/dynconf.cpp \ query/dynconf.h \ query/filtseq.cpp \ query/filtseq.h \ common/plaintorich.cpp \ common/plaintorich.h \ query/qresultstore.cpp \ query/qresultstore.h \ query/recollq.cpp \ query/recollq.h \ query/reslistpager.cpp \ query/reslistpager.h \ query/sortseq.cpp \ query/sortseq.h \ query/wasaparse.ypp \ query/wasaparseaux.cpp \ query/wasaparserdriver.h \ query/wasatorcl.h \ rcldb/daterange.cpp \ rcldb/daterange.h \ rcldb/expansiondbs.cpp \ rcldb/expansiondbs.h \ rcldb/rclabstract.cpp \ rcldb/rclabsfromtext.cpp \ rcldb/rcldb.cpp \ rcldb/rcldb.h \ rcldb/rcldb_p.h \ rcldb/rcldoc.cpp \ rcldb/rcldoc.h \ rcldb/rcldups.cpp \ rcldb/rclquery.cpp \ rcldb/rclquery.h \ rcldb/rclquery_p.h \ rcldb/rclterms.cpp \ rcldb/rclvalues.cpp \ rcldb/rclvalues.h \ rcldb/searchdata.cpp \ rcldb/searchdata.h \ rcldb/searchdatatox.cpp \ rcldb/searchdataxml.cpp \ rcldb/stemdb.cpp \ rcldb/stemdb.h \ rcldb/stoplist.cpp \ rcldb/stoplist.h \ rcldb/synfamily.cpp \ rcldb/synfamily.h \ rcldb/termproc.h \ rcldb/xmacros.h \ unac/unac.cpp \ unac/unac.h \ unac/unac_version.h \ utils/appformime.cpp \ utils/appformime.h \ utils/base64.cpp \ utils/base64.h \ utils/cancelcheck.cpp \ utils/cancelcheck.h \ utils/chrono.h \ utils/chrono.cpp \ utils/circache.cpp \ utils/circache.h \ utils/closefrom.cpp \ utils/closefrom.h \ utils/cmdtalk.cpp \ utils/cmdtalk.h \ utils/conftree.cpp \ utils/conftree.h \ utils/copyfile.cpp \ utils/copyfile.h \ utils/cpuconf.cpp \ utils/cpuconf.h \ utils/damlev.h \ utils/dlib.cpp \ utils/dlib.h \ utils/ecrontab.cpp \ utils/ecrontab.h \ utils/execmd.cpp \ utils/execmd.h \ utils/fileudi.cpp \ utils/fileudi.h \ utils/fstreewalk.cpp \ utils/fstreewalk.h \ utils/hldata.h \ utils/hldata.cpp \ utils/idfile.cpp \ utils/idfile.h \ utils/listmem.cpp \ utils/listmem.h \ utils/log.cpp \ utils/log.h \ utils/md5.cpp \ utils/md5.h \ utils/md5ut.cpp \ utils/md5ut.h \ utils/mimeparse.cpp \ utils/mimeparse.h \ utils/miniz.cpp \ utils/miniz.h \ utils/netcon.cpp \ utils/netcon.h \ utils/pathut.cpp \ utils/pathut.h \ utils/picoxml.h \ utils/pxattr.cpp \ utils/pxattr.h \ utils/rclionice.cpp \ utils/rclionice.h \ utils/rclutil.h \ utils/rclutil.cpp \ utils/readfile.cpp \ utils/readfile.h \ utils/smallut.cpp \ utils/smallut.h \ utils/strmatcher.cpp \ utils/strmatcher.h \ utils/transcode.cpp \ utils/transcode.h \ utils/utf8iter.cpp \ utils/utf8iter.h \ utils/wipedir.cpp \ utils/wipedir.h \ utils/workqueue.h \ utils/x11mon.cpp \ utils/x11mon.h \ utils/zlibut.cpp \ utils/zlibut.h \ xaposix/safefcntl.h \ xaposix/safesysstat.h \ xaposix/safesyswait.h \ xaposix/safeunistd.h BUILT_SOURCES = query/wasaparse.cpp AM_YFLAGS = -d # We use -release: the lib is only shared between recoll programs from the same release. # So the lib is just named after the recoll version (e.g librecoll-1.27.1.so) # -version-info $(VERSION_INFO) would handle ABI compat issues, we don't need it librecoll_la_LDFLAGS = -release $(VERSION) -no-undefined @NO_UNDEF_LINK_FLAG@ librecoll_la_LIBADD = $(XSLT_LIBS) $(XAPIAN_LIBS) $(LIBICONV) $(X_LIBX11) $(LIBTHREADS) @PUBLIC_LIB_TRUE@incdir = $(includedir)/recoll @PUBLIC_LIB_TRUE@inc_HEADERS = \ @PUBLIC_LIB_TRUE@ common/plaintorich.h \ @PUBLIC_LIB_TRUE@ common/rclconfig.h \ @PUBLIC_LIB_TRUE@ common/rclinit.h \ @PUBLIC_LIB_TRUE@ index/idxstatus.h \ @PUBLIC_LIB_TRUE@ internfile/Filter.h \ @PUBLIC_LIB_TRUE@ internfile/internfile.h \ @PUBLIC_LIB_TRUE@ internfile/mimehandler.h \ @PUBLIC_LIB_TRUE@ query/docseq.h \ @PUBLIC_LIB_TRUE@ query/docseqdb.h \ @PUBLIC_LIB_TRUE@ query/qresultstore.h \ @PUBLIC_LIB_TRUE@ query/reslistpager.h \ @PUBLIC_LIB_TRUE@ query/wasatorcl.h \ @PUBLIC_LIB_TRUE@ rcldb/rcldb.h \ @PUBLIC_LIB_TRUE@ rcldb/rcldoc.h \ @PUBLIC_LIB_TRUE@ rcldb/rclquery.h \ @PUBLIC_LIB_TRUE@ rcldb/searchdata.h \ @PUBLIC_LIB_TRUE@ utils/hldata.h \ @PUBLIC_LIB_TRUE@ utils/log.h \ @PUBLIC_LIB_TRUE@ utils/pathut.h \ @PUBLIC_LIB_TRUE@ utils/rclutil.h \ @PUBLIC_LIB_TRUE@ utils/readfile.h \ @PUBLIC_LIB_TRUE@ utils/smallut.h @MAKEXADUMP_TRUE@xadump_man1_MANS = doc/man/xadump.1 recollindex_SOURCES = \ index/checkindexed.cpp \ index/checkindexed.h \ index/fsindexer.cpp \ index/fsindexer.h \ index/indexer.cpp \ index/indexer.h \ index/rclmonprc.cpp \ index/rclmonrcv.cpp \ index/recollindex.cpp \ index/webqueue.cpp \ index/webqueue.h recollindex_LDADD = librecoll.la recollq_SOURCES = query/recollqmain.cpp recollq_LDADD = librecoll.la xadump_SOURCES = query/xadump.cpp xadump_LDADD = librecoll.la $(XAPIAN_LIBS) $(LIBICONV) EXTRA_DIST = \ bincimapmime/00README.recoll bincimapmime/AUTHORS bincimapmime/COPYING \ \ desktop/hotrecoll.py \ desktop/recoll.appdata.xml \ desktop/recollindex.desktop \ desktop/recoll_index_on_ac.sh \ desktop/recoll-searchgui.desktop \ desktop/recoll.png desktop/recoll.svg desktop/recoll.xcf \ \ doc/man/recoll.1 doc/man/recollq.1 \ doc/man/recollindex.1 doc/man/rclgrep.1 \ doc/man/xadump.1 doc/man/recoll.conf.5 \ \ doc/prog/Makefile doc/prog/Doxyfile doc/prog/filters.txt doc/prog/top.txt \ \ doc/user/usermanual.html doc/user/docbook-xsl.css doc/user/docbook.css \ doc/user/Makefile doc/user/recoll.conf.xml \ doc/user/custom.xsl doc/user/usermanual.xml \ \ filters/injectcommon.sh filters/recfiltcommon filters/rcltxtlines.py \ \ index/rclmon.sh \ index/recollindex@.service \ index/recollindex.service \ \ kde/kioslave/kio_recoll/00README.txt \ kde/kioslave/kio_recoll/CMakeLists.txt \ kde/kioslave/kio_recoll/data/help.html \ kde/kioslave/kio_recoll/data/searchable.html \ kde/kioslave/kio_recoll/data/welcome.html \ kde/kioslave/kio_recoll/dirif.cpp \ kde/kioslave/kio_recoll/htmlif.cpp \ kde/kioslave/kio_recoll/kio_recoll.cpp \ kde/kioslave/kio_recoll/kio_recoll.h \ kde/kioslave/kio_recoll/recoll.json \ \ kde/kioslave/kio_recoll-kde4/00README.txt \ kde/kioslave/kio_recoll-kde4/CMakeLists.txt \ kde/kioslave/kio_recoll-kde4/data/help.html \ kde/kioslave/kio_recoll-kde4/data/searchable.html \ kde/kioslave/kio_recoll-kde4/data/welcome.html \ kde/kioslave/kio_recoll-kde4/dirif.cpp \ kde/kioslave/kio_recoll-kde4/htmlif.cpp \ kde/kioslave/kio_recoll-kde4/kio_recoll.cpp \ kde/kioslave/kio_recoll-kde4/kio_recoll.h \ kde/kioslave/kio_recoll-kde4/recollf.protocol \ kde/kioslave/kio_recoll-kde4/recollnolist.protocol \ kde/kioslave/kio_recoll-kde4/recoll.protocol \ \ kde/krunner/recollrunner.json \ kde/krunner/recollrunner.h \ kde/krunner/recollrunner.cpp \ kde/krunner/CMakeLists.txt \ \ query/location.hh query/position.hh query/stack.hh \ \ qtgui/actsearch.ui \ qtgui/actsearch_w.cpp \ qtgui/actsearch_w.h \ qtgui/advsearch.ui \ qtgui/advsearch_w.cpp \ qtgui/advsearch_w.h \ qtgui/advshist.cpp \ qtgui/advshist.h \ qtgui/confgui/confgui.cpp \ qtgui/confgui/confgui.h \ qtgui/confgui/confguiindex.cpp \ qtgui/confgui/confguiindex.h \ qtgui/crontool.cpp \ qtgui/crontool.h \ qtgui/crontool.ui \ qtgui/firstidx.h \ qtgui/firstidx.ui \ qtgui/fragbuts.cpp \ qtgui/fragbuts.h \ qtgui/guiutils.cpp \ qtgui/guiutils.h \ qtgui/i18n/*.ts \ qtgui/idxmodel.cpp \ qtgui/idxmodel.h \ qtgui/idxsched.h \ qtgui/idxsched.ui \ qtgui/images/asearch.png \ qtgui/images/cancel.png \ qtgui/images/close.png \ qtgui/images/clock.png \ qtgui/images/menu.png \ qtgui/images/code-block.png \ qtgui/images/down.png \ qtgui/images/firstpage.png \ qtgui/images/history.png \ qtgui/images/interro.png \ qtgui/images/nextpage.png \ qtgui/images/prevpage.png \ qtgui/images/recoll.icns \ qtgui/images/recoll.png \ qtgui/images/sortparms.png \ qtgui/images/spell.png \ qtgui/images/table.png \ qtgui/images/up.png \ qtgui/main.cpp \ qtgui/mtpics/License_sidux.txt \ qtgui/mtpics/README \ qtgui/mtpics/aptosid-book.png \ qtgui/mtpics/aptosid-manual-copyright.txt \ qtgui/mtpics/aptosid-manual.png \ qtgui/mtpics/archive.png \ qtgui/mtpics/book.png \ qtgui/mtpics/bookchap.png \ qtgui/mtpics/document.png \ qtgui/mtpics/drawing.png \ qtgui/mtpics/emblem-symbolic-link.png \ qtgui/mtpics/folder.png \ qtgui/mtpics/html.png \ qtgui/mtpics/image.png \ qtgui/mtpics/message.png \ qtgui/mtpics/mozilla_doc.png \ qtgui/mtpics/pdf.png \ qtgui/mtpics/pidgin.png \ qtgui/mtpics/postscript.png \ qtgui/mtpics/presentation.png \ qtgui/mtpics/sidux-book.png \ qtgui/mtpics/soffice.png \ qtgui/mtpics/source.png \ qtgui/mtpics/sownd.png \ qtgui/mtpics/spreadsheet.png \ qtgui/mtpics/text-x-python.png \ qtgui/mtpics/txt.png \ qtgui/mtpics/video.png \ qtgui/mtpics/wordprocessing.png \ qtgui/multisave.cpp \ qtgui/multisave.h \ qtgui/preview_load.cpp \ qtgui/preview_load.h \ qtgui/preview_plaintorich.cpp \ qtgui/preview_plaintorich.h \ qtgui/preview_w.cpp \ qtgui/preview_w.h \ qtgui/preview.ui \ qtgui/ptrans.ui \ qtgui/ptrans_w.cpp \ qtgui/ptrans_w.h \ qtgui/rclhelp.cpp \ qtgui/rclhelp.h \ qtgui/rclm_idx.cpp \ qtgui/rclm_menus.cpp \ qtgui/rclm_preview.cpp \ qtgui/rclm_saveload.cpp \ qtgui/rclm_sidefilters.cpp \ qtgui/rclm_view.cpp \ qtgui/rclm_wins.cpp \ qtgui/rclmain.ui \ qtgui/rclmain_w.cpp \ qtgui/rclmain_w.h \ qtgui/rclzg.cpp \ qtgui/rclzg.h \ qtgui/recoll.h \ qtgui/recoll.pro.in \ qtgui/recoll.qrc \ qtgui/reslist.cpp \ qtgui/reslist.h \ qtgui/respopup.cpp \ qtgui/respopup.h \ qtgui/restable.cpp \ qtgui/restable.h \ qtgui/restable.ui \ qtgui/rtitool.cpp \ qtgui/rtitool.h \ qtgui/rtitool.ui \ qtgui/scbase.cpp \ qtgui/scbase.h \ qtgui/searchclause_w.cpp \ qtgui/searchclause_w.h \ qtgui/snippets.ui \ qtgui/snippets_w.cpp \ qtgui/snippets_w.h \ qtgui/specialindex.h \ qtgui/specialindex.ui \ qtgui/spell.ui \ qtgui/spell_w.cpp \ qtgui/spell_w.h \ qtgui/ssearch_w.cpp \ qtgui/ssearch_w.h \ qtgui/ssearchb.ui \ qtgui/systray.cpp \ qtgui/systray.h \ qtgui/uiprefs.ui \ qtgui/uiprefs_w.cpp \ qtgui/uiprefs_w.h \ qtgui/viewaction.ui \ qtgui/viewaction_w.cpp \ qtgui/viewaction_w.h \ qtgui/webcache.ui \ qtgui/webcache.cpp \ qtgui/webcache.h \ qtgui/widgets/editdialog.h \ qtgui/widgets/editdialog.ui \ qtgui/widgets/listdialog.h \ qtgui/widgets/listdialog.ui \ qtgui/widgets/qxtconfirmationmessage.cpp \ qtgui/widgets/qxtconfirmationmessage.h \ qtgui/widgets/qxtglobal.h \ qtgui/winschedtool.cpp \ qtgui/winschedtool.h \ qtgui/winschedtool.ui \ qtgui/xmltosd.cpp \ qtgui/xmltosd.h \ \ python/README.txt \ python/pyaspell/aspell.c \ python/pyaspell/LICENSE \ python/pyaspell/pyaspell.py \ python/pyaspell/README.rst \ python/pyaspell/setup.py.in \ python/pychm/AUTHORS \ python/pychm/COPYING \ python/pychm/MANIFEST.in \ python/pychm/README-RECOLL.txt \ python/pychm/recollchm \ python/pychm/recollchm/__init__.py \ python/pychm/recollchm/chm.py \ python/pychm/recollchm/chmlib.py \ python/pychm/recollchm/extra.c \ python/pychm/recollchm/swig_chm.c \ python/pychm/recollchm/swig_chm.i \ python/pychm/setup.py.in \ python/recoll/pyrclextract.cpp \ python/recoll/pyrecoll.cpp \ python/recoll/pyrecoll.h \ python/recoll/pyresultstore.cpp \ python/recoll/recoll/__init__.py \ python/recoll/recoll/conftree.py \ python/recoll/recoll/rclconfig.py \ python/recoll/recoll/rclextract.py \ python/recoll/recoll/recoll.py \ python/recoll/setup.py.in \ python/samples/docdups.py \ python/samples/mutt-recoll.py \ python/samples/rcldlkp.py \ python/samples/rclmbox.py \ python/samples/recollgui/Makefile \ python/samples/recollgui/qrecoll.py \ python/samples/recollgui/rclmain.ui \ python/samples/recollq.py \ python/samples/recollqsd.py \ \ rclgrep/Makefile.am \ rclgrep/rclgrep.cpp \ \ sampleconf/backends sampleconf/fields sampleconf/fragment-buttons.xml sampleconf/mimeconf \ sampleconf/mimemap sampleconf/mimeview sampleconf/macos/mimeview \ sampleconf/recoll.conf sampleconf/recoll.qss \ sampleconf/recoll-common.css sampleconf/recoll-common.qss \ sampleconf/recoll-dark.qss sampleconf/recoll-dark.css \ \ testmains/Makefile.am \ \ unac/AUTHORS unac/COPYING unac/README unac/README.recoll unac/unac.c \ \ RECOLL-VERSION.txt # EXTRA_DIST: The Php Code does not build anymore. No need to ship it until # someone fixes it: # php/00README.txt php/recoll/config.m4 php/recoll/make.sh # php/recoll/php_recoll.h php/recoll/recoll.cpp php/sample/shell.php OPTSFORPYTHON = $(shell test -f /etc/debian_version && echo --install-layout=deb) defconfdir = $(pkgdatadir)/examples defconf_DATA = \ desktop/recollindex.desktop \ index/rclmon.sh \ index/recollindex.service \ index/recollindex@.service \ sampleconf/backends \ sampleconf/fields \ sampleconf/fragment-buttons.xml \ sampleconf/mimeconf \ sampleconf/mimemap \ sampleconf/mimeview \ sampleconf/recoll-common.css \ sampleconf/recoll-common.qss \ sampleconf/recoll-dark.css \ sampleconf/recoll-dark.qss \ sampleconf/recoll.conf \ sampleconf/recoll.qss filterdir = $(pkgdatadir)/filters dist_filter_DATA = \ desktop/hotrecoll.py \ filters/abiword.xsl \ filters/cmdtalk.py \ filters/fb2.xsl \ filters/gnumeric.xsl \ filters/kosplitter.py \ filters/msodump.zip \ filters/okular-note.xsl \ filters/opendoc-body.xsl \ filters/opendoc-flat.xsl \ filters/opendoc-meta.xsl \ filters/openxml-xls-body.xsl \ filters/openxml-word-body.xsl \ filters/openxml-meta.xsl \ filters/ppt-dump.py \ filters/rcl7z.py \ filters/rclaptosidman \ filters/rclaspell-sugg.py \ filters/rclaudio.py \ filters/rclbasehandler.py \ filters/rclbibtex.sh \ filters/rclcheckneedretry.sh \ filters/rclchm.py \ filters/rcldia.py \ filters/rcldjvu.py \ filters/rcldoc.py \ filters/rcldvi \ filters/rclepub.py \ filters/rclepub1.py \ filters/rclexec1.py \ filters/rclexecm.py \ filters/rclfb2.py \ filters/rclgaim \ filters/rclgenxslt.py \ filters/rclhwp.py \ filters/rclics.py \ filters/rclimg \ filters/rclimg.py \ filters/rclinfo.py \ filters/rclipynb.py \ filters/rcljoplin.py \ filters/rclkar.py \ filters/rclkwd \ filters/rcllatinclass.py \ filters/rcllatinstops.zip \ filters/rcllyx \ filters/rclman \ filters/rclmidi.py \ filters/rclocrcache.py \ filters/rclocr.py \ filters/rclocrabbyy.py \ filters/rclocrtesseract.py \ filters/rclopxml.py \ filters/rclorgmode.py \ filters/rclpdf.py \ filters/rclppt.py \ filters/rclps \ filters/rclpst.py \ filters/rclpurple \ filters/rclpython.py \ filters/rclrar.py \ filters/rclrtf.py \ filters/rclscribus \ filters/rclshowinfo \ filters/rcltar.py \ filters/rcltex \ filters/rcltext.py \ filters/rcluncomp \ filters/rcluncomp.py \ filters/rclwar.py \ filters/rclxls.py \ filters/rclxml.py \ filters/rclxmp.py \ filters/rclxslt.py \ filters/rclzip.py \ filters/recoll-we-move-files.py \ filters/recollepub.zip \ filters/svg.xsl \ filters/thunderbird-open-message.sh \ filters/xls-dump.py \ filters/xlsxmltocsv.py \ filters/xml.xsl \ python/recoll/recoll/conftree.py \ python/recoll/recoll/rclconfig.py @INSTALL_SYSTEMD_UNITS_TRUE@systemd_system_unitdir = @SYSTEMD_SYSTEM_UNIT_DIR@ @INSTALL_SYSTEMD_UNITS_TRUE@systemd_user_unitdir = @SYSTEMD_USER_UNIT_DIR@ @INSTALL_SYSTEMD_UNITS_TRUE@systemd_system_unit_DATA = index/recollindex@.service @INSTALL_SYSTEMD_UNITS_TRUE@systemd_user_unit_DATA = index/recollindex.service @MAKEUSERDOC_TRUE@rdocdir = $(pkgdatadir)/doc @MAKEUSERDOC_TRUE@rdoc_DATA = doc/user/usermanual.html doc/user/docbook-xsl.css dist_man1_MANS = doc/man/recoll.1 doc/man/recollq.1 \ doc/man/recollindex.1 $(rclgrep_man1_MANS) \ $(xadump_man1_MANS) dist_man5_MANS = doc/man/recoll.conf.5 all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: .SUFFIXES: .cc .cpp .lo .o .obj .ypp am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): common/autoconfig.h: common/stamp-h1 @test -f $@ || rm -f common/stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) common/stamp-h1 common/stamp-h1: $(top_srcdir)/common/autoconfig.h.in $(top_builddir)/config.status @rm -f common/stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status common/autoconfig.h $(top_srcdir)/common/autoconfig.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f common/stamp-h1 touch $@ distclean-hdr: -rm -f common/autoconfig.h common/stamp-h1 python/recoll/setup.py: $(top_builddir)/config.status $(top_srcdir)/python/recoll/setup.py.in cd $(top_builddir) && $(SHELL) ./config.status $@ python/recoll/setup.cfg: $(top_builddir)/config.status $(top_srcdir)/python/recoll/setup.cfg.in cd $(top_builddir) && $(SHELL) ./config.status $@ python/pychm/setup.py: $(top_builddir)/config.status $(top_srcdir)/python/pychm/setup.py.in cd $(top_builddir) && $(SHELL) ./config.status $@ python/pychm/setup.cfg: $(top_builddir)/config.status $(top_srcdir)/python/pychm/setup.cfg.in cd $(top_builddir) && $(SHELL) ./config.status $@ python/pyaspell/setup.py: $(top_builddir)/config.status $(top_srcdir)/python/pyaspell/setup.py.in cd $(top_builddir) && $(SHELL) ./config.status $@ install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } install-librclLTLIBRARIES: $(librcl_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(librcl_LTLIBRARIES)'; test -n "$(librcldir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(librcldir)'"; \ $(MKDIR_P) "$(DESTDIR)$(librcldir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(librcldir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(librcldir)"; \ } uninstall-librclLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(librcl_LTLIBRARIES)'; test -n "$(librcldir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(librcldir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(librcldir)/$$f"; \ done clean-librclLTLIBRARIES: -test -z "$(librcl_LTLIBRARIES)" || rm -f $(librcl_LTLIBRARIES) @list='$(librcl_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } aspell/$(am__dirstamp): @$(MKDIR_P) aspell @: > aspell/$(am__dirstamp) aspell/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) aspell/$(DEPDIR) @: > aspell/$(DEPDIR)/$(am__dirstamp) aspell/rclaspell.lo: aspell/$(am__dirstamp) \ aspell/$(DEPDIR)/$(am__dirstamp) bincimapmime/$(am__dirstamp): @$(MKDIR_P) bincimapmime @: > bincimapmime/$(am__dirstamp) bincimapmime/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) bincimapmime/$(DEPDIR) @: > bincimapmime/$(DEPDIR)/$(am__dirstamp) bincimapmime/convert.lo: bincimapmime/$(am__dirstamp) \ bincimapmime/$(DEPDIR)/$(am__dirstamp) bincimapmime/mime-parsefull.lo: bincimapmime/$(am__dirstamp) \ bincimapmime/$(DEPDIR)/$(am__dirstamp) bincimapmime/mime-parseonlyheader.lo: bincimapmime/$(am__dirstamp) \ bincimapmime/$(DEPDIR)/$(am__dirstamp) bincimapmime/mime-printbody.lo: bincimapmime/$(am__dirstamp) \ bincimapmime/$(DEPDIR)/$(am__dirstamp) bincimapmime/mime.lo: bincimapmime/$(am__dirstamp) \ bincimapmime/$(DEPDIR)/$(am__dirstamp) common/$(am__dirstamp): @$(MKDIR_P) common @: > common/$(am__dirstamp) common/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) common/$(DEPDIR) @: > common/$(DEPDIR)/$(am__dirstamp) common/webstore.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) common/cstr.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) common/rclconfig.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) common/rclinit.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) common/syngroups.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) common/textsplit.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) common/textsplitko.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) common/unacpp.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) common/utf8fn.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) index/$(am__dirstamp): @$(MKDIR_P) index @: > index/$(am__dirstamp) index/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) index/$(DEPDIR) @: > index/$(DEPDIR)/$(am__dirstamp) index/webqueuefetcher.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/checkretryfailed.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/exefetcher.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/fetcher.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/fsfetcher.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/idxdiags.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/idxstatus.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/mimetype.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/subtreelist.lo: index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) internfile/$(am__dirstamp): @$(MKDIR_P) internfile @: > internfile/$(am__dirstamp) internfile/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) internfile/$(DEPDIR) @: > internfile/$(DEPDIR)/$(am__dirstamp) internfile/extrameta.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/htmlparse.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/internfile.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/mh_exec.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/mh_execm.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/mh_html.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/mh_mail.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/mh_mbox.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/mh_text.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/mh_xslt.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/mimehandler.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/myhtmlparse.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/txtdcode.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) internfile/uncomp.lo: internfile/$(am__dirstamp) \ internfile/$(DEPDIR)/$(am__dirstamp) query/$(am__dirstamp): @$(MKDIR_P) query @: > query/$(am__dirstamp) query/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) query/$(DEPDIR) @: > query/$(DEPDIR)/$(am__dirstamp) query/docseq.lo: query/$(am__dirstamp) query/$(DEPDIR)/$(am__dirstamp) query/docseqdb.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) query/docseqhist.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) query/dynconf.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) query/filtseq.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) common/plaintorich.lo: common/$(am__dirstamp) \ common/$(DEPDIR)/$(am__dirstamp) query/qresultstore.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) query/recollq.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) query/reslistpager.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) query/sortseq.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) query/wasaparse.hpp: query/wasaparse.cpp @if test ! -f $@; then rm -f query/wasaparse.cpp; else :; fi @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) query/wasaparse.cpp; else :; fi query/wasaparse.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) query/wasaparseaux.lo: query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) rcldb/$(am__dirstamp): @$(MKDIR_P) rcldb @: > rcldb/$(am__dirstamp) rcldb/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) rcldb/$(DEPDIR) @: > rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/daterange.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/expansiondbs.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/rclabstract.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/rclabsfromtext.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/rcldb.lo: rcldb/$(am__dirstamp) rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/rcldoc.lo: rcldb/$(am__dirstamp) rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/rcldups.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/rclquery.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/rclterms.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/rclvalues.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/searchdata.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/searchdatatox.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/searchdataxml.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/stemdb.lo: rcldb/$(am__dirstamp) rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/stoplist.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) rcldb/synfamily.lo: rcldb/$(am__dirstamp) \ rcldb/$(DEPDIR)/$(am__dirstamp) unac/$(am__dirstamp): @$(MKDIR_P) unac @: > unac/$(am__dirstamp) unac/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) unac/$(DEPDIR) @: > unac/$(DEPDIR)/$(am__dirstamp) unac/unac.lo: unac/$(am__dirstamp) unac/$(DEPDIR)/$(am__dirstamp) utils/$(am__dirstamp): @$(MKDIR_P) utils @: > utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) utils/$(DEPDIR) @: > utils/$(DEPDIR)/$(am__dirstamp) utils/appformime.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/base64.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/cancelcheck.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/chrono.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/circache.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/closefrom.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/cmdtalk.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/conftree.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/copyfile.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/cpuconf.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/dlib.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/ecrontab.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/execmd.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/fileudi.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/fstreewalk.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/hldata.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/idfile.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/listmem.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/log.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/md5.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/md5ut.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/mimeparse.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/miniz.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/netcon.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/pathut.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/pxattr.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/rclionice.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/rclutil.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/readfile.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/smallut.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/strmatcher.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/transcode.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/utf8iter.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/wipedir.lo: utils/$(am__dirstamp) \ utils/$(DEPDIR)/$(am__dirstamp) utils/x11mon.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) utils/zlibut.lo: utils/$(am__dirstamp) utils/$(DEPDIR)/$(am__dirstamp) librecoll.la: $(librecoll_la_OBJECTS) $(librecoll_la_DEPENDENCIES) $(EXTRA_librecoll_la_DEPENDENCIES) $(AM_V_CXXLD)$(librecoll_la_LINK) $(am_librecoll_la_rpath) $(librecoll_la_OBJECTS) $(librecoll_la_LIBADD) $(LIBS) index/checkindexed.$(OBJEXT): index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/fsindexer.$(OBJEXT): index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/indexer.$(OBJEXT): index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/rclmonprc.$(OBJEXT): index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/rclmonrcv.$(OBJEXT): index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/recollindex.$(OBJEXT): index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) index/webqueue.$(OBJEXT): index/$(am__dirstamp) \ index/$(DEPDIR)/$(am__dirstamp) recollindex$(EXEEXT): $(recollindex_OBJECTS) $(recollindex_DEPENDENCIES) $(EXTRA_recollindex_DEPENDENCIES) @rm -f recollindex$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(recollindex_OBJECTS) $(recollindex_LDADD) $(LIBS) query/recollqmain.$(OBJEXT): query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) recollq$(EXEEXT): $(recollq_OBJECTS) $(recollq_DEPENDENCIES) $(EXTRA_recollq_DEPENDENCIES) @rm -f recollq$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(recollq_OBJECTS) $(recollq_LDADD) $(LIBS) query/xadump.$(OBJEXT): query/$(am__dirstamp) \ query/$(DEPDIR)/$(am__dirstamp) xadump$(EXEEXT): $(xadump_OBJECTS) $(xadump_DEPENDENCIES) $(EXTRA_xadump_DEPENDENCIES) @rm -f xadump$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(xadump_OBJECTS) $(xadump_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f aspell/*.$(OBJEXT) -rm -f aspell/*.lo -rm -f bincimapmime/*.$(OBJEXT) -rm -f bincimapmime/*.lo -rm -f common/*.$(OBJEXT) -rm -f common/*.lo -rm -f index/*.$(OBJEXT) -rm -f index/*.lo -rm -f internfile/*.$(OBJEXT) -rm -f internfile/*.lo -rm -f query/*.$(OBJEXT) -rm -f query/*.lo -rm -f rcldb/*.$(OBJEXT) -rm -f rcldb/*.lo -rm -f unac/*.$(OBJEXT) -rm -f unac/*.lo -rm -f utils/*.$(OBJEXT) -rm -f utils/*.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@aspell/$(DEPDIR)/rclaspell.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@bincimapmime/$(DEPDIR)/convert.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@bincimapmime/$(DEPDIR)/mime-parsefull.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@bincimapmime/$(DEPDIR)/mime-parseonlyheader.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@bincimapmime/$(DEPDIR)/mime-printbody.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@bincimapmime/$(DEPDIR)/mime.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/cstr.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/plaintorich.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/rclconfig.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/rclinit.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/syngroups.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/textsplit.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/textsplitko.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/unacpp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/utf8fn.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@common/$(DEPDIR)/webstore.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/checkindexed.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/checkretryfailed.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/exefetcher.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/fetcher.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/fsfetcher.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/fsindexer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/idxdiags.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/idxstatus.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/indexer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/mimetype.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/rclmonprc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/rclmonrcv.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/recollindex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/subtreelist.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/webqueue.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@index/$(DEPDIR)/webqueuefetcher.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/extrameta.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/htmlparse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/internfile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/mh_exec.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/mh_execm.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/mh_html.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/mh_mail.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/mh_mbox.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/mh_text.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/mh_xslt.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/mimehandler.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/myhtmlparse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/txtdcode.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@internfile/$(DEPDIR)/uncomp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/docseq.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/docseqdb.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/docseqhist.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/dynconf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/filtseq.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/qresultstore.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/recollq.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/recollqmain.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/reslistpager.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/sortseq.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/wasaparse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/wasaparseaux.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@query/$(DEPDIR)/xadump.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/daterange.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/expansiondbs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/rclabsfromtext.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/rclabstract.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/rcldb.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/rcldoc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/rcldups.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/rclquery.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/rclterms.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/rclvalues.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/searchdata.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/searchdatatox.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/searchdataxml.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/stemdb.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/stoplist.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@rcldb/$(DEPDIR)/synfamily.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unac/$(DEPDIR)/unac.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/appformime.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/base64.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/cancelcheck.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/chrono.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/circache.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/closefrom.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/cmdtalk.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/conftree.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/copyfile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/cpuconf.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/dlib.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/ecrontab.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/execmd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/fileudi.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/fstreewalk.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/hldata.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/idfile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/listmem.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/log.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/md5.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/md5ut.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/mimeparse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/miniz.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/netcon.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/pathut.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/pxattr.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/rclionice.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/rclutil.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/readfile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/smallut.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/strmatcher.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/transcode.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/utf8iter.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/wipedir.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/x11mon.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/zlibut.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .cc.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cc.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cc.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< .ypp.cpp: $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE) mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf aspell/.libs aspell/_libs -rm -rf bincimapmime/.libs bincimapmime/_libs -rm -rf common/.libs common/_libs -rm -rf index/.libs index/_libs -rm -rf internfile/.libs internfile/_libs -rm -rf query/.libs query/_libs -rm -rf rcldb/.libs rcldb/_libs -rm -rf unac/.libs unac/_libs -rm -rf utils/.libs utils/_libs distclean-libtool: -rm -f libtool config.lt install-man1: $(dist_man1_MANS) @$(NORMAL_INSTALL) @list1='$(dist_man1_MANS)'; \ list2=''; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list='$(dist_man1_MANS)'; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) install-man5: $(dist_man5_MANS) @$(NORMAL_INSTALL) @list1='$(dist_man5_MANS)'; \ list2=''; \ test -n "$(man5dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.5[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ done; } uninstall-man5: @$(NORMAL_UNINSTALL) @list='$(dist_man5_MANS)'; test -n "$(man5dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir) install-defconfDATA: $(defconf_DATA) @$(NORMAL_INSTALL) @list='$(defconf_DATA)'; test -n "$(defconfdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(defconfdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(defconfdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(defconfdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(defconfdir)" || exit $$?; \ done uninstall-defconfDATA: @$(NORMAL_UNINSTALL) @list='$(defconf_DATA)'; test -n "$(defconfdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(defconfdir)'; $(am__uninstall_files_from_dir) install-dist_filterDATA: $(dist_filter_DATA) @$(NORMAL_INSTALL) @list='$(dist_filter_DATA)'; test -n "$(filterdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(filterdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(filterdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(filterdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(filterdir)" || exit $$?; \ done uninstall-dist_filterDATA: @$(NORMAL_UNINSTALL) @list='$(dist_filter_DATA)'; test -n "$(filterdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(filterdir)'; $(am__uninstall_files_from_dir) install-rdocDATA: $(rdoc_DATA) @$(NORMAL_INSTALL) @list='$(rdoc_DATA)'; test -n "$(rdocdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(rdocdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(rdocdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(rdocdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(rdocdir)" || exit $$?; \ done uninstall-rdocDATA: @$(NORMAL_UNINSTALL) @list='$(rdoc_DATA)'; test -n "$(rdocdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(rdocdir)'; $(am__uninstall_files_from_dir) install-systemd_system_unitDATA: $(systemd_system_unit_DATA) @$(NORMAL_INSTALL) @list='$(systemd_system_unit_DATA)'; test -n "$(systemd_system_unitdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(systemd_system_unitdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(systemd_system_unitdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemd_system_unitdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(systemd_system_unitdir)" || exit $$?; \ done uninstall-systemd_system_unitDATA: @$(NORMAL_UNINSTALL) @list='$(systemd_system_unit_DATA)'; test -n "$(systemd_system_unitdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(systemd_system_unitdir)'; $(am__uninstall_files_from_dir) install-systemd_user_unitDATA: $(systemd_user_unit_DATA) @$(NORMAL_INSTALL) @list='$(systemd_user_unit_DATA)'; test -n "$(systemd_user_unitdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(systemd_user_unitdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(systemd_user_unitdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemd_user_unitdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(systemd_user_unitdir)" || exit $$?; \ done uninstall-systemd_user_unitDATA: @$(NORMAL_UNINSTALL) @list='$(systemd_user_unit_DATA)'; test -n "$(systemd_user_unitdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(systemd_user_unitdir)'; $(am__uninstall_files_from_dir) install-incHEADERS: $(inc_HEADERS) @$(NORMAL_INSTALL) @list='$(inc_HEADERS)'; test -n "$(incdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(incdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(incdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(incdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(incdir)" || exit $$?; \ done uninstall-incHEADERS: @$(NORMAL_UNINSTALL) @list='$(inc_HEADERS)'; test -n "$(incdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(incdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-zstd: distdir tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ *.tar.zst*) \ zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-recursive @MAKEPYTHONASPELL_FALSE@@MAKEPYTHONCHM_FALSE@@MAKEPYTHON_FALSE@@MAKEQT_FALSE@all-local: all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(MANS) $(DATA) $(HEADERS) \ all-local install-binPROGRAMS: install-libLTLIBRARIES install-librclLTLIBRARIES: install-libLTLIBRARIES installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(librcldir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(defconfdir)" "$(DESTDIR)$(filterdir)" "$(DESTDIR)$(rdocdir)" "$(DESTDIR)$(systemd_system_unitdir)" "$(DESTDIR)$(systemd_user_unitdir)" "$(DESTDIR)$(incdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-recursive install-exec: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f aspell/$(DEPDIR)/$(am__dirstamp) -rm -f aspell/$(am__dirstamp) -rm -f bincimapmime/$(DEPDIR)/$(am__dirstamp) -rm -f bincimapmime/$(am__dirstamp) -rm -f common/$(DEPDIR)/$(am__dirstamp) -rm -f common/$(am__dirstamp) -rm -f index/$(DEPDIR)/$(am__dirstamp) -rm -f index/$(am__dirstamp) -rm -f internfile/$(DEPDIR)/$(am__dirstamp) -rm -f internfile/$(am__dirstamp) -rm -f query/$(DEPDIR)/$(am__dirstamp) -rm -f query/$(am__dirstamp) -rm -f rcldb/$(DEPDIR)/$(am__dirstamp) -rm -f rcldb/$(am__dirstamp) -rm -f unac/$(DEPDIR)/$(am__dirstamp) -rm -f unac/$(am__dirstamp) -rm -f utils/$(DEPDIR)/$(am__dirstamp) -rm -f utils/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -rm -f query/wasaparse.cpp -rm -f query/wasaparse.hpp -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) @MAKEPYTHONASPELL_FALSE@@MAKEPYTHONCHM_FALSE@@MAKEPYTHON_FALSE@@MAKEQT_FALSE@clean-local: @MAKEPYTHONASPELL_FALSE@@MAKEPYTHONCHM_FALSE@@MAKEPYTHON_FALSE@@MAKEQT_FALSE@install-exec-local: clean: clean-recursive clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-librclLTLIBRARIES clean-libtool clean-local \ mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f aspell/$(DEPDIR)/rclaspell.Plo -rm -f bincimapmime/$(DEPDIR)/convert.Plo -rm -f bincimapmime/$(DEPDIR)/mime-parsefull.Plo -rm -f bincimapmime/$(DEPDIR)/mime-parseonlyheader.Plo -rm -f bincimapmime/$(DEPDIR)/mime-printbody.Plo -rm -f bincimapmime/$(DEPDIR)/mime.Plo -rm -f common/$(DEPDIR)/cstr.Plo -rm -f common/$(DEPDIR)/plaintorich.Plo -rm -f common/$(DEPDIR)/rclconfig.Plo -rm -f common/$(DEPDIR)/rclinit.Plo -rm -f common/$(DEPDIR)/syngroups.Plo -rm -f common/$(DEPDIR)/textsplit.Plo -rm -f common/$(DEPDIR)/textsplitko.Plo -rm -f common/$(DEPDIR)/unacpp.Plo -rm -f common/$(DEPDIR)/utf8fn.Plo -rm -f common/$(DEPDIR)/webstore.Plo -rm -f index/$(DEPDIR)/checkindexed.Po -rm -f index/$(DEPDIR)/checkretryfailed.Plo -rm -f index/$(DEPDIR)/exefetcher.Plo -rm -f index/$(DEPDIR)/fetcher.Plo -rm -f index/$(DEPDIR)/fsfetcher.Plo -rm -f index/$(DEPDIR)/fsindexer.Po -rm -f index/$(DEPDIR)/idxdiags.Plo -rm -f index/$(DEPDIR)/idxstatus.Plo -rm -f index/$(DEPDIR)/indexer.Po -rm -f index/$(DEPDIR)/mimetype.Plo -rm -f index/$(DEPDIR)/rclmonprc.Po -rm -f index/$(DEPDIR)/rclmonrcv.Po -rm -f index/$(DEPDIR)/recollindex.Po -rm -f index/$(DEPDIR)/subtreelist.Plo -rm -f index/$(DEPDIR)/webqueue.Po -rm -f index/$(DEPDIR)/webqueuefetcher.Plo -rm -f internfile/$(DEPDIR)/extrameta.Plo -rm -f internfile/$(DEPDIR)/htmlparse.Plo -rm -f internfile/$(DEPDIR)/internfile.Plo -rm -f internfile/$(DEPDIR)/mh_exec.Plo -rm -f internfile/$(DEPDIR)/mh_execm.Plo -rm -f internfile/$(DEPDIR)/mh_html.Plo -rm -f internfile/$(DEPDIR)/mh_mail.Plo -rm -f internfile/$(DEPDIR)/mh_mbox.Plo -rm -f internfile/$(DEPDIR)/mh_text.Plo -rm -f internfile/$(DEPDIR)/mh_xslt.Plo -rm -f internfile/$(DEPDIR)/mimehandler.Plo -rm -f internfile/$(DEPDIR)/myhtmlparse.Plo -rm -f internfile/$(DEPDIR)/txtdcode.Plo -rm -f internfile/$(DEPDIR)/uncomp.Plo -rm -f query/$(DEPDIR)/docseq.Plo -rm -f query/$(DEPDIR)/docseqdb.Plo -rm -f query/$(DEPDIR)/docseqhist.Plo -rm -f query/$(DEPDIR)/dynconf.Plo -rm -f query/$(DEPDIR)/filtseq.Plo -rm -f query/$(DEPDIR)/qresultstore.Plo -rm -f query/$(DEPDIR)/recollq.Plo -rm -f query/$(DEPDIR)/recollqmain.Po -rm -f query/$(DEPDIR)/reslistpager.Plo -rm -f query/$(DEPDIR)/sortseq.Plo -rm -f query/$(DEPDIR)/wasaparse.Plo -rm -f query/$(DEPDIR)/wasaparseaux.Plo -rm -f query/$(DEPDIR)/xadump.Po -rm -f rcldb/$(DEPDIR)/daterange.Plo -rm -f rcldb/$(DEPDIR)/expansiondbs.Plo -rm -f rcldb/$(DEPDIR)/rclabsfromtext.Plo -rm -f rcldb/$(DEPDIR)/rclabstract.Plo -rm -f rcldb/$(DEPDIR)/rcldb.Plo -rm -f rcldb/$(DEPDIR)/rcldoc.Plo -rm -f rcldb/$(DEPDIR)/rcldups.Plo -rm -f rcldb/$(DEPDIR)/rclquery.Plo -rm -f rcldb/$(DEPDIR)/rclterms.Plo -rm -f rcldb/$(DEPDIR)/rclvalues.Plo -rm -f rcldb/$(DEPDIR)/searchdata.Plo -rm -f rcldb/$(DEPDIR)/searchdatatox.Plo -rm -f rcldb/$(DEPDIR)/searchdataxml.Plo -rm -f rcldb/$(DEPDIR)/stemdb.Plo -rm -f rcldb/$(DEPDIR)/stoplist.Plo -rm -f rcldb/$(DEPDIR)/synfamily.Plo -rm -f unac/$(DEPDIR)/unac.Plo -rm -f utils/$(DEPDIR)/appformime.Plo -rm -f utils/$(DEPDIR)/base64.Plo -rm -f utils/$(DEPDIR)/cancelcheck.Plo -rm -f utils/$(DEPDIR)/chrono.Plo -rm -f utils/$(DEPDIR)/circache.Plo -rm -f utils/$(DEPDIR)/closefrom.Plo -rm -f utils/$(DEPDIR)/cmdtalk.Plo -rm -f utils/$(DEPDIR)/conftree.Plo -rm -f utils/$(DEPDIR)/copyfile.Plo -rm -f utils/$(DEPDIR)/cpuconf.Plo -rm -f utils/$(DEPDIR)/dlib.Plo -rm -f utils/$(DEPDIR)/ecrontab.Plo -rm -f utils/$(DEPDIR)/execmd.Plo -rm -f utils/$(DEPDIR)/fileudi.Plo -rm -f utils/$(DEPDIR)/fstreewalk.Plo -rm -f utils/$(DEPDIR)/hldata.Plo -rm -f utils/$(DEPDIR)/idfile.Plo -rm -f utils/$(DEPDIR)/listmem.Plo -rm -f utils/$(DEPDIR)/log.Plo -rm -f utils/$(DEPDIR)/md5.Plo -rm -f utils/$(DEPDIR)/md5ut.Plo -rm -f utils/$(DEPDIR)/mimeparse.Plo -rm -f utils/$(DEPDIR)/miniz.Plo -rm -f utils/$(DEPDIR)/netcon.Plo -rm -f utils/$(DEPDIR)/pathut.Plo -rm -f utils/$(DEPDIR)/pxattr.Plo -rm -f utils/$(DEPDIR)/rclionice.Plo -rm -f utils/$(DEPDIR)/rclutil.Plo -rm -f utils/$(DEPDIR)/readfile.Plo -rm -f utils/$(DEPDIR)/smallut.Plo -rm -f utils/$(DEPDIR)/strmatcher.Plo -rm -f utils/$(DEPDIR)/transcode.Plo -rm -f utils/$(DEPDIR)/utf8iter.Plo -rm -f utils/$(DEPDIR)/wipedir.Plo -rm -f utils/$(DEPDIR)/x11mon.Plo -rm -f utils/$(DEPDIR)/zlibut.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-defconfDATA install-dist_filterDATA \ install-incHEADERS install-librclLTLIBRARIES install-man \ install-rdocDATA install-systemd_system_unitDATA \ install-systemd_user_unitDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-binPROGRAMS install-exec-local \ install-libLTLIBRARIES install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-man1 install-man5 install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f aspell/$(DEPDIR)/rclaspell.Plo -rm -f bincimapmime/$(DEPDIR)/convert.Plo -rm -f bincimapmime/$(DEPDIR)/mime-parsefull.Plo -rm -f bincimapmime/$(DEPDIR)/mime-parseonlyheader.Plo -rm -f bincimapmime/$(DEPDIR)/mime-printbody.Plo -rm -f bincimapmime/$(DEPDIR)/mime.Plo -rm -f common/$(DEPDIR)/cstr.Plo -rm -f common/$(DEPDIR)/plaintorich.Plo -rm -f common/$(DEPDIR)/rclconfig.Plo -rm -f common/$(DEPDIR)/rclinit.Plo -rm -f common/$(DEPDIR)/syngroups.Plo -rm -f common/$(DEPDIR)/textsplit.Plo -rm -f common/$(DEPDIR)/textsplitko.Plo -rm -f common/$(DEPDIR)/unacpp.Plo -rm -f common/$(DEPDIR)/utf8fn.Plo -rm -f common/$(DEPDIR)/webstore.Plo -rm -f index/$(DEPDIR)/checkindexed.Po -rm -f index/$(DEPDIR)/checkretryfailed.Plo -rm -f index/$(DEPDIR)/exefetcher.Plo -rm -f index/$(DEPDIR)/fetcher.Plo -rm -f index/$(DEPDIR)/fsfetcher.Plo -rm -f index/$(DEPDIR)/fsindexer.Po -rm -f index/$(DEPDIR)/idxdiags.Plo -rm -f index/$(DEPDIR)/idxstatus.Plo -rm -f index/$(DEPDIR)/indexer.Po -rm -f index/$(DEPDIR)/mimetype.Plo -rm -f index/$(DEPDIR)/rclmonprc.Po -rm -f index/$(DEPDIR)/rclmonrcv.Po -rm -f index/$(DEPDIR)/recollindex.Po -rm -f index/$(DEPDIR)/subtreelist.Plo -rm -f index/$(DEPDIR)/webqueue.Po -rm -f index/$(DEPDIR)/webqueuefetcher.Plo -rm -f internfile/$(DEPDIR)/extrameta.Plo -rm -f internfile/$(DEPDIR)/htmlparse.Plo -rm -f internfile/$(DEPDIR)/internfile.Plo -rm -f internfile/$(DEPDIR)/mh_exec.Plo -rm -f internfile/$(DEPDIR)/mh_execm.Plo -rm -f internfile/$(DEPDIR)/mh_html.Plo -rm -f internfile/$(DEPDIR)/mh_mail.Plo -rm -f internfile/$(DEPDIR)/mh_mbox.Plo -rm -f internfile/$(DEPDIR)/mh_text.Plo -rm -f internfile/$(DEPDIR)/mh_xslt.Plo -rm -f internfile/$(DEPDIR)/mimehandler.Plo -rm -f internfile/$(DEPDIR)/myhtmlparse.Plo -rm -f internfile/$(DEPDIR)/txtdcode.Plo -rm -f internfile/$(DEPDIR)/uncomp.Plo -rm -f query/$(DEPDIR)/docseq.Plo -rm -f query/$(DEPDIR)/docseqdb.Plo -rm -f query/$(DEPDIR)/docseqhist.Plo -rm -f query/$(DEPDIR)/dynconf.Plo -rm -f query/$(DEPDIR)/filtseq.Plo -rm -f query/$(DEPDIR)/qresultstore.Plo -rm -f query/$(DEPDIR)/recollq.Plo -rm -f query/$(DEPDIR)/recollqmain.Po -rm -f query/$(DEPDIR)/reslistpager.Plo -rm -f query/$(DEPDIR)/sortseq.Plo -rm -f query/$(DEPDIR)/wasaparse.Plo -rm -f query/$(DEPDIR)/wasaparseaux.Plo -rm -f query/$(DEPDIR)/xadump.Po -rm -f rcldb/$(DEPDIR)/daterange.Plo -rm -f rcldb/$(DEPDIR)/expansiondbs.Plo -rm -f rcldb/$(DEPDIR)/rclabsfromtext.Plo -rm -f rcldb/$(DEPDIR)/rclabstract.Plo -rm -f rcldb/$(DEPDIR)/rcldb.Plo -rm -f rcldb/$(DEPDIR)/rcldoc.Plo -rm -f rcldb/$(DEPDIR)/rcldups.Plo -rm -f rcldb/$(DEPDIR)/rclquery.Plo -rm -f rcldb/$(DEPDIR)/rclterms.Plo -rm -f rcldb/$(DEPDIR)/rclvalues.Plo -rm -f rcldb/$(DEPDIR)/searchdata.Plo -rm -f rcldb/$(DEPDIR)/searchdatatox.Plo -rm -f rcldb/$(DEPDIR)/searchdataxml.Plo -rm -f rcldb/$(DEPDIR)/stemdb.Plo -rm -f rcldb/$(DEPDIR)/stoplist.Plo -rm -f rcldb/$(DEPDIR)/synfamily.Plo -rm -f unac/$(DEPDIR)/unac.Plo -rm -f utils/$(DEPDIR)/appformime.Plo -rm -f utils/$(DEPDIR)/base64.Plo -rm -f utils/$(DEPDIR)/cancelcheck.Plo -rm -f utils/$(DEPDIR)/chrono.Plo -rm -f utils/$(DEPDIR)/circache.Plo -rm -f utils/$(DEPDIR)/closefrom.Plo -rm -f utils/$(DEPDIR)/cmdtalk.Plo -rm -f utils/$(DEPDIR)/conftree.Plo -rm -f utils/$(DEPDIR)/copyfile.Plo -rm -f utils/$(DEPDIR)/cpuconf.Plo -rm -f utils/$(DEPDIR)/dlib.Plo -rm -f utils/$(DEPDIR)/ecrontab.Plo -rm -f utils/$(DEPDIR)/execmd.Plo -rm -f utils/$(DEPDIR)/fileudi.Plo -rm -f utils/$(DEPDIR)/fstreewalk.Plo -rm -f utils/$(DEPDIR)/hldata.Plo -rm -f utils/$(DEPDIR)/idfile.Plo -rm -f utils/$(DEPDIR)/listmem.Plo -rm -f utils/$(DEPDIR)/log.Plo -rm -f utils/$(DEPDIR)/md5.Plo -rm -f utils/$(DEPDIR)/md5ut.Plo -rm -f utils/$(DEPDIR)/mimeparse.Plo -rm -f utils/$(DEPDIR)/miniz.Plo -rm -f utils/$(DEPDIR)/netcon.Plo -rm -f utils/$(DEPDIR)/pathut.Plo -rm -f utils/$(DEPDIR)/pxattr.Plo -rm -f utils/$(DEPDIR)/rclionice.Plo -rm -f utils/$(DEPDIR)/rclutil.Plo -rm -f utils/$(DEPDIR)/readfile.Plo -rm -f utils/$(DEPDIR)/smallut.Plo -rm -f utils/$(DEPDIR)/strmatcher.Plo -rm -f utils/$(DEPDIR)/transcode.Plo -rm -f utils/$(DEPDIR)/utf8iter.Plo -rm -f utils/$(DEPDIR)/wipedir.Plo -rm -f utils/$(DEPDIR)/x11mon.Plo -rm -f utils/$(DEPDIR)/zlibut.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-defconfDATA \ uninstall-dist_filterDATA uninstall-incHEADERS \ uninstall-libLTLIBRARIES uninstall-librclLTLIBRARIES \ uninstall-man uninstall-rdocDATA \ uninstall-systemd_system_unitDATA \ uninstall-systemd_user_unitDATA uninstall-man: uninstall-man1 uninstall-man5 .MAKE: $(am__recursive_targets) all check install install-am \ install-data-am install-exec install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am all-local \ am--depfiles am--refresh check check-am clean \ clean-binPROGRAMS clean-cscope clean-generic \ clean-libLTLIBRARIES clean-librclLTLIBRARIES clean-libtool \ clean-local cscope cscopelist-am ctags ctags-am dist dist-all \ dist-bzip2 dist-gzip dist-hook dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip dist-zstd distcheck distclean \ distclean-compile distclean-generic distclean-hdr \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-binPROGRAMS install-data \ install-data-am install-data-hook install-defconfDATA \ install-dist_filterDATA install-dvi install-dvi-am \ install-exec install-exec-am install-exec-local install-html \ install-html-am install-incHEADERS install-info \ install-info-am install-libLTLIBRARIES \ install-librclLTLIBRARIES install-man install-man1 \ install-man5 install-pdf install-pdf-am install-ps \ install-ps-am install-rdocDATA install-strip \ install-systemd_system_unitDATA install-systemd_user_unitDATA \ installcheck installcheck-am installdirs installdirs-am \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS uninstall-defconfDATA \ uninstall-dist_filterDATA uninstall-incHEADERS \ uninstall-libLTLIBRARIES uninstall-librclLTLIBRARIES \ uninstall-man uninstall-man1 uninstall-man5 uninstall-rdocDATA \ uninstall-systemd_system_unitDATA \ uninstall-systemd_user_unitDATA .PRECIOUS: Makefile CXXFLAGS ?= @CXXFLAGS@ @PUBLIC_LIB_FALSE@ # There is probably a better way to do this. The KIO needs to be linked @PUBLIC_LIB_FALSE@ # with librecoll, but librecoll is installed into a non-standard place @PUBLIC_LIB_FALSE@ # (/usr/lib/recoll). Debian packaging has something against setting an @PUBLIC_LIB_FALSE@ # rpath on the kio (cause it's not the same package as the lib), so I don't @PUBLIC_LIB_FALSE@ # know how to link it dynamically. The other thing I don't know is how to @PUBLIC_LIB_FALSE@ # force automake to build a static lib with the PIC objects. So the @PUBLIC_LIB_FALSE@ # following target, which is only used from the KIO build, deletes any .a @PUBLIC_LIB_FALSE@ # and .so and rebuilds the .a with the pic objs (the kio build calls @PUBLIC_LIB_FALSE@ # configure --disable-static). @PUBLIC_LIB_FALSE@ # Of course this is very uncomfortably close to automake/libtool internals @PUBLIC_LIB_FALSE@ # and may not work on all systems. @PUBLIC_LIB_FALSE@PicStatic: $(librecoll_la_OBJECTS) @PUBLIC_LIB_FALSE@ rm -f .libs/librecoll.a @PUBLIC_LIB_FALSE@ rm -f .libs/librecoll.so @PUBLIC_LIB_FALSE@ $(LIBTOOL) --tag=LD --mode=link gcc -g -O -o librecoll.la $(librecoll_la_OBJECTS) @MAKEPYTHON_TRUE@all-local:: recollpython @MAKEPYTHON_TRUE@install-exec-local:: recollpython-install @MAKEPYTHON_TRUE@clean-local:: recollpython-clean @MAKEPYTHON_TRUE@recollpython: librecoll.la @MAKEPYTHON_TRUE@ (cd python/recoll; set -x; \ @MAKEPYTHON_TRUE@ for v in 2 3;do test -n "`which python$${v}`" && \ @MAKEPYTHON_TRUE@ libdir=$(libdir) python$${v} setup.py build; \ @MAKEPYTHON_TRUE@ done \ @MAKEPYTHON_TRUE@ ) @MAKEPYTHON_TRUE@recollpython-install: @MAKEPYTHON_TRUE@ (cd python/recoll; set -x; \ @MAKEPYTHON_TRUE@ for v in 2 3;do test -n "`which python$${v}`" && \ @MAKEPYTHON_TRUE@ python$${v} setup.py install \ @MAKEPYTHON_TRUE@ --prefix=${prefix} --root=$${DESTDIR:-/} $(OPTSFORPYTHON); \ @MAKEPYTHON_TRUE@ done; \ @MAKEPYTHON_TRUE@ ) @MAKEPYTHON_TRUE@recollpython-clean: @MAKEPYTHON_TRUE@ rm -f python/recoll/*.pyc @MAKEPYTHON_TRUE@ rm -rf python/recoll/Recoll.egg-info @MAKEPYTHON_TRUE@ rm -rf python/recoll/__pycache__ @MAKEPYTHON_TRUE@ rm -rf python/recoll/build @MAKEPYTHONCHM_TRUE@all-local:: rclpychm @MAKEPYTHONCHM_TRUE@install-exec-local:: rclpychm-install @MAKEPYTHONCHM_TRUE@clean-local:: rclpychm-clean @MAKEPYTHONCHM_TRUE@rclpychm: @MAKEPYTHONCHM_TRUE@ (cd python/pychm; set -x; \ @MAKEPYTHONCHM_TRUE@ for v in 3;do \ @MAKEPYTHONCHM_TRUE@ test -n "`which python$${v}`" && python$${v} setup.py build;\ @MAKEPYTHONCHM_TRUE@ done \ @MAKEPYTHONCHM_TRUE@ ) @MAKEPYTHONCHM_TRUE@rclpychm-install: @MAKEPYTHONCHM_TRUE@ (cd python/pychm; set -x; \ @MAKEPYTHONCHM_TRUE@ for v in 3;do test -n "`which python$${v}`" && \ @MAKEPYTHONCHM_TRUE@ python$${v} setup.py install \ @MAKEPYTHONCHM_TRUE@ --prefix=${prefix} --root=$${DESTDIR:-/} $(OPTSFORPYTHON); \ @MAKEPYTHONCHM_TRUE@ done \ @MAKEPYTHONCHM_TRUE@ ) @MAKEPYTHONCHM_TRUE@rclpychm-clean: @MAKEPYTHONCHM_TRUE@ rm -rf python/pychm/build @MAKEPYTHONCHM_TRUE@ rm -rf python/pychm/build @MAKEPYTHONCHM_TRUE@ rm -rf python/pychm/dist/* @MAKEPYTHONCHM_TRUE@ rm -rf python/pychm/recollchm.egg-info @MAKEPYTHONASPELL_TRUE@all-local:: rclpyaspell @MAKEPYTHONASPELL_TRUE@install-exec-local:: rclpyaspell-install @MAKEPYTHONASPELL_TRUE@clean-local:: rclpyaspell-clean @MAKEPYTHONASPELL_TRUE@rclpyaspell: @MAKEPYTHONASPELL_TRUE@ (cd python/pyaspell; set -x; \ @MAKEPYTHONASPELL_TRUE@ for v in 3;do \ @MAKEPYTHONASPELL_TRUE@ test -n "`which python$${v}`" && python$${v} setup.py build;\ @MAKEPYTHONASPELL_TRUE@ done \ @MAKEPYTHONASPELL_TRUE@ ) @MAKEPYTHONASPELL_TRUE@rclpyaspell-install: @MAKEPYTHONASPELL_TRUE@ (cd python/pyaspell; set -x; \ @MAKEPYTHONASPELL_TRUE@ for v in 3;do test -n "`which python$${v}`" && \ @MAKEPYTHONASPELL_TRUE@ python$${v} setup.py install \ @MAKEPYTHONASPELL_TRUE@ --prefix=${prefix} --root=$${DESTDIR:-/} $(OPTSFORPYTHON); \ @MAKEPYTHONASPELL_TRUE@ done \ @MAKEPYTHONASPELL_TRUE@ ) @MAKEPYTHONASPELL_TRUE@rclpyaspell-clean: @MAKEPYTHONASPELL_TRUE@ rm -rf python/pyaspell/build @MAKEPYTHONASPELL_TRUE@ rm -rf python/pyaspell/dist/* @MAKEQT_TRUE@all-local:: recollqt @MAKEQT_TRUE@recollqt: librecoll.la @MAKEQT_TRUE@ (cd $(QTGUI); ${QMAKE} PREFIX=${prefix} DEFINE_BTIME=${ext4_birth_time} recoll.pro) @MAKEQT_TRUE@ $(MAKE) -C $(QTGUI) LFLAGS="$(LDFLAGS)" prefix=$(prefix) \ @MAKEQT_TRUE@ exec_prefix=$(exec_prefix) libdir=$(libdir) @MAKEQT_TRUE@clean-local:: recollqt-clean @MAKEQT_TRUE@recollqt-clean: @MAKEQT_TRUE@ -$(MAKE) -C $(QTGUI) clean @MAKEQT_TRUE@ rm -rf qtgui/.qm @MAKEQT_TRUE@install-exec-local:: recollqt-install @MAKEQT_TRUE@recollqt-install: @MAKEQT_TRUE@ $(MAKE) -C $(QTGUI) LFLAGS="$(LDFLAGS)" INSTALL_ROOT=$(DESTDIR) \ @MAKEQT_TRUE@ prefix=$(prefix) exec_prefix=$(exec_prefix) libdir=$(libdir) \ @MAKEQT_TRUE@ install install-data-hook: (cd $(DESTDIR)/$(filterdir); \ chmod a+x rcl* ppt-dump.py xls-dump.py xlsxmltocsv.py hotrecoll.py; \ chmod a+x recoll-we-move-files.py ../examples/rclmon.sh kosplitter.py; \ chmod a+x thunderbird-open-message.sh rclaspell-sugg.py; \ chmod 0644 msodump.zip recollepub.zip rclexecm.py rcllatinstops.zip \ rclconfig.py conftree.py rclmidi.py rclexec1.py rcluncomp.py rclxslt.py) @MAKEUSERDOC_TRUE@doc/user/usermanual.html: doc/user/usermanual.xml @MAKEUSERDOC_TRUE@ mkdir -p doc/user @MAKEUSERDOC_TRUE@ test -f doc/user/Makefile || \ @MAKEUSERDOC_TRUE@ cp -p $(top_srcdir)/doc/user/Makefile doc/user @MAKEUSERDOC_TRUE@ $(MAKE) -C doc/user VPATH=$(VPATH):$(VPATH)/doc/user usermanual.html dist-hook: (cd $(top_srcdir); find . \ \( -name '*.pyc' -o -name '#*' -o -name '*~' \) -delete) if test -z "$(NOTAG)";then \ test -z "`git status -s|grep -v recoll-$(RECOLL-VERSION.txt)`"||exit 1; \ vers=`echo $(VERSION) | sed -e 's/~/_/g'`;\ git tag -a RECOLL-$$vers -m "Release $$vers tagged"; \ fi # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: recoll-1.36.1/install-sh0000755000175000017500000003577614521160720012027 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Email bug reports to bug-automake@gnu.org. Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: recoll-1.36.1/desktop/0000755000175000017500000000000014521161751011537 500000000000000recoll-1.36.1/desktop/recoll.png0000644000175000017500000000101114410615043013431 00000000000000PNG  IHDR00` gAMA a cHRMz&u0`:pQ<?PLTEfY3LӘ@MLSȣFavvvۅQbS;#bKGD5 pHYs  tIME,gIDATH Et*˞-b@qbbtô 2u`s+eO Czd0x"¨7Dx}E;gfI[+eO(v̎)#f[/J_)A8פN+tEXtCommentCreated with The GIMPd%n%tEXtdate:create2018-05-31T15:44:15+02:00 XO%tEXtdate:modify2018-05-31T15:44:15+02:00|IENDB`recoll-1.36.1/desktop/recoll.xcf0000644000175000017500000000736114410615043013443 00000000000000gimp xcf fileBBS gimp-commentCreated with The GIMPgimp-image-grid(style intersections) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches)  Background     i %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% v%v%v%v%v%v%v%v%v%v%v%v %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% v%v%v%v%v%v%v%v%v%v%v%v f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3 f3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f 3f%f%f%f%f%f%f%f%f%f%f%f f %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%% f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3f 3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f3 f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3fv%v%v%v%v%v%v%v%v%v%v%v%v%v %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%% v%v%v%v%v%v%v%v%v%v%v%v%v%v %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%% f%f%f%f%f%f%f%f%f%f%f%f%f%f f3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f 3f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3 f %%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%% f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3f 3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f3 f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3%f3f@@recoll-1.36.1/desktop/recoll.svg0000644000175000017500000001570414410615043013462 00000000000000 image/svg+xml recoll-1.36.1/desktop/hotrecoll.py0000755000175000017500000000507614521150006014026 00000000000000#!/usr/bin/python3 # # This script should be linked to a keyboard shortcut. Under gnome, # you can do this from the main preferences menu, or directly execute # "gnome-keybinding-properties" # # Make the script executable. Install it somewhere in the executable # path ("echo $PATH" to check what's in there), and then just enter # its name as the action to perform, or copy it anywhere and copy the # full path as the action. # # The script will start recoll if there is currently no instance # running, or else toggle between minimized/shown import gi gi.require_version('Wnck', '3.0') from gi.repository import Wnck from gi.repository import Gtk import os import sys from optparse import OptionParser def deb(s): print("%s"%s, file=sys.stderr) def main(): parser = OptionParser() parser.add_option("-m", "--move-away", action="store_true", default=False, dest="clear_workspace", help="iconify to other workspace to avoid crowding panel") (options, args) = parser.parse_args() screen = Wnck.Screen.get_default() while Gtk.events_pending(): Gtk.main_iteration() recollMain = "" recollwins = []; for window in screen.get_windows(): #deb(f"Win class name: [{window.get_class_group().get_name()}] name [{window.get_name()}]") if window.get_class_group().get_name().lower() == "recoll": if window.get_name().lower().startswith("recoll"): recollMain = window recollwins.append(window) if not recollMain: deb("No Recoll window found, starting program") os.system("recoll&") sys.exit(0) # Check the main window state, and either activate or minimize all # recoll windows. workspace = screen.get_active_workspace() if not recollMain.is_visible_on_workspace(workspace): for win in recollwins: win.move_to_workspace(workspace) if win != recollMain: win.unminimize(Gtk.get_current_event_time()) recollMain.activate(Gtk.get_current_event_time()) else: otherworkspace = None if options.clear_workspace: # We try to minimize to another workspace wkspcs = screen.get_workspaces() for wkspc in wkspcs: if wkspc.get_number() != workspace.get_number(): otherworkspace = wkspc break for win in recollwins: if otherworkspace: win.move_to_workspace(otherworkspace) win.minimize() if __name__ == '__main__': main() recoll-1.36.1/desktop/recoll-searchgui.desktop0000644000175000017500000000073314410615043016300 00000000000000[Desktop Entry] Categories=Qt;Utility;Filesystem;Database; Comment=Find documents by specifying search terms Comment[ru]=Поиск документов по заданным условиям Comment[de]=Finde Dokumente durch Angeben von Suchbegriffen Exec=recoll GenericName=Local Text Search GenericName[ru]=Локальный текстовый поиск GenericName[de]=Lokale Textsuche Icon=recoll Name=Recoll Terminal=false Type=Application Keywords=Search;Full Text; recoll-1.36.1/desktop/recoll_index_on_ac.sh0000755000175000017500000000404214410615043015617 00000000000000#!/bin/sh # This is a shell script that starts and stops the recollindex daemon # depending on whether or not the power supply is plugged in. It should be # called from the file ~/.config/autostart/recollindex.desktop. # # That is: make the script executable (chmod +x) and replace in # recollindex.desk the line: # Exec=recollindex -w 60 -m # With # Exec=/path/to/recoll_index_on_ac.sh # # # By: The Doctor (drwho at virtadpt dot net) # License: GPLv3 # # Modifications by J.F Dockes # - replaced "acpi" usage with "on_ac_power" which seems to be both # more common and more universal. # - Changed the default to be that we run recollindex if we can't determine # power status (ie: on_ac_power not installed or not working: we're most # probably not running on a laptop). INDEXER="recollindex -w 60 -m" ACPI=`which on_ac_power` # If the on_ac_power script isn't installed, warn, but run anyway. Maybe # this is not a laptop or not linux. if test "x$ACPI" = "x" ; then echo "on_ac_power utility not found. Starting recollindex anyway." fi while true; do # Determine whether or not the power supply is plugged in. if test "x$ACPI" != "x" ; then on_ac_power STATUS=$? else STATUS=0 fi # Get the PID of the indexing daemon. if test -f ~/.recoll/index.pid ; then PID=`cat ~/.recoll/index.pid` # Make sure that this is recollindex running. pid could have # been reallocated ps ax | egrep "^[ \t]*$PID " | grep -q recollindex || PID="" fi # echo "Recollindex pid is $PID" if test $STATUS -eq 1 ; then # The power supply is not plugged in. See if the indexing daemon is # running, and if it is, kill it. The indexing daemon will not be # started. if test x"$PID" != x; then kill $PID fi else # The power supply is plugged in or we just don't know. # See if the indexing daemon is running, and if it's not start it. if test -z "$PID" ; then $INDEXER fi fi # Go to sleep for a while. sleep 120 continue done recoll-1.36.1/desktop/recollindex.desktop0000644000175000017500000000054014410615043015354 00000000000000[Desktop Entry] Name=Recoll real time indexer Comment=Runs in background to extract and index text from modified documents Icon=system-run Exec=recollindex -w 60 -m Terminal=false TerminalOptions= Type=Application Categories=Utility;Filesystem;Database; NoDisplay=true X-GNOME-Autostart-enabled=true X-KDE-autostart-after=panel X-KDE-UniqueApplet=true recoll-1.36.1/desktop/recoll.appdata.xml0000644000175000017500000000173114410615043015067 00000000000000 recoll.desktop CC0-1.0 GPL-2.0+ Recoll Find documents by specifying search terms

Recoll finds keywords inside documents text as well as file names.

  • It can search most document formats.
  • It can reach any storage place: files, archive members, email attachments, transparently handling decompression.
  • One click will open the document inside a native editor or display an even quicker text preview.
http://www.recoll.org/files/recoll-mainwin-h-1248x702.png http://www.recoll.org/ contact@recoll.org
recoll-1.36.1/Makefile.am0000644000175000017500000005475414521123636012061 00000000000000 # Conditionally enable building the small test drivers, but don't # distribute them, they are not generally useful if COND_TESTMAINS MAYBE_TESTMAINS = testmains endif if COND_RCLGREP MAYBE_RCLGREP = rclgrep rclgrep_man1_MANS = doc/man/rclgrep.1 endif SUBDIRS = . $(MAYBE_TESTMAINS) $(MAYBE_RCLGREP) DIST_SUBDIRS = . CXXFLAGS ?= @CXXFLAGS@ XAPIAN_LIBS=@XAPIAN_LIBS@ XAPIAN_CFLAGS=@XAPIAN_CFLAGS@ XSLT_CFLAGS=@XSLT_CFLAGS@ XSLT_LIBS=@XSLT_LIBS@ LIBICONV=@LIBICONV@ INCICONV=@INCICONV@ LIBFAM = @LIBFAM@ RCLLIBVERSION=@RCLLIBVERSION@ X_CFLAGS=@X_CFLAGS@ X_PRE_LIBS=@X_PRE_LIBS@ X_LIBS=@X_LIBS@ X_EXTRA_LIBS=@X_EXTRA_LIBS@ X_LIBX11=@X_LIBX11@ DEFS=@DEFS@ COMMONCPPFLAGS = -I. \ -I$(top_srcdir)/aspell \ -I$(top_srcdir)/bincimapmime \ -I$(top_srcdir)/common \ -I$(top_srcdir)/index \ -I$(top_srcdir)/internfile \ -I$(top_srcdir)/rcldb \ -I$(top_srcdir)/unac \ -I$(top_srcdir)/utils \ -I$(top_srcdir)/xaposix \ -DBUILDING_RECOLL AM_CPPFLAGS = -Wall -Wno-unused -std=c++14 \ $(COMMONCPPFLAGS) \ $(INCICONV) \ $(XAPIAN_CFLAGS) \ $(XSLT_CFLAGS) \ $(X_CFLAGS) \ -DRECOLL_DATADIR=\"${pkgdatadir}\" \ -DREADFILE_ENABLE_ZLIB -DREADFILE_ENABLE_MINIZ -DREADFILE_ENABLE_MD5 \ -D_GNU_SOURCE \ $(DEFS) if COND_BTIME CXXFLAGS += -DEXT4_BIRTH_TIME ext4_birth_time =1 endif ACLOCAL_AMFLAGS = -I m4 if NOTHREADS LIBTHREADS= else LIBTHREADS= $(LIBSYSTHREADS) endif if PUBLIC_LIB lib_LTLIBRARIES = if MAKE_RECOLL_LIB lib_LTLIBRARIES += librecoll.la endif else librcldir = $(libdir)/recoll librcl_LTLIBRARIES = if MAKE_RECOLL_LIB librcl_LTLIBRARIES += librecoll.la endif endif librecoll_la_SOURCES = \ aspell/rclaspell.cpp \ aspell/rclaspell.h \ bincimapmime/convert.cc \ bincimapmime/convert.h \ bincimapmime/mime-inputsource.h \ bincimapmime/mime-parsefull.cc \ bincimapmime/mime-parseonlyheader.cc \ bincimapmime/mime-printbody.cc \ bincimapmime/mime-utils.h \ bincimapmime/mime.cc \ bincimapmime/mime.h \ common/webstore.cpp \ common/webstore.h \ common/conf_post.h \ common/cstr.cpp \ common/cstr.h \ common/rclconfig.cpp \ common/rclconfig.h \ common/rclinit.cpp \ common/rclinit.h \ common/syngroups.cpp \ common/syngroups.h \ common/textsplit.cpp \ common/textsplitko.cpp \ common/textsplit.h \ common/unacpp.cpp \ common/unacpp.h \ common/uproplist.h \ common/utf8fn.cpp \ common/utf8fn.h \ index/webqueuefetcher.cpp \ index/webqueuefetcher.h \ index/checkretryfailed.cpp \ index/checkretryfailed.h \ index/exefetcher.cpp \ index/exefetcher.h \ index/fetcher.cpp \ index/fetcher.h \ index/fsfetcher.cpp \ index/fsfetcher.h \ index/idxdiags.h \ index/idxdiags.cpp \ index/idxstatus.h \ index/idxstatus.cpp \ index/mimetype.cpp \ index/mimetype.h \ index/rclmon.h \ index/recollindex.h \ index/subtreelist.cpp \ index/subtreelist.h \ internfile/Filter.h \ internfile/extrameta.cpp \ internfile/extrameta.h \ internfile/htmlparse.cpp \ internfile/htmlparse.h \ internfile/indextext.h \ internfile/internfile.cpp \ internfile/internfile.h \ internfile/mh_exec.cpp \ internfile/mh_exec.h \ internfile/mh_execm.cpp \ internfile/mh_execm.h \ internfile/mh_html.cpp \ internfile/mh_html.h \ internfile/mh_mail.cpp \ internfile/mh_mail.h \ internfile/mh_mbox.cpp \ internfile/mh_mbox.h \ internfile/mh_null.h \ internfile/mh_symlink.h \ internfile/mh_text.cpp \ internfile/mh_text.h \ internfile/mh_unknown.h \ internfile/mh_xslt.cpp \ internfile/mh_xslt.h \ internfile/mimehandler.cpp \ internfile/mimehandler.h \ internfile/myhtmlparse.cpp \ internfile/myhtmlparse.h \ internfile/txtdcode.cpp \ internfile/uncomp.cpp \ internfile/uncomp.h \ query/docseq.cpp \ query/docseq.h \ query/docseqdb.cpp \ query/docseqdb.h \ query/docseqdocs.h \ query/docseqhist.cpp \ query/docseqhist.h \ query/dynconf.cpp \ query/dynconf.h \ query/filtseq.cpp \ query/filtseq.h \ common/plaintorich.cpp \ common/plaintorich.h \ query/qresultstore.cpp \ query/qresultstore.h \ query/recollq.cpp \ query/recollq.h \ query/reslistpager.cpp \ query/reslistpager.h \ query/sortseq.cpp \ query/sortseq.h \ query/wasaparse.ypp \ query/wasaparseaux.cpp \ query/wasaparserdriver.h \ query/wasatorcl.h \ rcldb/daterange.cpp \ rcldb/daterange.h \ rcldb/expansiondbs.cpp \ rcldb/expansiondbs.h \ rcldb/rclabstract.cpp \ rcldb/rclabsfromtext.cpp \ rcldb/rcldb.cpp \ rcldb/rcldb.h \ rcldb/rcldb_p.h \ rcldb/rcldoc.cpp \ rcldb/rcldoc.h \ rcldb/rcldups.cpp \ rcldb/rclquery.cpp \ rcldb/rclquery.h \ rcldb/rclquery_p.h \ rcldb/rclterms.cpp \ rcldb/rclvalues.cpp \ rcldb/rclvalues.h \ rcldb/searchdata.cpp \ rcldb/searchdata.h \ rcldb/searchdatatox.cpp \ rcldb/searchdataxml.cpp \ rcldb/stemdb.cpp \ rcldb/stemdb.h \ rcldb/stoplist.cpp \ rcldb/stoplist.h \ rcldb/synfamily.cpp \ rcldb/synfamily.h \ rcldb/termproc.h \ rcldb/xmacros.h \ unac/unac.cpp \ unac/unac.h \ unac/unac_version.h \ utils/appformime.cpp \ utils/appformime.h \ utils/base64.cpp \ utils/base64.h \ utils/cancelcheck.cpp \ utils/cancelcheck.h \ utils/chrono.h \ utils/chrono.cpp \ utils/circache.cpp \ utils/circache.h \ utils/closefrom.cpp \ utils/closefrom.h \ utils/cmdtalk.cpp \ utils/cmdtalk.h \ utils/conftree.cpp \ utils/conftree.h \ utils/copyfile.cpp \ utils/copyfile.h \ utils/cpuconf.cpp \ utils/cpuconf.h \ utils/damlev.h \ utils/dlib.cpp \ utils/dlib.h \ utils/ecrontab.cpp \ utils/ecrontab.h \ utils/execmd.cpp \ utils/execmd.h \ utils/fileudi.cpp \ utils/fileudi.h \ utils/fstreewalk.cpp \ utils/fstreewalk.h \ utils/hldata.h \ utils/hldata.cpp \ utils/idfile.cpp \ utils/idfile.h \ utils/listmem.cpp \ utils/listmem.h \ utils/log.cpp \ utils/log.h \ utils/md5.cpp \ utils/md5.h \ utils/md5ut.cpp \ utils/md5ut.h \ utils/mimeparse.cpp \ utils/mimeparse.h \ utils/miniz.cpp \ utils/miniz.h \ utils/netcon.cpp \ utils/netcon.h \ utils/pathut.cpp \ utils/pathut.h \ utils/picoxml.h \ utils/pxattr.cpp \ utils/pxattr.h \ utils/rclionice.cpp \ utils/rclionice.h \ utils/rclutil.h \ utils/rclutil.cpp \ utils/readfile.cpp \ utils/readfile.h \ utils/smallut.cpp \ utils/smallut.h \ utils/strmatcher.cpp \ utils/strmatcher.h \ utils/transcode.cpp \ utils/transcode.h \ utils/utf8iter.cpp \ utils/utf8iter.h \ utils/wipedir.cpp \ utils/wipedir.h \ utils/workqueue.h \ utils/x11mon.cpp \ utils/x11mon.h \ utils/zlibut.cpp \ utils/zlibut.h \ xaposix/safefcntl.h \ xaposix/safesysstat.h \ xaposix/safesyswait.h \ xaposix/safeunistd.h BUILT_SOURCES = query/wasaparse.cpp AM_YFLAGS = -d # We use -release: the lib is only shared between recoll programs from the same release. # So the lib is just named after the recoll version (e.g librecoll-1.27.1.so) # -version-info $(VERSION_INFO) would handle ABI compat issues, we don't need it librecoll_la_LDFLAGS = -release $(VERSION) -no-undefined @NO_UNDEF_LINK_FLAG@ librecoll_la_LIBADD = $(XSLT_LIBS) $(XAPIAN_LIBS) $(LIBICONV) $(X_LIBX11) $(LIBTHREADS) if PUBLIC_LIB incdir = $(includedir)/recoll inc_HEADERS = \ common/plaintorich.h \ common/rclconfig.h \ common/rclinit.h \ index/idxstatus.h \ internfile/Filter.h \ internfile/internfile.h \ internfile/mimehandler.h \ query/docseq.h \ query/docseqdb.h \ query/qresultstore.h \ query/reslistpager.h \ query/wasatorcl.h \ rcldb/rcldb.h \ rcldb/rcldoc.h \ rcldb/rclquery.h \ rcldb/searchdata.h \ utils/hldata.h \ utils/log.h \ utils/pathut.h \ utils/rclutil.h \ utils/readfile.h \ utils/smallut.h else # There is probably a better way to do this. The KIO needs to be linked # with librecoll, but librecoll is installed into a non-standard place # (/usr/lib/recoll). Debian packaging has something against setting an # rpath on the kio (cause it's not the same package as the lib), so I don't # know how to link it dynamically. The other thing I don't know is how to # force automake to build a static lib with the PIC objects. So the # following target, which is only used from the KIO build, deletes any .a # and .so and rebuilds the .a with the pic objs (the kio build calls # configure --disable-static). # Of course this is very uncomfortably close to automake/libtool internals # and may not work on all systems. PicStatic: $(librecoll_la_OBJECTS) rm -f .libs/librecoll.a rm -f .libs/librecoll.so $(LIBTOOL) --tag=LD --mode=link gcc -g -O -o librecoll.la $(librecoll_la_OBJECTS) endif bin_PROGRAMS = if MAKEINDEXER bin_PROGRAMS += recollindex endif if MAKECMDLINE bin_PROGRAMS += recollq endif if MAKEXADUMP bin_PROGRAMS += xadump xadump_man1_MANS = doc/man/xadump.1 endif recollindex_SOURCES = \ index/checkindexed.cpp \ index/checkindexed.h \ index/fsindexer.cpp \ index/fsindexer.h \ index/indexer.cpp \ index/indexer.h \ index/rclmonprc.cpp \ index/rclmonrcv.cpp \ index/recollindex.cpp \ index/webqueue.cpp \ index/webqueue.h recollindex_LDADD = librecoll.la recollq_SOURCES = query/recollqmain.cpp recollq_LDADD = librecoll.la xadump_SOURCES = query/xadump.cpp xadump_LDADD = librecoll.la $(XAPIAN_LIBS) $(LIBICONV) EXTRA_DIST = \ bincimapmime/00README.recoll bincimapmime/AUTHORS bincimapmime/COPYING \ \ desktop/hotrecoll.py \ desktop/recoll.appdata.xml \ desktop/recollindex.desktop \ desktop/recoll_index_on_ac.sh \ desktop/recoll-searchgui.desktop \ desktop/recoll.png desktop/recoll.svg desktop/recoll.xcf \ \ doc/man/recoll.1 doc/man/recollq.1 \ doc/man/recollindex.1 doc/man/rclgrep.1 \ doc/man/xadump.1 doc/man/recoll.conf.5 \ \ doc/prog/Makefile doc/prog/Doxyfile doc/prog/filters.txt doc/prog/top.txt \ \ doc/user/usermanual.html doc/user/docbook-xsl.css doc/user/docbook.css \ doc/user/Makefile doc/user/recoll.conf.xml \ doc/user/custom.xsl doc/user/usermanual.xml \ \ filters/injectcommon.sh filters/recfiltcommon filters/rcltxtlines.py \ \ index/rclmon.sh \ index/recollindex@.service \ index/recollindex.service \ \ kde/kioslave/kio_recoll/00README.txt \ kde/kioslave/kio_recoll/CMakeLists.txt \ kde/kioslave/kio_recoll/data/help.html \ kde/kioslave/kio_recoll/data/searchable.html \ kde/kioslave/kio_recoll/data/welcome.html \ kde/kioslave/kio_recoll/dirif.cpp \ kde/kioslave/kio_recoll/htmlif.cpp \ kde/kioslave/kio_recoll/kio_recoll.cpp \ kde/kioslave/kio_recoll/kio_recoll.h \ kde/kioslave/kio_recoll/recoll.json \ \ kde/kioslave/kio_recoll-kde4/00README.txt \ kde/kioslave/kio_recoll-kde4/CMakeLists.txt \ kde/kioslave/kio_recoll-kde4/data/help.html \ kde/kioslave/kio_recoll-kde4/data/searchable.html \ kde/kioslave/kio_recoll-kde4/data/welcome.html \ kde/kioslave/kio_recoll-kde4/dirif.cpp \ kde/kioslave/kio_recoll-kde4/htmlif.cpp \ kde/kioslave/kio_recoll-kde4/kio_recoll.cpp \ kde/kioslave/kio_recoll-kde4/kio_recoll.h \ kde/kioslave/kio_recoll-kde4/recollf.protocol \ kde/kioslave/kio_recoll-kde4/recollnolist.protocol \ kde/kioslave/kio_recoll-kde4/recoll.protocol \ \ kde/krunner/recollrunner.json \ kde/krunner/recollrunner.h \ kde/krunner/recollrunner.cpp \ kde/krunner/CMakeLists.txt \ \ query/location.hh query/position.hh query/stack.hh \ \ qtgui/actsearch.ui \ qtgui/actsearch_w.cpp \ qtgui/actsearch_w.h \ qtgui/advsearch.ui \ qtgui/advsearch_w.cpp \ qtgui/advsearch_w.h \ qtgui/advshist.cpp \ qtgui/advshist.h \ qtgui/confgui/confgui.cpp \ qtgui/confgui/confgui.h \ qtgui/confgui/confguiindex.cpp \ qtgui/confgui/confguiindex.h \ qtgui/crontool.cpp \ qtgui/crontool.h \ qtgui/crontool.ui \ qtgui/firstidx.h \ qtgui/firstidx.ui \ qtgui/fragbuts.cpp \ qtgui/fragbuts.h \ qtgui/guiutils.cpp \ qtgui/guiutils.h \ qtgui/i18n/*.ts \ qtgui/idxmodel.cpp \ qtgui/idxmodel.h \ qtgui/idxsched.h \ qtgui/idxsched.ui \ qtgui/images/asearch.png \ qtgui/images/cancel.png \ qtgui/images/close.png \ qtgui/images/clock.png \ qtgui/images/menu.png \ qtgui/images/code-block.png \ qtgui/images/down.png \ qtgui/images/firstpage.png \ qtgui/images/history.png \ qtgui/images/interro.png \ qtgui/images/nextpage.png \ qtgui/images/prevpage.png \ qtgui/images/recoll.icns \ qtgui/images/recoll.png \ qtgui/images/sortparms.png \ qtgui/images/spell.png \ qtgui/images/table.png \ qtgui/images/up.png \ qtgui/main.cpp \ qtgui/mtpics/License_sidux.txt \ qtgui/mtpics/README \ qtgui/mtpics/aptosid-book.png \ qtgui/mtpics/aptosid-manual-copyright.txt \ qtgui/mtpics/aptosid-manual.png \ qtgui/mtpics/archive.png \ qtgui/mtpics/book.png \ qtgui/mtpics/bookchap.png \ qtgui/mtpics/document.png \ qtgui/mtpics/drawing.png \ qtgui/mtpics/emblem-symbolic-link.png \ qtgui/mtpics/folder.png \ qtgui/mtpics/html.png \ qtgui/mtpics/image.png \ qtgui/mtpics/message.png \ qtgui/mtpics/mozilla_doc.png \ qtgui/mtpics/pdf.png \ qtgui/mtpics/pidgin.png \ qtgui/mtpics/postscript.png \ qtgui/mtpics/presentation.png \ qtgui/mtpics/sidux-book.png \ qtgui/mtpics/soffice.png \ qtgui/mtpics/source.png \ qtgui/mtpics/sownd.png \ qtgui/mtpics/spreadsheet.png \ qtgui/mtpics/text-x-python.png \ qtgui/mtpics/txt.png \ qtgui/mtpics/video.png \ qtgui/mtpics/wordprocessing.png \ qtgui/multisave.cpp \ qtgui/multisave.h \ qtgui/preview_load.cpp \ qtgui/preview_load.h \ qtgui/preview_plaintorich.cpp \ qtgui/preview_plaintorich.h \ qtgui/preview_w.cpp \ qtgui/preview_w.h \ qtgui/preview.ui \ qtgui/ptrans.ui \ qtgui/ptrans_w.cpp \ qtgui/ptrans_w.h \ qtgui/rclhelp.cpp \ qtgui/rclhelp.h \ qtgui/rclm_idx.cpp \ qtgui/rclm_menus.cpp \ qtgui/rclm_preview.cpp \ qtgui/rclm_saveload.cpp \ qtgui/rclm_sidefilters.cpp \ qtgui/rclm_view.cpp \ qtgui/rclm_wins.cpp \ qtgui/rclmain.ui \ qtgui/rclmain_w.cpp \ qtgui/rclmain_w.h \ qtgui/rclzg.cpp \ qtgui/rclzg.h \ qtgui/recoll.h \ qtgui/recoll.pro.in \ qtgui/recoll.qrc \ qtgui/reslist.cpp \ qtgui/reslist.h \ qtgui/respopup.cpp \ qtgui/respopup.h \ qtgui/restable.cpp \ qtgui/restable.h \ qtgui/restable.ui \ qtgui/rtitool.cpp \ qtgui/rtitool.h \ qtgui/rtitool.ui \ qtgui/scbase.cpp \ qtgui/scbase.h \ qtgui/searchclause_w.cpp \ qtgui/searchclause_w.h \ qtgui/snippets.ui \ qtgui/snippets_w.cpp \ qtgui/snippets_w.h \ qtgui/specialindex.h \ qtgui/specialindex.ui \ qtgui/spell.ui \ qtgui/spell_w.cpp \ qtgui/spell_w.h \ qtgui/ssearch_w.cpp \ qtgui/ssearch_w.h \ qtgui/ssearchb.ui \ qtgui/systray.cpp \ qtgui/systray.h \ qtgui/uiprefs.ui \ qtgui/uiprefs_w.cpp \ qtgui/uiprefs_w.h \ qtgui/viewaction.ui \ qtgui/viewaction_w.cpp \ qtgui/viewaction_w.h \ qtgui/webcache.ui \ qtgui/webcache.cpp \ qtgui/webcache.h \ qtgui/widgets/editdialog.h \ qtgui/widgets/editdialog.ui \ qtgui/widgets/listdialog.h \ qtgui/widgets/listdialog.ui \ qtgui/widgets/qxtconfirmationmessage.cpp \ qtgui/widgets/qxtconfirmationmessage.h \ qtgui/widgets/qxtglobal.h \ qtgui/winschedtool.cpp \ qtgui/winschedtool.h \ qtgui/winschedtool.ui \ qtgui/xmltosd.cpp \ qtgui/xmltosd.h \ \ python/README.txt \ python/pyaspell/aspell.c \ python/pyaspell/LICENSE \ python/pyaspell/pyaspell.py \ python/pyaspell/README.rst \ python/pyaspell/setup.py.in \ python/pychm/AUTHORS \ python/pychm/COPYING \ python/pychm/MANIFEST.in \ python/pychm/README-RECOLL.txt \ python/pychm/recollchm \ python/pychm/recollchm/__init__.py \ python/pychm/recollchm/chm.py \ python/pychm/recollchm/chmlib.py \ python/pychm/recollchm/extra.c \ python/pychm/recollchm/swig_chm.c \ python/pychm/recollchm/swig_chm.i \ python/pychm/setup.py.in \ python/recoll/pyrclextract.cpp \ python/recoll/pyrecoll.cpp \ python/recoll/pyrecoll.h \ python/recoll/pyresultstore.cpp \ python/recoll/recoll/__init__.py \ python/recoll/recoll/conftree.py \ python/recoll/recoll/rclconfig.py \ python/recoll/recoll/rclextract.py \ python/recoll/recoll/recoll.py \ python/recoll/setup.py.in \ python/samples/docdups.py \ python/samples/mutt-recoll.py \ python/samples/rcldlkp.py \ python/samples/rclmbox.py \ python/samples/recollgui/Makefile \ python/samples/recollgui/qrecoll.py \ python/samples/recollgui/rclmain.ui \ python/samples/recollq.py \ python/samples/recollqsd.py \ \ rclgrep/Makefile.am \ rclgrep/rclgrep.cpp \ \ sampleconf/backends sampleconf/fields sampleconf/fragment-buttons.xml sampleconf/mimeconf \ sampleconf/mimemap sampleconf/mimeview sampleconf/macos/mimeview \ sampleconf/recoll.conf sampleconf/recoll.qss \ sampleconf/recoll-common.css sampleconf/recoll-common.qss \ sampleconf/recoll-dark.qss sampleconf/recoll-dark.css \ \ testmains/Makefile.am \ \ unac/AUTHORS unac/COPYING unac/README unac/README.recoll unac/unac.c \ \ RECOLL-VERSION.txt # EXTRA_DIST: The Php Code does not build anymore. No need to ship it until # someone fixes it: # php/00README.txt php/recoll/config.m4 php/recoll/make.sh # php/recoll/php_recoll.h php/recoll/recoll.cpp php/sample/shell.php OPTSFORPYTHON = $(shell test -f /etc/debian_version && echo --install-layout=deb) if MAKEPYTHON all-local:: recollpython install-exec-local:: recollpython-install clean-local:: recollpython-clean recollpython: librecoll.la (cd python/recoll; set -x; \ for v in 2 3;do test -n "`which python$${v}`" && \ libdir=$(libdir) python$${v} setup.py build; \ done \ ) recollpython-install: (cd python/recoll; set -x; \ for v in 2 3;do test -n "`which python$${v}`" && \ python$${v} setup.py install \ --prefix=${prefix} --root=$${DESTDIR:-/} $(OPTSFORPYTHON); \ done; \ ) recollpython-clean: rm -f python/recoll/*.pyc rm -rf python/recoll/Recoll.egg-info rm -rf python/recoll/__pycache__ rm -rf python/recoll/build endif if MAKEPYTHONCHM all-local:: rclpychm install-exec-local:: rclpychm-install clean-local:: rclpychm-clean rclpychm: (cd python/pychm; set -x; \ for v in 3;do \ test -n "`which python$${v}`" && python$${v} setup.py build;\ done \ ) rclpychm-install: (cd python/pychm; set -x; \ for v in 3;do test -n "`which python$${v}`" && \ python$${v} setup.py install \ --prefix=${prefix} --root=$${DESTDIR:-/} $(OPTSFORPYTHON); \ done \ ) rclpychm-clean: rm -rf python/pychm/build rm -rf python/pychm/build rm -rf python/pychm/dist/* rm -rf python/pychm/recollchm.egg-info endif if MAKEPYTHONASPELL all-local:: rclpyaspell install-exec-local:: rclpyaspell-install clean-local:: rclpyaspell-clean rclpyaspell: (cd python/pyaspell; set -x; \ for v in 3;do \ test -n "`which python$${v}`" && python$${v} setup.py build;\ done \ ) rclpyaspell-install: (cd python/pyaspell; set -x; \ for v in 3;do test -n "`which python$${v}`" && \ python$${v} setup.py install \ --prefix=${prefix} --root=$${DESTDIR:-/} $(OPTSFORPYTHON); \ done \ ) rclpyaspell-clean: rm -rf python/pyaspell/build rm -rf python/pyaspell/dist/* endif if MAKEQT all-local:: recollqt recollqt: librecoll.la (cd $(QTGUI); ${QMAKE} PREFIX=${prefix} DEFINE_BTIME=${ext4_birth_time} recoll.pro) $(MAKE) -C $(QTGUI) LFLAGS="$(LDFLAGS)" prefix=$(prefix) \ exec_prefix=$(exec_prefix) libdir=$(libdir) clean-local:: recollqt-clean recollqt-clean: -$(MAKE) -C $(QTGUI) clean rm -rf qtgui/.qm install-exec-local:: recollqt-install recollqt-install: $(MAKE) -C $(QTGUI) LFLAGS="$(LDFLAGS)" INSTALL_ROOT=$(DESTDIR) \ prefix=$(prefix) exec_prefix=$(exec_prefix) libdir=$(libdir) \ install endif defconfdir = $(pkgdatadir)/examples defconf_DATA = \ desktop/recollindex.desktop \ index/rclmon.sh \ index/recollindex.service \ index/recollindex@.service \ sampleconf/backends \ sampleconf/fields \ sampleconf/fragment-buttons.xml \ sampleconf/mimeconf \ sampleconf/mimemap \ sampleconf/mimeview \ sampleconf/recoll-common.css \ sampleconf/recoll-common.qss \ sampleconf/recoll-dark.css \ sampleconf/recoll-dark.qss \ sampleconf/recoll.conf \ sampleconf/recoll.qss filterdir = $(pkgdatadir)/filters dist_filter_DATA = \ desktop/hotrecoll.py \ filters/abiword.xsl \ filters/cmdtalk.py \ filters/fb2.xsl \ filters/gnumeric.xsl \ filters/kosplitter.py \ filters/msodump.zip \ filters/okular-note.xsl \ filters/opendoc-body.xsl \ filters/opendoc-flat.xsl \ filters/opendoc-meta.xsl \ filters/openxml-xls-body.xsl \ filters/openxml-word-body.xsl \ filters/openxml-meta.xsl \ filters/ppt-dump.py \ filters/rcl7z.py \ filters/rclaptosidman \ filters/rclaspell-sugg.py \ filters/rclaudio.py \ filters/rclbasehandler.py \ filters/rclbibtex.sh \ filters/rclcheckneedretry.sh \ filters/rclchm.py \ filters/rcldia.py \ filters/rcldjvu.py \ filters/rcldoc.py \ filters/rcldvi \ filters/rclepub.py \ filters/rclepub1.py \ filters/rclexec1.py \ filters/rclexecm.py \ filters/rclfb2.py \ filters/rclgaim \ filters/rclgenxslt.py \ filters/rclhwp.py \ filters/rclics.py \ filters/rclimg \ filters/rclimg.py \ filters/rclinfo.py \ filters/rclipynb.py \ filters/rcljoplin.py \ filters/rclkar.py \ filters/rclkwd \ filters/rcllatinclass.py \ filters/rcllatinstops.zip \ filters/rcllyx \ filters/rclman \ filters/rclmidi.py \ filters/rclocrcache.py \ filters/rclocr.py \ filters/rclocrabbyy.py \ filters/rclocrtesseract.py \ filters/rclopxml.py \ filters/rclorgmode.py \ filters/rclpdf.py \ filters/rclppt.py \ filters/rclps \ filters/rclpst.py \ filters/rclpurple \ filters/rclpython.py \ filters/rclrar.py \ filters/rclrtf.py \ filters/rclscribus \ filters/rclshowinfo \ filters/rcltar.py \ filters/rcltex \ filters/rcltext.py \ filters/rcluncomp \ filters/rcluncomp.py \ filters/rclwar.py \ filters/rclxls.py \ filters/rclxml.py \ filters/rclxmp.py \ filters/rclxslt.py \ filters/rclzip.py \ filters/recoll-we-move-files.py \ filters/recollepub.zip \ filters/svg.xsl \ filters/thunderbird-open-message.sh \ filters/xls-dump.py \ filters/xlsxmltocsv.py \ filters/xml.xsl \ python/recoll/recoll/conftree.py \ python/recoll/recoll/rclconfig.py if INSTALL_SYSTEMD_UNITS systemd_system_unitdir = @SYSTEMD_SYSTEM_UNIT_DIR@ systemd_user_unitdir = @SYSTEMD_USER_UNIT_DIR@ systemd_system_unit_DATA = index/recollindex@.service systemd_user_unit_DATA = index/recollindex.service endif install-data-hook: (cd $(DESTDIR)/$(filterdir); \ chmod a+x rcl* ppt-dump.py xls-dump.py xlsxmltocsv.py hotrecoll.py; \ chmod a+x recoll-we-move-files.py ../examples/rclmon.sh kosplitter.py; \ chmod a+x thunderbird-open-message.sh rclaspell-sugg.py; \ chmod 0644 msodump.zip recollepub.zip rclexecm.py rcllatinstops.zip \ rclconfig.py conftree.py rclmidi.py rclexec1.py rcluncomp.py rclxslt.py) if MAKEUSERDOC rdocdir = $(pkgdatadir)/doc rdoc_DATA = doc/user/usermanual.html doc/user/docbook-xsl.css doc/user/usermanual.html: doc/user/usermanual.xml mkdir -p doc/user test -f doc/user/Makefile || \ cp -p $(top_srcdir)/doc/user/Makefile doc/user $(MAKE) -C doc/user VPATH=$(VPATH):$(VPATH)/doc/user usermanual.html endif dist_man1_MANS = doc/man/recoll.1 doc/man/recollq.1 \ doc/man/recollindex.1 $(rclgrep_man1_MANS) \ $(xadump_man1_MANS) dist_man5_MANS = doc/man/recoll.conf.5 dist-hook: (cd $(top_srcdir); find . \ \( -name '*.pyc' -o -name '#*' -o -name '*~' \) -delete) if test -z "$(NOTAG)";then \ test -z "`git status -s|grep -v recoll-$(RECOLL-VERSION.txt)`"||exit 1; \ vers=`echo $(VERSION) | sed -e 's/~/_/g'`;\ git tag -a RECOLL-$$vers -m "Release $$vers tagged"; \ fi recoll-1.36.1/config.guess0000755000175000017500000014051214521160720012324 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-09' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # Just in case it came from the environment. GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039,SC3028 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c89 c99 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` case $UNAME_MACHINE_ARCH in aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case $UNAME_VERSION in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. GUESS=$machine-${os}${release}${abi-} ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE ;; *:SecBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE ;; *:MidnightBSD:*:*) GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE ;; *:ekkoBSD:*:*) GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE ;; *:SolidBSD:*:*) GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE ;; *:OS108:*:*) GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE ;; macppc:MirBSD:*:*) GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE ;; *:MirBSD:*:*) GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE ;; *:Sortix:*:*) GUESS=$UNAME_MACHINE-unknown-sortix ;; *:Twizzler:*:*) GUESS=$UNAME_MACHINE-unknown-twizzler ;; *:Redox:*:*) GUESS=$UNAME_MACHINE-unknown-redox ;; mips:OSF1:*.*) GUESS=mips-dec-osf1 ;; alpha:OSF1:*:*) # Reset EXIT trap before exiting to avoid spurious non-zero exit code. trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` GUESS=$UNAME_MACHINE-dec-osf$OSF_REL ;; Amiga*:UNIX_System_V:4.0:*) GUESS=m68k-unknown-sysv4 ;; *:[Aa]miga[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-amigaos ;; *:[Mm]orph[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-morphos ;; *:OS/390:*:*) GUESS=i370-ibm-openedition ;; *:z/VM:*:*) GUESS=s390-ibm-zvmoe ;; *:OS400:*:*) GUESS=powerpc-ibm-os400 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) GUESS=arm-acorn-riscix$UNAME_RELEASE ;; arm*:riscos:*:*|arm*:RISCOS:*:*) GUESS=arm-unknown-riscos ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) GUESS=hppa1.1-hitachi-hiuxmpp ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. case `(/bin/universe) 2>/dev/null` in att) GUESS=pyramid-pyramid-sysv3 ;; *) GUESS=pyramid-pyramid-bsd ;; esac ;; NILE*:*:*:dcosx) GUESS=pyramid-pyramid-svr4 ;; DRS?6000:unix:4.0:6*) GUESS=sparc-icl-nx6 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) GUESS=sparc-icl-nx7 ;; esac ;; s390x:SunOS:*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL ;; sun4H:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-hal-solaris2$SUN_REL ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris2$SUN_REL ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) GUESS=i386-pc-auroraux$UNAME_RELEASE ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris3$SUN_REL ;; sun4*:SunOS:*:*) case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; sun3*:SunOS:*:*) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case `/bin/arch` in sun3) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac ;; aushp:SunOS:*:*) GUESS=sparc-auspex-sunos$UNAME_RELEASE ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) GUESS=m68k-milan-mint$UNAME_RELEASE ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) GUESS=m68k-hades-mint$UNAME_RELEASE ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) GUESS=m68k-unknown-mint$UNAME_RELEASE ;; m68k:machten:*:*) GUESS=m68k-apple-machten$UNAME_RELEASE ;; powerpc:machten:*:*) GUESS=powerpc-apple-machten$UNAME_RELEASE ;; RISC*:Mach:*:*) GUESS=mips-dec-mach_bsd4.3 ;; RISC*:ULTRIX:*:*) GUESS=mips-dec-ultrix$UNAME_RELEASE ;; VAX*:ULTRIX*:*:*) GUESS=vax-dec-ultrix$UNAME_RELEASE ;; 2020:CLIX:*:* | 2430:CLIX:*:*) GUESS=clipper-intergraph-clix$UNAME_RELEASE ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } GUESS=mips-mips-riscos$UNAME_RELEASE ;; Motorola:PowerMAX_OS:*:*) GUESS=powerpc-motorola-powermax ;; Motorola:*:4.3:PL8-*) GUESS=powerpc-harris-powermax ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) GUESS=powerpc-harris-powermax ;; Night_Hawk:Power_UNIX:*:*) GUESS=powerpc-harris-powerunix ;; m88k:CX/UX:7*:*) GUESS=m88k-harris-cxux7 ;; m88k:*:4*:R4*) GUESS=m88k-motorola-sysv4 ;; m88k:*:3*:R3*) GUESS=m88k-motorola-sysv3 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then GUESS=m88k-dg-dgux$UNAME_RELEASE else GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else GUESS=i586-dg-dgux$UNAME_RELEASE fi ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) GUESS=m88k-dolphin-sysv3 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 GUESS=m88k-motorola-sysv3 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) GUESS=m88k-tektronix-sysv3 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) GUESS=m68k-tektronix-bsd ;; *:IRIX*:*:*) IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` GUESS=mips-sgi-irix$IRIX_REL ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) GUESS=i386-ibm-aix ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then GUESS=$SYSTEM_NAME else GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then GUESS=rs6000-ibm-aix3.2.4 else GUESS=rs6000-ibm-aix3.2 fi ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if test -x /usr/bin/lslpp ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$IBM_ARCH-ibm-aix$IBM_REV ;; *:AIX:*:*) GUESS=rs6000-ibm-aix ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) GUESS=romp-ibm-bsd4.4 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) GUESS=rs6000-bull-bosx ;; DPX/2?00:B.O.S.:*:*) GUESS=m68k-bull-sysv3 ;; 9000/[34]??:4.3bsd:1.*:*) GUESS=m68k-hp-bsd ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) GUESS=m68k-hp-bsd4.4 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if test "$HP_ARCH" = hppa2.0w then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi GUESS=$HP_ARCH-hp-hpux$HPUX_REV ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` GUESS=ia64-hp-hpux$HPUX_REV ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } GUESS=unknown-hitachi-hiuxwe2 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) GUESS=hppa1.1-hp-bsd ;; 9000/8??:4.3bsd:*:*) GUESS=hppa1.0-hp-bsd ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) GUESS=hppa1.0-hp-mpeix ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) GUESS=hppa1.1-hp-osf ;; hp8??:OSF1:*:*) GUESS=hppa1.0-hp-osf ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then GUESS=$UNAME_MACHINE-unknown-osf1mk else GUESS=$UNAME_MACHINE-unknown-osf1 fi ;; parisc*:Lites*:*:*) GUESS=hppa1.1-hp-lites ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) GUESS=c1-convex-bsd ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) GUESS=c34-convex-bsd ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) GUESS=c38-convex-bsd ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) GUESS=c4-convex-bsd ;; CRAY*Y-MP:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=ymp-cray-unicos$CRAY_REL ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=t90-cray-unicos$CRAY_REL ;; CRAY*T3E:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=alphaev5-cray-unicosmk$CRAY_REL ;; CRAY*SV1:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=sv1-cray-unicos$CRAY_REL ;; *:UNICOS/mp:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=craynv-cray-unicosmp$CRAY_REL ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE ;; sparc*:BSD/OS:*:*) GUESS=sparc-unknown-bsdi$UNAME_RELEASE ;; *:BSD/OS:*:*) GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi else FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf fi ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL ;; i*:CYGWIN*:*) GUESS=$UNAME_MACHINE-pc-cygwin ;; *:MINGW64*:*) GUESS=$UNAME_MACHINE-pc-mingw64 ;; *:MINGW*:*) GUESS=$UNAME_MACHINE-pc-mingw32 ;; *:MSYS*:*) GUESS=$UNAME_MACHINE-pc-msys ;; i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; *:SerenityOS:*:*) GUESS=$UNAME_MACHINE-pc-serenity ;; *:Interix*:*) case $UNAME_MACHINE in x86) GUESS=i586-pc-interix$UNAME_RELEASE ;; authenticamd | genuineintel | EM64T) GUESS=x86_64-unknown-interix$UNAME_RELEASE ;; IA64) GUESS=ia64-unknown-interix$UNAME_RELEASE ;; esac ;; i*:UWIN*:*) GUESS=$UNAME_MACHINE-pc-uwin ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) GUESS=x86_64-pc-cygwin ;; prep*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=powerpcle-unknown-solaris2$SUN_REL ;; *:GNU:*:*) # the GNU system GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL ;; *:GNU/*:*:*) # other systems with GNU libc and userland GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi ;; avr32*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; cris:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; crisv32:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; e2k:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; frv:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; hexagon:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:Linux:*:*) GUESS=$UNAME_MACHINE-pc-linux-$LIBC ;; ia64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m68*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` eval "$cc_set_vars" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) GUESS=or1k-unknown-linux-$LIBC ;; or32:Linux:*:* | or1k*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; padre:Linux:*:*) GUESS=sparc-unknown-linux-$LIBC ;; parisc64:Linux:*:* | hppa64:Linux:*:*) GUESS=hppa64-unknown-linux-$LIBC ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; *) GUESS=hppa-unknown-linux-$LIBC ;; esac ;; ppc64:Linux:*:*) GUESS=powerpc64-unknown-linux-$LIBC ;; ppc:Linux:*:*) GUESS=powerpc-unknown-linux-$LIBC ;; ppc64le:Linux:*:*) GUESS=powerpc64le-unknown-linux-$LIBC ;; ppcle:Linux:*:*) GUESS=powerpcle-unknown-linux-$LIBC ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; s390:Linux:*:* | s390x:Linux:*:*) GUESS=$UNAME_MACHINE-ibm-linux-$LIBC ;; sh64*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sh*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; vax:Linux:*:*) GUESS=$UNAME_MACHINE-dec-linux-$LIBC ;; x86_64:Linux:*:*) set_cc_for_build LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_X32 >/dev/null then LIBCABI=${LIBC}x32 fi fi GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. GUESS=i386-sequent-sysv4 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; i*86:XTS-300:*:STOP) GUESS=$UNAME_MACHINE-unknown-stop ;; i*86:atheos:*:*) GUESS=$UNAME_MACHINE-unknown-atheos ;; i*86:syllable:*:*) GUESS=$UNAME_MACHINE-pc-syllable ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) GUESS=i386-unknown-lynxos$UNAME_RELEASE ;; i*86:*DOS:*:*) GUESS=$UNAME_MACHINE-pc-msdosdjgpp ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv32 fi ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. GUESS=i586-pc-msdosdjgpp ;; Intel:Mach:3*:*) GUESS=i386-pc-mach3 ;; paragon:*:*:*) GUESS=i860-intel-osf1 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi ;; mini*:CTIX:SYS*5:*) # "miniframe" GUESS=m68010-convergent-sysv ;; mc68k:UNIX:SYSTEM5:3.51m) GUESS=m68k-convergent-sysv ;; M680?0:D-NIX:5.3:*) GUESS=m68k-diab-dnix ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) GUESS=m68k-unknown-lynxos$UNAME_RELEASE ;; mc68030:UNIX_System_V:4.*:*) GUESS=m68k-atari-sysv4 ;; TSUNAMI:LynxOS:2.*:*) GUESS=sparc-unknown-lynxos$UNAME_RELEASE ;; rs6000:LynxOS:2.*:*) GUESS=rs6000-unknown-lynxos$UNAME_RELEASE ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) GUESS=powerpc-unknown-lynxos$UNAME_RELEASE ;; SM[BE]S:UNIX_SV:*:*) GUESS=mips-dde-sysv$UNAME_RELEASE ;; RM*:ReliantUNIX-*:*:*) GUESS=mips-sni-sysv4 ;; RM*:SINIX-*:*:*) GUESS=mips-sni-sysv4 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm GUESS=hppa1.1-stratus-sysv4 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. GUESS=i860-stratus-sysv4 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. GUESS=$UNAME_MACHINE-stratus-vos ;; *:VOS:*:*) # From Paul.Green@stratus.com. GUESS=hppa1.1-stratus-vos ;; mc68*:A/UX:*:*) GUESS=m68k-apple-aux$UNAME_RELEASE ;; news*:NEWS-OS:6*:*) GUESS=mips-sony-newsos6 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then GUESS=mips-nec-sysv$UNAME_RELEASE else GUESS=mips-unknown-sysv$UNAME_RELEASE fi ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. GUESS=powerpc-be-beos ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. GUESS=powerpc-apple-beos ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. GUESS=i586-pc-beos ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; x86_64:Haiku:*:*) GUESS=x86_64-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE ;; SX-5:SUPER-UX:*:*) GUESS=sx5-nec-superux$UNAME_RELEASE ;; SX-6:SUPER-UX:*:*) GUESS=sx6-nec-superux$UNAME_RELEASE ;; SX-7:SUPER-UX:*:*) GUESS=sx7-nec-superux$UNAME_RELEASE ;; SX-8:SUPER-UX:*:*) GUESS=sx8-nec-superux$UNAME_RELEASE ;; SX-8R:SUPER-UX:*:*) GUESS=sx8r-nec-superux$UNAME_RELEASE ;; SX-ACE:SUPER-UX:*:*) GUESS=sxace-nec-superux$UNAME_RELEASE ;; Power*:Rhapsody:*:*) GUESS=powerpc-apple-rhapsody$UNAME_RELEASE ;; *:Rhapsody:*:*) GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE ;; arm64:Darwin:*:*) GUESS=aarch64-apple-darwin$UNAME_RELEASE ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE ;; *:QNX:*:4*) GUESS=i386-pc-qnx ;; NEO-*:NONSTOP_KERNEL:*:*) GUESS=neo-tandem-nsk$UNAME_RELEASE ;; NSE-*:NONSTOP_KERNEL:*:*) GUESS=nse-tandem-nsk$UNAME_RELEASE ;; NSR-*:NONSTOP_KERNEL:*:*) GUESS=nsr-tandem-nsk$UNAME_RELEASE ;; NSV-*:NONSTOP_KERNEL:*:*) GUESS=nsv-tandem-nsk$UNAME_RELEASE ;; NSX-*:NONSTOP_KERNEL:*:*) GUESS=nsx-tandem-nsk$UNAME_RELEASE ;; *:NonStop-UX:*:*) GUESS=mips-compaq-nonstopux ;; BS2000:POSIX*:*:*) GUESS=bs2000-siemens-sysv ;; DS/*:UNIX_System_V:*:*) GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "${cputype-}" = 386; then UNAME_MACHINE=i386 elif test "x${cputype-}" != x; then UNAME_MACHINE=$cputype fi GUESS=$UNAME_MACHINE-unknown-plan9 ;; *:TOPS-10:*:*) GUESS=pdp10-unknown-tops10 ;; *:TENEX:*:*) GUESS=pdp10-unknown-tenex ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) GUESS=pdp10-dec-tops20 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) GUESS=pdp10-xkl-tops20 ;; *:TOPS-20:*:*) GUESS=pdp10-unknown-tops20 ;; *:ITS:*:*) GUESS=pdp10-unknown-its ;; SEI:*:*:SEIUX) GUESS=mips-sei-seiux$UNAME_RELEASE ;; *:DragonFly:*:*) DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case $UNAME_MACHINE in A*) GUESS=alpha-dec-vms ;; I*) GUESS=ia64-dec-vms ;; V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) GUESS=i386-pc-xenix ;; i*86:skyos:*:*) SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL ;; i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; i*86:Fiwix:*:*) GUESS=$UNAME_MACHINE-pc-fiwix ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; x86_64:VMkernel:*:*) GUESS=$UNAME_MACHINE-unknown-esx ;; amd64:Isilon\ OneFS:*:*) GUESS=x86_64-unknown-onefs ;; *:Unleashed:*:*) GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; esac # Do we have a guess based on uname results? if test "x$GUESS" != x; then echo "$GUESS" exit fi # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: recoll-1.36.1/config.sub0000755000175000017500000010511614521160720011770 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Split fields of configuration type # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) # A lone config we happen to match not fitting any pattern case $field1-$field2 in decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 basic_os=$field2 ;; zephyr*) basic_machine=$field1-unknown basic_os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ | unicom* | ibm* | next | hp | isi* | apollo | altos* \ | convergent* | ncr* | news | 32* | 3600* | 3100* \ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ | ultra | tti* | harris | dolphin | highlevel | gould \ | cbm | ns | masscomp | apple | axis | knuth | cray \ | microblaze* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 basic_os= ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; convex-c1) basic_machine=c1-convex basic_os=bsd ;; convex-c2) basic_machine=c2-convex basic_os=bsd ;; convex-c32) basic_machine=c32-convex basic_os=bsd ;; convex-c34) basic_machine=c34-convex basic_os=bsd ;; convex-c38) basic_machine=c38-convex basic_os=bsd ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) cpu=m68k vendor=motorola ;; dpx2*) cpu=m68k vendor=bull basic_os=sysv3 ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next case $basic_os in openstep*) ;; nextstep*) ;; ns2*) basic_os=nextstep2 ;; *) basic_os=nextstep3 ;; esac ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x$basic_os != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. case $basic_os in gnu/linux*) kernel=linux os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` ;; os2-emx) kernel=os2 os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` ;; nto-qnx*) kernel=nto os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read kernel os <&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os in linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ | linux-musl* | linux-relibc* | linux-uclibc* ) ;; uclinux-uclibc* ) ;; -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 exit 1 ;; kfreebsd*-gnu* | kopensolaris*-gnu*) ;; vxworks-simlinux | vxworks-simwindows | vxworks-spe) ;; nto-qnx*) ;; os2-emx) ;; *-eabi* | *-gnueabi*) ;; -*) # Blank kernel with real OS is always fine. ;; *-*) echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$os in *-riscix*) vendor=acorn ;; *-sunos*) vendor=sun ;; *-cnk* | *-aix*) vendor=ibm ;; *-beos*) vendor=be ;; *-hpux*) vendor=hp ;; *-mpeix*) vendor=hp ;; *-hiux*) vendor=hitachi ;; *-unos*) vendor=crds ;; *-dgux*) vendor=dg ;; *-luna*) vendor=omron ;; *-genix*) vendor=ns ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) vendor=ibm ;; *-ptx*) vendor=sequent ;; *-tpf*) vendor=ibm ;; *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; *-aux*) vendor=apple ;; *-hms*) vendor=hitachi ;; *-mpw* | *-macos*) vendor=apple ;; *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; *-vos*) vendor=stratus ;; esac ;; esac echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: recoll-1.36.1/ltmain.sh0000755000175000017500000117720314410766352011651 00000000000000#! /bin/sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2014-01-03.01 # libtool (GNU libtool) 2.4.6 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.6 Debian-2.4.6-15build2" package_revision=2.4.6 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2015-01-20.17; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # Copyright (C) 2004-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # As a special exception to the GNU General Public License, if you distribute # this file as part of a program or library that is built using GNU Libtool, # you may include this file under the same distribution terms that you use # for the rest of that program. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1+=\\ \$func_quote_for_eval_result" }' else func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1=\$$1\\ \$func_quote_for_eval_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_for_eval ARG... # -------------------------- # Aesthetically quote ARGs to be evaled later. # This function returns two values: # i) func_quote_for_eval_result # double-quoted, suitable for a subsequent eval # ii) func_quote_for_eval_unquoted_result # has all characters that are still active within double # quotes backslashified. func_quote_for_eval () { $debug_cmd func_quote_for_eval_unquoted_result= func_quote_for_eval_result= while test 0 -lt $#; do case $1 in *[\\\`\"\$]*) _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; *) _G_unquoted_arg=$1 ;; esac if test -n "$func_quote_for_eval_unquoted_result"; then func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" else func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" fi case $_G_unquoted_arg in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_quoted_arg=\"$_G_unquoted_arg\" ;; *) _G_quoted_arg=$_G_unquoted_arg ;; esac if test -n "$func_quote_for_eval_result"; then func_append func_quote_for_eval_result " $_G_quoted_arg" else func_append func_quote_for_eval_result "$_G_quoted_arg" fi shift done } # func_quote_for_expand ARG # ------------------------- # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { $debug_cmd case $1 in *[\\\`\"]*) _G_arg=`$ECHO "$1" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; *) _G_arg=$1 ;; esac case $_G_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_arg=\"$_G_arg\" ;; esac func_quote_for_expand_result=$_G_arg } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_for_expand "$_G_cmd" eval "func_notquiet $func_quote_for_expand_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_for_expand "$_G_cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # Set a version string for this script. scriptversion=2015-10-07.11; # UTC # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # Copyright (C) 2010-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# warranty; '. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # to the main code. A hook is just a named list of of function, that can # be run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of functions called by FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It is assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd _G_rc_run_hooks=false case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook funcions.n" ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do if eval $_G_hook '"$@"'; then # store returned options list back into positional # parameters for next 'cmd' execution. eval _G_hook_result=\$${_G_hook}_result eval set dummy "$_G_hook_result"; shift _G_rc_run_hooks=: fi done $_G_rc_run_hooks && func_run_hooks_result=$_G_hook_result } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list in your hook function, you may remove/edit # any options that you action, and then pass back the remaining unprocessed # options in '_result', escaped suitably for # 'eval'. In this case you also must return $EXIT_SUCCESS to let the # hook's caller know that it should pay attention to # '_result'. Returning $EXIT_FAILURE signalizes that # arguments are left untouched by the hook and therefore caller will ignore the # result variable. # # Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # No change in '$@' (ignored completely by this hook). There is # # no need to do the equivalent (but slower) action: # # func_quote_for_eval ${1+"$@"} # # my_options_prep_result=$func_quote_for_eval_result # false # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # args_changed=false # # # Note that for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: # args_changed=: # ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # args_changed=: # ;; # *) # Make sure the first unrecognised option "$_G_opt" # # is added back to "$@", we could need that later # # if $args_changed is true. # set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # # if $args_changed; then # func_quote_for_eval ${1+"$@"} # my_silent_option_result=$func_quote_for_eval_result # fi # # $args_changed # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # # false # } # func_add_hook func_validate_options my_option_validation # # You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options_finish [ARG]... # ---------------------------- # Finishing the option parse loop (call 'func_options' hooks ATM). func_options_finish () { $debug_cmd _G_func_options_finish_exit=false if func_run_hooks func_options ${1+"$@"}; then func_options_finish_result=$func_run_hooks_result _G_func_options_finish_exit=: fi $_G_func_options_finish_exit } # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd _G_rc_options=false for my_func in options_prep parse_options validate_options options_finish do if eval func_$my_func '${1+"$@"}'; then eval _G_res_var='$'"func_${my_func}_result" eval set dummy "$_G_res_var" ; shift _G_rc_options=: fi done # Save modified positional parameters for caller. As a top-level # options-parser function we always need to set the 'func_options_result' # variable (regardless the $_G_rc_options value). if $_G_rc_options; then func_options_result=$_G_res_var else func_quote_for_eval ${1+"$@"} func_options_result=$func_quote_for_eval_result fi $_G_rc_options } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propagate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before # returning $EXIT_SUCCESS (otherwise $EXIT_FAILURE is returned). func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= _G_rc_options_prep=false if func_run_hooks func_options_prep ${1+"$@"}; then _G_rc_options_prep=: # save modified positional parameters for caller func_options_prep_result=$func_run_hooks_result fi $_G_rc_options_prep } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd func_parse_options_result= _G_rc_parse_options=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. if func_run_hooks func_parse_options ${1+"$@"}; then eval set dummy "$func_run_hooks_result"; shift _G_rc_parse_options=: fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) if test $# = 0 && func_missing_arg $_G_opt; then _G_rc_parse_options=: break fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) _G_rc_parse_options=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift _G_match_parse_options=false break ;; esac $_G_match_parse_options && _G_rc_parse_options=: done if $_G_rc_parse_options; then # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} func_parse_options_result=$func_quote_for_eval_result fi $_G_rc_parse_options } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd _G_rc_validate_options=false # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" if func_run_hooks func_validate_options ${1+"$@"}; then # save modified positional parameters for caller func_validate_options_result=$func_run_hooks_result _G_rc_validate_options=: fi # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE $_G_rc_validate_options } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables after # splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} test "x$func_split_equals_lhs" = "x$1" \ && func_split_equals_rhs= }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /(C)/!b go :more /\./!{ N s|\n# | | b more } :go /^# Written by /,/# warranty; / { s|^# || s|^# *$|| s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| p } /^# Written by / { s|^# || p } /^warranty; /q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.6' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion Debian-2.4.6-15build2 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func__fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= _G_rc_lt_options_prep=: # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; *) _G_rc_lt_options_prep=false ;; esac if $_G_rc_lt_options_prep; then # Pass back the list of options. func_quote_for_eval ${1+"$@"} libtool_options_prep_result=$func_quote_for_eval_result fi $_G_rc_lt_options_prep } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd _G_rc_lt_parse_options=false # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"} ; shift _G_match_lt_parse_options=false break ;; esac $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done if $_G_rc_lt_parse_options; then # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} libtool_parse_options_result=$func_quote_for_eval_result fi $_G_rc_lt_parse_options } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote_for_eval ${1+"$@"} libtool_validate_options_result=$func_quote_for_eval_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang # -fsanitize=* Clang/GCC memory and address sanitizer # -fuse-ld=* Linker select flags for GCC # -static-* direct GCC to link specific libraries statically # -fcilkplus Cilk Plus language extension features for C/C++ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_for_eval "$arg" arg=$func_quote_for_eval_result fi ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type '$version_type'" ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: recoll-1.36.1/utils/0000755000175000017500000000000014521161751011226 500000000000000recoll-1.36.1/utils/rclionice.h0000644000175000017500000000172014427373216013274 00000000000000/* Copyright (C) 2011 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RCLIONICE_H_INCLUDED_ #define _RCLIONICE_H_INCLUDED_ #include extern bool rclionice(const std::string& clss, const std::string& classdata); #endif /* _RCLIONICE_H_INCLUDED_ */ recoll-1.36.1/utils/base64.cpp0000644000175000017500000002602414410615043012735 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include using std::string; #undef DEBUG_BASE64 #ifdef DEBUG_BASE64 #define DPRINT(X) fprintf X #else #define DPRINT(X) #endif // This is adapted from FreeBSD's code, quite modified for performance. // Tests on a Mac pro 2.1G with a 166MB base64 file // // The original version used strchr to lookup the base64 value from // the input code: // real 0m13.053s user 0m12.574s sys 0m0.471s // Using a direct access, 256 entries table: // real 0m3.073s user 0m2.600s sys 0m0.439s // Using a variable to hold the array length (instead of in.length()): // real 0m2.972s user 0m2.527s sys 0m0.433s // Using values from the table instead of isspace() (final) // real 0m2.513s user 0m2.059s sys 0m0.439s // // The table has one entry per char value (0-256). Invalid base64 // chars take value 256, whitespace 255, Pad ('=') 254. // Valid char points contain their base64 value (0-63) static const int b64values[] = { /* 0 */ 256,/* 1 */ 256,/* 2 */ 256,/* 3 */ 256,/* 4 */ 256, /* 5 */ 256,/* 6 */ 256,/* 7 */ 256,/* 8 */ 256, /*9 ht */ 255,/* 10 nl */ 255,/* 11 vt */ 255,/* 12 np/ff*/ 255,/* 13 cr */ 255, /* 14 */ 256,/* 15 */ 256,/* 16 */ 256,/* 17 */ 256,/* 18 */ 256,/* 19 */ 256, /* 20 */ 256,/* 21 */ 256,/* 22 */ 256,/* 23 */ 256,/* 24 */ 256,/* 25 */ 256, /* 26 */ 256,/* 27 */ 256,/* 28 */ 256,/* 29 */ 256,/* 30 */ 256,/* 31 */ 256, /* 32 sp */ 255, /* ! */ 256,/* " */ 256,/* # */ 256,/* $ */ 256,/* % */ 256, /* & */ 256,/* ' */ 256,/* ( */ 256,/* ) */ 256,/* * */ 256, /* + */ 62, /* , */ 256,/* - */ 256,/* . */ 256, /* / */ 63, /* 0 */ 52,/* 1 */ 53,/* 2 */ 54,/* 3 */ 55,/* 4 */ 56,/* 5 */ 57,/* 6 */ 58, /* 7 */ 59,/* 8 */ 60,/* 9 */ 61, /* : */ 256,/* ; */ 256,/* < */ 256, /* = */ 254, /* > */ 256,/* ? */ 256,/* @ */ 256, /* A */ 0,/* B */ 1,/* C */ 2,/* D */ 3,/* E */ 4,/* F */ 5,/* G */ 6,/* H */ 7, /* I */ 8,/* J */ 9,/* K */ 10,/* L */ 11,/* M */ 12,/* N */ 13,/* O */ 14, /* P */ 15,/* Q */ 16,/* R */ 17,/* S */ 18,/* T */ 19,/* U */ 20,/* V */ 21, /* W */ 22,/* X */ 23,/* Y */ 24,/* Z */ 25, /* [ */ 256,/* \ */ 256,/* ] */ 256,/* ^ */ 256,/* _ */ 256,/* ` */ 256, /* a */ 26,/* b */ 27,/* c */ 28,/* d */ 29,/* e */ 30,/* f */ 31,/* g */ 32, /* h */ 33,/* i */ 34,/* j */ 35,/* k */ 36,/* l */ 37,/* m */ 38,/* n */ 39, /* o */ 40,/* p */ 41,/* q */ 42,/* r */ 43,/* s */ 44,/* t */ 45,/* u */ 46, /* v */ 47,/* w */ 48,/* x */ 49,/* y */ 50,/* z */ 51, /* { */ 256,/* | */ 256,/* } */ 256,/* ~ */ 256, 256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, 256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, 256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, 256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, 256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, 256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, 256,256,256,256,256,256,256,256, }; static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; bool base64_decode(const string& in, string& out) { int io = 0, state = 0, ch = 0; unsigned int ii = 0; out.clear(); size_t ilen = in.length(); out.reserve(ilen); for (ii = 0; ii < ilen; ii++) { ch = (unsigned char)in[ii]; int value = b64values[ch]; if (value == 255) /* Skip whitespace anywhere. */ continue; if (ch == Pad64) break; if (value == 256) { /* A non-base64 character. */ DPRINT((stderr, "base64_dec: non-base64 char at pos %d\n", ii)); return false; } switch (state) { case 0: out += value << 2; state = 1; break; case 1: out[io] |= value >> 4; out += (value & 0x0f) << 4 ; io++; state = 2; break; case 2: out[io] |= value >> 2; out += (value & 0x03) << 6; io++; state = 3; break; case 3: out[io] |= value; io++; state = 0; break; default: fprintf(stderr, "base64_dec: internal!bad state!\n"); return false; } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = in[ii++]; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ DPRINT((stderr, "base64_dec: pad char in state 0/1\n")); return false; case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for (; ii < in.length(); ch = in[ii++]) if (!isspace((unsigned char)ch)) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) { DPRINT((stderr, "base64_dec: missing pad char!\n")); // Well, there are bad encoders out there. Let it pass // return false; } ch = in[ii++]; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for (; ii < in.length(); ch = in[ii++]) if (!isspace((unsigned char)ch)) { DPRINT((stderr, "base64_dec: non-white at eod: 0x%x\n", (unsigned int)((unsigned char)ch))); // Well, there are bad encoders out there. Let it pass //return false; } /* * Now make sure for cases 2 and 3 that the "extra" * bits that slopped past the last full byte were * zeros. If we don't check them, they become a * subliminal channel. */ if (out[io] != 0) { DPRINT((stderr, "base64_dec: bad extra bits!\n")); // Well, there are bad encoders out there. Let it pass out[io] = 0; // return false; } // We've appended an extra 0. out.resize(io); } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) { DPRINT((stderr, "base64_dec: bad final state\n")); return false; } } DPRINT((stderr, "base64_dec: ret ok, io %d sz %d len %d value [%s]\n", io, (int)out.size(), (int)out.length(), out.c_str())); return true; } #undef Assert #define Assert(X) void base64_encode(const string &in, string &out) { unsigned char input[3]; unsigned char output[4]; out.clear(); string::size_type srclength = in.length(); int sidx = 0; while (2 < srclength) { input[0] = in[sidx++]; input[1] = in[sidx++]; input[2] = in[sidx++]; srclength -= 3; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[3] = input[2] & 0x3f; Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); Assert(output[3] < 64); out += Base64[output[0]]; out += Base64[output[1]]; out += Base64[output[2]]; out += Base64[output[3]]; } /* Now we worry about padding. */ if (0 != srclength) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for (string::size_type i = 0; i < srclength; i++) input[i] = in[sidx++]; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); out += Base64[output[0]]; out += Base64[output[1]]; if (srclength == 1) out += Pad64; else out += Base64[output[2]]; out += Pad64; } return; } #ifdef TEST_BASE64 #include #include #include "readfile.h" const char *thisprog; static char usage [] = "testfile\n\n" ; static void Usage(void) { fprintf(stderr, "%s: usage:\n%s", thisprog, usage); exit(1); } static int op_flags; #define OPT_MOINS 0x1 #define OPT_i 0x2 #define OPT_P 0x4 int main(int argc, char **argv) { thisprog = argv[0]; argc--; argv++; while (argc > 0 && **argv == '-') { (*argv)++; if (!(**argv)) /* Cas du "adb - core" */ Usage(); while (**argv) switch (*(*argv)++) { case 'i': op_flags |= OPT_i; break; default: Usage(); break; } argc--; argv++; } if (op_flags & OPT_i) { const char *values[] = {"", "1", "12", "123", "1234", "12345", "123456"}; int nvalues = sizeof(values) / sizeof(char *); string in, out, back; int err = 0; for (int i = 0; i < nvalues; i++) { in = values[i]; base64_encode(in, out); base64_decode(out, back); if (in != back) { fprintf(stderr, "In [%s] %d != back [%s] %d (out [%s] %d\n", in.c_str(), int(in.length()), back.c_str(), int(back.length()), out.c_str(), int(out.length()) ); err++; } } in.erase(); in += char(0); in += char(0); in += char(0); in += char(0); base64_encode(in, out); base64_decode(out, back); if (in != back) { fprintf(stderr, "In [%s] %d != back [%s] %d (out [%s] %d\n", in.c_str(), int(in.length()), back.c_str(), int(back.length()), out.c_str(), int(out.length()) ); err++; } exit(!(err == 0)); } else { if (argc > 1) Usage(); string infile; if (argc == 1) infile = *argv++;argc--; string idata, reason; if (!file_to_string(infile, idata, &reason)) { fprintf(stderr, "Can't read file: %s\n", reason.c_str()); exit(1); } string odata; if (!base64_decode(idata, odata)) { fprintf(stderr, "Decoding failed\n"); exit(1); } fwrite(odata.c_str(), 1, odata.size() * sizeof(string::value_type), stdout); exit(0); } } #endif recoll-1.36.1/utils/cpuconf.h0000644000175000017500000000225114410615043012747 00000000000000/* Copyright (C) 2013 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _CPUCONF_H_INCLUDED_ #define _CPUCONF_H_INCLUDED_ /** Guess how many CPUs there are on this machine, to help with configuring threads */ struct CpuConf { CpuConf() : ncpus(1) {} // Virtual ones, including hyperthreading, we only care about this for now int ncpus; // int ncores; // int nsockets; }; extern bool getCpuConf(CpuConf& conf); #endif /* _CPUCONF_H_INCLUDED_ */ recoll-1.36.1/utils/rclutil.cpp0000644000175000017500000006445314477551452013357 00000000000000/* Copyright (C) 2016-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include "safefcntl.h" #include "safeunistd.h" #include "cstr.h" #ifdef _WIN32 #include "safewindows.h" #include #else #include #include #include #endif #ifdef __APPLE__ #include #endif #include #include #include "safesysstat.h" #include #include #include #include #include #include #include "rclutil.h" #include "pathut.h" #include "wipedir.h" #include "transcode.h" #include "md5ut.h" #include "log.h" #include "smallut.h" #include "rclconfig.h" #include "damlev.h" #include "utf8iter.h" using namespace std; template void map_ss_cp_noshr(T s, T *d) { for (const auto& ent : s) { d->insert( pair(string(ent.first.begin(), ent.first.end()), string(ent.second.begin(), ent.second.end()))); } } template void map_ss_cp_noshr >( map s, map*d); template void map_ss_cp_noshr >( unordered_map s, unordered_map*d); // Add data to metadata field, store multiple values as CSV, avoid // appending multiple identical instances. template void addmeta( T& store, const string& nm, const string& value) { auto it = store.find(nm); if (it == store.end() || it->second.empty()) { store[nm] = value; } else if (it->second.find(value) == string::npos) { store[nm] += ','; store[nm] += value; } } template void addmeta>( map&, const string&, const string&); template void addmeta>( unordered_map&, const string&, const string&); #ifdef _WIN32 static bool path_hasdrive(const string& s) { if (s.size() >= 2 && isalpha(s[0]) && s[1] == ':') { return true; } return false; } static bool path_isdriveabs(const string& s) { if (s.size() >= 3 && isalpha(s[0]) && s[1] == ':' && s[2] == '/') { return true; } return false; } #include #pragma comment(lib, "shlwapi.lib") string path_thisexecpath() { wchar_t text[MAX_PATH]; GetModuleFileNameW(NULL, text, MAX_PATH); #ifdef NTDDI_WIN8_future PathCchRemoveFileSpec(text, MAX_PATH); #else PathRemoveFileSpecW(text); #endif string path; wchartoutf8(text, path); if (path.empty()) { path = "c:/"; } return path; } // On Windows, we use a subdirectory named "rcltmp" inside the windows // temp location to create the temporary files in. static const string& path_wingetrcltmpdir() { // Constant: only need to compute once static string tdir; if (tdir.empty()) { wchar_t dbuf[MAX_PATH + 1]; GetTempPathW(MAX_PATH, dbuf); if (!wchartoutf8(dbuf, tdir)) { LOGERR("path_wingetrcltmpdir: wchartoutf8 failed. Using c:/Temp\n"); tdir = "C:/Temp"; } LOGDEB1("path_wingetrcltmpdir(): gettemppathw ret: " << tdir << "\n"); tdir = path_cat(tdir, "rcltmp"); if (!path_exists(tdir)) { if (!path_makepath(tdir, 0700)) { LOGSYSERR("path_wingettempfilename", "path_makepath", tdir); } } } return tdir; } static bool path_gettempfilename(string& filename, string&) { string tdir = tmplocation(); LOGDEB0("path_gettempfilename: tdir: [" << tdir << "]\n"); wchar_t dbuf[MAX_PATH + 1]; utf8towchar(tdir, dbuf, MAX_PATH); wchar_t buf[MAX_PATH + 1]; static wchar_t prefix[]{L"rcl"}; GetTempFileNameW(dbuf, prefix, 0, buf); wchartoutf8(buf, filename); // Windows will have created a temp file, we delete it. if (!DeleteFileW(buf)) { LOGSYSERR("path_wingettempfilename", "DeleteFileW", filename); } else { LOGDEB1("path_wingettempfilename: DeleteFile " << filename << " Ok\n"); } path_slashize(filename); LOGDEB1("path_gettempfilename: filename: [" << filename << "]\n"); return true; } #else // _WIN32 above static bool path_gettempfilename(string& filename, string& reason) { filename = path_cat(tmplocation(), "rcltmpfXXXXXX"); char *cp = strdup(filename.c_str()); if (!cp) { reason = "Out of memory (for file name !)\n"; return false; } // Using mkstemp this way is awful (bot the suffix adding and // using mkstemp() instead of mktemp just to avoid the warnings) int fd; if ((fd = mkstemp(cp)) < 0) { free(cp); reason = "TempFileInternal: mkstemp failed\n"; return false; } close(fd); path_unlink(cp); filename = cp; free(cp); return true; } #endif // posix // The default place to store the default config and other stuff (e.g webqueue) string path_homedata() { #ifdef _WIN32 wchar_t *cp; SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &cp); string dir; if (cp != 0) { wchartoutf8(cp, dir); } if (!dir.empty()) { dir = path_canon(dir); } else { dir = path_cat(path_home(), "AppData/Local/"); } return dir; #else // We should use an xdg-conforming location, but, history... return path_home(); #endif } // Check if path is either non-existing or an empty directory. bool path_empty(const string& path) { if (path_isdir(path)) { string reason; std::set entries; if (!listdir(path, reason, entries) || entries.empty()) { return true; } return false; } else { return !path_exists(path); } } string path_defaultrecollconfsubdir() { #ifdef _WIN32 return "Recoll"; #else return ".recoll"; #endif } // Location for sample config, filters, etc. E.g. /usr/share/recoll/ on linux // or c:/program files (x86)/recoll/share on Windows const string& path_pkgdatadir() { static string datadir; if (!datadir.empty()) { return datadir; } const char *cdatadir = getenv("RECOLL_DATADIR"); if (nullptr != cdatadir) { datadir = cdatadir; return datadir; } #if defined(_WIN32) // Try a path relative with the exec. This works if we are // recoll/recollindex etc. // But maybe we are the python module, and execpath is the python // exe which could be anywhere. Try the default installation // directory, else tell the user to set the environment // variable. vector paths{path_thisexecpath(), "c:/program files (x86)/recoll", "c:/program files/recoll"}; for (const auto& path : paths) { datadir = path_cat(path, "Share"); if (path_exists(datadir)) { return datadir; } } // Not found std::cerr << "Could not find the recoll installation data. It is usually " "a subfolder of the installation directory. \n" "Please set the RECOLL_DATADIR environment variable to point to it\n" "(e.g. setx RECOLL_DATADIR \"C:/Program Files (X86)/Recoll/Share)\"\n"; #elif defined(__APPLE__) && defined(RECOLL_AS_MAC_BUNDLE) // The package manager builds (Macports, Homebrew, Nixpkgs ...) all arrange to set a proper // compiled value for RECOLL_DATADIR. We can't do this when building a native bundle with // QCreator, in which case we use the executable location. uint32_t size = 0; _NSGetExecutablePath(nullptr, &size); char *path= (char*)malloc(size+1); _NSGetExecutablePath(path, &size); datadir = path_cat(path_getfather(path_getfather(path)), "Resources"); free(path); #else // If not in environment, use the compiled-in constant. datadir = RECOLL_DATADIR; #endif return datadir; } /* There is a lot of vagueness about what should be percent-encoded or * not in a file:// url. The constraint that we have is that we may use * the encoded URL to compute (MD5) a thumbnail path according to the * freedesktop.org thumbnail spec, which itself does not define what * should be escaped. We choose to exactly escape what gio does, as * implemented in glib/gconvert.c:g_escape_uri_string(uri, UNSAFE_PATH). * Hopefully, the other desktops have the same set of escaped chars. * Note that $ is not encoded, so the value is not shell-safe. */ string url_encode(const string& url, string::size_type offs) { string out = url.substr(0, offs); const char *cp = url.c_str(); for (string::size_type i = offs; i < url.size(); i++) { unsigned int c; const char *h = "0123456789ABCDEF"; c = cp[i]; if (c <= 0x20 || c >= 0x7f || c == '"' || c == '#' || c == '%' || c == ';' || c == '<' || c == '>' || c == '?' || c == '[' || c == '\\' || c == ']' || c == '^' || c == '`' || c == '{' || c == '|' || c == '}') { out += '%'; out += h[(c >> 4) & 0xf]; out += h[c & 0xf]; } else { out += char(c); } } return out; } string url_gpath(const string& url) { // Remove the access schema part (or whatever it's called) string::size_type colon = url.find_first_of(":"); if (colon == string::npos || colon == url.size() - 1) { return url; } // If there are non-alphanum chars before the ':', then there // probably is no scheme. Whatever... for (string::size_type i = 0; i < colon; i++) { if (!isalnum(url.at(i))) { return url; } } // In addition we canonize the path to remove empty host parts (for compatibility with older // versions of recoll where file:// was hardcoded, but the local path was used for doc // identification. return path_canon(url.substr(colon + 1)); } string url_parentfolder(const string& url) { // In general, the parent is the directory above the full path string parenturl = path_getfather(url_gpath(url)); // But if this is http, make sure to keep the host part. Recoll // only has file or http urls for now. bool isfileurl = urlisfileurl(url); if (!isfileurl && parenturl == "/") { parenturl = url_gpath(url); } return isfileurl ? cstr_fileu + parenturl : string("http://") + parenturl; } // Convert to file path if url is like file: // Note: this only works with our internal pseudo-urls which are not // encoded/escaped string fileurltolocalpath(string url) { if (url.find(cstr_fileu) == 0) { url = url.substr(7, string::npos); } else { return string(); } // If this looks like a Windows path: absolute file urls are like: file:///c:/mydir/... // Get rid of the initial '/' if (url.size() >= 3 && url[0] == '/' && isalpha(url[1]) && url[2] == ':') { url = url.substr(1); } // Removing the fragment part. This is exclusively used when // executing a viewer for the recoll manual, and we only strip the // part after # if it is preceded by .html string::size_type pos; if ((pos = url.rfind(".html#")) != string::npos) { url.erase(pos + 5); } else if ((pos = url.rfind(".htm#")) != string::npos) { url.erase(pos + 4); } return url; } string path_pathtofileurl(const string& path) { // We're supposed to receive a canonic absolute path, but on windows we // may need to add a '/' in front of the drive spec string url(cstr_fileu); if (path.empty() || path[0] != '/') { url.push_back('/'); } url += path; return url; } bool urlisfileurl(const string& url) { return url.find(cstr_fileu) == 0; } // Printable url: this is used to transcode from the system charset // into either utf-8 if transcoding succeeds, or url-encoded bool printableUrl(const string& fcharset, const string& in, string& out) { #ifdef _WIN32 PRETEND_USE(fcharset); // On windows our paths are always utf-8 out = in; #else int ecnt = 0; if (!transcode(in, out, fcharset, "UTF-8", &ecnt) || ecnt) { out = url_encode(in, 7); } #endif return true; } #ifdef _WIN32 // Convert X:/path to /X/path for path splitting inside the index string path_slashdrive(const string& path) { string npath; if (path_hasdrive(path)) { npath.append(1, '/'); npath.append(1, path[0]); if (path_isdriveabs(path)) { npath.append(path.substr(2)); } else { // This should be an error really npath.append(1, '/'); npath.append(path.substr(2)); } } else { npath = path; ///?? } return npath; } #endif // _WIN32 string url_gpathS(const string& url) { #ifdef _WIN32 return path_slashdrive(url_gpath(url)); #else return url_gpath(url); #endif } std::string utf8datestring(const std::string& format, struct tm *tm) { string u8date; #ifdef _WIN32 wchar_t wformat[200]; utf8towchar(format, wformat, 199); wchar_t wdate[250]; wcsftime(wdate, 250, wformat, tm); wchartoutf8(wdate, u8date); #else char datebuf[200]; strftime(datebuf, 199, format.c_str(), tm); transcode(datebuf, u8date, RclConfig::getLocaleCharset(), "UTF-8"); #endif return u8date; } const string& tmplocation() { static string stmpdir; if (stmpdir.empty()) { const char *tmpdir = getenv("RECOLL_TMPDIR"); #ifndef _WIN32 /* Don't use these under windows because they will return * non-ascii non-unicode stuff (would have to call _wgetenv() * instead. path_wingetrcltmpdir() will manage */ if (tmpdir == nullptr) { tmpdir = getenv("TMPDIR"); } if (tmpdir == nullptr) { tmpdir = getenv("TMP"); } if (tmpdir == nullptr) { tmpdir = getenv("TEMP"); } #endif if (tmpdir == nullptr) { #ifdef _WIN32 stmpdir = path_wingetrcltmpdir(); #else stmpdir = "/tmp"; #endif } else { stmpdir = tmpdir; } stmpdir = path_canon(stmpdir); } return stmpdir; } bool maketmpdir(string& tdir, string& reason) { #ifndef _WIN32 tdir = path_cat(tmplocation(), "rcltmpXXXXXX"); char *cp = strdup(tdir.c_str()); if (!cp) { reason = "maketmpdir: out of memory (for file name !)\n"; tdir.erase(); return false; } // There is a race condition between name computation and // mkdir. try to make sure that we at least don't shoot ourselves // in the foot #if !defined(HAVE_MKDTEMP) static std::mutex mmutex; std::unique_lock lock(mmutex); #endif if (! #ifdef HAVE_MKDTEMP mkdtemp(cp) #else mktemp(cp) #endif // HAVE_MKDTEMP ) { free(cp); reason = "maketmpdir: mktemp failed for [" + tdir + "] : " + strerror(errno); tdir.erase(); return false; } tdir = cp; free(cp); #else // _WIN32 // There is a race condition between name computation and // mkdir. try to make sure that we at least don't shoot ourselves // in the foot static std::mutex mmutex; std::unique_lock lock(mmutex); if (!path_gettempfilename(tdir, reason)) { return false; } #endif // At this point the directory does not exist yet except if we used // mkdtemp #if !defined(HAVE_MKDTEMP) || defined(_WIN32) if (mkdir(tdir.c_str(), 0700) < 0) { reason = string("maketmpdir: mkdir ") + tdir + " failed"; tdir.erase(); return false; } #endif return true; } class TempFile::Internal { public: Internal(const std::string& suffix); ~Internal(); friend class TempFile; private: std::string m_filename; std::string m_reason; bool m_noremove{false}; }; TempFile::TempFile(const string& suffix) : m(new Internal(suffix)) { } TempFile::TempFile() { m = std::shared_ptr(); } const char *TempFile::filename() const { return m ? m->m_filename.c_str() : ""; } const std::string& TempFile::getreason() const { static string fatal{"fatal error"}; return m ? m->m_reason : fatal; } void TempFile::setnoremove(bool onoff) { if (m) m->m_noremove = onoff; } bool TempFile::ok() const { return m ? !m->m_filename.empty() : false; } TempFile::Internal::Internal(const string& suffix) { // Because we need a specific suffix, can't use mkstemp // well. There is a race condition between name computation and // file creation. try to make sure that we at least don't shoot // our own selves in the foot. maybe we'll use mkstemps one day. static std::mutex mmutex; std::unique_lock lock(mmutex); if (!path_gettempfilename(m_filename, m_reason)) { return; } m_filename += suffix; std::fstream fout; if (!path_streamopen(m_filename, ios::out|ios::trunc, fout)) { m_reason = string("Open/create error. errno : ") + lltodecstr(errno) + " file name: " + m_filename; LOGSYSERR("Tempfile::Internal::Internal", "open/create", m_filename); m_filename.erase(); } } const std::string& TempFile::rcltmpdir() { return tmplocation(); } #ifdef _WIN32 static list remainingTempFileNames; static std::mutex remTmpFNMutex; #endif TempFile::Internal::~Internal() { if (!m_filename.empty() && !m_noremove) { LOGDEB1("TempFile:~: unlinking " << m_filename << endl); if (!path_unlink(m_filename)) { LOGSYSERR("TempFile:~", "unlink", m_filename); #ifdef _WIN32 { std::unique_lock lock(remTmpFNMutex); remainingTempFileNames.push_back(m_filename); } #endif } else { LOGDEB1("TempFile:~: unlink " << m_filename << " Ok\n"); } } } // On Windows we sometimes fail to remove temporary files because // they are open. It's difficult to make sure this does not // happen, so we add a cleaning pass after clearing the input // handlers cache (which should kill subprocesses etc.) void TempFile::tryRemoveAgain() { #ifdef _WIN32 LOGDEB1("TempFile::tryRemoveAgain. List size: " << remainingTempFileNames.size() << endl); std::unique_lock lock(remTmpFNMutex); std::list::iterator pos = remainingTempFileNames.begin(); while (pos != remainingTempFileNames.end()) { if (!path_unlink(*pos)) { LOGSYSERR("TempFile::tryRemoveAgain", "unlink", *pos); pos++; } else { pos = remainingTempFileNames.erase(pos); } } #endif } TempDir::TempDir() { if (!maketmpdir(m_dirname, m_reason)) { m_dirname.erase(); return; } LOGDEB("TempDir::TempDir: -> " << m_dirname << endl); } TempDir::~TempDir() { if (!m_dirname.empty()) { LOGDEB("TempDir::~TempDir: erasing " << m_dirname << endl); (void)wipedir(m_dirname, true, true); m_dirname.erase(); } } bool TempDir::wipe() { if (m_dirname.empty()) { m_reason = "TempDir::wipe: no directory !\n"; return false; } if (wipedir(m_dirname, false, true)) { m_reason = "TempDir::wipe: wipedir failed\n"; return false; } return true; } // Freedesktop standard paths for cache directory (thumbnails are now in there) static const string& xdgcachedir() { static string xdgcache; if (xdgcache.empty()) { const char *cp = getenv("XDG_CACHE_HOME"); if (nullptr == cp) { xdgcache = path_cat(path_home(), ".cache"); } else { xdgcache = string(cp); } } return xdgcache; } static const string& thumbnailsdir() { static string thumbnailsd; if (thumbnailsd.empty()) { thumbnailsd = path_cat(xdgcachedir(), "thumbnails"); if (access(thumbnailsd.c_str(), 0) != 0) { thumbnailsd = path_cat(path_home(), ".thumbnails"); } } return thumbnailsd; } // Place for 1024x1024 files static const string thmbdirxxlarge = "xx-large"; // Place for 512x512 files static const string thmbdirxlarge = "x-large"; // Place for 256x256 files static const string thmbdirlarge = "large"; // 128x128 static const string thmbdirnormal = "normal"; static const vector thmbdirs{thmbdirxxlarge, thmbdirxlarge, thmbdirlarge, thmbdirnormal}; static void thumbname(const string& url, string& name) { string digest; string l_url = url_encode(url); MD5String(l_url, digest); MD5HexPrint(digest, name); name += ".png"; } bool thumbPathForUrl(const string& url, int size, string& path) { string name, path128, path256, path512, path1024; thumbname(url, name); if (size <= 128) { path = path_cat(thumbnailsdir(), thmbdirnormal); path = path_cat(path, name); path128 = path; } else if (size <= 256) { path = path_cat(thumbnailsdir(), thmbdirlarge); path = path_cat(path, name); path256 = path; } else if (size <= 512) { path = path_cat(thumbnailsdir(), thmbdirxlarge); path = path_cat(path, name); path512 = path; } else { path = path_cat(thumbnailsdir(), thmbdirxxlarge); path = path_cat(path, name); path1024 = path; } if (access(path.c_str(), R_OK) == 0) { return true; } // Not found in requested size. Try to find any size and return it. Let the client scale. for (const auto& tdir : thmbdirs) { path = path_cat(thumbnailsdir(), tdir); path = path_cat(path, name); if (access(path.c_str(), R_OK) == 0) { return true; } } // File does not exist. Return appropriate path anyway. if (size <= 128) { path = path128; } else if (size <= 256) { path = path256; } else if (size <= 512) { path = path512; } else { path = path1024; } return false; } // Compare charset names, removing the more common spelling variations bool samecharset(const string& cs1, const string& cs2) { auto mcs1 = std::accumulate(cs1.begin(), cs1.end(), "", [](const char* m, char i) { return (i != '_' && i != '-') ? m + ::tolower(i) : m; }); auto mcs2 = std::accumulate(cs2.begin(), cs2.end(), "", [](const char* m, char i) { return (i != '_' && i != '-') ? m + ::tolower(i) : m; }); return mcs1 == mcs2; } static const std::unordered_map lang_to_code { {"be", "cp1251"}, {"bg", "cp1251"}, {"cs", "iso-8859-2"}, {"el", "iso-8859-7"}, {"he", "iso-8859-8"}, {"hr", "iso-8859-2"}, {"hu", "iso-8859-2"}, {"ja", "eucjp"}, {"kk", "pt154"}, {"ko", "euckr"}, {"lt", "iso-8859-13"}, {"lv", "iso-8859-13"}, {"pl", "iso-8859-2"}, {"rs", "iso-8859-2"}, {"ro", "iso-8859-2"}, {"ru", "koi8-r"}, {"sk", "iso-8859-2"}, {"sl", "iso-8859-2"}, {"sr", "iso-8859-2"}, {"th", "iso-8859-11"}, {"tr", "iso-8859-9"}, {"uk", "koi8-u"}, }; string langtocode(const string& lang) { const auto it = lang_to_code.find(lang); // Use cp1252 by default... if (it == lang_to_code.end()) { return cstr_cp1252; } return it->second; } string localelang() { const char *lang = getenv("LANG"); if (lang == nullptr || *lang == 0 || !strcmp(lang, "C") || !strcmp(lang, "POSIX")) { return "en"; } string locale(lang); string::size_type under = locale.find_first_of('_'); if (under == string::npos) { return locale; } return locale.substr(0, under); } class IntString { public: IntString(const std::string& utf8) { m_len = utf8len(utf8); m_vec = (int*)malloc(m_len * sizeof(int)); Utf8Iter it(utf8); int i = 0; for (; !it.eof(); it++) { if (it.error()) { LOGERR("IntString: Illegal seq at byte position " << it.getBpos() <<"\n"); goto error; } unsigned int value = *it; if (value == (unsigned int)-1) { LOGERR("IntString: Conversion error\n"); goto error; } if (i >= m_len) { LOGFAT("IntString:: OVERFLOW!?!\n"); abort(); } m_vec[i++] = value; } return; error: if (m_vec) { free(m_vec); m_vec = nullptr; } m_len = 0; } ~IntString() { if (m_vec) free(m_vec); } int size() const { return m_len; } const int& operator[](int i) const { return m_vec[i]; } private: int *m_vec{nullptr}; int m_len{0}; }; int u8DLDistance(const std::string& str1, const std::string str2) { IntString istr1(str1); IntString istr2(str2); if ((str1.size() && istr1.size() == 0) || (str2.size() && istr2.size() == 0)) { return -1; } return DLDistance(istr1, istr2); } // Extract MIME type from a string looking like: ": text/plain; charset=us-ascii". string growmimearoundslash(string mime) { string::size_type start; string::size_type nd; // File -i will sometimes return strange stuff (ie: "very small file") if((start = nd = mime.find("/")) == string::npos) { return string(); } while (start > 0) { start--; if (!isalpha(mime[start])) { start++; break; } } const static string allowedpunct("+-."); while (nd < mime.size() - 1) { nd++; if (!isalnum(mime[nd]) && allowedpunct.find(mime[nd]) == string::npos) { nd--; break; } } mime = mime.substr(start, nd-start+1); return mime; } void rclutil_init_mt() { path_pkgdatadir(); tmplocation(); thumbnailsdir(); // Init langtocode() static table langtocode(""); } recoll-1.36.1/utils/log.cpp0000644000175000017500000000405414473577712012454 00000000000000/* Copyright (C) 2006-2016 J.F.Dockes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "log.h" #include #include #include #ifdef _MSC_VER #define localtime_r(A,B) localtime_s(B,A) #endif Logger::Logger(const std::string& fn) : m_fn(fn) { reopen(fn); } bool Logger::reopen(const std::string& fn) { #if LOGGER_THREADSAFE std::unique_lock lock(m_mutex); #endif if (!fn.empty()) { m_fn = fn; } if (!m_tocerr && m_stream.is_open()) { m_stream.close(); } if (!m_fn.empty() && m_fn != "stderr") { m_stream.open(m_fn, std::fstream::out | std::ofstream::trunc); if (!m_stream.is_open()) { std::cerr << "Logger::Logger: log open failed: for [" << fn << "] errno " << errno << "\n"; m_tocerr = true; } else { m_tocerr = false; } } else { m_tocerr = true; } return true; } const char *Logger::datestring() { time_t clk = time(nullptr); struct tm tmb; localtime_r(&clk, &tmb); if (strftime(m_datebuf, LOGGER_DATESIZE, m_datefmt.c_str(), &tmb)) { return m_datebuf; } else { return ""; } } static Logger *theLog; Logger *Logger::getTheLog(const std::string& fn) { if (nullptr == theLog) theLog = new Logger(fn); return theLog; } recoll-1.36.1/utils/pathut.cpp0000644000175000017500000011162314477605225013174 00000000000000/* Copyright (C) 2004-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * flock emulation: * Emulate flock on platforms that lack it, primarily Windows and MinGW. * * This is derived from sqlite3 sources. * https://www.sqlite.org/src/finfo?name=src/os_win.c * https://www.sqlite.org/copyright.html * * Written by Richard W.M. Jones * * Copyright (C) 2008-2019 Free Software Foundation, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #endif #include "pathut.h" #include "smallut.h" #ifdef MDU_INCLUDE_LOG #include MDU_INCLUDE_LOG #else #include "log.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include // Listing directories: we include the normal dirent.h on Unix-derived // systems, and on MinGW, where it comes with a supplemental wide char // interface. When building with MSVC, we use our bundled msvc_dirent.h, // which is equivalent to the one in MinGW #ifdef _MSC_VER #include "msvc_dirent.h" #else // !_MSC_VER #include #endif // _MSC_VER #ifdef _WIN32 #ifndef _MSC_VER #undef WINVER #define WINVER 0x0601 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0601 #define LOGFONTW void #endif #ifndef NOMINMAX #define NOMINMAX #endif #define WIN32_LEAN_AND_MEAN #define NOGDI #include #include #include #include #include #include #include #if !defined(S_IFLNK) #define S_IFLNK 0 #endif #ifndef S_ISDIR # define S_ISDIR(ST_MODE) (((ST_MODE) & _S_IFMT) == _S_IFDIR) #endif #ifndef S_ISREG # define S_ISREG(ST_MODE) (((ST_MODE) & _S_IFMT) == _S_IFREG) #endif #define MAXPATHLEN PATH_MAX #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif #ifndef R_OK #define R_OK 4 #endif #define STAT _wstati64 #define LSTAT _wstati64 #define STATBUF _stati64 #define ACCESS _waccess #define OPENDIR ::_wopendir #define DIRHDL _WDIR #define CLOSEDIR _wclosedir #define READDIR ::_wreaddir #define REWINDDIR ::_wrewinddir #define DIRENT _wdirent #define DIRHDL _WDIR #define MKDIR(a,b) _wmkdir(a) #define OPEN ::_wopen #define UNLINK _wunlink #define RMDIR _wrmdir #define CHDIR _wchdir #define SYSPATH(PATH, SPATH) wchar_t PATH ## _buf[2048]; \ utf8towchar(PATH, PATH ## _buf, 2048); \ wchar_t *SPATH = PATH ## _buf; #define ftruncate _chsize_s #ifdef _MSC_VER // For getpid #include #define getpid _getpid #define PATHUT_SSIZE_T int #endif // _MSC_VER #else /* !_WIN32 -> */ #include #include #include #include #include #include #include #include #define STAT stat #define LSTAT lstat #define STATBUF stat #define ACCESS access #define OPENDIR ::opendir #define DIRHDL DIR #define CLOSEDIR closedir #define READDIR ::readdir #define REWINDDIR ::rewinddir #define DIRENT dirent #define DIRHDL DIR #define MKDIR(a,b) mkdir(a,b) #define OPEN ::open #define UNLINK ::unlink #define RMDIR ::rmdir #define CHDIR ::chdir #define SYSPATH(PATH, SPATH) const char *SPATH = PATH.c_str() #endif /* !_WIN32 */ #ifndef PATHUT_SSIZE_T #define PATHUT_SSIZE_T ssize_t #endif namespace MedocUtils { #ifdef _WIN32 std::string wchartoutf8(const wchar_t *in, size_t len) { std::string out; wchartoutf8(in, out, len); return out; } bool wchartoutf8(const wchar_t *in, std::string& out, size_t wlen) { LOGDEB1("WCHARTOUTF8: in [" << in << "]\n"); out.clear(); if (nullptr == in) { return true; } if (wlen == 0) { wlen = wcslen(in); } int flags = WC_ERR_INVALID_CHARS; int bytes = ::WideCharToMultiByte(CP_UTF8, flags, in, wlen, nullptr, 0, nullptr, nullptr); if (bytes <= 0) { LOGERR("wchartoutf8: conversion error1\n"); fwprintf(stderr, L"wchartoutf8: conversion error1 for [%s]\n", in); return false; } DirtySmartBuf buffer(bytes+1); bytes = ::WideCharToMultiByte(CP_UTF8, flags, in, wlen, buffer.buf(), bytes, nullptr, nullptr); if (bytes <= 0) { LOGERR("wchartoutf8: CONVERSION ERROR2\n"); return false; } buffer.buf()[bytes] = 0; out = buffer.buf(); //fwprintf(stderr, L"wchartoutf8: in: [%s]\n", in); //fprintf(stderr, "wchartoutf8: out: [%s]\n", out.c_str()); return true; } bool utf8towchar(const std::string& in, wchar_t *out, size_t obytescap) { size_t wcharsavail = obytescap / sizeof(wchar_t); if (nullptr == out || wcharsavail < 1) { return false; } out[0] = 0; int wcharcnt = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, in.c_str(), in.size(), nullptr, 0); if (wcharcnt <= 0) { LOGERR("utf8towchar: conversion error for [" << in << "]\n"); return false; } if (wcharcnt + 1 > int(wcharsavail)) { LOGERR("utf8towchar: not enough space\n"); return false; } wcharcnt = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, in.c_str(), in.size(), out, wcharsavail); if (wcharcnt <= 0) { LOGERR("utf8towchar: conversion error for [" << in << "]\n"); return false; } out[wcharcnt] = 0; return true; } std::unique_ptr utf8towchar(const std::string& in) { // Note that as we supply in.size(), mbtowch computes the size // without a terminating 0 (and won't write in the second call of // course). We take this into account by allocating one more and // terminating the output. int wcharcnt = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, in.c_str(), in.size(), nullptr, 0); if (wcharcnt <= 0) { LOGERR("utf8towchar: conversion error for [" << in << "]\n"); return std::unique_ptr(); } auto buf = std::unique_ptr(new wchar_t[wcharcnt+1]); wcharcnt = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, in.c_str(), in.size(), buf.get(), wcharcnt); if (wcharcnt <= 0) { LOGERR("utf8towchar: conversion error for [" << in << "]\n"); return std::unique_ptr(); } buf.get()[wcharcnt] = 0; return buf; } /// Convert \ separators to / void path_slashize(std::string& s) { for (std::string::size_type i = 0; i < s.size(); i++) { if (s[i] == '\\') { s[i] = '/'; } } } void path_backslashize(std::string& s) { for (std::string::size_type i = 0; i < s.size(); i++) { if (s[i] == '/') { s[i] = '\\'; } } } static bool path_strlookslikedrive(const std::string& s) { return s.size() == 2 && isalpha(s[0]) && s[1] == ':'; } static bool path_hasdrive(const std::string& s) { if (s.size() >= 2 && isalpha(s[0]) && s[1] == ':') { return true; } return false; } static bool path_isdriveabs(const std::string& s) { if (s.size() >= 3 && isalpha(s[0]) && s[1] == ':' && s[2] == '/') { return true; } return false; } /* Operations for the 'flock' call (same as Linux kernel constants). */ # define LOCK_SH 1 /* Shared lock. */ # define LOCK_EX 2 /* Exclusive lock. */ # define LOCK_UN 8 /* Unlock. */ /* Can be OR'd in to one of the above. */ # define LOCK_NB 4 /* Don't block when locking. */ /* Determine the current size of a file. Because the other braindead * APIs we'll call need lower/upper 32 bit pairs, keep the file size * like that too. */ static BOOL file_size (HANDLE h, DWORD * lower, DWORD * upper) { *lower = GetFileSize (h, upper); /* It appears that we can't lock an empty file, a lock is always over a data section. But we seem to be able to set a lock beyond the current file size, which is enough to get Pidfile working */ if (*lower == 0 && *upper == 0) { *lower = 100; } return 1; } /* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */ # ifndef LOCKFILE_FAIL_IMMEDIATELY # define LOCKFILE_FAIL_IMMEDIATELY 1 # endif /* Acquire a lock. */ static BOOL do_lock (HANDLE h, int non_blocking, int exclusive) { BOOL res; DWORD size_lower, size_upper; OVERLAPPED ovlp; int flags = 0; /* We're going to lock the whole file, so get the file size. */ res = file_size (h, &size_lower, &size_upper); if (!res) return 0; /* Start offset is 0, and also zero the remaining members of this struct. */ memset (&ovlp, 0, sizeof ovlp); if (non_blocking) flags |= LOCKFILE_FAIL_IMMEDIATELY; if (exclusive) flags |= LOCKFILE_EXCLUSIVE_LOCK; return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp); } /* Unlock reader or exclusive lock. */ static BOOL do_unlock (HANDLE h) { int res; DWORD size_lower, size_upper; res = file_size (h, &size_lower, &size_upper); if (!res) return 0; return UnlockFile (h, 0, 0, size_lower, size_upper); } /* Now our BSD-like flock operation. */ int flock (int fd, int operation) { HANDLE h = (HANDLE) _get_osfhandle (fd); DWORD res; int non_blocking; if (h == INVALID_HANDLE_VALUE) { errno = EBADF; return -1; } non_blocking = operation & LOCK_NB; operation &= ~LOCK_NB; switch (operation) { case LOCK_SH: res = do_lock (h, non_blocking, 0); break; case LOCK_EX: res = do_lock (h, non_blocking, 1); break; case LOCK_UN: res = do_unlock (h); break; default: errno = EINVAL; return -1; } /* Map Windows errors into Unix errnos. As usual MSDN fails to * document the permissible error codes. */ if (!res) { DWORD err = GetLastError (); switch (err){ /* This means someone else is holding a lock. */ case ERROR_LOCK_VIOLATION: errno = EAGAIN; break; /* Out of memory. */ case ERROR_NOT_ENOUGH_MEMORY: errno = ENOMEM; break; case ERROR_BAD_COMMAND: errno = EINVAL; break; /* Unlikely to be other errors, but at least don't lose the * error code. */ default: errno = err; } return -1; } return 0; } std::string path_shortpath(const std::string& path) { SYSPATH(path, syspath); wchar_t wspath[MAX_PATH]; int ret = GetShortPathNameW(syspath, wspath, MAX_PATH); if (ret == 0) { LOGERR("GetShortPathNameW failed for [" << path << "]\n"); return path; } else if (ret >= MAX_PATH) { LOGERR("GetShortPathNameW [" << path << "] too long " << path.size() << " MAX_PATH " << MAX_PATH << "\n"); return path; } std::string shortpath; wchartoutf8(wspath, shortpath); return shortpath; } #endif /* _WIN32 */ // This is only actually used on Windows currently, but compiled everywhere so that it can be // tested, as there are no reall windows dependencies in there. // The input is a slashized UNC path (like //host/share/path), or not, which we determine, returning // true or false depending. // On return, uncvolume contains the //host/share part. We take care to reject values with empty // host or share parts which maybe somehow generated by other strange parts in recoll. bool path_isunc(const std::string& s, std::string& uncvolume) { if (s.size() < 5 || s[0] != '/' || s[1] != '/') { return false; } auto slash2 = s.find('/', 2); if (slash2 == std::string::npos || slash2 == s.size() - 1 || slash2 == 2) { return false; } auto slash3 = s.find('/', slash2 + 1); if (slash3 == slash2 + 1) { return false; } if (slash3 == std::string::npos ) { uncvolume = s; } else { uncvolume = s.substr(0, slash3); } return true; } bool fsocc(const std::string& path, int *pc, long long *avmbs) { static const int FSOCC_MB = 1024 * 1024; #ifdef _WIN32 ULARGE_INTEGER freebytesavail; ULARGE_INTEGER totalbytes; SYSPATH(path, syspath); if (!GetDiskFreeSpaceExW(syspath, &freebytesavail, &totalbytes, NULL)) { return false; } if (pc) { *pc = int((100 * freebytesavail.QuadPart) / totalbytes.QuadPart); } if (avmbs) { *avmbs = int(totalbytes.QuadPart / FSOCC_MB); } return true; #else /* !_WIN32 */ struct statvfs buf; if (statvfs(path.c_str(), &buf) != 0) { return false; } if (pc) { double fsocc_used = double(buf.f_blocks - buf.f_bfree); double fsocc_totavail = fsocc_used + double(buf.f_bavail); double fpc = 100.0; if (fsocc_totavail > 0) { fpc = 100.0 * fsocc_used / fsocc_totavail; } *pc = int(fpc); } if (avmbs) { *avmbs = 0; if (buf.f_bsize > 0) { int ratio = buf.f_frsize > FSOCC_MB ? buf.f_frsize / FSOCC_MB : FSOCC_MB / buf.f_frsize; *avmbs = buf.f_frsize > FSOCC_MB ? ((long long)buf.f_bavail) * ratio : ((long long)buf.f_bavail) / ratio; } } return true; #endif /* !_WIN32 */ } const std::string& path_PATHsep() { static const std::string w(";"); static const std::string u(":"); #ifdef _WIN32 return w; #else return u; #endif } void path_catslash(std::string& s) { #ifdef _WIN32 path_slashize(s); #endif if (s.empty() || s[s.length() - 1] != '/') { s += '/'; } } std::string path_cat(const std::string& s1, const std::string& s2) { std::string res = s1.empty() ? "./" : s1; if (!s2.empty()) { path_catslash(res); res += s2; } return res; } std::string path_cat(const std::string& s1, std::initializer_list pathelts) { std::string res = s1.empty() ? "./" : s1; for (const auto& p : pathelts) { if (!p.empty()) { res = path_cat(res, p); } } return res; } std::string path_getfather(const std::string& s) { std::string father = s; #ifdef _WIN32 path_slashize(father); #endif // ?? if (father.empty()) { return "./"; } if (path_isroot(father)) { return father; } if (father[father.length() - 1] == '/') { // Input ends with /. Strip it, root special case was tested above father.erase(father.length() - 1); } std::string::size_type slp = father.rfind('/'); if (slp == std::string::npos) { return "./"; } father.erase(slp); path_catslash(father); return father; } std::string path_getsimple(const std::string& s) { std::string simple = s; #ifdef _WIN32 path_slashize(simple); #endif if (simple.empty()) { return simple; } std::string::size_type slp = simple.rfind('/'); if (slp == std::string::npos) { return simple; } simple.erase(0, slp + 1); return simple; } // Unlike path_getsimple(), we ignore right-side '/' chars, like the basename command does. #ifdef _WIN32 std::string path_basename(const std::string& _s, const std::string& suff) { std::string s{_s}; path_slashize(s); #else std::string path_basename(const std::string& s, const std::string& suff) { #endif if (path_isroot(s)) return s; std::string simple(s); rtrimstring(simple, "/"); simple = path_getsimple(simple); std::string::size_type pos = std::string::npos; if (suff.length() && simple.length() > suff.length()) { pos = simple.rfind(suff); if (pos != std::string::npos && pos + suff.length() == simple.length()) { return simple.substr(0, pos); } } return simple; } std::string path_suffix(const std::string& s) { std::string::size_type dotp = s.rfind('.'); if (dotp == std::string::npos) { return std::string(); } return s.substr(dotp + 1); } std::string path_home() { #ifdef _WIN32 std::string dir; // Using wgetenv does not work well, depending on the // environment I get wrong values for the accented chars (works // with recollindex started from msys command window, does not // work when started from recoll. SHGet... fixes this //const wchar_t *cp = _wgetenv(L"USERPROFILE"); wchar_t *cp; SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &cp); if (cp != 0) { wchartoutf8(cp, dir); } if (dir.empty()) { cp = _wgetenv(L"HOMEDRIVE"); wchartoutf8(cp, dir); if (cp != 0) { std::string dir1; const wchar_t *cp1 = _wgetenv(L"HOMEPATH"); wchartoutf8(cp1, dir1); if (cp1 != 0) { dir = path_cat(dir, dir1); } } } if (dir.empty()) { dir = "C:/"; } dir = path_canon(dir); path_catslash(dir); return dir; #else const char *cp = getenv("HOME"); if (nullptr == cp) { uid_t uid = getuid(); struct passwd *entry = getpwuid(uid); if (nullptr == entry) { return "/"; } cp = entry->pw_dir; } std::string homedir{cp}; path_catslash(homedir); return homedir; #endif } std::string path_cachedir() { #ifdef _WIN32 std::string dir; wchar_t *cp; SHGetKnownFolderPath(FOLDERID_InternetCache, 0, nullptr, &cp); if (cp != 0) { wchartoutf8(cp, dir); } if (dir.empty()) { cp = _wgetenv(L"HOMEDRIVE"); wchartoutf8(cp, dir); if (cp != 0) { std::string dir1; const wchar_t *cp1 = _wgetenv(L"HOMEPATH"); wchartoutf8(cp1, dir1); if (cp1 != 0) { dir = path_cat(dir, dir1); } } } if (dir.empty()) { dir = "C:/"; } dir = path_canon(dir); path_catslash(dir); return dir; #else static std::string xdgcache; if (xdgcache.empty()) { const char *cp = getenv("XDG_CACHE_HOME"); if (nullptr == cp) { xdgcache = path_cat(path_home(), ".cache"); } else { xdgcache = std::string(cp); } path_catslash(xdgcache); } return xdgcache; #endif } std::string path_tildexpand(const std::string& s) { if (s.empty() || s[0] != '~') { return s; } std::string o = s; #ifdef _WIN32 path_slashize(o); #endif if (s.length() == 1) { o.replace(0, 1, path_home()); } else if (s[1] == '/') { o.replace(0, 2, path_home()); } else { std::string::size_type pos = s.find('/'); std::string::size_type l = (pos == std::string::npos) ? s.length() - 1 : pos - 1; #ifdef _WIN32 // Dont know what this means. Just replace with HOME o.replace(0, l + 1, path_home()); #else struct passwd *entry = getpwnam(s.substr(1, l).c_str()); if (entry) { o.replace(0, l + 1, entry->pw_dir); } #endif } return o; } bool path_isroot(const std::string& path) { if (path.size() == 1 && path[0] == '/') { return true; } #ifdef _WIN32 if (path.size() == 3 && isalpha(path[0]) && path[1] == ':' && (path[2] == '/' || path[2] == '\\')) { return true; } #endif return false; } bool path_isdesc(const std::string& _top, const std::string& _sub) { if (_top.empty() || _sub.empty()) return false; std::string top = path_canon(_top); std::string sub = path_canon(_sub); path_catslash(top); path_catslash(sub); for (;;) { if (sub == top) { return true; } std::string::size_type l = sub.size(); sub = path_getfather(sub); if (sub.size() == l || sub.size() < top.size()) { // At root or sub shorter than top: done if (sub == top) { return true; } else { return false; } } } } bool path_isabsolute(const std::string& path) { if (!path.empty() && (path[0] == '/' #ifdef _WIN32 || path_isdriveabs(path) #endif )) { return true; } return false; } std::string path_absolute(const std::string& is) { if (is.length() == 0) { return is; } std::string s = is; #ifdef _WIN32 path_slashize(s); #endif if (!path_isabsolute(s)) { s = path_cat(path_cwd(), s); #ifdef _WIN32 path_slashize(s); #endif } return s; } std::string path_canon(const std::string& is, const std::string* cwd) { std::string s = is; #ifdef _WIN32 path_slashize(s); std::string uncvolume; if (path_isunc(s, uncvolume)) { s = s.substr(uncvolume.size()); if (s.empty()) s = "/"; } else { // fix possible path from file: absolute url if (s.size() && s[0] == '/' && path_hasdrive(s.substr(1))) { s = s.substr(1); } } #endif if (!path_isabsolute(s)) { if (cwd) { s = path_cat(*cwd, s); } else { s = path_cat(path_cwd(), s); } } std::vector elems; stringToTokens(s, elems, "/"); std::vector cleaned; for (const auto& elem : elems) { if (elem == "..") { if (!cleaned.empty()) { cleaned.pop_back(); } } else if (elem.empty() || elem == ".") { } else { cleaned.push_back(elem); } } std::string ret; if (!cleaned.empty()) { for (const auto& elem : cleaned) { ret += "/"; #ifdef _WIN32 if (ret == "/" && path_strlookslikedrive(elem)) { // Get rid of just added initial "/" ret.clear(); } #endif ret += elem; } } else { ret = "/"; } #ifdef _WIN32 if (uncvolume.size()) { ret = uncvolume + ret; } else if (path_strlookslikedrive(ret)) { // Raw drive needs a final / path_catslash(ret); } #endif return ret; } bool path_makepath(const std::string& ipath, int mode) { std::string path = path_canon(ipath); std::vector elems; stringToTokens(path, elems, "/"); path = "/"; for (const auto& elem : elems) { #ifdef _WIN32 PRETEND_USE(mode); if (path == "/" && path_strlookslikedrive(elem)) { path = ""; } #endif path += elem; // Not using path_isdir() here, because this cant grok symlinks // If we hit an existing file, no worry, mkdir will just fail. LOGDEB1("path_makepath: testing existence: [" << path << "]\n"); if (!path_exists(path)) { LOGDEB1("path_makepath: creating directory [" << path << "]\n"); SYSPATH(path, syspath); if (MKDIR(syspath, mode) != 0) { //cerr << "mkdir " << path << " failed, errno " << errno << "\n"; return false; } } path += "/"; } return true; } bool path_chdir(const std::string& path) { SYSPATH(path, syspath); return CHDIR(syspath) == 0; } std::string path_cwd() { #ifdef _WIN32 wchar_t *wd = _wgetcwd(nullptr, 0); if (nullptr == wd) { return std::string(); } std::string sdname; wchartoutf8(wd, sdname); free(wd); path_slashize(sdname); return sdname; #else char wd[MAXPATHLEN+1]; if (nullptr == getcwd(wd, MAXPATHLEN+1)) { return std::string(); } return wd; #endif } bool path_unlink(const std::string& path) { SYSPATH(path, syspath); return UNLINK(syspath) == 0; } bool path_rmdir(const std::string& path) { SYSPATH(path, syspath); return RMDIR(syspath) == 0; } bool path_utimes(const std::string& path, struct path_timeval _tv[2]) { #ifdef _WIN32 struct _utimbuf times; if (nullptr == _tv) { times.actime = times.modtime = time(0L); } else { times.actime = _tv[0].tv_sec; times.modtime = _tv[1].tv_sec; } SYSPATH(path, syspath); return _wutime(syspath, ×) != -1; #else struct timeval tvb[2]; if (nullptr == _tv) { gettimeofday(tvb, nullptr); tvb[1].tv_sec = tvb[0].tv_sec; tvb[1].tv_usec = tvb[0].tv_usec; } else { tvb[0].tv_sec = _tv[0].tv_sec; tvb[0].tv_usec = _tv[0].tv_usec; tvb[1].tv_sec = _tv[1].tv_sec; tvb[1].tv_usec = _tv[1].tv_usec; } return utimes(path.c_str(), tvb) == 0; #endif } bool path_streamopen(const std::string& path, int mode, std::fstream& outstream) { #if defined(_WIN32) && defined (_MSC_VER) // MSC STL has support for using wide chars in fstream // constructor. We need this if, e.g. the user name/home directory // is not ASCII. Actually don't know how to do this with gcc wchar_t wpath[MAX_PATH + 1]; utf8towchar(path, wpath, MAX_PATH); outstream.open(wpath, std::ios_base::openmode(mode)); #else outstream.open(path, std::ios_base::openmode(mode)); #endif if (!outstream.is_open()) { return false; } return true; } bool path_isdir(const std::string& path, bool follow) { struct STATBUF st; SYSPATH(path, syspath); int ret = follow ? STAT(syspath, &st) : LSTAT(syspath, &st); if (ret < 0) { return false; } if (S_ISDIR(st.st_mode)) { return true; } return false; } bool path_isfile(const std::string& path, bool follow) { struct STATBUF st; SYSPATH(path, syspath); int ret = follow ? STAT(syspath, &st) : LSTAT(syspath, &st); if (ret < 0) { return false; } if (S_ISREG(st.st_mode)) { return true; } return false; } long long path_filesize(const std::string& path) { struct STATBUF st; SYSPATH(path, syspath); if (STAT(syspath, &st) < 0) { return -1; } return (long long)st.st_size; } bool path_samefile(const std::string& p1, const std::string& p2) { #ifdef _WIN32 std::string cp1, cp2; cp1 = path_canon(p1); cp2 = path_canon(p2); return cp1 == cp2; #else struct stat st1, st2; if (stat(p1.c_str(), &st1)) return false; if (stat(p2.c_str(), &st2)) return false; if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) { return true; } return false; #endif } #if defined(STATX_TYPE) #include #define MINORBITS 20 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) static ssize_t _statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) { return syscall(__NR_statx, dfd, filename, flags, mask, buffer); } static int statx(const char *filename, struct statx *buffer) { int ret, atflag = 0; unsigned int mask = STATX_BASIC_STATS | STATX_BTIME; ret = _statx(AT_FDCWD, filename, atflag, mask, buffer); if (ret < 0) { perror(filename); } return ret; } static int lstatx(const char *filename, struct statx *buffer) { int ret, atflag = AT_SYMLINK_NOFOLLOW; unsigned int mask = STATX_BASIC_STATS | STATX_BTIME; ret = _statx(AT_FDCWD, filename, atflag, mask, buffer); if (ret < 0) { perror(filename); } return ret; } #define ST_SIZE stx_size #define ST_MODE stx_mode #define ST_MTIME stx_mtime.tv_sec #define ST_CTIME stx_ctime.tv_sec #define ST_BTIME stx_btime.tv_sec #define ST_INO stx_ino #define ST_DEVICE(ST) MKDEV((ST).stx_dev_major, (ST).stx_dev_minor) #define ST_BLOCKS stx_blocks #define ST_BLKSIZE stx_blksize #define ST_MODE stx_mode #define STATXSTRUCT statx #define STATXCALL statx #define LSTATXCALL lstatx #else /* -> !defined(STATX_TYPE) */ /* Using traditional stat */ #define ST_SIZE st_size #define ST_MODE st_mode #define ST_MTIME st_mtime #define ST_CTIME st_ctime #define ST_BTIME st_ctime #define ST_INO st_ino #define ST_DEVICE(ST) (ST).st_dev #define ST_BLOCKS st_blocks #define ST_BLKSIZE st_blksize #define ST_MODE st_mode #define STATXSTRUCT STATBUF #define STATXCALL STAT #define LSTATXCALL LSTAT #endif /* Not using statx */ int path_fileprops(const std::string path, struct PathStat *stp, bool follow) { if (nullptr == stp) { return -1; } *stp = PathStat{PathStat::PST_INVALID,0,0,0,0,0,0,0,0,0}; struct STATXSTRUCT mst; SYSPATH(path, syspath); int ret = follow ? STATXCALL(syspath, &mst) : LSTATXCALL(syspath, &mst); if (ret != 0) { stp->pst_type = PathStat::PST_INVALID; return ret; } stp->pst_size = mst.ST_SIZE; stp->pst_mode = mst.ST_MODE; stp->pst_mtime = mst.ST_MTIME; stp->pst_btime = mst.ST_BTIME; #ifdef _WIN32 stp->pst_ctime = mst.ST_MTIME; #else stp->pst_ino = mst.ST_INO; stp->pst_dev = ST_DEVICE(mst); stp->pst_ctime = mst.ST_CTIME; stp->pst_blocks = mst.ST_BLOCKS; stp->pst_blksize = mst.ST_BLKSIZE; #endif switch (mst.ST_MODE & S_IFMT) { case S_IFDIR: stp->pst_type = PathStat::PST_DIR;break; case S_IFLNK: stp->pst_type = PathStat::PST_SYMLINK;break; case S_IFREG: stp->pst_type = PathStat::PST_REGULAR;break; default: stp->pst_type = PathStat::PST_OTHER;break; } return 0; } bool path_exists(const std::string& path) { SYSPATH(path, syspath); return ACCESS(syspath, 0) == 0; } bool path_readable(const std::string& path) { SYSPATH(path, syspath); return ACCESS(syspath, R_OK) == 0; } bool path_access(const std::string& path, int mode) { SYSPATH(path, syspath); return ACCESS(syspath, mode) == 0; } /// Directory reading interface. UTF-8 on Windows. class PathDirContents::Internal { public: ~Internal() { if (dirhdl) { CLOSEDIR(dirhdl); } } DIRHDL *dirhdl{nullptr}; PathDirContents::Entry entry; std::string dirpath; }; PathDirContents::PathDirContents(const std::string& dirpath) { m = new Internal; m->dirpath = dirpath; } PathDirContents::~PathDirContents() { delete m; } bool PathDirContents::opendir() { if (m->dirhdl) { CLOSEDIR(m->dirhdl); m->dirhdl = nullptr; } const std::string& dp{m->dirpath}; SYSPATH(dp, sysdir); m->dirhdl = OPENDIR(sysdir); #ifdef _WIN32 if (nullptr == m->dirhdl) { int rc = GetLastError(); LOGERR("opendir failed: LastError " << rc << "\n"); if (rc == ERROR_NETNAME_DELETED) { // 64: share disconnected. // Not too sure of the errno in this case. // Make sure it's not one of the permissible ones errno = ENODEV; } } #endif return nullptr != m->dirhdl; } void PathDirContents::rewinddir() { REWINDDIR(m->dirhdl); } const struct PathDirContents::Entry* PathDirContents::readdir() { struct DIRENT *ent = READDIR(m->dirhdl); if (nullptr == ent) { return nullptr; } #ifdef _WIN32 std::string sdname; if (!wchartoutf8(ent->d_name, sdname)) { LOGERR("wchartoutf8 failed for " << ent->d_name << "\n"); return nullptr; } const char *dname = sdname.c_str(); #else const char *dname = ent->d_name; #endif m->entry.d_name = dname; return &m->entry; } bool listdir(const std::string& dir, std::string& reason, std::set& entries) { std::ostringstream msg; PathDirContents dc(dir); if (!path_isdir(dir)) { msg << "listdir: " << dir << " not a directory"; goto out; } if (!path_access(dir, R_OK)) { msg << "listdir: no read access to " << dir; goto out; } if (!dc.opendir()) { msg << "listdir: cant opendir " << dir << ", errno " << errno; goto out; } const struct PathDirContents::Entry *ent; while ((ent = dc.readdir()) != nullptr) { if (ent->d_name == "." || ent->d_name == "..") { continue; } entries.insert(ent->d_name); } out: reason = msg.str(); if (reason.empty()) { return true; } return false; } // We do not want to mess with the pidfile content in the destructor: // the lock might still be in use in a child process. In fact as much // as we'd like to reset the pid inside the file when we're done, it // would be very difficult to do it right and it's probably best left // alone. Pidfile::~Pidfile() { this->close(); } #ifdef _WIN32 // It appears that we can't read the locked file on Windows, so we use // separate files for locking and holding the data. static std::string pid_data_path(const std::string& path) { // Remove extension. append -data to name, add back extension. auto ext = path_suffix(path); auto spath = path_cat(path_getfather(path), path_basename(path, ext)); if (spath.back() == '.') spath.pop_back(); if (!ext.empty()) spath += std::string("-data") + "." + ext; return spath; } #endif // _WIN32 int Pidfile::read_pid() { #ifdef _WIN32 // It appears that we can't read the locked file on Windows, so use an aux file auto path = pid_data_path(m_path); SYSPATH(path, syspath); #else SYSPATH(m_path, syspath); #endif int fd = OPEN(syspath, O_RDONLY); if (fd == -1) { if (errno != ENOENT) m_reason = "Open RDONLY failed: [" + m_path + "]: " + strerror(errno); return -1; } char buf[16]; int i = read(fd, buf, sizeof(buf) - 1); ::close(fd); if (i <= 0) { m_reason = "Read failed: [" + m_path + "]: " + strerror(errno); return -1; } buf[i] = '\0'; char *endptr; int pid = strtol(buf, &endptr, 10); if (endptr != &buf[i]) { m_reason = "Bad pid contents: [" + m_path + "]: " + strerror(errno); return - 1; } return pid; } int path_open(const std::string& path, int flags, int mode) { SYSPATH(path, syspath); return OPEN(syspath, flags, mode); } int Pidfile::flopen() { SYSPATH(m_path, syspath); if ((m_fd = OPEN(syspath, O_RDWR | O_CREAT, 0644)) == -1) { m_reason = "Open failed: [" + m_path + "]: " + strerror(errno); return -1; } #ifdef sun struct flock lockdata; lockdata.l_start = 0; lockdata.l_len = 0; lockdata.l_type = F_WRLCK; lockdata.l_whence = SEEK_SET; if (fcntl(m_fd, F_SETLK, &lockdata) != 0) { int serrno = errno; this->close(); errno = serrno; m_reason = "fcntl lock failed"; return -1; } #else int operation = LOCK_EX | LOCK_NB; if (flock(m_fd, operation) == -1) { int serrno = errno; this->close(); errno = serrno; m_reason = "flock failed"; return -1; } #endif // ! sun if (ftruncate(m_fd, 0) != 0) { /* can't happen [tm] */ int serrno = errno; this->close(); errno = serrno; m_reason = "ftruncate failed"; return -1; } return 0; } int Pidfile::open() { if (flopen() < 0) { return read_pid(); } return 0; } int Pidfile::write_pid() { #ifdef _WIN32 // It appears that we can't read the locked file on Windows, so use an aux file auto path = pid_data_path(m_path); SYSPATH(path, syspath); int fd; if ((fd = OPEN(syspath, O_RDWR | O_CREAT, 0644)) == -1) { m_reason = "Open failed: [" + path + "]: " + strerror(errno); return -1; } #else int fd = m_fd; #endif /* truncate to allow multiple calls */ if (ftruncate(fd, 0) == -1) { m_reason = "ftruncate failed"; return -1; } char pidstr[20]; sprintf(pidstr, "%u", int(getpid())); ::lseek(fd, 0, 0); if (::write(fd, pidstr, strlen(pidstr)) != (PATHUT_SSIZE_T)strlen(pidstr)) { m_reason = "write failed"; return -1; } #ifdef _WIN32 ::close(fd); #endif return 0; } int Pidfile::close() { int ret = -1; if (m_fd >= 0) { ret = ::close(m_fd); m_fd = -1; } return ret; } int Pidfile::remove() { SYSPATH(m_path, syspath); return UNLINK(syspath); } // Call funcs that need static init (not initially reentrant) void pathut_init_mt() { path_home(); } } // End namespace MedocUtils recoll-1.36.1/utils/rclionice.cpp0000644000175000017500000000315114410615043013614 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include "rclionice.h" #include "execmd.h" #include "log.h" using namespace std; bool rclionice(const string& clss, const string& cdata) { string ionicexe; if (!ExecCmd::which("ionice", ionicexe)) { // ionice not found, bail out LOGDEB0("rclionice: ionice not found\n"); return false; } vector args; args.push_back("-c"); args.push_back(clss); if (!cdata.empty()) { args.push_back("-n"); args.push_back(cdata); } char cpid[100]; sprintf(cpid, "%d", getpid()); args.push_back("-p"); args.push_back(cpid); ExecCmd cmd; int status = cmd.doexec(ionicexe, args); if (status) { LOGERR("rclionice: failed, status 0x" << status << "\n"); return false; } return true; } recoll-1.36.1/utils/pxattr.cpp0000644000175000017500000003631614506737325013216 00000000000000/* Copyright (c) 2009 Jean-Francois Dockes Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** \file pxattr.cpp \brief Portable External Attributes API */ // PXALINUX: platforms like kfreebsd which aren't Linux but use the same xattr interface #if defined(__linux__) || \ (defined(__FreeBSD_kernel__)&&defined(__GLIBC__)&&!defined(__FreeBSD__)) || \ defined(__CYGWIN__) #define PXALINUX #endif // FreeBSD and NetBSD use the same interface. Let's call this PXAFREEBSD #if defined(__FreeBSD__) || defined(__NetBSD__) #define PXAFREEBSD #endif // __FreeBSD__ or __NetBSD__ // Not exactly true for win32, but makes my life easier by avoiding ifdefs in recoll (the calls just // fail, which is expected) #if defined(__DragonFly__) || defined(__OpenBSD__) || defined(_WIN32) #define HAS_NO_XATTR #endif #if defined(_MSC_VER) #define ssize_t int #endif // If the platform is not known yet, let this file be empty instead of breaking the compile, this // will let the build work if the rest of the software is not actually calling us. If it does call // us, the undefined symbols will bring attention to the necessity of a port. // // If the platform is known but does not support extattrs (e.g.__OpenBSD__), just let the methods // return errors (like they would on a non-xattr fs on e.g. linux), no need to break the build in // this case. // All known systems/families: implement the interface, possibly by returning errors if the system // is not supported #if defined(PXAFREEBSD) || defined(PXALINUX) || defined(__APPLE__) || defined(HAS_NO_XATTR) #include "pxattr.h" #include #include #include #include #if defined(PXAFREEBSD) #include #include #elif defined(PXALINUX) #include #elif defined(__APPLE__) #include #elif defined(HAS_NO_XATTR) #else #error "Unknown system can't compile" #endif #ifndef PRETEND_USE #define PRETEND_USE(var) ((void)var) #endif using std::string; using std::vector; namespace pxattr { class AutoBuf { public: char *buf; AutoBuf() : buf(nullptr) {} ~AutoBuf() {if (buf) free(buf); buf = nullptr;} bool alloc(int n) { if (buf) { free(buf); buf = nullptr; } buf = (char *)malloc(n); return buf != nullptr; } }; static bool get(int fd, const string& path, const string& _name, string *value, flags flags, nspace dom) { string name; if (!sysname(dom, _name, &name)) return false; ssize_t ret = -1; AutoBuf buf; #if defined(PXAFREEBSD) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = extattr_get_link(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), 0, 0); } else { ret = extattr_get_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), 0, 0); } } else { ret = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name.c_str(), 0, 0); } if (ret < 0) return false; if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0 return false; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = extattr_get_link(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), buf.buf, ret); } else { ret = extattr_get_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), buf.buf, ret); } } else { ret = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name.c_str(), buf.buf, ret); } #elif defined(PXALINUX) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = lgetxattr(path.c_str(), name.c_str(), nullptr, 0); } else { ret = getxattr(path.c_str(), name.c_str(), nullptr, 0); } } else { ret = fgetxattr(fd, name.c_str(), nullptr, 0); } if (ret < 0) return false; if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0 return false; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = lgetxattr(path.c_str(), name.c_str(), buf.buf, ret); } else { ret = getxattr(path.c_str(), name.c_str(), buf.buf, ret); } } else { ret = fgetxattr(fd, name.c_str(), buf.buf, ret); } #elif defined(__APPLE__) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = getxattr(path.c_str(), name.c_str(), 0, 0, 0, XATTR_NOFOLLOW); } else { ret = getxattr(path.c_str(), name.c_str(), 0, 0, 0, 0); } } else { ret = fgetxattr(fd, name.c_str(), 0, 0, 0, 0); } if (ret < 0) return false; if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0 return false; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = getxattr(path.c_str(), name.c_str(), buf.buf, ret, 0, XATTR_NOFOLLOW); } else { ret = getxattr(path.c_str(), name.c_str(), buf.buf, ret, 0, 0); } } else { ret = fgetxattr(fd, name.c_str(), buf.buf, ret, 0, 0); } #else PRETEND_USE(fd); PRETEND_USE(flags); PRETEND_USE(path); errno = ENOTSUP; #endif if (ret >= 0) value->assign(buf.buf, ret); return ret >= 0; } static bool set(int fd, const string& path, const string& _name, const string& value, flags flags, nspace dom) { string name; if (!sysname(dom, _name, &name)) return false; ssize_t ret = -1; #if defined(PXAFREEBSD) if (flags & (PXATTR_CREATE|PXATTR_REPLACE)) { // Need to test existence bool exists = false; ssize_t eret; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { eret = extattr_get_link(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), 0, 0); } else { eret = extattr_get_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), 0, 0); } } else { eret = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name.c_str(), 0, 0); } if (eret >= 0) exists = true; if (eret < 0 && errno != ENOATTR) return false; if ((flags & PXATTR_CREATE) && exists) { errno = EEXIST; return false; } if ((flags & PXATTR_REPLACE) && !exists) { errno = ENOATTR; return false; } } if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = extattr_set_link(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), value.c_str(), value.length()); } else { ret = extattr_set_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), value.c_str(), value.length()); } } else { ret = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, name.c_str(), value.c_str(), value.length()); } #elif defined(PXALINUX) int opts = 0; if (flags & PXATTR_CREATE) opts = XATTR_CREATE; else if (flags & PXATTR_REPLACE) opts = XATTR_REPLACE; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = lsetxattr(path.c_str(), name.c_str(), value.c_str(), value.length(), opts); } else { ret = setxattr(path.c_str(), name.c_str(), value.c_str(), value.length(), opts); } } else { ret = fsetxattr(fd, name.c_str(), value.c_str(), value.length(), opts); } #elif defined(__APPLE__) int opts = 0; if (flags & PXATTR_CREATE) opts = XATTR_CREATE; else if (flags & PXATTR_REPLACE) opts = XATTR_REPLACE; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = setxattr(path.c_str(), name.c_str(), value.c_str(), value.length(), 0, XATTR_NOFOLLOW|opts); } else { ret = setxattr(path.c_str(), name.c_str(), value.c_str(), value.length(), 0, opts); } } else { ret = fsetxattr(fd, name.c_str(), value.c_str(), value.length(), 0, opts); } #else PRETEND_USE(fd); PRETEND_USE(flags); PRETEND_USE(value); PRETEND_USE(path); errno = ENOTSUP; #endif return ret >= 0; } static bool del(int fd, const string& path, const string& _name, flags flags, nspace dom) { string name; if (!sysname(dom, _name, &name)) return false; int ret = -1; #if defined(PXAFREEBSD) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = extattr_delete_link(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str()); } else { ret = extattr_delete_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str()); } } else { ret = extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, name.c_str()); } #elif defined(PXALINUX) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = lremovexattr(path.c_str(), name.c_str()); } else { ret = removexattr(path.c_str(), name.c_str()); } } else { ret = fremovexattr(fd, name.c_str()); } #elif defined(__APPLE__) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = removexattr(path.c_str(), name.c_str(), XATTR_NOFOLLOW); } else { ret = removexattr(path.c_str(), name.c_str(), 0); } } else { ret = fremovexattr(fd, name.c_str(), 0); } #else PRETEND_USE(fd); PRETEND_USE(flags); PRETEND_USE(path); errno = ENOTSUP; #endif return ret >= 0; } static bool list(int fd, const string& path, vector* names, flags flags, nspace dom) { ssize_t ret = -1; AutoBuf buf; #if defined(PXAFREEBSD) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = extattr_list_link(path.c_str(), EXTATTR_NAMESPACE_USER, 0, 0); } else { ret = extattr_list_file(path.c_str(), EXTATTR_NAMESPACE_USER, 0, 0); } } else { ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, 0, 0); } if (ret < 0) return false; if (!buf.alloc(ret+1)) // NEEDED on FreeBSD (no ending null) return false; buf.buf[ret] = 0; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = extattr_list_link(path.c_str(), EXTATTR_NAMESPACE_USER, buf.buf, ret); } else { ret = extattr_list_file(path.c_str(), EXTATTR_NAMESPACE_USER, buf.buf, ret); } } else { ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, buf.buf, ret); } #elif defined(PXALINUX) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = llistxattr(path.c_str(), nullptr, 0); } else { ret = listxattr(path.c_str(), nullptr, 0); } } else { ret = flistxattr(fd, nullptr, 0); } if (ret < 0) return false; if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0 return false; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = llistxattr(path.c_str(), buf.buf, ret); } else { ret = listxattr(path.c_str(), buf.buf, ret); } } else { ret = flistxattr(fd, buf.buf, ret); } #elif defined(__APPLE__) if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = listxattr(path.c_str(), 0, 0, XATTR_NOFOLLOW); } else { ret = listxattr(path.c_str(), 0, 0, 0); } } else { ret = flistxattr(fd, 0, 0, 0); } if (ret < 0) return false; if (!buf.alloc(ret+1)) // Don't want to deal with possible ret=0 return false; if (fd < 0) { if (flags & PXATTR_NOFOLLOW) { ret = listxattr(path.c_str(), buf.buf, ret, XATTR_NOFOLLOW); } else { ret = listxattr(path.c_str(), buf.buf, ret, 0); } } else { ret = flistxattr(fd, buf.buf, ret, 0); } #else PRETEND_USE(fd); PRETEND_USE(flags); PRETEND_USE(dom); PRETEND_USE(path); errno = ENOTSUP; #endif if (ret < 0) return false; char *bufstart = buf.buf; // All systems return a 0-separated string list except FreeBSD/NetBSD which has length, value // pairs, length is a byte. #if defined(PXAFREEBSD) char *cp = buf.buf; unsigned int len; while (cp < buf.buf + ret + 1) { len = *cp; *cp = 0; cp += len + 1; } bufstart = buf.buf + 1; *cp = 0; // don't forget, we allocated one more #endif if (ret > 0) { int pos = 0; while (pos < ret) { string n = string(bufstart + pos); string n1; if (pxname(PXATTR_USER, n, &n1)) { names->push_back(n1); } pos += n.length() + 1; } } return true; } static const string nullstring(""); bool get(const string& path, const string& _name, string *value, flags flags, nspace dom) { return get(-1, path, _name, value, flags, dom); } bool get(int fd, const string& _name, string *value, flags flags, nspace dom) { return get(fd, nullstring, _name, value, flags, dom); } bool set(const string& path, const string& _name, const string& value, flags flags, nspace dom) { return set(-1, path, _name, value, flags, dom); } bool set(int fd, const string& _name, const string& value, flags flags, nspace dom) { return set(fd, nullstring, _name, value, flags, dom); } bool del(const string& path, const string& _name, flags flags, nspace dom) { return del(-1, path, _name, flags, dom); } bool del(int fd, const string& _name, flags flags, nspace dom) { return del(fd, nullstring, _name, flags, dom); } bool list(const string& path, vector* names, flags flags, nspace dom) { return list(-1, path, names, flags, dom); } bool list(int fd, vector* names, flags flags, nspace dom) { return list(fd, nullstring, names, flags, dom); } #if defined(PXALINUX) || defined(COMPAT1) static const string userstring("user."); #else static const string userstring(""); #endif bool sysname(nspace dom, const string& pname, string* sname) { if (dom != PXATTR_USER) { errno = EINVAL; return false; } *sname = userstring + pname; return true; } bool pxname(nspace, const string& sname, string* pname) { if (!userstring.empty() && sname.find(userstring) != 0) { errno = EINVAL; return false; } *pname = sname.substr(userstring.length()); return true; } } // namespace pxattr #endif // Known systems. recoll-1.36.1/utils/md5.h0000644000175000017500000000307214410615043012001 00000000000000/* $OpenBSD: md5.h,v 1.3 2014/11/16 17:39:09 tedu Exp $ */ /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. */ #ifndef _MD5_H_ #define _MD5_H_ #include #include #include namespace MedocUtils { #define MD5_BLOCK_LENGTH 64 #define MD5_DIGEST_LENGTH 16 typedef struct MD5Context { uint32_t state[4]; /* state */ uint64_t count; /* number of bits, mod 2^64 */ uint8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX *); void MD5Update(MD5_CTX *, const void *, size_t); void MD5Final(uint8_t [MD5_DIGEST_LENGTH], MD5_CTX *); void MD5Transform(uint32_t [4], const uint8_t [MD5_BLOCK_LENGTH]); /** md5 c++ utility wrappers */ extern void MD5Final(std::string& digest, MD5_CTX *); extern std::string& MD5String(const std::string& data, std::string& digest); extern std::string& MD5HexPrint(const std::string& digest, std::string& out); extern std::string& MD5HexScan(const std::string& xdigest, std::string& digest); extern std::string MD5Hex(const std::string& data); } // End namespace MedocUtils using namespace MedocUtils; #endif /* _MD5_H_ */ recoll-1.36.1/utils/md5ut.h0000644000175000017500000000176014410615043012354 00000000000000/* Copyright (C) 2014 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _MD5UT_H_ #define _MD5UT_H_ #include "md5.h" /** md5 utility: compute file md5 */ extern bool MD5File(const std::string& filename, std::string& digest, std::string *reason); #endif /* _MD5UT_H_ */ recoll-1.36.1/utils/ecrontab.cpp0000644000175000017500000001025114427373216013454 00000000000000/* Copyright (C) 2004-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include "ecrontab.h" #include "execmd.h" #include "smallut.h" #include "log.h" using std::string; using std::vector; // Read crontab file and split it into lines. static bool eCrontabGetLines(vector& lines) { string crontab; ExecCmd croncmd; vector args; int status; // Retrieve current crontab contents. An error here means that no // crontab exists, and is not fatal, but we return a different // status than for an empty one args.push_back("-l"); if ((status = croncmd.doexec("crontab", args, nullptr, &crontab))) { lines.clear(); return false; } // Split crontab into lines stringToTokens(crontab, lines, "\n"); return true; } // Concatenate lines and write crontab static bool eCrontabWriteFile(const vector& lines, string& reason) { string crontab; ExecCmd croncmd; vector args; int status; for (const auto& line : lines) { crontab += line + "\n"; } args.push_back("-"); if ((status = croncmd.doexec("crontab", args, &crontab, nullptr))) { char nbuf[30]; sprintf(nbuf, "0x%x", status); reason = string("Exec crontab -l failed: status: ") + nbuf; return false; } return true; } // Add / change / delete entry identified by marker and id bool editCrontab(const string& marker, const string& id, const string& sched, const string& cmd, string& reason) { vector lines; if (!eCrontabGetLines(lines)) { // Special case: cmd is empty, no crontab, don't create one if (cmd.empty()) return true; } // Remove old copy if any for (auto it = lines.begin(); it != lines.end(); it++) { // Skip comment if (it->find_first_of("#") == it->find_first_not_of(" \t")) continue; if (it->find(marker) != string::npos && it->find(id) != string::npos) { lines.erase(it); break; } } if (!cmd.empty()) { string nline = sched + " " + marker + " " + id + " " + cmd; lines.push_back(nline); } if (!eCrontabWriteFile(lines, reason)) return false; return true; } bool checkCrontabUnmanaged(const string& marker, const string& data) { vector lines; if (!eCrontabGetLines(lines)) { // No crontab, answer is no return false; } // Scan crontab for (const auto& line : lines) { if (line.find(marker) == string::npos && line.find(data) != string::npos) { return true; } } return false; } /** Retrieve the scheduling for a crontab entry */ bool getCrontabSched(const string& marker, const string& id, vector& sched) { LOGDEB0("getCrontabSched: marker[" << marker << "], id[" << id << "]\n"); vector lines; if (!eCrontabGetLines(lines)) { // No crontab, answer is no sched.clear(); return false; } string theline; for (const auto& line : lines) { // Skip comment if (line.find_first_of("#") == line.find_first_not_of(" \t")) continue; if (line.find(marker) != string::npos && line.find(id) != string::npos) { theline = line; break; } } stringToTokens(theline, sched); sched.resize(5); return true; } recoll-1.36.1/utils/miniz.h0000644000175000017500000020245414410615043012447 00000000000000/* miniz.c 2.0.8 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #pragma once /* Defines to completely disable specific portions of miniz.c: If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ /*#define MINIZ_NO_STDIO */ /* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ /* get/set file times, and the C run-time funcs that get/set times won't be called. */ /* The current downside is the times written to your archives will be from 1979. */ /*#define MINIZ_NO_TIME */ /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_APIS */ /* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ #define MINIZ_NO_ARCHIVE_WRITING_APIS /* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ #define MINIZ_NO_ZLIB_APIS /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES /* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ #define MINIZ_NO_TIME #endif #include #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) /* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ #define MINIZ_X86_OR_X64_CPU 1 #else #define MINIZ_X86_OR_X64_CPU 0 #endif #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #if MINIZ_X86_OR_X64_CPU /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) /* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ #define MINIZ_HAS_64BIT_REGISTERS 1 #else #define MINIZ_HAS_64BIT_REGISTERS 0 #endif #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API Definitions. */ /* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ typedef unsigned long mz_ulong; /* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ void mz_free(void *p); #define MZ_ADLER32_INIT (1) /* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) /* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); /* Compression strategies. */ enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; /* Method */ #define MZ_DEFLATED 8 /* Heap allocation callbacks. Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */ typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); /* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; #define MZ_VERSION "10.0.3" #define MZ_VERNUM 0xA030 #define MZ_VER_MAJOR 10 #define MZ_VER_MINOR 0 #define MZ_VER_REVISION 3 #define MZ_VER_SUBREVISION 0 #ifndef MINIZ_NO_ZLIB_APIS /* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; /* Return status codes. MZ_PARAM_ERROR is non-standard. */ enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; /* Window bits */ #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; /* Compression/decompression stream struct. */ typedef struct mz_stream_s { const unsigned char *next_in; /* pointer to next byte to read */ unsigned int avail_in; /* number of bytes available at next_in */ mz_ulong total_in; /* total number of bytes consumed so far */ unsigned char *next_out; /* pointer to next byte to write */ unsigned int avail_out; /* number of bytes that can be written to next_out */ mz_ulong total_out; /* total number of bytes produced so far */ char *msg; /* error msg (unused) */ struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ mz_free_func zfree; /* optional heap free function (defaults to free) */ void *opaque; /* heap alloc function user pointer */ int data_type; /* data_type (unused) */ mz_ulong adler; /* adler32 of the source or uncompressed data */ mz_ulong reserved; /* not used */ } mz_stream; typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ const char *mz_version(void); /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ /* pStream must point to an initialized mz_stream struct. */ /* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ /* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ /* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if the input parameters are bogus. */ /* MZ_MEM_ERROR on out of memory. */ int mz_deflateInit(mz_streamp pStream, int level); /* mz_deflateInit2() is like mz_deflate(), except with more control: */ /* Additional parameters: */ /* method must be MZ_DEFLATED */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ /* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ int mz_deflateReset(mz_streamp pStream); /* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ /* Return values: */ /* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ /* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ int mz_deflate(mz_streamp pStream, int flush); /* mz_deflateEnd() deinitializes a compressor: */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ int mz_deflateEnd(mz_streamp pStream); /* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); /* Single-call compression functions mz_compress() and mz_compress2(): */ /* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); /* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ mz_ulong mz_compressBound(mz_ulong source_len); /* Initializes a decompressor. */ int mz_inflateInit(mz_streamp pStream); /* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ int mz_inflateInit2(mz_streamp pStream, int window_bits); /* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ /* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ /* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ /* Return values: */ /* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ /* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_DATA_ERROR if the deflate stream is invalid. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ /* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ int mz_inflate(mz_streamp pStream, int flush); /* Deinitializes a decompressor. */ int mz_inflateEnd(mz_streamp pStream); /* Single-call decompression. */ /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); /* Returns a string description of the specified error code, or NULL if the error code is invalid. */ const char *mz_error(int err); /* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ #endif /* MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif #pragma once #include #include #include #include /* ------------------- Types and macros */ typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef int64_t mz_int64; typedef uint64_t mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) /* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #define MZ_FILE FILE #endif /* #ifdef MINIZ_NO_STDIO */ #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { int m_dummy; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else #define MZ_TIME_T time_t #endif #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); extern void miniz_def_free_func(void *opaque, void *address); extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); #define MZ_UINT16_MAX (0xFFFFU) #define MZ_UINT32_MAX (0xFFFFFFFFU) #ifdef __cplusplus } #endif #pragma once #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression API Definitions */ /* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ #define TDEFL_LESS_MEMORY 0 /* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ /* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; /* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ /* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ /* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ /* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ /* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ /* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ /* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ /* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ /* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; /* High level compression functions: */ /* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ /* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must free() the returned block when it's no longer needed. */ void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ /* Returns 0 on failure. */ size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* Compresses an image to a compressed PNG file in memory. */ /* On entry: */ /* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ /* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ /* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ /* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pLen_out will be set to the size of the PNG image file. */ /* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); /* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); /* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; /* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif /* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1 } tdefl_status; /* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; /* tdefl's compression state structure. */ typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; /* Initializes the compressor. */ /* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ /* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ /* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ /* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); /* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); /* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ /* tdefl_compress_buffer() always consumes the entire input buffer. */ tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); mz_uint32 tdefl_get_adler32(tdefl_compressor *d); /* Create tdefl_compress() flags given zlib-style compression parameters. */ /* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ /* window_bits may be -15 (raw deflate) or 15 (zlib) */ /* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); /* Allocate the tdefl_compressor structure in C so that */ /* non-C language bindings to tdefl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tdefl_compressor *tdefl_compressor_alloc(); void tdefl_compressor_free(tdefl_compressor *pComp); #ifdef __cplusplus } #endif #pragma once /* ------------------- Low-level Decompression API Definitions */ #ifdef __cplusplus extern "C" { #endif /* Decompression flags used by tinfl_decompress(). */ /* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ /* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ /* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ /* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; /* High level decompression functions: */ /* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ /* On return: */ /* Function returns a pointer to the decompressed data, or NULL on failure. */ /* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must call mz_free() on the returned block when it's no longer needed. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ /* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ /* Returns 1 on success or 0 on failure. */ typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; /* Allocate the tinfl_decompressor structure in C so that */ /* non-C language bindings to tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tinfl_decompressor *tinfl_decompressor_alloc(); void tinfl_decompressor_free(tinfl_decompressor *pDecomp); /* Max size of LZ dictionary. */ #define TINFL_LZ_DICT_SIZE 32768 /* Return status. */ typedef enum { /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ TINFL_STATUS_BAD_PARAM = -3, /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ TINFL_STATUS_ADLER32_MISMATCH = -2, /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ TINFL_STATUS_FAILED = -1, /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ TINFL_STATUS_DONE = 0, /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ TINFL_STATUS_NEEDS_MORE_INPUT = 1, /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ /* so I may need to add some code to address this. */ TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; /* Initializes the decompressor to its initial state. */ #define tinfl_init(r) \ do \ { \ (r)->m_state = 0; \ } \ MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 /* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ /* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); /* Internal/private bits follow. */ enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else #define TINFL_USE_64BIT_BITBUF 0 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #ifdef __cplusplus } #endif #pragma once /* ------------------- ZIP archive reading/writing */ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif enum { /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 }; typedef struct { /* Central directory file index. */ mz_uint32 m_file_index; /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ mz_uint64 m_central_dir_ofs; /* These fields are copied directly from the zip's central dir. */ mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; #ifndef MINIZ_NO_TIME MZ_TIME_T m_time; #endif /* CRC-32 of uncompressed data. */ mz_uint32 m_crc32; /* File's compressed size. */ mz_uint64 m_comp_size; /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ mz_uint64 m_uncomp_size; /* Zip internal and external file attributes. */ mz_uint16 m_internal_attr; mz_uint32 m_external_attr; /* Entry's local header file offset in bytes. */ mz_uint64 m_local_header_ofs; /* Size of comment in bytes. */ mz_uint32 m_comment_size; /* MZ_TRUE if the entry appears to be a directory. */ mz_bool m_is_directory; /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ mz_bool m_is_encrypted; /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ mz_bool m_is_supported; /* Filename. If string ends in '/' it's a subdirectory entry. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; /* Comment field. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 } mz_zip_flags; typedef enum { MZ_ZIP_TYPE_INVALID = 0, MZ_ZIP_TYPE_USER, MZ_ZIP_TYPE_MEMORY, MZ_ZIP_TYPE_HEAP, MZ_ZIP_TYPE_FILE, MZ_ZIP_TYPE_CFILE, MZ_ZIP_TOTAL_TYPES } mz_zip_type; /* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ typedef enum { MZ_ZIP_NO_ERROR = 0, MZ_ZIP_UNDEFINED_ERROR, MZ_ZIP_TOO_MANY_FILES, MZ_ZIP_FILE_TOO_LARGE, MZ_ZIP_UNSUPPORTED_METHOD, MZ_ZIP_UNSUPPORTED_ENCRYPTION, MZ_ZIP_UNSUPPORTED_FEATURE, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, MZ_ZIP_NOT_AN_ARCHIVE, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, MZ_ZIP_UNSUPPORTED_MULTIDISK, MZ_ZIP_DECOMPRESSION_FAILED, MZ_ZIP_COMPRESSION_FAILED, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, MZ_ZIP_CRC_CHECK_FAILED, MZ_ZIP_UNSUPPORTED_CDIR_SIZE, MZ_ZIP_ALLOC_FAILED, MZ_ZIP_FILE_OPEN_FAILED, MZ_ZIP_FILE_CREATE_FAILED, MZ_ZIP_FILE_WRITE_FAILED, MZ_ZIP_FILE_READ_FAILED, MZ_ZIP_FILE_CLOSE_FAILED, MZ_ZIP_FILE_SEEK_FAILED, MZ_ZIP_FILE_STAT_FAILED, MZ_ZIP_INVALID_PARAMETER, MZ_ZIP_INVALID_FILENAME, MZ_ZIP_BUF_TOO_SMALL, MZ_ZIP_INTERNAL_ERROR, MZ_ZIP_FILE_NOT_FOUND, MZ_ZIP_ARCHIVE_TOO_LARGE, MZ_ZIP_VALIDATION_FAILED, MZ_ZIP_WRITE_CALLBACK_FAILED, MZ_ZIP_TOTAL_ERRORS } mz_zip_error; typedef struct { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; /* We only support up to UINT32_MAX files in zip64 mode. */ mz_uint32 m_total_files; mz_zip_mode m_zip_mode; mz_zip_type m_zip_type; mz_zip_error m_last_error; mz_uint64 m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; mz_file_needs_keepalive m_pNeeds_keepalive; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef struct { mz_zip_archive *pZip; mz_uint flags; int status; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint file_crc32; #endif mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf; void *pWrite_buf; size_t out_blk_remain; tinfl_decompressor inflator; } mz_zip_reader_extract_iter_state; /* -------- ZIP reading */ /* Inits a ZIP archive reader. */ /* These functions read and validate the archive's central directory. */ mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); #ifndef MINIZ_NO_STDIO /* Read a archive from a disk file. */ /* file_start_ofs is the file offset where the archive actually begins, or 0. */ /* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ /* Partial conversion to wchar_t on Windows. This is necessary to access Windows file names in a reasonably sane way. Only the very few calls used by Recoll have been converted though. A full conversion would be much more work. The original miniz does not use wchar_t at all */ #ifdef _WIN32 #define WCHAR_TYPE wchar_t #else #define WCHAR_TYPE char #endif mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const WCHAR_TYPE *pFilename, mz_uint32 flags); mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const WCHAR_TYPE *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); /* Read an archive from an already opened FILE, beginning at the current file position. */ /* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */ /* The FILE will NOT be closed when mz_zip_reader_end() is called. */ mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); #endif /* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ mz_bool mz_zip_reader_end(mz_zip_archive *pZip); /* -------- ZIP reading or writing */ /* Clears a mz_zip_archive struct to all zeros. */ /* Important: This must be done before passing the struct to any mz_zip functions. */ void mz_zip_zero_struct(mz_zip_archive *pZip); mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); /* Returns the total number of files in the archive. */ mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); /* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); /* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ /* Note that the m_last_error functionality is not thread safe. */ mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); const char *mz_zip_get_error_string(mz_zip_error mz_err); /* MZ_TRUE if the archive file entry is a directory entry. */ mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the file is encrypted/strong encrypted. */ mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); /* Retrieves the filename of an archive file entry. */ /* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); /* Attempts to locates a file in the archive's central directory. */ /* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ /* Returns -1 if the file cannot be found. */ int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); /* Returns detailed information about an archive file entry. */ mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); /* MZ_TRUE if the file is in zip64 format. */ /* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); /* Returns the total central directory size in bytes. */ /* The current max supported size is <= MZ_UINT32_MAX. */ size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); /* Extracts a archive file to a memory buffer using no memory allocation. */ /* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); /* Extracts a archive file to a memory buffer. */ mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); /* Extracts a archive file to a dynamically allocated heap buffer. */ /* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ /* Returns NULL and sets the last error on failure. */ void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); /* Extracts a archive file using a callback function to output the file's data. */ mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); /* Extract a file iteratively */ mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); #ifndef MINIZ_NO_STDIO /* Extracts a archive file to a disk file and sets its last accessed and modified times. */ /* This function only extracts files, not archive directory records. */ mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); /* Extracts a archive file starting at the current position in the destination FILE stream. */ mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); #endif #if 0 /* TODO */ typedef void *mz_zip_streaming_extract_state_ptr; mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif /* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ /* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); /* Validates an entire archive by calling mz_zip_validate_file() on each file. */ mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); /* Misc utils/helpers, valid for ZIP reading or writing */ mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); mz_bool mz_zip_validate_file_archive(const WCHAR_TYPE *pFilename, mz_uint flags, mz_zip_error *pErr); /* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ mz_bool mz_zip_end(mz_zip_archive *pZip); /* -------- ZIP writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS /* Inits a ZIP archive writer. */ /*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ /*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); #endif /* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ /* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ /* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ /* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ /* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ /* the archive is finalized the file's central directory will be hosed. */ mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); /* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ /* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ /* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #ifndef MINIZ_NO_STDIO /* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #endif /* Adds a file to an archive by fully cloning the data from another archive. */ /* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); /* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ /* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ /* An archive must be manually finalized by calling this function for it to be valid. */ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); /* Finalizes a heap archive, returning a poiner to the heap block and its size. */ /* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); /* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ /* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ mz_bool mz_zip_writer_end(mz_zip_archive *pZip); /* -------- Misc. high-level helper functions: */ /* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ /* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ /* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); /* Reads a single file from an archive into a heap block. */ /* If pComment is not NULL, only the file with the specified comment will be extracted. */ /* Returns NULL on failure. */ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifdef __cplusplus } #endif #endif /* MINIZ_NO_ARCHIVE_APIS */ recoll-1.36.1/utils/cmdtalk.cpp0000644000175000017500000001557114426500174013302 00000000000000/* Copyright (C) 2016 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "cmdtalk.h" #include #include #include #include #include "smallut.h" #include "execmd.h" #ifdef MDU_INCLUDE_LOG #include MDU_INCLUDE_LOG #else #include "log.h" #endif using namespace std; class TimeoutExcept {}; class Canceler : public ExecCmdAdvise { public: Canceler(int tmsecs) : m_timeosecs(tmsecs) {} virtual void newData(int) { if (m_starttime && (time(nullptr) - m_starttime) > m_timeosecs) { throw TimeoutExcept(); } } void reset() { m_starttime = time(nullptr); } int m_timeosecs; time_t m_starttime{0}; }; class CmdTalk::Internal { public: Internal(int timeosecs) : m_cancel(timeosecs) {} ~Internal() { delete cmd; } bool readDataElement(string& name, string &data); bool talk(const pair& arg0, const unordered_map& args, unordered_map& rep); bool running(); ExecCmd *cmd{0}; bool failed{false}; Canceler m_cancel; std::mutex mmutex; }; CmdTalk::CmdTalk(int timeosecs) { m = new Internal(timeosecs); } CmdTalk::~CmdTalk() { delete m; } bool CmdTalk::startCmd(const string& cmdname, const vector& args, const vector& env, const vector& path) { LOGDEB("CmdTalk::startCmd\n"); if (m->failed) { LOGINF("CmdTalk: command failed, not restarting\n"); return false; } delete m->cmd; m->cmd = new ExecCmd; m->cmd->setAdvise(&m->m_cancel); for (const auto& it : env) { m->cmd->putenv(it); } string acmdname(cmdname); if (!path.empty()) { string colonpath; for (const auto& it: path) { colonpath += it + ":"; } if (!colonpath.empty()) { colonpath.erase(colonpath.size()-1); } LOGDEB("CmdTalk::startCmd: PATH: [" << colonpath << "]\n"); ExecCmd::which(cmdname, acmdname, colonpath.c_str()); } if (m->cmd->startExec(acmdname, args, 1, 1) < 0) { return false; } return true; } // Messages are made of data elements. Each element is like: // name: len\ndata // An empty line signals the end of the message, so the whole thing // would look like: // Name1: Len1\nData1Name2: Len2\nData2\n bool CmdTalk::Internal::readDataElement(string& name, string &data) { string ibuf; m_cancel.reset(); try { // Read name and length if (cmd->getline(ibuf) <= 0) { LOGERR("CmdTalk: getline error\n"); return false; } } catch (TimeoutExcept) { LOGINF("CmdTalk:readDataElement: fatal timeout (" << m_cancel.m_timeosecs << " S)\n"); return false; } LOGDEB1("CmdTalk:rde: line [" << ibuf << "]\n"); // Empty line (end of message) ? if (!ibuf.compare("\n")) { LOGDEB1("CmdTalk: Got empty line\n"); return true; } // We're expecting something like Name: len\n vector tokens; stringToTokens(ibuf, tokens); if (tokens.size() != 2) { LOGERR("CmdTalk: bad line in filter output: [" << ibuf << "]\n"); return false; } vector::iterator it = tokens.begin(); name = *it++; string& slen = *it; int len; if (sscanf(slen.c_str(), "%d", &len) != 1) { LOGERR("CmdTalk: bad line in filter output: [" << ibuf << "]\n"); return false; } // Read element data data.erase(); if (len > 0 && cmd->receive(data, len) != len) { LOGERR("CmdTalk: expected " << len << " bytes of data, got " << data.length() << "\n"); return false; } LOGDEB1("CmdTalk:rde: got: name [" << name << "] len " << len <<"value ["<< (data.size() > 100 ? (data.substr(0, 100) + " ...") : data)<< endl); return true; } bool CmdTalk::Internal::running() { if (failed || nullptr == cmd || cmd->getChildPid() <= 0) { return false; } int status; if (cmd->maybereap(&status)) { // Command exited. Set error status so that a restart will fail. LOGERR("CmdTalk::talk: command exited\n"); failed = true; return false; } return true; } bool CmdTalk::Internal::talk(const pair& arg0, const unordered_map& args, unordered_map& rep) { std::unique_lock lock(mmutex); if (!running()) { LOGERR("CmdTalk::talk: no process\n"); return false; } ostringstream obuf; if (!arg0.first.empty()) { obuf << arg0.first << ": " << arg0.second.size() << "\n" << arg0.second; } for (const auto& it : args) { obuf << it.first << ": " << it.second.size() << "\n" << it.second; } obuf << "\n"; if (cmd->send(obuf.str()) < 0) { cmd->zapChild(); LOGERR("CmdTalk: send error\n"); return false; } // Read answer (multiple elements) LOGDEB1("CmdTalk: reading answer\n"); for (;;) { string name, data; if (!readDataElement(name, data)) { cmd->zapChild(); return false; } if (name.empty()) { break; } trimstring(name, ":"); LOGDEB1("CmdTalk: got [" << name << "] -> [" << data << "]\n"); rep[name] = data; } if (rep.find("cmdtalkstatus") != rep.end()) { return false; } else { return true; } } bool CmdTalk::running() { if (nullptr == m) return false; return m->running(); } bool CmdTalk::talk(const unordered_map& args, unordered_map& rep) { if (nullptr == m) return false; return m->talk({"",""}, args, rep); } bool CmdTalk::callproc( const string& proc, const unordered_map& args, unordered_map& rep) { if (nullptr == m) return false; return m->talk({"cmdtalk:proc", proc}, args, rep); } recoll-1.36.1/utils/listmem.cpp0000644000175000017500000000656014410615043013326 00000000000000#include "listmem.h" #include #include #include using namespace std; /* * Functions to list a memory buffer: */ /* Turn byte into Hexadecimal ascii representation */ static char *hexa(unsigned int i) { int j; static char asc[3]; asc[0] = (i >> 4) & 0x0f; asc[1] = i & 0x0f; asc[2] = 0; for (j = 0; j < 2; j++) if (asc[j] > 9) { asc[j] += 55; } else { asc[j] += 48; } return (asc); } static void swap16(unsigned char *d, const unsigned char *s, int n) { if (n & 1) { n >>= 1; n++; } else { n >>= 1; } while (n--) { int i; i = 2 * n; d[i] = s[i + 1]; d[i + 1] = s[i]; } } static void swap32(unsigned char *d, const unsigned char *s, int n) { if (n & 3) { n >>= 2; n++; } else { n >>= 2; } while (n--) { int i; i = 4 * n; d[i] = s[i + 3]; d[i + 1] = s[i + 2]; d[i + 2] = s[i + 1]; d[i + 3] = s[i]; } } /* Turn byte buffer into hexadecimal representation */ void charbuftohex(int len, unsigned char *dt, int maxlen, char *str) { int i; char *bf; for (i = 0, bf = str; i < len; i++) { char *cp; if (bf - str >= maxlen - 4) { break; } cp = hexa((unsigned int)dt[i]); *bf++ = *cp++; *bf++ = *cp++; *bf++ = ' '; } *bf++ = 0; } void listmem(ostream& os, const void *_ptr, int siz, int adr, int opts) { const unsigned char *ptr = (const unsigned char *)_ptr; int i, j, c; char lastlisted[16]; int alreadysame = 0; int oneout = 0; unsigned char *mpt; if (opts & (LISTMEM_SWAP16 | LISTMEM_SWAP32)) { if ((mpt = (unsigned char *)malloc(siz + 4)) == NULL) { os << "OUT OF MEMORY\n"; return; } if (opts & LISTMEM_SWAP16) { swap16(mpt, ptr, siz); } else if (opts & LISTMEM_SWAP32) { swap32(mpt, ptr, siz); } } else { mpt = (unsigned char *)ptr; } for (i = 0; i < siz; i += 16) { /* Check for same data (only print first line in this case) */ if (oneout != 0 && siz - i >= 16 && memcmp(lastlisted, mpt + i, 16) == 0) { if (alreadysame == 0) { os << "*\n"; alreadysame = 1; } continue; } alreadysame = 0; /* Line header */ os << std::setw(4) << i + adr << " "; /* Hexadecimal representation */ for (j = 0; j < 16; j++) { if ((i + j) < siz) { os << hexa(mpt[i + j]) << ((j & 1) ? " " : ""); } else { os << " " << ((j & 1) ? " " : ""); } } os << " "; /* Also print ascii for values that fit */ for (j = 0; j < 16; j++) { if ((i + j) < siz) { c = mpt[i + j]; if (c >= 0x20 && c <= 0x7f) { os << char(c); } else { os << "."; } } else { os << " "; } } os << "\n"; memcpy(lastlisted, mpt + i, 16); oneout = 1; } if (mpt != ptr) { free(mpt); } } recoll-1.36.1/utils/transcode.cpp0000644000175000017500000001047614426500174013644 00000000000000/* Copyright (C) 2004-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include "transcode.h" #include "log.h" using namespace std; // We gain approximately 25% exec time for word at a time conversions by // caching the iconv_open thing. // // We may also lose some concurrency on multiproc because of the // necessary locking, but we only have one processing-intensive // possible thread for now (the indexing one), so this is probably not // an issue (and could be worked around with a slightly more // sohisticated approach). #define ICONV_CACHE_OPEN bool transcode(const string &in, string &out, const string &icode, const string &ocode, int *ecnt) { LOGDEB2("Transcode: " << icode << " -> " << ocode << "\n"); #ifdef ICONV_CACHE_OPEN static iconv_t ic = (iconv_t)-1; static string cachedicode; static string cachedocode; static std::mutex o_cachediconv_mutex; std::unique_lock lock(o_cachediconv_mutex); #else iconv_t ic; #endif bool ret = false; const int OBSIZ = 8192; char obuf[OBSIZ], *op; bool icopen = false; int mecnt = 0; out.erase(); size_t isiz = in.length(); out.reserve(isiz); const char *ip = in.c_str(); #ifdef ICONV_CACHE_OPEN if (cachedicode.compare(icode) || cachedocode.compare(ocode)) { if (ic != (iconv_t)-1) { iconv_close(ic); ic = (iconv_t)-1; } #endif if((ic = iconv_open(ocode.c_str(), icode.c_str())) == (iconv_t)-1) { out = string("iconv_open failed for ") + icode + " -> " + ocode; #ifdef ICONV_CACHE_OPEN cachedicode.erase(); cachedocode.erase(); #endif goto error; } #ifdef ICONV_CACHE_OPEN cachedicode.assign(icode); cachedocode.assign(ocode); } #endif icopen = true; while (isiz > 0) { size_t osiz; op = obuf; osiz = OBSIZ; if(iconv(ic, (ICONV_CONST char **)&ip, &isiz, &op, &osiz) == (size_t)-1 && errno != E2BIG) { #if 0 out.erase(); out = string("iconv failed for ") + icode + " -> " + ocode + " : " + strerror(errno); #endif if (errno == EILSEQ) { LOGDEB1("transcode:iconv: bad input seq.: shift, retry\n"); LOGDEB1(" Input consumed " << ip - in.c_str() << " output produced " << out.length()+OBSIZ-osiz << "\n"); out.append(obuf, OBSIZ - osiz); out += "?"; mecnt++; ip++;isiz--; continue; } // Normally only EINVAL is possible here: incomplete // multibyte sequence at the end. This is not fatal. Any // other is supposedly impossible, we return an error if (errno == EINVAL) goto out; else goto error; } out.append(obuf, OBSIZ - osiz); } #ifndef ICONV_CACHE_OPEN icopen = false; if(iconv_close(ic) == -1) { out.erase(); out = string("iconv_close failed for ") + icode + " -> " + ocode; goto error; } #endif out: ret = true; error: if (icopen) { #ifndef ICONV_CACHE_OPEN iconv_close(ic); #else // Just reset conversion iconv(ic, nullptr, nullptr, nullptr, nullptr); #endif } if (mecnt) LOGDEB("transcode: [" << icode << "]->[" << ocode << "] " << mecnt << " errors\n"); if (ecnt) *ecnt = mecnt; return ret; } recoll-1.36.1/utils/fileudi.cpp0000644000175000017500000000562214410615043013273 00000000000000/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include "fileudi.h" #include "md5.h" #include "base64.h" using std::string; // Size of the hashed result (base64 of 16 bytes of md5, minus 2 pad chars) #define HASHLEN 22 // Convert longish paths by truncating and appending hash of path // The full length of the base64-encoded (minus pad) of the md5 is 22 chars // We append this to the truncated path void pathHash(const std::string &path, std::string &phash, unsigned int maxlen) { if (maxlen < HASHLEN) { std::cerr << "pathHash: internal error: requested len too small\n"; abort(); } if (path.length() <= maxlen) { phash = path; return; } // Compute the md5 unsigned char chash[16]; MD5_CTX ctx; MD5Init(&ctx); MD5Update(&ctx, (const unsigned char *)(path.c_str()+maxlen-HASHLEN), path.length() - (maxlen - HASHLEN)); MD5Final(chash, &ctx); // Encode it to ascii. This shouldn't be strictly necessary as // xapian terms can be binary string hash; base64_encode(string((char *)chash, 16), hash); // We happen to know there will be 2 pad chars in there, that we // don't need as this won't ever be decoded. Resulting length is 22 hash.resize(hash.length() - 2); // Truncate path and append hash phash = path.substr(0, maxlen - HASHLEN) + hash; } // Maximum length for path/unique terms stored for each document. We truncate // longer paths and uniquize them by appending a hashed value. This // is done to avoid xapian max term length limitations, not // to gain space (we gain very little even with very short maxlens // like 30). The xapian max key length is 245. // The value for PATHHASHLEN includes the length of the hash part. #define PATHHASHLEN 150 // Compute the unique term used to link documents to their file-system source: // Hashed path + possible internal path void make_udi(const string& fn, const string& ipath, string &udi) { string s(fn); // Note that we append a "|" in all cases. Historical, could be removed s.append("|"); s.append(ipath); pathHash(s, udi, PATHHASHLEN); return; } recoll-1.36.1/utils/utf8iter.h0000644000175000017500000002367014426501047013101 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _UTF8ITER_H_INCLUDED_ #define _UTF8ITER_H_INCLUDED_ #ifdef UTF8ITER_CHECK #include "assert.h" #endif #include #include /** * A small helper class to iterate over utf8 strings. This is not an * STL iterator and does not much error checking. It is designed purely * for recoll usage, where the utf-8 string comes out of iconv in most cases * and is assumed legal. We just try to catch cases where there would be * a risk of crash. */ class Utf8Iter { public: explicit Utf8Iter(const std::string &in) : m_sp(&in) { update_cl(); } const std::string& buffer() const { return *m_sp; } void rewind() { m_cl = 0; m_pos = 0; m_charpos = 0; update_cl(); } void retryfurther() { if (eof()) return; m_pos++; if (eof()) { return; } update_cl(); } /** "Direct" access. Awfully inefficient as we skip from start or current * position at best. This can only be useful for a lookahead from the * current position */ uint32_t operator[](std::string::size_type charpos) const { std::string::size_type mypos = 0; unsigned int mycp = 0; if (charpos >= m_charpos) { mypos = m_pos; mycp = m_charpos; } int l; while (mypos < m_sp->length() && mycp != charpos) { l = get_cl(mypos); if (l <= 0 || !poslok(mypos, l) || !checkvalidat(mypos, l)) return uint32_t(-1); mypos += l; ++mycp; } if (mypos < m_sp->length() && mycp == charpos) { l = get_cl(mypos); if (poslok(mypos, l) && checkvalidat(mypos, l)) return getvalueat(mypos, l); } return uint32_t(-1); } /** Increment current position to next utf-8 char */ std::string::size_type operator++(int) { // Note: m_cl may be zero at eof if user's test not right // this shouldn't crash the program until actual data access #ifdef UTF8ITER_CHECK assert(m_cl != 0); #endif if (m_cl == 0) return std::string::npos; m_pos += m_cl; m_charpos++; update_cl(); return m_pos; } /** operator* returns the ucs4 value as a machine integer*/ uint32_t operator*() { #ifdef UTF8ITER_CHECK assert(m_cl > 0); #endif return m_cl == 0 ? uint32_t(-1) : getvalueat(m_pos, m_cl); } /** Append current utf-8 possibly multi-byte character to string param. This needs to be fast. No error checking. */ unsigned int appendchartostring(std::string &out) const { #ifdef UTF8ITER_CHECK assert(m_cl != 0); #endif out.append(&(*m_sp)[m_pos], m_cl); return m_cl; } /** Return current character as string */ explicit operator std::string() { #ifdef UTF8ITER_CHECK assert(m_cl != 0); #endif return m_cl > 0 ? m_sp->substr(m_pos, m_cl) : std::string(); } bool eof() const { return m_pos == m_sp->length(); } bool error() const { return m_cl == 0; } /** Return current byte offset in input string */ std::string::size_type getBpos() const { return m_pos; } /** Return current character length */ std::string::size_type getBlen() const { return m_cl; } /** Return current unicode character offset in input string */ std::string::size_type getCpos() const { return m_charpos; } private: // String we're working with const std::string* m_sp; // Character length at current position. A value of zero indicates // an error. unsigned int m_cl{0}; // Current byte offset in string. std::string::size_type m_pos{0}; // Current character position unsigned int m_charpos{0}; // Check position and cl against string length bool poslok(std::string::size_type p, int l) const { return p != std::string::npos && l > 0 && p + l <= m_sp->length(); } // Update current char length in object state, check // for errors inline void update_cl() { m_cl = 0; if (m_pos >= m_sp->length()) return; m_cl = get_cl(m_pos); if (!poslok(m_pos, m_cl)) { // Used to set eof here for safety, but this is bad because it // basically prevents the caller to discriminate error and eof. // m_pos = m_sp->length(); m_cl = 0; return; } if (!checkvalidat(m_pos, m_cl)) { m_cl = 0; } } inline bool checkvalidat(std::string::size_type p, int l) const { switch (l) { case 1: return uint8_t((*m_sp)[p]) < 128; case 2: return uint8_t((*m_sp)[p] & 224) == 192 && uint8_t((*m_sp)[p+1] & 192) == 128; case 3: return uint8_t((*m_sp)[p] & 240) == 224 && uint8_t((*m_sp)[p+1] & 192) == 128 && uint8_t((*m_sp)[p+2] & 192) == 128 ; case 4: return uint8_t((*m_sp)[p] & 248) == 240 && uint8_t((*m_sp)[p+1] & 192) == 128 && uint8_t((*m_sp)[p+2] & 192) == 128 && uint8_t((*m_sp)[p+3] & 192) == 128 ; default: return false; } } // Get character byte length at specified position. Returns 0 for error. inline int get_cl(std::string::size_type p) const { unsigned int z = uint8_t((*m_sp)[p]); if (z <= 127) { return 1; } else if ((z & 224) == 192) { return 2; } else if ((z & 240) == 224) { return 3; } else if ((z & 248) == 240) { return 4; } #ifdef UTF8ITER_CHECK assert(z <= 127 || (z & 224) == 192 || (z & 240) == 224 || (z & 248) == 240); #endif return 0; } // Compute value at given position. No error checking. inline unsigned int getvalueat(std::string::size_type p, int l) const { switch (l) { case 1: #ifdef UTF8ITER_CHECK assert((unsigned char)(*m_sp)[p] < 128); #endif return uint8_t((*m_sp)[p]); case 2: #ifdef UTF8ITER_CHECK assert( uint8_t((*m_sp)[p] & 224) == 192 && ((unsigned char)(*m_sp)[p+1] & 192) == 128 ); #endif return uint8_t((*m_sp)[p] - 192) * 64 + uint8_t((*m_sp)[p+1] - 128); case 3: #ifdef UTF8ITER_CHECK assert( (((unsigned char)(*m_sp)[p]) & 240) == 224 && (((unsigned char)(*m_sp)[p+1]) & 192) == 128 && (((unsigned char)(*m_sp)[p+2]) & 192) == 128 ); #endif return uint8_t((*m_sp)[p] - 224) * 4096 + uint8_t((*m_sp)[p+1] - 128) * 64 + uint8_t((*m_sp)[p+2] - 128); case 4: #ifdef UTF8ITER_CHECK assert( uint8_t((*m_sp)[p] & 248) == 240 && uint8_t((*m_sp)[p+1] & 192) == 128 && uint8_t((*m_sp)[p+2] & 192) == 128 && uint8_t((*m_sp)[p+3] & 192) == 128 ); #endif return uint8_t((*m_sp)[p]-240)*262144 + uint8_t((*m_sp)[p+1]-128)*4096 + uint8_t((*m_sp)[p+2]-128)*64 + uint8_t((*m_sp)[p+3]-128); default: #ifdef UTF8ITER_CHECK assert(l <= 4); #endif return uint32_t(-1); } } }; enum Utf8TruncateFlag {UTF8T_NONE, UTF8T_ATWORD, UTF8T_ELLIPSIS}; /** Truncate utf8 string, maintaining encoding integrity * @param s input string to be modified in place * @param maxlen maximum size after truncation in bytes * @param flags Specify cutting at word position, adding an ellipsis */ void utf8truncate(std::string& s, int maxlen, int flags = 0, const std::string& ellipsis = "...", const std::string& ws = " \t\n\r"); /** Compute length in characters of utf-8 string */ size_t utf8len(const std::string& s); /** Return number of bytes for Unicode character */ inline int utf8codepointsize(uint32_t codepoint) { if (codepoint <= 0x7F) { return 1; } else if (codepoint <= 0x7FF) { return 2; } else if (codepoint < 0xFFFF) { return 3; } else { return 4; } } /** @brief Check and possibly fix string by replacing badly encoded * characters with the standard question mark replacement character. * * @param in the string to check * @param[out] if fixit is true, the fixed output string * @param fixit if true, copy a fixed string to out * @param maxrepl maximum replacements before we bail out * @return -1 for failure (fixit false or maxrepl reached). * 0 or positive: replacement count. */ int utf8check( const std::string& in, bool fixit=false, std::string* out = nullptr, int maxrepl=100); #endif /* _UTF8ITER_H_INCLUDED_ */ recoll-1.36.1/utils/log.h0000644000175000017500000001775414410615043012111 00000000000000/* Copyright (C) 2014-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _LOG_H_X_INCLUDED_ #define _LOG_H_X_INCLUDED_ #include #include #include #include #ifndef LOGGER_THREADSAFE #define LOGGER_THREADSAFE 1 #endif #if LOGGER_THREADSAFE #include #endif // Can't use the symbolic Logger::LLXX names in preproc. 6 is LLDEB1 // STATICVERBOSITY is the level above which logging statements are // preproc'ed out (can't be dynamically turned on). #ifndef LOGGER_STATICVERBOSITY #define LOGGER_STATICVERBOSITY 5 #endif #define LOGGER_DATESIZE 100 /** @brief This is a singleton class. The logger pointer is obtained * when needed by calls to @ref getTheLog(), only the first of which * actually creates the object and initializes the output. */ class Logger { public: /** Initialize logging to file name. Use "stderr" for stderr * output. Creates the singleton logger object. Only the first * call changes the state, further ones just return the Logger * pointer. */ static Logger *getTheLog(const std::string& fn = std::string()); /** Close and reopen the output file. For rotating the log: rename * then reopen. */ bool reopen(const std::string& fn); /** Retrieve the output stream in case you need to write directly * to it. In a multithreaded program, you probably also need to obtain * the mutex with @ref getmutex, and lock it. */ std::ostream& getstream() { return m_tocerr ? std::cerr : m_stream; } /** @brief Log level values. Messages at level above the current will * not be printed. Messages at a level above * LOGGER_STATICVERBOSITY will not even be compiled in. */ enum LogLevel {LLNON=0, LLFAT=1, LLERR=2, LLINF=3, LLDEB=4, LLDEB0=5, LLDEB1=6, LLDEB2=7}; /** @brief Set the log dynamic verbosity level */ void setLogLevel(LogLevel level) { m_loglevel = level; } /** @brief Set the log dynamic verbosity level */ void setloglevel(LogLevel level) { m_loglevel = level; } /** @brief Retrieve the current log level */ int getloglevel() const { return m_loglevel; } /** @brief Retrieve current log file name */ const std::string& getlogfilename() const { return m_fn; } /** @brief Logging to stderr ? */ bool logisstderr() const { return m_tocerr; } /** @brief turn date logging on or off (default is off) */ void logthedate(bool onoff) { m_logdate = onoff; } bool loggingdate() const { return m_logdate; } /** @brief Set the date format, as an strftime() format string. * Default: "%Y%m%d-%H%M%S" . */ void setdateformat(const std::string fmt) { m_datefmt = fmt; } /** Call with log locked */ const char *datestring(); #if LOGGER_THREADSAFE std::recursive_mutex& getmutex() { return m_mutex; } #endif private: bool m_tocerr{false}; bool m_logdate{false}; int m_loglevel{LLERR}; std::string m_datefmt{"%Y%m%d-%H%M%S"}; std::string m_fn; std::ofstream m_stream; #if LOGGER_THREADSAFE std::recursive_mutex m_mutex; #endif char m_datebuf[LOGGER_DATESIZE]; Logger(const std::string& fn); Logger(const Logger &); Logger& operator=(const Logger &); }; #define LOGGER_PRT (Logger::getTheLog()->getstream()) #if LOGGER_THREADSAFE #define LOGGER_LOCK \ std::unique_lock lock(Logger::getTheLog()->getmutex()) #else #define LOGGER_LOCK #endif #ifndef LOGGER_LOCAL_LOGINC #define LOGGER_LOCAL_LOGINC 0 #endif #define LOGGER_LEVEL (Logger::getTheLog()->getloglevel() + \ LOGGER_LOCAL_LOGINC) #define LOGGER_DATE (Logger::getTheLog()->loggingdate() ? \ Logger::getTheLog()->datestring() : "") #define LOGGER_DOLOG(L,X) LOGGER_PRT << LOGGER_DATE << ":" << L << ":" << \ __FILE__ << ":" << __LINE__ << "::" << X \ << std::flush #if LOGGER_STATICVERBOSITY >= 7 #define LOGDEB2(X) { \ if (LOGGER_LEVEL >= Logger::LLDEB2) { \ LOGGER_LOCK; \ LOGGER_DOLOG(Logger::LLDEB2, X); \ } \ } #else #define LOGDEB2(X) #endif #if LOGGER_STATICVERBOSITY >= 6 #define LOGDEB1(X) { \ if (LOGGER_LEVEL >= Logger::LLDEB1) { \ LOGGER_LOCK; \ LOGGER_DOLOG(Logger::LLDEB1, X); \ } \ } #else #define LOGDEB1(X) #endif #if LOGGER_STATICVERBOSITY >= 5 #define LOGDEB0(X) { \ if (LOGGER_LEVEL >= Logger::LLDEB0) { \ LOGGER_LOCK; \ LOGGER_DOLOG(Logger::LLDEB0, X); \ } \ } #else #define LOGDEB0(X) #endif #if LOGGER_STATICVERBOSITY >= 4 #define LOGDEB(X) { \ if (LOGGER_LEVEL >= Logger::LLDEB) { \ LOGGER_LOCK; \ LOGGER_DOLOG(Logger::LLDEB, X); \ } \ } #else #define LOGDEB(X) #endif #if LOGGER_STATICVERBOSITY >= 3 /** Log a message at level INFO. Other macros exist for other levels (LOGFAT, * LOGERR, LOGINF, LOGDEB, LOGDEB0... Use as: * LOGINF("some text" << other stuff << ... << "\n"); */ #define LOGINF(X) { \ if (LOGGER_LEVEL >= Logger::LLINF) { \ LOGGER_LOCK; \ LOGGER_DOLOG(Logger::LLINF, X); \ } \ } #else #define LOGINF(X) #endif #define LOGINFO LOGINF #if LOGGER_STATICVERBOSITY >= 2 #define LOGERR(X) { \ if (LOGGER_LEVEL >= Logger::LLERR) { \ LOGGER_LOCK; \ LOGGER_DOLOG(Logger::LLERR, X); \ } \ } #else #define LOGERR(X) #endif #if LOGGER_STATICVERBOSITY >= 1 #define LOGFAT(X) { \ if (LOGGER_LEVEL >= Logger::LLFAT) { \ LOGGER_LOCK; \ LOGGER_DOLOG(Logger::LLFAT, X); \ } \ } #else #define LOGFAT(X) #endif #define LOGFATAL LOGFAT #if defined(sun) || defined(_WIN32) #define LOGSYSERR(who, what, arg) { \ LOGERR(who << ": " << what << "(" << arg << "): errno " << errno << \ ": " << strerror(errno) << std::endl); \ } #else // not WINDOWS or sun inline char *_log_check_strerror_r(int, char *errbuf) {return errbuf;} inline char *_log_check_strerror_r(char *cp, char *){return cp;} #define LOGSYSERR(who, what, arg) { \ char buf[200]; buf[0] = 0; \ LOGERR(who << ": " << what << "(" << arg << "): errno " << errno << \ ": " << _log_check_strerror_r( \ strerror_r(errno, buf, 200), buf) << std::endl); \ } #endif // not windows #endif /* _LOG_H_X_INCLUDED_ */ recoll-1.36.1/utils/execmd.h0000644000175000017500000002675314471606655012614 00000000000000/* Copyright (C) 2004-2018 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _EXECMD_H_INCLUDED_ #define _EXECMD_H_INCLUDED_ #include #include #include #include #ifdef _MSC_VER typedef int pid_t; #endif /** * Callback function object to advise of new data arrival, or just periodic * heartbeat if cnt is 0. * * To interrupt the command, the code using ExecCmd should either * raise an exception inside newData() (and catch it in doexec's caller), or * call ExecCmd::setKill() * */ class ExecCmdAdvise { public: ExecCmdAdvise() {} virtual ~ExecCmdAdvise() {} ExecCmdAdvise(const ExecCmdAdvise&) = delete; ExecCmdAdvise &operator=(const ExecCmdAdvise &) = delete; virtual void newData(int cnt) = 0; }; /** * Callback function object to get more input data. Data has to be provided * into the initial input string, set it to empty to signify eof. */ class ExecCmdProvide { public: ExecCmdProvide() {} virtual ~ExecCmdProvide() {} ExecCmdProvide(const ExecCmdProvide&) = delete; ExecCmdProvide &operator=(const ExecCmdProvide &) = delete; virtual void newData() = 0; }; /** * Execute command possibly taking both input and output (will do * asynchronous io as appropriate for things to work). * * Input to the command can be provided either once in a parameter to doexec * or provided in chunks by setting a callback which will be called to * request new data. In this case, the 'input' parameter to doexec may be * empty (but not null) * * Output from the command is normally returned in a single string, but a * callback can be set to be called whenever new data arrives, in which case * it is permissible to consume the data and erase the string. * * Note that SIGPIPE should be ignored and SIGCLD blocked when calling doexec, * else things might fail randomly. (This is not done inside the class because * of concerns with multithreaded programs). * */ class ExecCmd { public: // Use vfork instead of fork. Our vfork usage is multithread-compatible as // far as I can see, but just in case... static void useVfork(bool on); /** * Add/replace environment variable before executing command. This must * be called before doexec() to have an effect (possibly multiple * times for several variables). * @param envassign an environment assignment string ("name=value") */ void putenv(const std::string& envassign); void putenv(const std::string& name, const std::string& value); /** * Try to set a limit on child process vm size. This will use * setrlimit() and RLIMIT_AS/VMEM if available. Parameter is in * units of 2**10. Must be called before starting the command, default * is inherit from parent. */ void setrlimit_as(int mbytes); /** * Set function objects to call whenever new data is available or on * select timeout. The data itself is stored in the output string. * Must be set before calling doexec. */ void setAdvise(ExecCmdAdvise *adv); /* * Set function object to call whenever new data is needed. The * data should be stored in the input string. Must be set before * calling doexec() */ void setProvide(ExecCmdProvide *p); /** * Set select timeout in milliseconds. The default is 1 S. * This is NOT a time after which an error will occur, but the period of * the calls to the advise routine (which normally checks for cancellation). */ void setTimeout(int mS); /** * Set destination for stderr data. The default is to let it alone (will * usually go to the terminal or to wherever the desktop messages go). * There is currently no option to put stderr data into a program variable * If the parameter can't be opened for writing, the command's * stderr will be closed. */ void setStderr(const std::string& stderrFile); /** * Set kill wait timeout. This is the maximum time we'll wait for * the command after sending a SIGTERM, before sending a SIGKILL. * @param mS the maximum number of mS to wait. Note that values * below 1000 mS make no sense as the program will sleep for * longer time before retrying the waitpid(). Use -1 for * forever (bad idea), 0 for absolutely no pity. */ void setKillTimeout(int mS); /** * Execute command. * * Both input and output can be specified, and asynchronous * io (select-based) is used to prevent blocking. This will not * work if input and output need to be synchronized (ie: Q/A), but * works ok for filtering. * The function is exception-safe. In case an exception occurs in the * advise callback, fds and pids will be cleaned-up properly. * * @param cmd the program to execute. This must be an absolute file name * or exist in the PATH. * @param args the argument vector (NOT including argv[0]). * @param input Input to send TO the command. * @param output Output FROM the command. * @return the exec output status (0 if ok), or -1 */ int doexec(const std::string& cmd, const std::vector& args, const std::string *input = nullptr, std::string *output = nullptr); /** Same as doexec but cmd and args in one vector */ int doexec(const std::vector& args, const std::string *in=0, std::string *out=0) { if (args.empty()) return -1; return doexec(args[0], std::vector(args.begin() + 1, args.end()), in, out); } /* * The next methods can be used when a Q/A dialog needs to be performed with the command */ int startExec(const std::string& cmd, const std::vector& args, bool has_input, bool has_output); /** Same as startExec but cmd and args in one vector */ int startExec(const std::vector& args, bool has_input, bool has_output) { if (args.empty()) return -1; return startExec(args[0], std::vector(args.begin()+1, args.end()), has_input, has_output); } int send(const std::string& data); int receive(std::string& data, int cnt = -1); /** Read line. Will call back periodically to check for cancellation */ int getline(std::string& data); /** Read line. Timeout after timeosecs seconds */ int getline(std::string& data, int timeosecs); int wait(); /** Wait with WNOHANG set. @return true if process exited, false else. @param O: status, the wait(2) call's status value */ bool maybereap(int *status); pid_t getChildPid(); /** * Cancel/kill command. This can be called from another thread or * from the advise callback, which could also raise an exception * to accomplish the same thing. In the owner thread, any I/O loop * will exit at the next iteration, and the process will be waited for. */ void setKill(); /** * Get rid of current process (become ready for start). This will signal * politely the process to stop, wait a moment, then terminate it. This * is a blocking call. */ void zapChild(); /** * Request process termination (SIGTERM or equivalent). This returns * immediately */ bool requestChildExit(); enum ExFlags {EXF_NONE, // Windows only: used when starting a viewer. The default is to hide the window, // because it avoids windows appearing and disappearing when executing stuff for // previewing. EXF_SHOWWINDOW = 0x1, // Windows only: show maximized EXF_MAXIMIZED = 0x2, EXF_NOSETPG = 0x4, }; ExecCmd(int flags = 0); ~ExecCmd(); ExecCmd(const ExecCmd&) = delete; ExecCmd &operator=(const ExecCmd &) = delete; /** * Utility routine: check if/where a command is found according to the * current PATH (or the specified one * @param cmd command name * @param exe on return, executable path name if found * @param path exec seach path to use instead of getenv(PATH) * @return true if found */ static bool which(const std::string& cmd, std::string& exe, const char* path = nullptr); /** * Execute command and return stdout output in a string * @param cmd input: command and args * @param out output: what the command printed * @return true if exec status was 0 */ static bool backtick(const std::vector cmd, std::string& out); static std::string waitStatusAsString(int wstatus); class Internal; private: Internal *m; }; /** * Rexecute self process with the same arguments. * * Note that there are some limitations: * - argv[0] has to be valid: an executable name which will be found in * the path when exec is called in the initial working directory. This is * by no means guaranteed. The shells do this, but argv[0] could be an * arbitrary string. * - The initial working directory must be found and remain valid. * - We don't try to do anything with fd 0,1,2. If they were changed by the * program, their initial meaning won't be the same as at the moment of the * initial invocation. * - We don't restore the signals. Signals set to be blocked * or ignored by the program will remain ignored even if this was not their * initial state. * - The environment is also not restored. * - Others system aspects ? * - Other program state: application-dependant. Any external cleanup * (temp files etc.) must be performed by the application. ReExec() * duplicates the atexit() function to make this easier, but the * ReExec().atexit() calls must be done explicitly, this is not automatic * * In short, this is usable in reasonably controlled situations and if there * are no security issues involved, but this does not perform miracles. */ class ReExec { public: ReExec() {} ReExec(int argc, char *argv[]); // Mostly useful with Windows and wmain: args must be utf-8 ReExec(const std::vector& args); int atexit(void (*function)(void)) { m_atexitfuncs.push(function); return 0; } void reexec(); const std::string& getreason() { return m_reason; } // Insert new args into the initial argv. idx designates the place // before which the new args are inserted (the default of 1 // inserts after argv[0] which would probably be an appropriate // place for additional options) void insertArgs(const std::vector& args, int idx = 1); void removeArg(const std::string& arg); private: std::vector m_argv; std::string m_curdir; int m_cfd{-1}; std::string m_reason; std::stack m_atexitfuncs; }; #endif /* _EXECMD_H_INCLUDED_ */ recoll-1.36.1/utils/conftree.cpp0000644000175000017500000006105114520415746013467 00000000000000/* Copyright (C) 2003-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #endif #include "conftree.h" #include #if defined(BUILDING_RECOLL) || !defined(_WIN32) #include #endif /* BUILDING_RECOLL */ #include #include #include #include #include #include #include #include #include "pathut.h" #include "smallut.h" #ifdef MDU_INCLUDE_LOG #include MDU_INCLUDE_LOG #else #include "log.h" #endif #undef DEBUG_CONFTREE #ifdef DEBUG_CONFTREE #define CONFDEB LOGERR #else #define CONFDEB LOGDEB2 #endif // type/data lookups in the lines array (m_order). Case-sensitive or not depending on what we are // searching and the ConfSimple case-sensitivity flags class OrderComp { public: OrderComp(ConfLine &ln, const CaseComparator& comp) : m_cfl(ln), m_comp(comp) {} bool operator()(const ConfLine& cfl) { return cfl.m_kind == m_cfl.m_kind && !m_comp(m_cfl.m_data, cfl.m_data) && !m_comp(cfl.m_data, m_cfl.m_data); } const ConfLine& m_cfl; const CaseComparator& m_comp; }; long long ConfNull::getInt(const std::string& name, long long dflt, const std::string& sk) { std::string val; if (!get(name, val, sk)) { return dflt; } char *endptr; long long ret = strtoll(val.c_str(), &endptr, 0); if (endptr == val.c_str()) { return dflt; } return ret; } double ConfNull::getFloat(const std::string& name, double dflt, const std::string& sk) { std::string val; if (!get(name, val, sk)) { return dflt; } char *endptr; double ret = strtod(val.c_str(), &endptr); if (endptr == val.c_str()) { return dflt; } return ret; } bool ConfNull::getBool(const std::string& name, bool dflt, const std::string& sk) { std::string val; if (!get(name, val, sk)) { return dflt; } return stringToBool(val); } static const SimpleRegexp varcomment_rx("[ \t]*#[ \t]*([a-zA-Z0-9]+)[ \t]*=", 0, 1); void ConfSimple::parseinput(std::istream& input) { std::string submapkey; std::string cline; bool appending = false; std::string line; bool eof = false; for (;;) { cline.clear(); std::getline(input, cline); CONFDEB("Parse:line: [" << cline << "] status " << status << "\n"); if (!input.good()) { if (input.bad()) { CONFDEB("Parse: input.bad()\n"); status = STATUS_ERROR; return; } CONFDEB("Parse: eof\n"); // Must be eof ? But maybe we have a partial line which // must be processed. This happens if the last line before // eof ends with a backslash, or there is no final \n eof = true; } { std::string::size_type pos = cline.find_last_not_of("\n\r"); if (pos == std::string::npos) { cline.clear(); } else if (pos != cline.length() - 1) { cline.erase(pos + 1); } } if (appending) { line += cline; } else { line = cline; } // Note that we trim whitespace before checking for backslash-eol // This avoids invisible whitespace problems. if (trimvalues) { trimstring(line); } else { ltrimstring(line); } if (line.empty() || line.at(0) == '#') { if (eof) { break; } if (varcomment_rx.simpleMatch(line)) { m_order.push_back(ConfLine(ConfLine::CFL_VARCOMMENT, line, varcomment_rx.getMatch(line, 1))); } else { m_order.push_back(ConfLine(ConfLine::CFL_COMMENT, line)); } continue; } if (line[line.length() - 1] == '\\') { line.erase(line.length() - 1); appending = true; continue; } appending = false; if (line[0] == '[') { trimstring(line, "[] \t"); if (dotildexpand) { submapkey = path_tildexpand(line); } else { submapkey = line; } m_subkeys_unsorted.push_back(submapkey); m_order.push_back(ConfLine(ConfLine::CFL_SK, submapkey)); continue; } // Look for first equal sign std::string::size_type eqpos = line.find("="); if (eqpos == std::string::npos) { m_order.push_back(ConfLine(ConfLine::CFL_COMMENT, line)); continue; } // Compute name and value, trim white space std::string nm, val; nm = line.substr(0, eqpos); trimstring(nm); val = line.substr(eqpos + 1, std::string::npos); if (trimvalues) { trimstring(val); } if (nm.length() == 0) { m_order.push_back(ConfLine(ConfLine::CFL_COMMENT, line)); continue; } i_set(nm, val, submapkey, true); if (eof) { break; } } } static int varsToFlags(int readonly, bool tildexp, bool trimv) { int flags = 0; if (readonly) flags |= ConfSimple::CFSF_RO; if (tildexp) flags |= ConfSimple::CFSF_TILDEXP; if (!trimv) flags |= ConfSimple::CFSF_NOTRIMVALUES; return flags; } ConfSimple::ConfSimple(int readonly, bool tildexp, bool trimv) : ConfSimple(varsToFlags(readonly, tildexp, trimv) | CFSF_FROMSTRING, std::string()) { } void ConfSimple::reparse(const std::string& d) { clear(); std::stringstream input(d, std::ios::in); parseinput(input); } ConfSimple::ConfSimple(const std::string& d, int readonly, bool tildexp, bool trimv) : ConfSimple(varsToFlags(readonly, tildexp, trimv) | CFSF_FROMSTRING, d) { } ConfSimple::ConfSimple(const char *fname, int readonly, bool tildexp, bool trimv) : ConfSimple(varsToFlags(readonly, tildexp, trimv), std::string(fname)) { } ConfSimple::ConfSimple(int flags, const std::string& dataorfn) { m_flags = flags; status = (flags & CFSF_RO) ? STATUS_RO : STATUS_RW; dotildexpand = (flags & CFSF_TILDEXP) != 0; trimvalues = (flags & CFSF_NOTRIMVALUES) == 0; if (flags & CFSF_SUBMAPNOCASE) { m_submaps = std::map, CaseComparator>(m_nocasecomp); } CONFDEB("ConfSimple::ConfSimple: RO: " << (status==STATUS_RO) << " tildexp " << dotildexpand << " trimvalues " << trimvalues << " from string? " << bool(flags & CFSF_FROMSTRING) << " file name: " << ((flags & CFSF_FROMSTRING)?" data input " : dataorfn.c_str()) << "\n"); if (flags & CFSF_FROMSTRING) { if (!dataorfn.empty()) { std::stringstream input(dataorfn, std::ios::in); parseinput(input); } } else { m_filename = dataorfn; std::fstream input; openfile((flags & CFSF_RO), input); if (status == STATUS_ERROR) return; parseinput(input); i_changed(true); } } ConfSimple::StatusCode ConfSimple::getStatus() const { switch (status) { case STATUS_RO: return STATUS_RO; case STATUS_RW: return STATUS_RW; default: return STATUS_ERROR; } } void ConfSimple::openfile(int readonly, std::fstream& input) { int mode = readonly ? std::ios::in : std::ios::in | std::ios::out; if (!readonly && !path_exists(m_filename)) { mode |= std::ios::trunc; } path_streamopen(m_filename, mode, input); if (!input.is_open()) { LOGDEB0("ConfSimple::ConfSimple: fstream(w)(" << m_filename << ", " << mode << ") errno " << errno << "\n"); } if (!readonly && !input.is_open()) { // reset errors input.clear(); status = STATUS_RO; // open readonly path_streamopen(m_filename, std::ios::in, input); } if (!input.is_open()) { // Don't log ENOENT, this is common with some recoll config files std::string reason; catstrerror(&reason, nullptr, errno); if (errno != 2) { LOGERR("ConfSimple::ConfSimple: fstream(" << m_filename << ", " << std::ios::in << ") " << reason << "\n"); } status = STATUS_ERROR; return; } } bool ConfSimple::sourceChanged() const { if (!m_filename.empty()) { PathStat st; if (path_fileprops(m_filename, &st) == 0) { if (m_fmtime != st.pst_mtime) { return true; } } } return false; } bool ConfSimple::i_changed(bool upd) { if (!m_filename.empty()) { PathStat st; if (path_fileprops(m_filename, &st) == 0) { if (m_fmtime != st.pst_mtime) { if (upd) { m_fmtime = st.pst_mtime; } return true; } } } return false; } int ConfSimple::get(const std::string& nm, std::string& value, const std::string& sk) const { if (!ok()) { return 0; } // Find submap const auto ss = m_submaps.find(sk); if (ss == m_submaps.end()) { return 0; } // Find named value const auto s = ss->second.find(nm); if (s == ss->second.end()) { return 0; } value = s->second; return 1; } // Appropriately output a subkey (nm=="") or variable line. // We can't make any assumption about the data except that it does not // contain line breaks. // Avoid long lines if possible (for hand-editing) // We used to break at arbitrary places, but this was ennoying for // files with pure UTF-8 encoding (some files can be binary anyway), // because it made later editing difficult, as the file would no // longer have a valid encoding. // Any ASCII byte would be a safe break point for utf-8, but could // break some other encoding with, e.g. escape sequences? So break at // whitespace (is this safe with all encodings?). // Note that the choice of break point does not affect the validity of // the file data (when read back by conftree), only its ease of // editing with a normal editor. static ConfSimple::WalkerCode varprinter(void *f, const std::string& nm, const std::string& value) { std::ostream& output = *((std::ostream *)f); if (nm.empty()) { output << "\n[" << value << "]\n"; } else { output << nm << " = "; if (nm.length() + value.length() < 75) { output << value; } else { std::string::size_type ll = 0; for (std::string::size_type pos = 0; pos < value.length(); pos++) { std::string::value_type c = value[pos]; output << c; ll++; // Break at whitespace if line too long and "a lot" of // remaining data if (ll > 50 && (value.length() - pos) > 10 && (c == ' ' || c == '\t')) { ll = 0; output << "\\\n"; } } } output << "\n"; } return ConfSimple::WALK_CONTINUE; } // Set variable and rewrite data int ConfSimple::set(const std::string& nm, const std::string& value, const std::string& sk) { if (status != STATUS_RW) { return 0; } CONFDEB("ConfSimple::set ["< [" << value << "]\n"); if (!i_set(nm, value, sk)) { return 0; } return write(); } int ConfSimple::set(const std::string& nm, long long val, const std::string& sk) { return this->set(nm, lltodecstr(val), sk); } // Internal set variable: no rw checking or file rewriting. If init is // set, we're doing initial parsing, else we are changing a parsed // tree (changes the way we update the order data) int ConfSimple::i_set(const std::string& nm, const std::string& value, const std::string& sk, bool init) { CONFDEB("ConfSimple::i_set: nm[" << nm << "] val[" << value << "] key[" << sk << "], init " << init << "\n"); // Values must not have embedded newlines if (value.find_first_of("\n\r") != std::string::npos) { CONFDEB("ConfSimple::i_set: LF in value\n"); return 0; } bool existing = false; auto ss = m_submaps.find(sk); // Test if submap already exists, else create it, and insert variable: if (ss == m_submaps.end()) { CONFDEB("ConfSimple::i_set: new submap\n"); std::map submap( (m_flags & CFSF_KEYNOCASE) ? m_nocasecomp : m_casecomp); submap[nm] = value; m_submaps[sk] = submap; // Maybe add sk entry to m_order data, if not already there (see comment below). if (!sk.empty()) { ConfLine nl(ConfLine::CFL_SK, sk); // Append SK entry only if it's not already there (erase // does not remove entries from the order data, and it may // be being recreated after deletion) OrderComp cmp(nl, (m_flags & CFSF_SUBMAPNOCASE) ? m_nocasecomp : m_casecomp); if (find_if(m_order.begin(), m_order.end(), cmp) == m_order.end()) { m_order.push_back(nl); } } } else { // Insert or update variable in existing map. auto it = ss->second.find(nm); if (it == ss->second.end()) { ss->second.insert(std::pair(nm, value)); } else { it->second = value; existing = true; } } // If the variable already existed, no need to change the m_order data if (existing) { CONFDEB("ConfSimple::i_set: existing var: no order update\n"); return 1; } // Add the new variable at the end of its submap in the order data. if (init) { // During the initial construction, just append: CONFDEB("ConfSimple::i_set: init true: append\n"); m_order.push_back(ConfLine(ConfLine::CFL_VAR, nm)); m_order.back().m_value = value; return 1; } // Look for the start and end of the subkey zone. Start is either // at begin() for a null subkey, or just behind the subkey // entry. End is either the next subkey entry, or the end of // list. We insert the new entry just before end. std::vector::iterator start, fin; if (sk.empty()) { start = m_order.begin(); CONFDEB("ConfSimple::i_set: null sk, start at top of order\n"); } else { ConfLine cfl(ConfLine::CFL_SK, sk); OrderComp cmp(cfl, (m_flags & CFSF_SUBMAPNOCASE) ? m_nocasecomp : m_casecomp); start = find_if(m_order.begin(), m_order.end(), cmp); if (start == m_order.end()) { // This is not logically possible. The subkey must exist. We're doomed std::cerr << "Logical failure during configuration variable insertion" << "\n"; abort(); } } fin = m_order.end(); if (start != m_order.end()) { // The null subkey has no entry (maybe it should) if (!sk.empty()) { start++; } for (std::vector::iterator it = start; it != m_order.end(); it++) { if (it->m_kind == ConfLine::CFL_SK) { fin = it; break; } } } // It may happen that the order entry already exists because erase doesnt // update m_order ConfLine cfl(ConfLine::CFL_VAR, nm); OrderComp cmp(cfl, (m_flags & CFSF_KEYNOCASE) ? m_nocasecomp : m_casecomp); if (find_if(start, fin, cmp) == fin) { // Look for a varcomment line, insert the value right after if // it's there. bool inserted(false); std::vector::iterator it; for (it = start; it != fin; it++) { if (it->m_kind == ConfLine::CFL_VARCOMMENT && it->m_aux == nm) { it++; m_order.insert(it, ConfLine(ConfLine::CFL_VAR, nm)); inserted = true; break; } } if (!inserted) { m_order.insert(fin, ConfLine(ConfLine::CFL_VAR, nm)); } } return 1; } int ConfSimple::erase(const std::string& nm, const std::string& sk) { if (status != STATUS_RW) { return 0; } auto ss = m_submaps.find(sk); if (ss == m_submaps.end()) { return 0; } ss->second.erase(nm); if (ss->second.empty()) { m_submaps.erase(ss); } return write(); } int ConfSimple::eraseKey(const std::string& sk) { std::vector nms = getNames(sk); for (const auto& nm : nms) { erase(nm, sk); } return write(); } int ConfSimple::clear() { m_submaps.clear(); m_order.clear(); return write(); } // Walk the tree, calling user function at each node ConfSimple::WalkerCode ConfSimple::sortwalk(WalkerCode(*walker)(void *, const std::string&, const std::string&), void *clidata) const { if (!ok()) { return WALK_STOP; } // For all submaps: for (const auto& submap : m_submaps) { // Possibly emit submap name: if (!submap.first.empty() && walker(clidata, std::string(), submap.first.c_str()) == WALK_STOP) { return WALK_STOP; } // Walk submap for (const auto& item : submap.second) { if (walker(clidata, item.first, item.second) == WALK_STOP) { return WALK_STOP; } } } return WALK_CONTINUE; } // Write to default output. This currently only does something if output is // a file bool ConfSimple::write() { if (!ok()) { return false; } if (m_holdWrites) { return true; } if (!m_filename.empty()) { std::fstream output; path_streamopen(m_filename, std::ios::out | std::ios::trunc, output); if (!output.is_open()) { return 0; } return this->write(output); } else { // No backing store, no writing. Maybe one day we'll need it with // some kind of output string. This can't be the original string which // is currently readonly. //ostringstream output(m_ostring, ios::out | std::ios::trunc); return 1; } } bool ConfSimple::content_write(std::ostream& out) const { return sortwalk(varprinter, &out) == WALK_CONTINUE ? true : false; } // Write out the tree in configuration file format: // This does not check holdWrites, this is done by write(void), which // lets this work even when holdWrites is set bool ConfSimple::write(std::ostream& out) const { if (!ok()) { return false; } if (m_order.empty()) { // Then we have no presentation data. Just output the values and subkeys content_write(out); } std::string sk; for (const auto& confline : m_order) { switch (confline.m_kind) { case ConfLine::CFL_COMMENT: case ConfLine::CFL_VARCOMMENT: out << confline.m_data << "\n"; if (!out.good()) { return false; } break; case ConfLine::CFL_SK: sk = confline.m_data; CONFDEB("ConfSimple::write: SK [" << sk << "]\n"); // Check that the submap still exists, and only output it if it // does if (m_submaps.find(sk) != m_submaps.end()) { out << "[" << confline.m_data << "]" << "\n"; if (!out.good()) { return false; } } break; case ConfLine::CFL_VAR: std::string nm = confline.m_data; CONFDEB("ConfSimple::write: VAR [" << nm << "], sk [" < ConfSimple::getNames(const std::string& sk, const char *pattern) const { std::vector mylist; if (!ok()) { return mylist; } const auto ss = m_submaps.find(sk); if (ss == m_submaps.end()) { return mylist; } mylist.reserve(ss->second.size()); for (const auto& item : ss->second) { if (pattern && #if defined(BUILDING_RECOLL) || !defined(_WIN32) 0 != fnmatch(pattern, item.first.c_str(), 0) #else /* Default to no match: yields easier to spot errors */ 1 #endif ) { continue; } mylist.push_back(item.first); } return mylist; } std::vector ConfSimple::getSubKeys() const { std::vector mylist; if (!ok()) { return mylist; } mylist.reserve(m_submaps.size()); for (const auto& submap : m_submaps) { mylist.push_back(submap.first); } return mylist; } bool ConfSimple::hasNameAnywhere(const std::string& nm) const { std::vectorkeys = getSubKeys(); for (const auto& key : keys) { std::string val; if (get(nm, val, key)) { return true; } } return false; } bool ConfSimple::commentsAsXML(std::ostream& out) { const std::vector& lines = getlines(); out << "\n"; for (const auto& line : lines) { switch (line.m_kind) { case ConfLine::CFL_COMMENT: case ConfLine::CFL_VARCOMMENT: { std::string::size_type pos = line.m_data.find_first_not_of("# "); if (pos != std::string::npos) { out << line.m_data.substr(pos) << "\n"; } else { out << "\n"; } break; } case ConfLine::CFL_SK: out << "" << line.m_data << "" << "\n"; break; case ConfLine::CFL_VAR: out << "" << line.m_data << " = " << line.m_value << "" << "\n"; break; default: break; } } out << "\n"; return true; } // ////////////////////////////////////////////////////////////////////////// // ConfTree Methods: conftree interpret keys like a hierarchical file tree // ////////////////////////////////////////////////////////////////////////// int ConfTree::get(const std::string& name, std::string& value, const std::string& sk) const { if (sk.empty() || !path_isabsolute(sk)) { CONFDEB("ConfTree::get: looking in global space for [" << sk << "]\n"); return ConfSimple::get(name, value, sk); } // Get writable copy of subkey path std::string msk = sk; // Handle the case where the config file path has an ending / and not // the input sk path_catslash(msk); // Look in subkey and up its parents until root ('') for (;;) { CONFDEB("ConfTree::get: looking for [" << name << "] in [" << msk << "]\n"); if (ConfSimple::get(name, value, msk)) { return 1; } std::string::size_type pos = msk.rfind("/"); if (pos != std::string::npos) { msk.replace(pos, std::string::npos, std::string()); } else { #ifdef _WIN32 if (msk.size() == 2 && isalpha(msk[0]) && msk[1] == ':') { msk.clear(); } else #endif break; } } return 0; } recoll-1.36.1/utils/transcode.h0000644000175000017500000000257714426500174013314 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _TRANSCODE_H_INCLUDED_ #define _TRANSCODE_H_INCLUDED_ #include /** * c++ized interface to iconv * * @param in input string * @param out output string * @param icode input encoding * @param ocode input encoding * @param ecnt (output) number of transcoding errors * @return true if transcoding succeeded, even with errors. False for global * errors like unknown charset names */ extern bool transcode(const std::string &in, std::string &out, const std::string &icode, const std::string &ocode, int *ecnt = nullptr); #endif /* _TRANSCODE_H_INCLUDED_ */ recoll-1.36.1/utils/md5ut.cpp0000644000175000017500000000335414410615043012710 00000000000000/* Copyright (C) 2015 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include "md5ut.h" #include "readfile.h" using namespace std; // Quite incredibly if this class is named FileScanMd5 like the // different one in readfile.cpp, the vtables get mixed up and mh_xslt // crashes while calling a virtual function (gcc 6.3 and 7.3) class FileScanMd5loc : public FileScanDo { public: FileScanMd5loc(string& d) : digest(d) {} virtual bool init(int64_t, string *) { MD5Init(&ctx); return true; } virtual bool data(const char *buf, int cnt, string*) { MD5Update(&ctx, (const unsigned char*)buf, cnt); return true; } string &digest; MD5_CTX ctx; }; bool MD5File(const string& filename, string &digest, string *reason) { FileScanMd5loc md5er(digest); if (!file_scan(filename, &md5er, reason)) return false; // We happen to know that digest and md5er.digest are the same object MD5Final(md5er.digest, &md5er.ctx); return true; } recoll-1.36.1/utils/cpuconf.cpp0000644000175000017500000000235314410615043013305 00000000000000/* Copyright (C) 2013-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "cpuconf.h" #include // Go c++11 ! bool getCpuConf(CpuConf& cpus) { #if defined(_WIN32) // On windows, indexing is actually twice slower with threads // enabled + there is a bug and the process does not exit at the // end of indexing. Until these are solved, pretend there is only // 1 cpu cpus.ncpus = 1; #else // c++11 cpus.ncpus = std::thread::hardware_concurrency(); #endif return true; } recoll-1.36.1/utils/md5.cpp0000644000175000017500000002416314410615043012340 00000000000000/* $OpenBSD: md5.c,v 1.4 2014/12/28 10:04:35 tedu Exp $ */ /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #endif #include "md5.h" #include #include namespace MedocUtils { #define PUT_BIT_LE(i, cp, value) do { \ (cp)[i] = uint8_t(((value) >> 8 * i) & 0xFF); \ } while (0) #define PUT_64BIT_LE(cp, value) do { \ PUT_BIT_LE(7, cp, value); \ PUT_BIT_LE(6, cp, value); \ PUT_BIT_LE(5, cp, value); \ PUT_BIT_LE(4, cp, value); \ PUT_BIT_LE(3, cp, value); \ PUT_BIT_LE(2, cp, value); \ PUT_BIT_LE(1, cp, value); \ PUT_BIT_LE(0, cp, value); \ } while (0) #define PUT_32BIT_LE(cp, value) do { \ PUT_BIT_LE(3, cp, value); \ PUT_BIT_LE(2, cp, value); \ PUT_BIT_LE(1, cp, value); \ PUT_BIT_LE(0, cp, value); \ } while (0) static constexpr uint8_t PADDING[MD5_BLOCK_LENGTH] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(MD5_CTX *ctx) { ctx->count = 0; ctx->state[0] = 0x67452301; ctx->state[1] = 0xefcdab89; ctx->state[2] = 0x98badcfe; ctx->state[3] = 0x10325476; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(MD5_CTX *ctx, const void *inputptr, size_t len) { auto input = static_cast(inputptr); size_t have, need; /* Check how many bytes we already have and how many more we need. */ have = static_cast((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); need = MD5_BLOCK_LENGTH - have; /* Update bitcount */ ctx->count += static_cast(len) << 3; if (len >= need) { if (have != 0) { memcpy(ctx->buffer + have, input, need); MD5Transform(ctx->state, ctx->buffer); input += need; len -= need; have = 0; } /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ while (len >= MD5_BLOCK_LENGTH) { MD5Transform(ctx->state, input); input += MD5_BLOCK_LENGTH; len -= MD5_BLOCK_LENGTH; } } /* Handle any remaining bytes of data. */ if (len != 0) memcpy(ctx->buffer + have, input, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) { uint8_t count[8]; size_t padlen; int i; /* Convert count to 8 bytes in little endian order. */ PUT_64BIT_LE(count, ctx->count); /* Pad out to 56 mod 64. */ padlen = MD5_BLOCK_LENGTH - ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); if (padlen < 1 + 8) padlen += MD5_BLOCK_LENGTH; MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ MD5Update(ctx, count, 8); for (i = 0; i < 4; i++) PUT_32BIT_LE(digest + i * 4, ctx->state[i]); memset(ctx, 0, sizeof(*ctx)); /* in case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ void MD5Transform(uint32_t state[4], const uint8_t block[MD5_BLOCK_LENGTH]) { uint32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; #if BYTE_ORDER == LITTLE_ENDIAN memcpy(in, block, sizeof(in)); #else for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { in[a] = (uint32_t)( (uint32_t)(block[a * 4 + 0]) | (uint32_t)(block[a * 4 + 1]) << 8 | (uint32_t)(block[a * 4 + 2]) << 16 | (uint32_t)(block[a * 4 + 3]) << 24); } #endif a = state[0]; b = state[1]; c = state[2]; d = state[3]; MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21); state[0] += a; state[1] += b; state[2] += c; state[3] += d; } // C++ utilities using std::string; void MD5Final(string &digest, MD5_CTX *context) { unsigned char d[16]; MD5Final (d, context); digest.assign(reinterpret_cast(d), 16); } string& MD5String(const string& data, string& digest) { MD5_CTX ctx; MD5Init(&ctx); MD5Update(&ctx, reinterpret_cast(data.c_str()), data.length()); MD5Final(digest, &ctx); return digest; } string& MD5HexPrint(const string& digest, string &out) { out.erase(); out.reserve(33); static const char hex[]="0123456789abcdef"; auto hash = reinterpret_cast(digest.c_str()); for (int i = 0; i < 16; i++) { out.append(1, hex[hash[i] >> 4]); out.append(1, hex[hash[i] & 0x0f]); } return out; } std::string MD5Hex(const std::string& data) { std::string digest, out; MD5String(data, digest); MD5HexPrint(digest, out); return out; } string& MD5HexScan(const string& xdigest, string& digest) { digest.erase(); if (xdigest.length() != 32) { return digest; } for (unsigned int i = 0; i < 16; i++) { unsigned int val; if (sscanf(xdigest.c_str() + 2*i, "%2x", &val) != 1) { digest.erase(); return digest; } digest.append(1, static_cast(val)); } return digest; } } // End namespace MedocUtils recoll-1.36.1/utils/ecrontab.h0000644000175000017500000000554714444307651013134 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _ECRONTAB_H_INCLUDED_ #define _ECRONTAB_H_INCLUDED_ /** Utility function to manage lines inside a user crontab * * Lines managed by this routine are marked with a hopefully unique marker * and discriminated by a selector, both environment variable settings. * Example: * 30 8 * * * RCLCRONTAB_RCLINDEX= RECOLL_CONFDIR=/path/to/dir recollindex ... * RCLCRONTAB_RCLINDEX is the line marker, and the RECOLL_CONFDIR value * allows selecting the affected line. * * This approach allows leaving alone lines which do have a * RECOLL_CONFDIR value but not managed by us. The marker and selector * values are chosen by the caller, which should apply some thought to * choosing sane values. */ #include #include /** Add, replace or delete a command inside a crontab file * * @param marker selects lines managed by this module and should take the form * of a (possibly empty) environment variable assignment. * @param id selects the appropriate line to affect and will usually be an * actual variable assignment (see above) * @param sched is a standard cron schedule spec (ie: 30 8 * * *) * @param cmd is the command to execute (the last part of the line). * Set it to an empty string to delete the line from the crontab * @param reason error message * * "marker" and "id" should look like reasonable env variable assignments. * Only ascii capital letters, numbers and _ before the '=' */ bool editCrontab(const std::string& marker, const std::string& id, const std::string& sched, const std::string& cmd, std::string& reason); /** * check crontab for unmanaged lines * @param marker same as above, typically RCLCRONTAB_RCLINDEX= * @param data string to look for on lines NOT marked, typically "recollindex" * @return true if unmanaged lines exist, false else. */ bool checkCrontabUnmanaged(const std::string& marker, const std::string& data); /** Retrieve the scheduling for a crontab entry */ bool getCrontabSched(const std::string& marker, const std::string& id, std::vector& sched); #endif /* _ECRONTAB_H_INCLUDED_ */ recoll-1.36.1/utils/x11mon.cpp0000644000175000017500000000425414426500174013002 00000000000000/* Copyright (C) 2006-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Poll state of X11 connectibility (to detect end of user session). #include "autoconfig.h" #ifndef DISABLE_X11MON #include "x11mon.h" #include #include #include #include #include "log.h" static Display *m_display; static bool m_ok; static jmp_buf env; static int errorHandler(Display *, XErrorEvent*) { LOGERR("x11mon: error handler: Got X11 error\n"); m_ok = false; return 0; } static int ioErrorHandler(Display *) { LOGERR("x11mon: error handler: Got X11 IO error\n"); m_ok = false; m_display = nullptr; longjmp(env, 1); } bool x11IsAlive() { // Xlib always exits on IO errors. Need a setjmp to avoid this (will jump // from IO error handler instead of returning). if (setjmp(env)) { LOGDEB("x11IsAlive: got long jump: X11 error\n"); return false; } if (nullptr == m_display) { signal(SIGPIPE, SIG_IGN); XSetErrorHandler(errorHandler); XSetIOErrorHandler(ioErrorHandler); if ((m_display = XOpenDisplay(nullptr)) == nullptr) { LOGERR("x11IsAlive: cant connect\n"); m_ok = false; return false; } } m_ok = true; bool sync= XSynchronize(m_display, true); XNoOp(m_display); XSynchronize(m_display, sync); return m_ok; } #else // DISABLE_X11MON-> bool x11IsAlive() { return true; } #endif /* DISABLE_X11MON */ recoll-1.36.1/utils/hldata.h0000644000175000017500000001245214426500174012560 00000000000000/* Copyright (C) 2017-2019 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _hldata_h_included_ #define _hldata_h_included_ #include #include #include #include /** Store data about user search terms and their expansions. This is used * mostly for highlighting result text and walking the matches, generating * spelling suggestions. */ struct HighlightData { /** The user terms, excluding those with wildcards. This list is * intended for orthographic suggestions so the terms are always * lowercased, unaccented or not depending on the type of index * (as the spelling dictionary is generated from the index terms). */ std::set uterms; /** The db query terms linked to the uterms entry they were expanded from. * This is used for aggregating term stats when generating snippets (for * choosing the best terms, allocating slots, etc. ) */ std::unordered_map terms; /** The original user terms-or-groups. This is for display * purposes: ie when creating a menu to look for a specific * matched group inside a preview window. We want to show the * user-entered data in the menu, not some transformation, so * these are always raw, diacritics and case preserved. */ std::vector > ugroups; /** Processed/expanded terms and groups. Used for looking for * regions to highlight. A group can be a PHRASE or NEAR entry * Terms are just groups with 1 entry. All * terms are transformed to be compatible with index content * (unaccented and lowercased as needed depending on * configuration), and the list may include values * expanded from the original terms by stem or wildcard expansion. */ struct TermGroup { // We'd use an union but no can do std::string term; std::vector > orgroups; int slack{0}; /* Index into ugroups. As a user term or group may generate * many processed/expanded terms or groups, this is how we * relate an expansion to its source (used, e.g. for * generating anchors for walking search matches in the * preview window). */ size_t grpsugidx{0}; enum TGK {TGK_TERM, TGK_NEAR, TGK_PHRASE}; TGK kind{TGK_TERM}; }; std::vector index_term_groups; /** Terms generated by automatic spelling approximation. This is used to indicate to the user * how their query was changed. */ std::vector spellexpands; void clear() { uterms.clear(); terms.clear(); ugroups.clear(); index_term_groups.clear(); spellexpands.clear(); } void append(const HighlightData&); // Print (debug) std::string toString() const; }; /* The following is used by plaintorich.cpp for finding zones to highlight and by rclabsfromtext.cpp to choose fragments for the abstract */ struct GroupMatchEntry { // Start/End byte offsets in the document text std::pair offs; // Index of the search group this comes from: this is to relate a // match to the original user input. size_t grpidx; GroupMatchEntry(int sta, int sto, size_t idx) : offs(sta, sto), grpidx(idx) { } }; // Find NEAR or PHRASE matches for one group of terms. // // @param hldata User query expansion descriptor (see above). We only use // the index_term_groups entry // // @param grpidx Index in hldata.index_term_groups for the group we // process. This is used by us to get the terms, group type // (phrase/near) and slacks. We also set it in the output // GroupMatchEntry structures to allow the caller to link a match // with a specific user input (e.g. for walking the match in the // GUI preview) // // @param inplists Position lists for the the group terms. This is the // data used to look for matches. // // @param gpostobytes Translation of term position to start/end byte // offsets. This is used to translate term positions to byte // positions in the output, for ease of use by caller. // // @param[out] tboffs Found matches. Each match has a begin and end // byte offset and an index linking to the origin data in the // HighlightData structure. extern bool matchGroup( const HighlightData& hldata, unsigned int grpidx, const std::unordered_map>& inplists, const std::unordered_map>& gpostobytes, std::vector& tboffs ); #endif /* _hldata_h_included_ */ recoll-1.36.1/utils/cmdtalk.h0000644000175000017500000000742714410615043012743 00000000000000/* Copyright (C) 2016 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _CMDTALK_H_INCLUDED_ #define _CMDTALK_H_INCLUDED_ /** * Execute commands and exchange messages with it. * * A simple stream protocol is used for the dialog. HTTP or some kind * of full-blown RPC could have been used, but there was also good * reason to keep it simple (yet powerful), given the limited context * of dialog through a pipe. * * The data is exchanged in TLV fashion, in a way that should be * usable in most script languages. The basic unit of data has one line * with a data type and a count (both ASCII), followed by the data. A * 'message' is made of one or several units or tags and ends with one empty * line. * * Example:(the message begins before 'Filename' and has 'Filename' and * 'Ipath' tags): * Filename: 24 /my/home/mail/somefolderIpath: 2 22 #include #include class CmdTalk { public: CmdTalk(int timeosecs); virtual ~CmdTalk(); CmdTalk(const CmdTalk&) = delete; CmdTalk& operator=(const CmdTalk&) = delete; // @param env each entry should be of the form name=value. They // augment the subprocess environnement. // @param path replaces the PATH variable when looking for the command. // // Note that cmdtalk.py:main() method is a test routine which // expects data pairs on the command line. If actual parameters // need to be passed, it can't be used by the processor. virtual bool startCmd(const std::string& cmdname, const std::vector& args = std::vector(), const std::vector& env = std::vector(), const std::vector& path = std::vector() ); virtual bool running(); // Single exchange: send and receive data. virtual bool talk(const std::unordered_map& args, std::unordered_map& rep); // Specialized version with special argument used by dispatcher to call // designated method virtual bool callproc( const std::string& proc, const std::unordered_map& args, std::unordered_map& rep); private: class Internal; Internal *m{0}; }; #endif /* _CMDTALK_H_INCLUDED_ */ recoll-1.36.1/utils/rclutil.h0000644000175000017500000001257014427373216013010 00000000000000/* Copyright (C) 2016 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RCLUTIL_H_INCLUDED_ #define _RCLUTIL_H_INCLUDED_ // Misc stuff not generic enough to get into smallut or pathut #include #include #include extern void rclutil_init_mt(); /// Sub-directory for default recoll config (e.g: .recoll) extern std::string path_defaultrecollconfsubdir(); // Check if path is either non-existing or an empty directory. extern bool path_empty(const std::string& path); /// Where we create the user data subdirs extern std::string path_homedata(); /// e.g. /usr/share/recoll. Depends on OS and config extern const std::string& path_pkgdatadir(); #ifdef _WIN32 extern std::string path_thisexecpath(); #endif /// Encode according to rfc 1738 extern std::string url_encode(const std::string& url, std::string::size_type offs = 0); //// Convert to file path if url is like file://. This modifies the //// input (and returns a copy for convenience) extern std::string fileurltolocalpath(std::string url); /// Test for file:/// url extern bool urlisfileurl(const std::string& url); /// extern std::string url_parentfolder(const std::string& url); /// Return the host+path part of an url. This is not a general /// routine, it does the right thing only in the recoll context extern std::string url_gpath(const std::string& url); /// Turn absolute path into file:// url extern std::string path_pathtofileurl(const std::string& path); /// Transcode to utf-8 if possible or url encoding, for display. extern bool printableUrl(const std::string& fcharset, const std::string& in, std::string& out); /// Same but, in the case of a Windows local path, also turn "c:/" into /// "/c/" This should be used only for splitting the path in rcldb. #ifdef _WIN32 extern std::string path_slashdrive(const std::string& path); #endif extern std::string url_gpathS(const std::string& url); /// Like strftime but guaranteed utf-8 output (esp. useful on Windows) struct tm; extern std::string utf8datestring(const std::string& format, struct tm *tm); /// Retrieve the temp dir location: $RECOLL_TMPDIR else $TMPDIR else /tmp extern const std::string& tmplocation(); /// Create temporary directory (inside the temp location) extern bool maketmpdir(std::string& tdir, std::string& reason); /// Temporary file class class TempFile { public: TempFile(const std::string& suffix); TempFile(); const char *filename() const; const std::string& getreason() const; void setnoremove(bool onoff); bool ok() const; // Attempt to delete all files which could not be deleted on the // first try (typically on Windows: because they are open by some // process). Called after clearing the mimeHandler cache. Does // nothing if not _WIN32 static void tryRemoveAgain(); // Also for Windows: for adding the temp files path to the default // skippedPaths static const std::string& rcltmpdir(); class Internal; private: std::shared_ptr m; }; /// Temporary directory class. Recursively deleted by destructor. class TempDir { public: TempDir(); ~TempDir(); TempDir(const TempDir&) = delete; TempDir& operator=(const TempDir&) = delete; const char *dirname() { return m_dirname.c_str(); } const std::string& getreason() { return m_reason; } bool ok() { return !m_dirname.empty(); } /// Recursively delete contents but not self. bool wipe(); private: std::string m_dirname; std::string m_reason; }; // Freedesktop thumbnail standard path routine // On return, path will have the appropriate value in all cases, // returns true if the file already exists extern bool thumbPathForUrl(const std::string& url, int size, std::string& path); // Duplicate (unordered)map while ensuring no shared // string data (to pass to other thread): template void map_ss_cp_noshr(T s, T *d); // Set or extend metadata field. We store the data as CSV template void addmeta(T& store, const std::string& nm, const std::string& value); // Compare charset names, removing the more common spelling variations extern bool samecharset(const std::string& cs1, const std::string& cs2); // Divine language from locale extern std::string localelang(); // Divine 8bit charset from language extern std::string langtocode(const std::string& lang); extern int u8DLDistance(const std::string& str1, const std::string str2); // Extract MIME type from a string looking like: ": text/plain; charset=us-ascii". (the string is // "file -i" output after the path has been removed). Exported here so that it can be unit-tested. extern std::string growmimearoundslash(std::string mime); #endif /* _RCLUTIL_H_INCLUDED_ */ recoll-1.36.1/utils/listmem.h0000644000175000017500000000054214410615043012765 00000000000000#ifndef _LISTMEM_H_INCLUDED_ #define _LISTMEM_H_INCLUDED_ #include enum ListmemOpts {LISTMEM_SWAP16 = 1, LISTMEM_SWAP32 = 2}; /// @param startadr starting value for offset listings on the right extern void listmem(std::ostream&, const void *ptr, int sz, int startadr = 0, int opts = 0); #endif /* _LISTMEM_H_INCLUDED_ */ recoll-1.36.1/utils/smallut.h0000644000175000017500000002570514520415746013016 00000000000000/* Copyright (C) 2006-2022 J.F.Dockes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef _SMALLUT_H_INCLUDED_ #define _SMALLUT_H_INCLUDED_ #include #include #include #include #include #include #include struct tm; namespace MedocUtils { // Miscellaneous mostly string-oriented small utilities // Note that none of the following code knows about utf-8. // Call this before going multithread. void smallut_init_mt(); #ifndef SMALLUT_DISABLE_MACROS #ifndef MIN #define MIN(A,B) (((A)<(B)) ? (A) : (B)) #endif #ifndef MAX #define MAX(A,B) (((A)>(B)) ? (A) : (B)) #endif #ifndef deleteZ #define deleteZ(X) {delete X;X = nullptr;} #endif #ifndef PRETEND_USE #define PRETEND_USE(var) ((void)(var)) #endif #ifndef VERSION_AT_LEAST #define VERSION_AT_LEAST(LIBMAJ,LIBMIN,LIBREV,TARGMAJ,TARGMIN,TARGREV) \ ((LIBMAJ) > (TARGMAJ) || \ ((LIBMAJ) == (TARGMAJ) && \ ((LIBMIN) > (TARGMIN) || \ ((LIBMIN) == (TARGMIN) && (LIBREV) >= (TARGREV))))) #endif #endif /* SMALLUT_DISABLE_MACROS */ // Case-insensitive compare. ASCII ONLY ! extern int stringicmp(const std::string& s1, const std::string& s2); // For find_if etc. struct StringIcmpPred { explicit StringIcmpPred(const std::string& s1) : m_s1(s1) { } bool operator()(const std::string& s2) { return stringicmp(m_s1, s2) == 0; } const std::string& m_s1; }; extern int stringlowercmp(const std::string& s1, // already lower const std::string& s2); extern int stringuppercmp(const std::string& s1, // already upper const std::string& s2); extern void stringtolower(std::string& io); extern std::string stringtolower(const std::string& io); extern void stringtoupper(std::string& io); extern std::string stringtoupper(const std::string& io); extern bool beginswith(const std::string& b, const std::string& sml); // Parse date interval specifier into pair of y,m,d dates. The format // for the time interval is based on a subset of iso 8601 with // the addition of open intervals, and removal of all time indications. // 'P' is the Period indicator, it's followed by a length in // years/months/days (or any subset thereof) // Dates: YYYY-MM-DD YYYY-MM YYYY // Periods: P[nY][nM][nD] where n is an integer value. // At least one of YMD must be specified // The separator for the interval is /. Interval examples // YYYY/ (from YYYY) YYYY-MM-DD/P3Y (3 years after date) etc. // This returns a pair of y,m,d dates. struct DateInterval { int y1; int m1; int d1; int y2; int m2; int d2; }; extern bool parsedateinterval(const std::string& s, DateInterval *di); extern int monthdays(int mon, int year); /** Note for all templated functions: * By default, smallut.cpp has explicit instantiations for common * containers (list, vector, set, etc.). If this is not enough, or * conversely, if you want to minimize the module size, you can chose * the instantiations by defining the SMALLUT_EXTERNAL_INSTANTIATIONS * compilation flag, and defining the instances in a file named * smallut_instantiations.h */ /** * Parse input string into list of strings. See instantiation note above. * * Token delimiter is " \t\n" except inside dquotes. dquote inside * dquotes can be escaped with \ etc... * Input is handled a byte at a time, things will work as long as * space tab etc. have the ascii values and can't appear as part of a * multibyte char. utf-8 ok but so are the iso-8859-x and surely * others. addseps do have to be single-bytes */ template bool stringToStrings(const std::string& s, T& tokens, const std::string& addseps = ""); /** * Inverse operation. See instantiation note above. */ template void stringsToString(const T& tokens, std::string& s); template std::string stringsToString(const T& tokens); /** * Strings to CSV string. tokens containing the separator are quoted (") * " inside tokens is escaped as "" ([word "quote"] =>["word ""quote"""] * See instantiation note above. */ template void stringsToCSV(const T& tokens, std::string& s, char sep = ','); /** Find longest common prefix for bunch of strings */ template std::string commonprefix(const T& values); /** * Split input string. No handling of quoting. */ extern void stringToTokens(const std::string& s, std::vector& tokens, const std::string& delims = " \t", bool skipinit = true, bool allowempty = false); /** Like toTokens but with multichar separator */ extern void stringSplitString(const std::string& str, std::vector& tokens, const std::string& sep); /** Convert string to boolean */ extern bool stringToBool(const std::string& s); /** Remove instances of characters belonging to set (default {space, tab}) at beginning and end of input string */ extern std::string& trimstring(std::string& s, const char *ws = " \t"); extern std::string& rtrimstring(std::string& s, const char *ws = " \t"); extern std::string& ltrimstring(std::string& s, const char *ws = " \t"); /** Escape things like < or & by turning them into entities */ extern std::string escapeHtml(const std::string& in); /** Double-quote and escape to produce C source code string (prog generation) */ extern std::string makeCString(const std::string& in); /** Replace some chars with spaces (ie: newline chars). */ extern std::string neutchars(const std::string& str, const std::string& chars, char rep = ' '); extern void neutchars(const std::string& str, std::string& out, const std::string& chars, char rep = ' '); /** Turn string into something that won't be expanded by a shell. In practise * quote with double-quotes and escape $`\ */ extern std::string escapeShell(const std::string& in); /** Truncate a string to a given maxlength, avoiding cutting off midword * if reasonably possible. */ extern std::string truncate_to_word(const std::string& input, std::string::size_type maxlen); void ulltodecstr(uint64_t val, std::string& buf); void lltodecstr(int64_t val, std::string& buf); std::string lltodecstr(int64_t val); std::string ulltodecstr(uint64_t val); /** Convert byte count into unit (KB/MB...) appropriate for display */ std::string displayableBytes(int64_t size); /** Break big string into lines */ std::string breakIntoLines(const std::string& in, unsigned int ll = 100, unsigned int maxlines = 50); /** Small utility to substitute printf-like percents cmds in a string */ bool pcSubst(const std::string& in, std::string& out, const std::map& subs); /** Substitute printf-like percents and also %(key) */ bool pcSubst(const std::string& in, std::string& out, const std::map& subs); /** Substitute printf-like percents and %(nm), using result of function call */ bool pcSubst(const std::string& i, std::string& o, const std::function&); /** Stupid little smart buffer handler avoiding value-initialization when not needed (e.g. for using as read buffer **/ class DirtySmartBuf { public: explicit DirtySmartBuf(size_t sz) : m_buf(new char[sz]) {} ~DirtySmartBuf() { delete [] m_buf; } DirtySmartBuf(const DirtySmartBuf&) = delete; DirtySmartBuf& operator=(const DirtySmartBuf&) = delete; char *buf() { return m_buf; } private: char *m_buf; }; /** Append system error message */ void catstrerror(std::string *reason, const char *what, int _errno); /** Portable timegm. MS C has _mkgmtime, but there is a bug in Gminw which * makes it inaccessible */ time_t portable_timegm(struct tm *tm); inline void leftzeropad(std::string &s, unsigned len) { if (s.length() && s.length() < len) { s = s.insert(0, len - s.length(), '0'); } } // Print binary string in hexa, separate bytes with character separ if not zero // (e.g. ac:23:0c:4f:46:fd) extern std::string hexprint(const std::string& in, char separ= 0); #ifndef SMALLUT_NO_REGEX // A class to solve platorm/compiler issues for simple regex // matches. Uses the appropriate native lib under the hood. // This always uses extended regexp syntax. class SimpleRegexp { public: enum Flags {SRE_NONE = 0, SRE_ICASE = 1, SRE_NOSUB = 2}; /// @param nmatch must be >= the number of parenthesed subexp in exp SimpleRegexp(const std::string& exp, int flags, int nmatch = 0); ~SimpleRegexp(); SimpleRegexp(const SimpleRegexp&) = delete; SimpleRegexp& operator=(const SimpleRegexp&) = delete; /// Match input against exp, return true if matches bool simpleMatch(const std::string& val) const; /// After simpleMatch success, get nth submatch, 0 is the whole /// match, 1 first parentheses, etc. std::string getMatch(const std::string& val, int i) const; /// Calls simpleMatch() bool operator() (const std::string& val) const; /// Replace the first occurrence of regexp. std::string simpleSub(const std::string& input, const std::string& repl); /// Check after construction bool ok() const; class Internal; private: std::unique_ptr m; }; #endif // SMALLUT_NO_REGEX /// Utilities for printing names for defined values (Ex: O_RDONLY->"O_RDONLY") /// Entries for the descriptive table struct CharFlags { CharFlags(int v, const char *y, const char *n=nullptr) : value(v), yesname(y), noname(n) {} unsigned int value; // Flag or value const char *yesname;// String to print if flag set or equal const char *noname; // String to print if flag not set (unused for values) }; /// Helper macro for the common case where we want to print the /// flag/value defined name #define CHARFLAGENTRY(NM) {NM, #NM} /// Translate a bitfield into string description extern std::string flagsToString(const std::vector&, unsigned int val); /// Translate a value into a name extern std::string valToString(const std::vector&, unsigned int val); /// Decode percent-encoded URL extern std::string url_decode(const std::string&); } // End namespace MedocUtils using namespace MedocUtils; #endif /* _SMALLUT_H_INCLUDED_ */ recoll-1.36.1/utils/fstreewalk.h0000644000175000017500000001371214427373216013500 00000000000000/* Copyright (C) 2004-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _FSTREEWALK_H_INCLUDED_ #define _FSTREEWALK_H_INCLUDED_ #include #include #include "pathut.h" class FsTreeWalkerCB; /** * Class implementing a Unix directory recursive walk. * * A user-defined function object is called for every file or * directory. Patterns to be ignored can be set before starting the * walk. Options control whether we follow symlinks and whether we recurse * on subdirectories. */ class FsTreeWalker { public: // Global option to use FNM_PATHNAME when matching paths (for // skippedPaths). // We initially used FNM_PATHNAME, and we can't change it now // (because of all the config files around). So add global option // to not use the flag, which can be set from rclconfig by adding // a value to the config file (skippedPathsNoFnmPathname) static bool o_useFnmPathname; static void setNoFnmPathname() { o_useFnmPathname = false; } // Global option to observe a "nowalk" file, which makes us treat // directories as if they were in skippedPaths) if the file exists // inside the directory. static std::string o_nowalkfn; static void setNoWalkFn(const std::string& nowalkfn) { o_nowalkfn = nowalkfn; } // Flags for call to processone(). FtwDirEnter is used when // entering a directory. FtwDirReturn is used when returning to it // after processing a subdirectory. enum CbFlag {FtwRegular, FtwDirEnter, FtwDirReturn, FtwSkipped}; enum Status {FtwOk=0, FtwError=1, FtwStop=2, FtwStatAll = FtwError|FtwStop}; enum Options {FtwOptNone = 0, FtwNoRecurse = 1, FtwFollow = 2, FtwNoCanon = 4, FtwSkipDotFiles = 8, // Only callback for skipped files and directories, // for getting a list of skipped stuff. We don't // descend into skipped directories. // ** The callback will receive a null struct stat pointer.** FtwOnlySkipped = 0x10, // Tree walking options. Natural is close to depth first: process // directory entries as we see them, recursing into subdirectories at // once // Breadth means we process all files and dirs at a given directory level // before going deeper. // // FilesThenDirs is close to Natural, except that we process all files in a // given directory before going deeper: allows keeping only a single // directory open // We don't do pure depth first (process subdirs before files), this does // not appear to make any sense. FtwTravNatural = 0x10000, FtwTravBreadth = 0x20000, FtwTravFilesThenDirs = 0x40000, FtwTravBreadthThenDepth = 0x80000 }; static const int FtwTravMask; FsTreeWalker(int opts = FtwTravNatural); ~FsTreeWalker(); FsTreeWalker(const FsTreeWalker&) = delete; FsTreeWalker& operator=(const FsTreeWalker&) = delete; void setOpts(int opts); int getOpts(); void setDepthSwitch(int); void setMaxDepth(int); /** * Begin file system walk. * @param dir is not checked against the ignored patterns (this is * a feature and must not change. * @param cb the function object that will be called back for every * file-system object (called both at entry and exit for directories). */ Status walk(const std::string &dir, FsTreeWalkerCB& cb); /** Get explanation for error */ std::string getReason(); int getErrCnt(); /** * Add a pattern (file or dir) to be ignored (ie: #* , *~) */ bool addSkippedName(const std::string &pattern); /** Set the ignored patterns set */ bool setSkippedNames(const std::vector &patterns); /** Set the exclusive patterns set */ bool setOnlyNames(const std::vector &patterns); /** Same for skipped paths: this are paths, not names, under which we do not descend (ie: /home/me/.recoll) */ bool addSkippedPath(const std::string &path); /** Set the ignored paths list */ bool setSkippedPaths(const std::vector &patterns); /** Test if path/name should be skipped. This can be used independently of * an actual tree walk */ bool inSkippedPaths(const std::string& path, bool ckparents = false); bool inSkippedNames(const std::string& name); bool inOnlyNames(const std::string& name); private: Status iwalk(const std::string &dir, const struct PathStat& stp, FsTreeWalkerCB& cb); class Internal; Internal *data; }; class FsTreeWalkerCB { public: FsTreeWalkerCB() {} virtual ~FsTreeWalkerCB() {} FsTreeWalkerCB(const FsTreeWalkerCB&) = delete; FsTreeWalkerCB& operator=(const FsTreeWalkerCB&) = delete; // Only st_mtime, st_ctime, st_size, st_mode (filetype bits: dir/reg/lnk), virtual FsTreeWalker::Status processone(const std::string&, FsTreeWalker::CbFlag, const struct PathStat& = PathStat()) = 0; }; // Utility function. Somewhat like du. int64_t fsTreeBytes(const std::string& topdir); #endif /* _FSTREEWALK_H_INCLUDED_ */ recoll-1.36.1/utils/closefrom.cpp0000644000175000017500000001714114426501047013647 00000000000000/* Copyright (C) 2009 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * Close all file descriptors above a given value. * * A Unix execXX() call used to execute another program does not close open * file descriptors by default. * * The only descriptors closed are those on which the FD_CLOEXEC flag was * set. FD_CLOEXEC is not easily usable on files opened by external * libraries. * * There are many reasons for closing file descriptors before * an exec (security, pipe control, the possibility that a bug will * trigger an unwanted write, etc.) * * A process has currently no POSIX way to determine the set of open file * descriptors or at least the highest value. Closing all files (except a few), * thus implies performing a close() system call on each entry up to the * maximum, which can be both relatively difficult to determine, and quite * high (ie: several thousands), incurring a non-negligible cost. * * A number of systems have non-portable support for mitigating or solving * this problem. * * This module supplies a portable interface to this functionality. * * The initial data on system interfaces was obtained from: * http://stackoverflow.com/questions/899038/\ * getting-the-highest-allocated-file-descriptor * * System interfaces: * FreeBSD/DragonFly: * - Have a closefrom() system call as of release 7.x around Sep 2009 * - Have a /dev/fd, directory which shows the current process' open * descriptors. Only descriptors 0, 1, 2 are shown except if * fdescfs is mounted which it is not by default * * Solaris: * - Solaris 10+ has closefrom, and can specify closefrom to posix_spawn() * * Linux: * - Has nothing. The method we initially used (listing /dev/fd) could * deadlock in multithread fork/exec context. We now use a close() * loop but there is no completely reliable way to determine the high limit. * The glibc maintainer (drepper) thinks that closefrom() is a bad idea * *especially* because it is implemented on *BSD and Solaris. Go figure... * https://sourceware.org/bugzilla/show_bug.cgi?id=10353 * Bug updated 2021/07/08, apparently fixed in glibc 2.35 (not yet available 2022-01, fed35 and * tumble are at 2.34). * * Interface: * * int libclf_closefrom(fd) * @param fd All open file descriptors with equal or higher numeric * values will be closed. fd needs not be a valid descriptor. * @return 0 for success, -1 for error. */ #include "closefrom.h" #include "safeunistd.h" #include #ifndef _WIN32 #include #include #include #endif // #define DEBUG_CLOSEFROM #ifdef DEBUG_CLOSEFROM #include #include #define DPRINT(X) X #else #define DPRINT(X) #endif /* Note: sudo has a closefrom implementation, needs a lot of autoconfig, but * we could use it instead. It's quite close to this though */ /*************************************************************************/ /* closefrom() exists on Solaris, netbsd and openbsd, but someone will * have to provide me the appropriate macro to test */ #if ((defined(__FreeBSD__) && __FreeBSD_version >= 702104)) || defined(__DragonFly__) /* Use closefrom() system call */ int libclf_closefrom(int fd0) { DPRINT((std::cerr << "libclf_closefrom: using closefrom(2)\n")); closefrom(fd0); return 0; } /*************************************************************************/ #elif defined(F_CLOSEM) /* Use fcntl(fd, F_CLOSEM) */ int libclf_closefrom(int fd0) { DPRINT((std::cerr << "libclf_closefrom: using fcntl(F_CLOSEM)\n")); // We need a valid descriptor for this to work. Try to dup stdin, else go wild if (fcntl(0, F_GETFL) != -1) { if (fd0 != 0) dup2(0, fd0); } else { int fd = open("/etc/group", 0); // yes i am a unix man if (fd >= 0 && fd != fd0) { dup2(fd, fd0); close(fd); } } return fcntl(fd0, F_CLOSEM, 0); } /*************************************************************************/ #elif 0 && (defined(linux) || defined(__linux)) /* We don't do this on linux anymore because opendir() may call malloc which is unsafe in the [fork-exec] interval for a multithreaded program. Linux does not have a good solution for implementing closefrom as far as I know */ /* Use /proc/self/fd directory */ #include #include int libclf_closefrom(int fd0) { DIR *dirp; struct dirent *ent; DPRINT((std::cerr << "libclf_closefrom: using /proc\n")); dirp = opendir("/proc/self/fd"); if (dirp == 0) return -1; while ((ent = readdir(dirp)) != 0) { int fd; if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) continue; if (sscanf(ent->d_name, "%d", &fd) == 1 && fd >= fd0 && fd != dirfd(dirp)) { close(fd); } } closedir(dirp); return 0; } /*************************************************************************/ #else /* System has no native support for this functionality. * * Close all descriptors up to compiled/configured maximum. The caller will usually have an idea of * a reasonable maximum, else we retrieve a value from the system. * * Note that there is actually no real guarantee that no open descriptor higher than the reported * limit can exist, as noted by the Solaris man page for closefrom() */ static int closefrom_maxfd = -1; void libclf_setmaxfd(int max) { closefrom_maxfd = max; } #include #ifndef OPEN_MAX #define OPEN_MAX 1024 #endif int libclf_closefrom(int fd0) { DPRINT((std::cerr << "libclf_closefrom: no system support, winging it...\n")); int i, maxfd = closefrom_maxfd; if (maxfd < 0) { maxfd = libclf_maxfd(); } if (maxfd < 0) maxfd = OPEN_MAX; for (i = fd0; i < maxfd; i++) { (void)close(i); } return 0; } #endif // Note that this will not work if the limit was lowered after a higher fd was opened. But we don't // call setrlimit(nofile) inside recoll code, so we should be ok. It seems that // sysconf(_SC_OPEN_MAX) usually reports the soft limit, so it's redundant, but it could be useful // in case getrlimit() is not implemented (unlikely as they're both POSIX.1-2001?) // // On some systems / environments, getrlimit() returns an unworkably high value. For example on an // Arch Linux Docker environment, we get 1E9, which results in a seemingly looping process. Have to // put a limit somewhere, so it's 8192... You can still use libclf_setmaxfd() before the first // closefrom call to use a higher value. int libclf_maxfd(int) { #ifdef _WIN32 // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setmaxstdio?view=msvc-170 return 8192; #else struct rlimit lim; getrlimit(RLIMIT_NOFILE, &lim); auto max = lim.rlim_cur; DPRINT((std::cerr << "Libclf_maxfd: got " << max << "\n")); if (max > 8192) max = 8192; return int(max); #endif } recoll-1.36.1/utils/cancelcheck.h0000644000175000017500000000431214410615043013535 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _CANCELCHECK_H_INCLUDED_ #define _CANCELCHECK_H_INCLUDED_ /** * Common cancel checking mechanism * * The CancelCheck class is used as a singleton objet (private constructor). * The single instance can be accessed as CancelCheck::instance. * It is used as follows, in an asynchronous program where there is an * interactive (or otherwise controlling) task and a long-working one: * - The control task calls setCancel(), usually as a result of user * interaction, if the worker takes too long. * - The worker task calls checkCancel() at regular intervals, possibly as * a side-effect of some other progress-reporting call. If cancellation has * been requested, this will raise an exception, to be catched and processed * wherever the worker was invoked. * The worker side must be exception-clean, but this otherwise avoids * having to set-up code to handle a special cancellation error along * the whole worker call stack. */ class CancelExcept {}; class CancelCheck { public: static CancelCheck& instance(); void setCancel(bool on = true) { cancelRequested = on; } void checkCancel() { if (cancelRequested) { throw CancelExcept(); } } bool cancelState() {return cancelRequested;} private: bool cancelRequested; CancelCheck() : cancelRequested(false) {} CancelCheck& operator=(CancelCheck&); CancelCheck(const CancelCheck&); }; #endif /* _CANCELCHECK_H_INCLUDED_ */ recoll-1.36.1/utils/zlibut.cpp0000644000175000017500000001201214426500174013157 00000000000000/* Copyright (C) 2017 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "zlibut.h" #include #include "log.h" using namespace std; static void *allocmem( void *cp, /* The array to grow. may be NULL */ int sz, /* Unit size in bytes */ int *np, /* Pointer to current allocation number */ int min, /* Number to allocate the first time */ int maxinc) /* Maximum increment */ { if (nullptr == cp) { cp = malloc(min * sz); *np = cp ? min : 0; return cp; } int inc = (*np > maxinc) ? maxinc : *np; if ((cp = realloc(cp, (*np + inc) * sz)) != nullptr) { *np += inc; } return cp; } class ZLibUtBuf::Internal { public: Internal() {} ~Internal() { if (buf && dofree) { free(buf); } } bool grow(size_t n) { if (!initsz) initsz = n; buf = (char *)allocmem(buf, initsz, &alloc, 1, 20); return nullptr != buf; } int getAlloc() { return alloc * initsz; } char *buf{nullptr}; int initsz{0}; // Set to first alloc size int alloc{0}; // Allocation count (allocmem()). Capa is alloc*inisz int datacnt{0}; // Data count bool dofree{true}; // Does buffer belong to me ? friend bool inflateToBuf(void* inp, unsigned int inlen, ZLibUtBuf& buf); }; ZLibUtBuf::ZLibUtBuf() { m = new Internal; } ZLibUtBuf::~ZLibUtBuf() { delete m; } char *ZLibUtBuf::getBuf() const { return m->buf; } char *ZLibUtBuf::takeBuf() { m->dofree = false; return m->buf; } size_t ZLibUtBuf::getCnt() { return m->datacnt; } bool inflateToBuf(const void* inp, unsigned int inlen, ZLibUtBuf& buf) { LOGDEB1("inflateToBuf: inlen " << inlen << "\n"); z_stream d_stream; /* decompression stream */ d_stream.zalloc = (alloc_func)0; d_stream.zfree = (free_func)0; d_stream.opaque = (voidpf)0; d_stream.next_in = (Bytef*)inp; d_stream.avail_in = inlen; d_stream.next_out = nullptr; d_stream.avail_out = 0; int err; if ((err = inflateInit(&d_stream)) != Z_OK) { LOGERR("Inflate: inflateInit: err " << err << " msg " << d_stream.msg << "\n"); return false; } for (;;) { LOGDEB2("InflateToDynBuf: avail_in " << d_stream.avail_in << " total_in " << d_stream.total_in << " avail_out " << d_stream.avail_out << " total_out " << d_stream.total_out << "\n"); if (d_stream.avail_out == 0) { if (!buf.m->grow(inlen)) { LOGERR("Inflate: out of memory, current alloc " << buf.m->getAlloc() << "\n"); inflateEnd(&d_stream); return false; } d_stream.avail_out = buf.m->getAlloc() - d_stream.total_out; d_stream.next_out = (Bytef*)(buf.getBuf() + d_stream.total_out); } err = inflate(&d_stream, Z_NO_FLUSH); if (err == Z_STREAM_END) { break; } if (err != Z_OK) { LOGERR("Inflate: error " << err << " msg " << (d_stream.msg ? d_stream.msg : "") << endl); inflateEnd(&d_stream); return false; } } if ((err = inflateEnd(&d_stream)) != Z_OK) { LOGERR("Inflate: inflateEnd error " << err << " msg " << (d_stream.msg ? d_stream.msg : "") << endl); return false; } buf.m->datacnt = d_stream.total_out; LOGDEB1("inflateToBuf: ok, output size " << buf.getCnt() << endl); return true; } bool deflateToBuf(const void* inp, unsigned int inlen, ZLibUtBuf& buf) { uLongf len = compressBound(static_cast(inlen)); // This needs cleanup: because the buffer is reused inside // e.g. circache, we want a minimum size in case the 1st doc size, // which sets the grow increment is small. It would be better to // let the user set a min size hint. if (len < 500 *1024) len = 500 * 1024; while (buf.m->getAlloc() < int(len)) { if (!buf.m->grow(len)) { LOGERR("deflateToBuf: can't get buffer for " << len << " bytes\n"); return false; } } bool ret = compress((Bytef*)buf.getBuf(), &len, (Bytef*)inp, static_cast(inlen)) == Z_OK; buf.m->datacnt = len; return ret; } recoll-1.36.1/utils/fileudi.h0000644000175000017500000000252014427373216012745 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _FILEUDI_H_INCLUDED_ #define _FILEUDI_H_INCLUDED_ #include // Unique Document Ids for the file-based indexer (main Recoll // indexer). Document Ids are built from a concatenation of the file // path and the internal path (ie: email number inside // folder/attachment number/etc.) As the size of Xapian terms is // limited, the Id path is truncated to a maximum length, and completed // by a hash of the remainder (including the ipath) extern void make_udi(const std::string& fn, const std::string& ipath, std::string &udi); #endif /* _FILEUDI_H_INCLUDED_ */ recoll-1.36.1/utils/picoxml.h0000644000175000017500000003701514467431307013006 00000000000000/* Copyright (C) 2016 J.F.Dockes * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * (1) Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * (2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * (3)The name of the author may not be used to * endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. **********************************************************/ #ifndef _PICOXML_H_INCLUDED_ #define _PICOXML_H_INCLUDED_ /** * PicoXMLParser: a single include file parser for an XML-like, but restricted language, adequate * for config files, not for arbitrary externally generated data. * * - The code depends on nothing but the C++ standard library * - The input to the parser is a single C++ string. We do not deal with * input in several pieces or files. * - SAX mode only. You have access to the current tag stack. * - Checks for proper tag nesting and not much else. * - MISSING support: * - NO literal '>' inside attribute values. Use > * - No CDATA support * - No DOCTYPE support * - No processing of namespaces. * - Not fast: a lot of std::string and memory allocations. * * A typical input would be like the following (you can add XML declarations, whitespace and * newlines to taste). * * top chrs1sub chrstop chrs2 * * Usage: subclass PicoXMLParser, overriding the methods in the "protected:" section (look there * for more details), call the constructor with your input, then call parse(). */ #include #include #include #include #include #include // Expat compat typedef char XML_Char; class PicoXMLParser { public: PicoXMLParser(const std::string& input) : m_in(input), m_pos(0) {} virtual ~PicoXMLParser() = default; PicoXMLParser(const PicoXMLParser&) = delete; PicoXMLParser& operator=(const PicoXMLParser&) = delete; virtual bool parse() { return _parse(); } virtual bool Parse() { return _parse(); } virtual std::string getLastErrorMessage() { return m_reason.str(); } protected: /* Methods to be overriden */ /** * Tag open handler. * @param tagname the tag name * @param attrs a map of attribute name/value pairs */ virtual void startElement(const std::string& /* nm */, const std::map& /* attrs */) {} /** Expatmm compat. */ virtual void StartElement(const XML_Char *, const XML_Char **) {} /** * Tag close handler. * You should probably have been accumulating text and stuff since * the tag opening. * @param tagname the tag name. */ virtual void endElement(const std::string& /* nm */) {} /** Expatmm compat */ virtual void EndElement(const XML_Char * /* nm */) {} /** * Non-tag data handler. * @param data the data. */ virtual void characterData(const std::string& /*data*/) {} /** Expatmm compat */ virtual void CharacterData(const XML_Char *, int) {} /** * Return current tag name stack. Deprecated, use m_path. * This does not include the current (bottom) tag. * Attributes are not kept in there, you'll have to do this yourself. * @return a const ref to a vector of tag names. */ virtual const std::vector& tagStack() { return m_tagstack; } /** * Current element stack, including the bottom one * Each entry includes the attributes and the starting character offset. * The stack includes the last element (the one open is called for). */ class StackEl { public: StackEl(const std::string& nm) : name(nm) {} std::string name; std::string::size_type start_index; std::map attributes; std::string data; // Derived class usage }; std::vector m_path; private: const std::string& m_in; std::string::size_type m_pos{0}; std::stringstream m_reason; std::vector m_tagstack; void _startelem(const std::string& tagname, const std::map& attrs, bool empty) { m_path.push_back(StackEl(tagname)); StackEl& lastelt = m_path.back(); lastelt.start_index = m_pos; lastelt.attributes = attrs; startElement(tagname, attrs); #ifdef PICOXML_EXPAT_STARTELEMENT_COMPAT { // Call Expat-compatible StartElement auto sz = 2*(attrs.size()+1); if (m_vattrs.size() < sz) { m_vattrs.resize(sz); } auto it = m_vattrs.begin(); for (const auto& attr : attrs) { *it++ = attr.first.c_str(); *it++ = attr.second.c_str(); } *it++ = nullptr; *it++ = nullptr; StartElement(tagname.c_str(), m_vattrs.data()); } #else StartElement(tagname.c_str(), nullptr); #endif m_tagstack.push_back(tagname); // Compat if (empty) { _endelem(tagname); } } void _endelem(const std::string& tagname) { m_tagstack.pop_back(); endElement(tagname); EndElement(tagname.c_str()); m_path.pop_back(); } bool _parse() { // skip initial whitespace and XML decl. On success, returns with // current pos on first tag '<' if (!skipDecl()) { return false; } if (nomore()) { // empty file return true; } for (;;) { // Current char is '<' and the next char is not '?' //std::cerr << "m_pos " << m_pos << " char " << m_in[m_pos] << "\n"; // skipComment also processes bool wascomment; if (!skipComment(wascomment)) { return false; } if (nomore()) { if (!m_tagstack.empty()) { m_reason << "EOF hit inside open element at cpos " << m_pos; return false; } return true; } if (wascomment) continue; m_pos++; if (nomore()) { m_reason << "EOF within tag"; return false; } std::string::size_type spos = m_pos; int isendtag = m_in[m_pos] == '/' ? 1 : 0; skipStr(">"); if (m_pos == std::string::npos || m_pos <= spos + 1) { m_reason << "Empty tag or EOF inside tag. pos " << spos; return false; } int emptyel = m_in[m_pos-2] == '/' ? 1 : 0; if (emptyel && isendtag) { m_reason << "Bad tag at cpos " << spos; return false; } std::string tag = m_in.substr(spos + isendtag, m_pos - (spos + 1 + isendtag + emptyel)); //std::cerr << "TAG NAME [" << tag << "]\n"; trimtag(tag); std::map attrs; if (!parseattrs(tag, attrs)) { return false; } if (isendtag) { if (m_tagstack.empty() || tag.compare(m_tagstack.back())) { m_reason << "Closing not open tag " << tag << " at cpos " << m_pos; return false; } _endelem(tag); } else { _startelem(tag, attrs, emptyel); } spos = m_pos; if (!_chardata()) { return false; } } return false; } bool _chardata() { std::string::size_type spos = m_pos; m_pos = m_in.find("<", m_pos); if (nomore()) { return true; } if (m_pos != spos) { std::string data{unQuote(m_in.substr(spos, m_pos - spos))}; if (m_unquoteError) { return false; } characterData(data); CharacterData(data.c_str(), data.size()); } return true; } bool nomore(int sz = 0) const { return m_pos == std::string::npos || m_pos >= m_in.size() - sz; } bool skipWS(const std::string& in, std::string::size_type& pos) { if (pos == std::string::npos) return false; pos = in.find_first_not_of(" \t\n\r", pos); return pos != std::string::npos; } bool skipStr(const std::string& str) { if (m_pos == std::string::npos) return false; m_pos = m_in.find(str, m_pos); if (m_pos != std::string::npos) m_pos += str.size(); return m_pos != std::string::npos; } int peek(int sz = 0) const { if (nomore(sz)) return -1; return m_in[m_pos + 1 + sz]; } void trimtag(std::string& tag) { std::string::size_type trimpos = tag.find_last_not_of(" \t\n\r"); if (trimpos != std::string::npos) { tag = tag.substr(0, trimpos+1); } } bool skipDecl() { for (;;) { if (!skipWS(m_in, m_pos)) { m_reason << "EOF during initial ws skip"; return true; } if (m_in[m_pos] != '<') { m_reason << "EOF file does not begin with decl/tag: m_pos " << m_pos << " char [" << m_in[m_pos] << "]\n"; return false; } if (peek() == '?') { if (!skipStr("?>")) { m_reason << "EOF while looking for end of xml decl"; return false; } } else { break; } } return true; } bool skipComment(bool& wascomment) { wascomment = false; if (nomore()) { return true; } if (m_in[m_pos] != '<') { m_reason << "Internal error: skipComment called with wrong " "start: m_pos " << m_pos << " char [" << m_in[m_pos] << "]\n"; return false; } if (peek() == '!' && peek(1) == '-' && peek(2) == '-') { if (!skipStr("-->")) { m_reason << "EOF while looking for end of XML comment"; return false; } // Process possible characters until next tag wascomment = true; return _chardata(); } return true; } bool parseattrs(std::string& tag, std::map& attrs) { //std::cerr << "parseattrs: [" << tag << "]\n"; attrs.clear(); std::string::size_type spos = tag.find_first_of(" \t\n\r"); if (spos == std::string::npos) return true; std::string tagname = tag.substr(0, spos); //std::cerr << "tag name [" << tagname << "] pos " << spos << "\n"; skipWS(tag, spos); for (;;) { //std::cerr << "top of loop [" << tag.substr(spos) << "]\n"; std::string::size_type epos = tag.find_first_of(" \t\n\r=", spos); if (epos == std::string::npos) { m_reason << "Bad attributes syntax at cpos " << m_pos + epos; return false; } std::string attrnm = tag.substr(spos, epos - spos); if (attrnm.empty()) { m_reason << "Empty attribute name ?? at cpos " << m_pos + epos; return false; } //std::cerr << "attr name [" << attrnm << "]\n"; skipWS(tag, epos); if (epos == std::string::npos || epos == tag.size() - 1 || tag[epos] != '=') { m_reason <<"Missing equal sign or value at cpos " << m_pos+epos; return false; } epos++; skipWS(tag, epos); char qc{0}; if ((tag[epos] != '"' && tag[epos] != '\'') || epos == tag.size() - 1) { m_reason << "Missing quote or value at cpos " << m_pos+epos; return false; } qc = tag[epos]; spos = epos + 1; epos = tag.find_first_of(qc, spos); if (epos == std::string::npos) { m_reason << "Missing closing quote at cpos " << m_pos+spos; return false; } attrs[attrnm] = unQuote(tag.substr(spos, epos - spos)); if (m_unquoteError) { return false; } //std::cerr << "attr value [" << attrs[attrnm] << "]\n"; if (epos == tag.size() - 1) { break; } epos++; skipWS(tag, epos); if (epos == tag.size() - 1) { break; } spos = epos; } tag = tagname; return true; } std::string unQuote(const std::string &s) { static const std::string e_quot{"quot"}; static const std::string e_amp{"amp"}; static const std::string e_apos{"apos"}; static const std::string e_lt{"lt"}; static const std::string e_gt{"gt"}; m_unquoteError = false; std::string out; out.reserve(s.size()); std::string::const_iterator it = s.begin(); auto epos = 0; while (it != s.end()) { if (*it != '&') { out += *it++; continue; } // Position: m_pos minus the string size (including 2 quotes) plus position inside s epos = m_pos - (s.size() + 2) + it - s.begin(); it++; std::string code; while (it != s.end() && *it != ';') { code += *it++; } if (it == s.end()) { // Unexpected m_reason << "End of quoted string, inside entity name at cpos " << epos; m_unquoteError = true; out.clear(); break; } it++; if (code == e_quot) { out += '"'; } else if (code == e_amp) { out += '&'; } else if (code == e_apos) { out += '\''; } else if (code == e_lt) { out += '<'; } else if (code == e_gt) { out += '>'; } } return out; } std::vector m_vattrs; bool m_unquoteError; }; #endif /* _PICOXML_H_INCLUDED_ */ recoll-1.36.1/utils/chrono.h0000644000175000017500000000376614410615043012616 00000000000000/* Copyright (C) 2014 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _CHRONO_H_INCLUDED_ #define _CHRONO_H_INCLUDED_ #include #include /** Easy interface to measuring time intervals */ class Chrono { public: /** Initialize, setting the origin time */ Chrono(); /** Re-store current time and return mS since init or last call */ int64_t restart(); /** Re-store current time and return uS since init or last call */ int64_t urestart(); /** Snapshot current time to static storage */ static void refnow(); /** Return interval value in various units. * * If frozen is set this gives the time since the last refnow call * (this is to allow for using one actual system call to get values from many chrono objects, like when examining timeouts in a queue */ int64_t nanos(bool frozen = false); int64_t micros(bool frozen = false); int64_t millis(bool frozen = false); double secs(bool frozen = false); /** Return the absolute value of the current origin */ int64_t amicros() const; struct TimeSpec { time_t tv_sec; /* Time in seconds */ long tv_nsec; /* And nanoseconds (< 10E9) */ }; private: TimeSpec m_orig; static TimeSpec o_now; }; #endif /* _CHRONO_H_INCLUDED_ */ recoll-1.36.1/utils/execmd.cpp0000644000175000017500000010432514471607415013132 00000000000000/* Copyright (C) 2004-2018 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SPAWN_H #ifndef __USE_GNU #define __USE_GNU #define undef__USE_GNU #endif #include #ifdef undef__USE_GNU #undef __USE_GNU #endif #endif #include "execmd.h" #include "netcon.h" #include "closefrom.h" #include "smallut.h" #include "pathut.h" #ifdef MDU_INCLUDE_LOG #include MDU_INCLUDE_LOG #else #include "log.h" #endif using namespace std; extern char **environ; class ExecCmd::Internal { public: Internal() { sigemptyset(&m_blkcld); } static bool o_useVfork; int m_flags{0}; vector m_env; ExecCmdAdvise *m_advise{0}; ExecCmdProvide *m_provide{0}; bool m_killRequest{false}; int m_timeoutMs{1000}; int m_killTimeoutMs{2000}; #ifdef HAVE_SETRLIMIT rlim_t m_rlimit_as_bytes{0}; #endif string m_stderrFile; // Pipe for data going to the command int m_pipein[2]{-1,-1}; std::shared_ptr m_tocmd; // Pipe for data coming out int m_pipeout[2]{-1,-1}; std::shared_ptr m_fromcmd; // Subprocess id pid_t m_pid{-1}; // Saved sigmask sigset_t m_blkcld; // Reset internal state indicators. Any resources should have been // previously freed void reset() { m_killRequest = false; m_pipein[0] = m_pipein[1] = m_pipeout[0] = m_pipeout[1] = -1; m_pid = -1; sigemptyset(&m_blkcld); } // Child process code inline void dochild(const std::string& cmd, const char **argv, const char **envv, bool has_input, bool has_output); }; bool ExecCmd::Internal::o_useVfork{false}; ExecCmd::ExecCmd(int flags) { m = new Internal(); m->reset(); m->m_flags = flags; } void ExecCmd::setAdvise(ExecCmdAdvise *adv) { m->m_advise = adv; } void ExecCmd::setProvide(ExecCmdProvide *p) { m->m_provide = p; } void ExecCmd::setTimeout(int mS) { if (mS > 30) { m->m_timeoutMs = mS; } } void ExecCmd::setKillTimeout(int mS) { m->m_killTimeoutMs = mS; } void ExecCmd::setStderr(const std::string& stderrFile) { m->m_stderrFile = stderrFile; } pid_t ExecCmd::getChildPid() { return m->m_pid; } void ExecCmd::setKill() { m->m_killRequest = true; } void ExecCmd::zapChild() { setKill(); (void)wait(); } bool ExecCmd::requestChildExit() { if (m->m_pid > 0) { if (kill(m->m_pid, SIGTERM) == 0) { return true; } } return false; } /* From FreeBSD's which command */ static bool exec_is_there(const char *candidate) { struct stat fin; /* XXX work around access(2) false positives for superuser */ if (access(candidate, X_OK) == 0 && stat(candidate, &fin) == 0 && S_ISREG(fin.st_mode) && (getuid() != 0 || (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) { return true; } return false; } bool ExecCmd::which(const string& cmd, string& exepath, const char* path) { if (cmd.empty()) { return false; } if (path_isabsolute(cmd)) { if (exec_is_there(cmd.c_str())) { exepath = cmd; return true; } return false; } const char *pp; if (path) { pp = path; } else { pp = getenv("PATH"); } if (nullptr == pp) { return false; } vector pels; stringToTokens(pp, pels, path_PATHsep()); for (const auto& dir : pels) { string candidate = path_cat(dir, cmd); if (exec_is_there(candidate.c_str())) { exepath = candidate; return true; } } return false; } void ExecCmd::useVfork(bool on) { // Just in case: there are competent people who believe that the // dynamic linker can sometimes deadlock if execve() is resolved // inside the vfork/exec window. Make sure it's done now. If "/" is // an executable file, we have a problem. const char *argv[] = {"/", nullptr}; execve("/", (char *const *)argv, environ); Internal::o_useVfork = on; } void ExecCmd::putenv(const string& ea) { m->m_env.push_back(ea); } void ExecCmd::putenv(const string& name, const string& value) { string ea = name + "=" + value; putenv(ea); } static void msleep(int millis) { struct timespec spec; spec.tv_sec = millis / 1000; spec.tv_nsec = (millis % 1000) * 1000000; nanosleep(&spec, nullptr); } /** A resource manager to ensure that execcmd cleans up if an exception is * raised in the callback, or at different places on errors occurring * during method executions */ class ExecCmdRsrc { public: ExecCmdRsrc(ExecCmd::Internal *parent) : m_parent(parent), m_active(true) { } void inactivate() { m_active = false; } ~ExecCmdRsrc() { if (!m_active || !m_parent) { return; } LOGDEB1("~ExecCmdRsrc: working. mypid: " << getpid() << "\n"); // Better to close the descs first in case the child is waiting in read if (m_parent->m_pipein[0] >= 0) { close(m_parent->m_pipein[0]); } if (m_parent->m_pipein[1] >= 0) { close(m_parent->m_pipein[1]); } if (m_parent->m_pipeout[0] >= 0) { close(m_parent->m_pipeout[0]); } if (m_parent->m_pipeout[1] >= 0) { close(m_parent->m_pipeout[1]); } // It's apparently possible for m_pid to be > 0 and getpgid to fail. In // this case, we have to conclude that the child process does // not exist. Not too sure what causes this, but the previous code // definitely tried to call killpg(-1,) from time to time. pid_t grp; if (m_parent->m_pid > 0 && (grp = getpgid(m_parent->m_pid)) > 0) { LOGDEB("ExecCmd: pid " << m_parent->m_pid << " killpg(" << grp << ", SIGTERM)\n"); int ret = killpg(grp, SIGTERM); if (ret == 0) { int ms_slept{0}; for (int i = 0; ; i++) { int tosleep = i == 0 ? 5 : (i == 1 ? 100 : 1000); msleep(tosleep); ms_slept += tosleep; int status; (void)waitpid(m_parent->m_pid, &status, WNOHANG); if (kill(m_parent->m_pid, 0) != 0) { break; } // killtimeout == -1 -> never KILL if (m_parent->m_killTimeoutMs >= 0 && ms_slept >= m_parent->m_killTimeoutMs) { LOGDEB("ExecCmd: killpg(" << grp << ", SIGKILL)\n"); killpg(grp, SIGKILL); (void)waitpid(m_parent->m_pid, &status, WNOHANG); break; } } } else { LOGERR("ExecCmd: error killing process group " << (grp) << ": " << errno << "\n"); } } m_parent->m_tocmd.reset(); m_parent->m_fromcmd.reset(); pthread_sigmask(SIG_UNBLOCK, &m_parent->m_blkcld, nullptr); m_parent->reset(); } private: ExecCmd::Internal *m_parent{nullptr}; bool m_active{false}; }; ExecCmd::~ExecCmd() { if (m) { ExecCmdRsrc r(m); } if (m) { delete m; m = nullptr; } } // In child process. Set up pipes and exec command. // This must not return. _exit() on error. //** This can be called after a vfork, so no modification of the process memory at all is allowed ** // // Any of the LOGXX calls could block on a mutex set in the father process, so that only absolutely // exceptional conditions should be logged, for debugging and post-mortem purposes. If one of the // calls block, the problem manifests itself by 20mn (filter timeout) of looping on // "ExecCmd::doexec: selectloop returned 1", because the father is waiting on the read descriptor. inline void ExecCmd::Internal::dochild(const string& cmd, const char **argv, const char **envv, bool has_input, bool has_output) { // Start our own process group if (!(m_flags & EXF_NOSETPG) && setpgid(0, 0)) { LOGINFO("ExecCmd::DOCHILD: setpgid(0, 0) failed: errno " << errno << "\n"); } // Restore SIGTERM to default. Really, signal handling should be specified when creating the // execmd, there might be other signals to reset. Resetting SIGTERM helps Recoll get rid of its // filter children for now though. To be fixed one day... Note that resetting to SIG_DFL is a // portable use of signal(). No need for sigaction() here. // // There is supposedly a risk of problems if another thread was calling a signal-affecting // function when vfork was called. This seems acceptable though as no self-respecting thread is // going to mess with the global process signal disposition. if (signal(SIGTERM, SIG_DFL) == SIG_ERR) { //LOGERR("ExecCmd::DOCHILD: signal() failed, errno " << errno << "\n"); } sigset_t sset; sigfillset(&sset); pthread_sigmask(SIG_UNBLOCK, &sset, nullptr); sigprocmask(SIG_UNBLOCK, &sset, nullptr); #ifdef HAVE_SETRLIMIT #if defined RLIMIT_AS || defined RLIMIT_VMEM || defined RLIMIT_DATA if (m_rlimit_as_bytes > 0) { struct rlimit ram_limit = { m_rlimit_as_bytes, RLIM_INFINITY }; // RLIMIT_AS and RLIMIT_VMEM are usually synonyms when VMEM is defined. RLIMIT_AS is // POSIX. Both don't really do what we want, because they count e.g. shared lib mappings, // which we don't really care about. // RLIMIT_DATA only limits the data segment. Modern mallocs use mmap and will not be bound. // (Otoh if we only have this, we're probably not modern). // So we're unsatisfied either way. #ifdef RLIMIT_AS setrlimit(RLIMIT_AS, &ram_limit); #elif defined RLIMIT_VMEM setrlimit(RLIMIT_VMEM, &ram_limit); #else setrlimit(RLIMIT_DATA, &ram_limit); #endif } #endif #endif // have_setrlimit if (has_input) { close(m_pipein[1]); if (m_pipein[0] != 0) { dup2(m_pipein[0], 0); close(m_pipein[0]); } } if (has_output) { close(m_pipeout[0]); if (m_pipeout[1] != 1) { if (dup2(m_pipeout[1], 1) < 0) { LOGERR("ExecCmd::DOCHILD: dup2() failed. errno " << errno << "\n"); } if (close(m_pipeout[1]) < 0) { LOGERR("ExecCmd::DOCHILD: close() failed. errno " << errno << "\n"); } } } // Do we need to redirect stderr ? if (!m_stderrFile.empty()) { int fd = open(m_stderrFile.c_str(), O_WRONLY | O_CREAT #ifdef O_APPEND | O_APPEND #endif , 0600); if (fd < 0) { close(2); } else { if (fd != 2) { dup2(fd, 2); } lseek(2, 0, 2); } } // Close all descriptors except 0,1,2 libclf_closefrom(3); execve(cmd.c_str(), (char *const*)argv, (char *const*)envv); // Hu ho. This should never have happened as we checked the // existence of the executable before calling dochild... Until we // did this check, this message was the chief cause of LOG mutex deadlocks. LOGERR("ExecCmd::DOCHILD: execve(" << cmd << ") failed. errno " << errno << "\n"); _exit(127); } void ExecCmd::setrlimit_as(int mbytes) { #ifdef HAVE_SETRLIMIT if (mbytes > 2000 && sizeof(rlim_t) < 8) { // Impossible limit, don't use it m->m_rlimit_as_bytes = 0; } m->m_rlimit_as_bytes = static_cast(mbytes) * 1024 * 1024; #endif } int ExecCmd::startExec(const string& cmd, const vector& args, bool has_input, bool has_output) { { // Debug and logging string command = cmd + " "; for (const auto& arg : args) { command += "{" + arg + "} "; } LOGDEB("ExecCmd::startExec: (" << has_input << "|" << has_output << ") " << command << "\n"); } // The resource manager ensures resources are freed if we return early ExecCmdRsrc e(m); if (has_input && pipe(m->m_pipein) < 0) { LOGERR("ExecCmd::startExec: pipe(2) failed. errno " << errno << "\n" ); return -1; } if (has_output && pipe(m->m_pipeout) < 0) { LOGERR("ExecCmd::startExec: pipe(2) failed. errno " << errno << "\n"); return -1; } //////////// vfork setup section // We do here things that we could/should do after a fork(), but // not a vfork(). Does no harm to do it here in both cases, except // that it needs cleanup (as compared to doing it just before // exec()). // Allocate arg vector (2 more for arg0 + final 0) typedef const char *Ccharp; Ccharp *argv; argv = (Ccharp *)malloc((args.size() + 2) * sizeof(char *)); if (nullptr == argv) { LOGERR("ExecCmd::doexec: malloc() failed. errno " << errno << "\n"); return -1; } // Fill up argv argv[0] = cmd.c_str(); int i = 1; for (const auto& arg : args) { argv[i++] = arg.c_str(); } argv[i] = nullptr; // Environment. We first merge our environment and the specified // variables in a map, overriding existing values, // then generate an appropriate char*[] Ccharp *envv; map envmap; for (int i = 0; environ[i] != nullptr; i++) { string entry(environ[i]); string::size_type eqpos = entry.find_first_of("="); if (eqpos == string::npos) { continue; } envmap[entry.substr(0, eqpos)] = entry.substr(eqpos+1); } for (const auto& entry : m->m_env) { string::size_type eqpos = entry.find_first_of("="); if (eqpos == string::npos) { continue; } envmap[entry.substr(0, eqpos)] = entry.substr(eqpos+1); } // Allocate space for the array + string storage in one block. unsigned int allocsize = (envmap.size() + 2) * sizeof(char *); for (const auto& it : envmap) { allocsize += it.first.size() + 1 + it.second.size() + 1; } envv = (Ccharp *)malloc(allocsize); if (nullptr == envv) { LOGERR("ExecCmd::doexec: malloc() failed. errno " << errno << "\n"); free(argv); return -1; } // Copy to new env array i = 0; char *cp = ((char *)envv) + (envmap.size() + 2) * sizeof(char *); for (const auto& it : envmap) { strcpy(cp, (it.first + "=" + it.second).c_str()); envv[i++] = cp; cp += it.first.size() + 1 + it.second.size() + 1; } envv[i++] = nullptr; // As we are going to use execve, not execvp, do the PATH thing. string exe; if (!which(cmd, exe)) { LOGERR("ExecCmd::startExec: " << cmd << " not found\n"); free(argv); free(envv); return 127 << 8; } //////////////////////////////// End vfork child prepare section. #if HAVE_POSIX_SPAWN && USE_POSIX_SPAWN // Note that posix_spawn provides no way to setrlimit() the child. { posix_spawnattr_t attrs; posix_spawnattr_init(&attrs); short flags; posix_spawnattr_getflags(&attrs, &flags); flags |= POSIX_SPAWN_USEVFORK; posix_spawnattr_setpgroup(&attrs, 0); flags |= POSIX_SPAWN_SETPGROUP; sigset_t sset; sigemptyset(&sset); posix_spawnattr_setsigmask(&attrs, &sset); flags |= POSIX_SPAWN_SETSIGMASK; sigemptyset(&sset); sigaddset(&sset, SIGTERM); posix_spawnattr_setsigdefault(&attrs, &sset); flags |= POSIX_SPAWN_SETSIGDEF; posix_spawnattr_setflags(&attrs, flags); posix_spawn_file_actions_t facts; posix_spawn_file_actions_init(&facts); if (has_input) { posix_spawn_file_actions_addclose(&facts, m->m_pipein[1]); if (m->m_pipein[0] != 0) { posix_spawn_file_actions_adddup2(&facts, m->m_pipein[0], 0); posix_spawn_file_actions_addclose(&facts, m->m_pipein[0]); } } if (has_output) { posix_spawn_file_actions_addclose(&facts, m->m_pipeout[0]); if (m->m_pipeout[1] != 1) { posix_spawn_file_actions_adddup2(&facts, m->m_pipeout[1], 1); posix_spawn_file_actions_addclose(&facts, m->m_pipeout[1]); } } // Do we need to redirect stderr ? if (!m->m_stderrFile.empty()) { int oflags = O_WRONLY | O_CREAT; #ifdef O_APPEND oflags |= O_APPEND; #endif posix_spawn_file_actions_addopen(&facts, 2, m->m_stderrFile.c_str(), oflags, 0600); } LOGDEB1("using SPAWN\n"); // posix_spawn() does not have any standard way to ask for // calling closefrom(). Afaik there is a solaris extension for this, // but let's just add all fds for (int i = 3; i < libclf_maxfd(); i++) { posix_spawn_file_actions_addclose(&facts, i); } int ret = posix_spawn(&m->m_pid, exe.c_str(), &facts, &attrs, (char *const *)argv, (char *const *)envv); posix_spawnattr_destroy(&attrs); posix_spawn_file_actions_destroy(&facts); if (ret) { LOGERR("ExecCmd::startExec: posix_spawn() failed. errno " << ret << "\n"); return -1; } } #else if (Internal::o_useVfork) { LOGDEB1("using VFORK\n"); m->m_pid = vfork(); } else { LOGDEB1("using FORK\n"); m->m_pid = fork(); } if (m->m_pid < 0) { LOGERR("ExecCmd::startExec: fork(2) failed. errno " << errno << "\n"); return -1; } if (m->m_pid == 0) { // e.inactivate() is not needed. As we do not return, the call // stack won't be unwound and destructors of local objects // won't be called. m->dochild(exe, argv, envv, has_input, has_output); // dochild does not return. Just in case... _exit(1); } #endif // Father process //////////////////// // Vfork cleanup section free(argv); free(envv); /////////////////// // Set the process group for the child. This is also done in the // child process see wikipedia(Process_group) if (setpgid(m->m_pid, m->m_pid)) { // This can fail with EACCES if the son has already done execve // (linux at least) LOGDEB2("ExecCmd: father setpgid(son)(" << m->m_pid << "," << m->m_pid << ") errno " << errno << " (ok)\n"); } sigemptyset(&m->m_blkcld); sigaddset(&m->m_blkcld, SIGCHLD); pthread_sigmask(SIG_BLOCK, &m->m_blkcld, nullptr); if (has_input) { close(m->m_pipein[0]); m->m_pipein[0] = -1; NetconCli *iclicon = new NetconCli(); iclicon->setconn(m->m_pipein[1]); m->m_tocmd = std::shared_ptr(iclicon); } if (has_output) { close(m->m_pipeout[1]); m->m_pipeout[1] = -1; NetconCli *oclicon = new NetconCli(); oclicon->setconn(m->m_pipeout[0]); m->m_fromcmd = std::shared_ptr(oclicon); } /* Don't want to undo what we just did ! */ e.inactivate(); return 0; } // Netcon callback. Send data to the command's input class ExecWriter : public NetconWorker { public: ExecWriter(const string *input, ExecCmdProvide *provide, ExecCmd::Internal *parent) : m_cmd(parent), m_input(input), m_cnt(0), m_provide(provide) { } void shutdown() { close(m_cmd->m_pipein[1]); m_cmd->m_pipein[1] = -1; m_cmd->m_tocmd.reset(); } virtual int data(NetconData *con, Netcon::Event reason) { PRETEND_USE(reason); if (!m_input) { return -1; } LOGDEB1("ExecWriter: input m_cnt " << m_cnt << " input length " << m_input->length() << "\n"); if (m_cnt >= m_input->length()) { // Fd ready for more but we got none. Try to get data, else // shutdown; if (!m_provide) { shutdown(); return 0; } m_provide->newData(); if (m_input->empty()) { shutdown(); return 0; } else { // Ready with new buffer, reset use count m_cnt = 0; } LOGDEB2("ExecWriter: provide m_cnt " << m_cnt << " input length " << m_input->length() << "\n"); } int ret = con->send(m_input->c_str() + m_cnt, m_input->length() - m_cnt); LOGDEB2("ExecWriter: wrote " << (ret) << " to command\n"); if (ret <= 0) { LOGERR("ExecWriter: data: can't write\n"); return -1; } m_cnt += ret; return ret; } private: ExecCmd::Internal *m_cmd; const string *m_input; unsigned int m_cnt; // Current offset inside m_input ExecCmdProvide *m_provide; }; // Netcon callback. Get data from the command output. class ExecReader : public NetconWorker { public: ExecReader(string *output, ExecCmdAdvise *advise) : m_output(output), m_advise(advise) { } virtual int data(NetconData *con, Netcon::Event reason) { PRETEND_USE(reason); char buf[8192]; int n = con->receive(buf, 8192); LOGDEB1("ExecReader: got " << (n) << " from command\n"); if (n < 0) { LOGERR("ExecCmd::doexec: receive failed. errno " << errno << "\n"); } else if (n > 0) { m_output->append(buf, n); if (m_advise) { m_advise->newData(n); } } // else n == 0, just return return n; } private: string *m_output; ExecCmdAdvise *m_advise; }; int ExecCmd::doexec(const string& cmd, const vector& args, const string *input, string *output) { int status = startExec(cmd, args, input != nullptr, output != nullptr); if (status) { return status; } // Cleanup in case we return early ExecCmdRsrc e(m); SelectLoop myloop; int ret = 0; if (input || output) { // Setup output if (output) { NetconCli *oclicon = m->m_fromcmd.get(); if (!oclicon) { LOGERR("ExecCmd::doexec: no connection from command\n"); return -1; } oclicon->setcallback(std::shared_ptr (new ExecReader(output, m->m_advise))); myloop.addselcon(m->m_fromcmd, Netcon::NETCONPOLL_READ); // Give up ownership m->m_fromcmd.reset(); } // Setup input if (input) { NetconCli *iclicon = m->m_tocmd.get(); if (!iclicon) { LOGERR("ExecCmd::doexec: no connection from command\n"); return -1; } iclicon->setcallback(std::shared_ptr (new ExecWriter(input, m->m_provide, m))); myloop.addselcon(m->m_tocmd, Netcon::NETCONPOLL_WRITE); // Give up ownership m->m_tocmd.reset(); } // Do the actual reading/writing/waiting myloop.setperiodichandler(nullptr, nullptr, m->m_timeoutMs); while ((ret = myloop.doLoop()) > 0) { LOGDEB("ExecCmd::doexec: selectloop returned " << (ret) << "\n"); if (m->m_advise) { m->m_advise->newData(0); } if (m->m_killRequest) { LOGINFO("ExecCmd::doexec: cancel request\n"); break; } } LOGDEB0("ExecCmd::doexec: selectloop returned " << (ret) << "\n"); // Check for interrupt request: we won't want to waitpid() if (m->m_advise) { m->m_advise->newData(0); } // The netcons don't take ownership of the fds: we have to close them // (have to do it before wait, this may be the signal the child is // waiting for exiting). if (input) { close(m->m_pipein[1]); m->m_pipein[1] = -1; } if (output) { close(m->m_pipeout[0]); m->m_pipeout[0] = -1; } } // Normal return: deactivate cleaner, wait() will do the cleanup e.inactivate(); int ret1 = ExecCmd::wait(); if (ret) { return -1; } return ret1; } int ExecCmd::send(const string& data) { NetconCli *con = m->m_tocmd.get(); if (nullptr == con) { LOGERR("ExecCmd::send: outpipe is closed\n"); return -1; } unsigned int nwritten = 0; while (nwritten < data.length()) { if (m->m_killRequest) { break; } int n = con->send(data.c_str() + nwritten, data.length() - nwritten); if (n < 0) { LOGERR("ExecCmd::send: send failed\n"); return -1; } nwritten += n; } return nwritten; } int ExecCmd::receive(string& data, int cnt) { NetconCli *con = m->m_fromcmd.get(); if (nullptr == con) { LOGERR("ExecCmd::receive: inpipe is closed\n"); return -1; } const int BS = 4096; char buf[BS]; int ntot = 0; do { int toread = cnt > 0 ? MIN(cnt - ntot, BS) : BS; int n = con->receive(buf, toread); if (n < 0) { LOGERR("ExecCmd::receive: error\n"); return -1; } else if (n > 0) { ntot += n; data.append(buf, n); } else { LOGDEB("ExecCmd::receive: got 0\n"); break; } } while (cnt > 0 && ntot < cnt); return ntot; } int ExecCmd::getline(string& data) { NetconCli *con = m->m_fromcmd.get(); if (nullptr == con) { LOGERR("ExecCmd::receive: inpipe is closed\n"); return -1; } const int BS = 1024; char buf[BS]; int timeosecs = m->m_timeoutMs / 1000; if (timeosecs == 0) { timeosecs = 1; } // Note that we only go once through here, except in case of // timeout, which is why I think that the goto is more expressive // than a loop again: int n = con->getline(buf, BS, timeosecs); if (n < 0) { if (con->timedout()) { LOGDEB0("ExecCmd::getline: select timeout, report and retry\n"); if (m->m_advise) { m->m_advise->newData(0); } goto again; } LOGERR("ExecCmd::getline: error\n"); } else if (n > 0) { data.append(buf, n); } else { LOGDEB("ExecCmd::getline: got 0\n"); } return n; } class GetlineWatchdog : public ExecCmdAdvise { public: GetlineWatchdog(int secs) : m_secs(secs), tstart(time(nullptr)) {} void newData(int cnt) { PRETEND_USE(cnt); if (time(nullptr) - tstart >= m_secs) { throw std::runtime_error("getline timeout"); } } int m_secs; time_t tstart; }; int ExecCmd::getline(string& data, int timeosecs) { GetlineWatchdog gwd(timeosecs); setAdvise(&gwd); try { return getline(data); } catch (...) { return -1; } } // Wait for command status and clean up all resources. // We would like to avoid blocking here too, but there is no simple // way to do this. The 2 possible approaches would be to: // - Use signals (alarm), waitpid() is interruptible. but signals and // threads... This would need a specialized thread, inter-thread comms etc. // - Use an intermediary process when starting the command. The // process forks a timer process, and the real command, then calls // a blocking waitpid on all at the end, and is guaranteed to get // at least the timer process status, thus yielding a select() // equivalent. This is bad too, because the timeout is on the whole // exec, not just the wait // Just calling waitpid() with WNOHANG with a sleep() between tries // does not work: the first waitpid() usually comes too early and // reaps nothing, resulting in almost always one sleep() or more. // // So no timeout here. This has not been a problem in practise inside recoll. // In case of need, using a semi-busy loop with short sleeps // increasing from a few mS might work without creating too much // overhead. int ExecCmd::wait() { ExecCmdRsrc e(m); int status = -1; if (!m->m_killRequest && m->m_pid > 0) { if (waitpid(m->m_pid, &status, 0) < 0) { LOGSYSERR("ExecCmd::wait", "waitpid", ""); status = -1; } LOGDEB("ExecCmd::wait: got status 0x" << std::hex << status << std::dec << ": " << waitStatusAsString(status) << "\n"); m->m_pid = -1; } // Let the ExecCmdRsrc cleanup, it will do the killing/waiting if needed return status; } bool ExecCmd::maybereap(int *status) { ExecCmdRsrc e(m); *status = -1; if (m->m_pid <= 0) { // Already waited for ?? return true; } pid_t pid = waitpid(m->m_pid, status, WNOHANG); if (pid < 0) { LOGERR("ExecCmd::maybereap: returned -1 errno " << errno << "\n"); m->m_pid = -1; return true; } else if (pid == 0) { LOGDEB1("ExecCmd::maybereap: not exited yet\n"); e.inactivate(); return false; } else { if (*status) LOGDEB("ExecCmd::maybereap: got status 0x" << *status << "\n"); m->m_pid = -1; return true; } } // Static bool ExecCmd::backtick(const vector cmd, string& out) { if (cmd.empty()) { LOGERR("ExecCmd::backtick: empty command\n"); return false; } vector::const_iterator it = cmd.begin(); it++; vector args(it, cmd.end()); ExecCmd mexec; int status = mexec.doexec(*cmd.begin(), args, nullptr, &out); return status == 0; } std::string ExecCmd::waitStatusAsString(int wstatus) { std::ostringstream oss; if (wstatus == -1) { return "Waitpid error"; } if (WIFEXITED(wstatus)) { oss << "Exit status: " << WEXITSTATUS(wstatus); } else { if (WIFSIGNALED(wstatus)) { oss << strsignal(WTERMSIG(wstatus)) << " "; } if (WCOREDUMP(wstatus)) { oss << "(core dumped)"; } } return oss.str(); } /// ReExec class methods /////////////////////////////////////////////////// ReExec::ReExec(int argc, char *args[]) { for (int i = 0; i < argc; i++) { m_argv.push_back(args[i]); } m_cfd = open(".", 0); char *cd = getcwd(nullptr, 0); if (cd) { m_curdir = cd; } free(cd); } ReExec::ReExec(const std::vector& args) : m_argv(args) { m_cfd = open(".", 0); char *cd = getcwd(nullptr, 0); if (cd) { m_curdir = cd; } free(cd); } void ReExec::insertArgs(const vector& args, int idx) { vector::iterator it; unsigned int cmpoffset = (unsigned int) - 1; if (idx == -1 || string::size_type(idx) >= m_argv.size()) { it = m_argv.end(); if (m_argv.size() >= args.size()) { cmpoffset = m_argv.size() - args.size(); } } else { it = m_argv.begin() + idx; if (idx + args.size() <= m_argv.size()) { cmpoffset = idx; } } // Check that the option is not already there if (cmpoffset != (unsigned int) - 1) { bool allsame = true; for (unsigned int i = 0; i < args.size(); i++) { if (m_argv[cmpoffset + i] != args[i]) { allsame = false; break; } } if (allsame) { return; } } m_argv.insert(it, args.begin(), args.end()); } void ReExec::removeArg(const string& arg) { for (auto it = m_argv.begin(); it != m_argv.end(); it++) { if (*it == arg) { it = m_argv.erase(it); } } } // Reexecute myself, as close as possible to the initial exec void ReExec::reexec() { #if 0 char *cwd; cwd = getcwd(0, 0); FILE *fp = stdout; //fopen("/tmp/exectrace", "w"); if (fp) { fprintf(fp, "reexec: pwd: [%s] args: ", cwd ? cwd : "getcwd failed"); for (const auto& arg: m_argv) { fprintf(fp, "[%s] ", arg.c_str()); } fprintf(fp, "\n"); } #endif // Execute the atexit funcs while (!m_atexitfuncs.empty()) { (m_atexitfuncs.top())(); m_atexitfuncs.pop(); } // Try to get back to the initial working directory if (m_cfd < 0 || fchdir(m_cfd) < 0) { LOGINFO("ReExec::reexec: fchdir failed, trying chdir\n"); if (!m_curdir.empty() && chdir(m_curdir.c_str())) { LOGERR("ReExec::reexec: chdir failed\n"); } } // Close all descriptors except 0,1,2 libclf_closefrom(3); // Allocate arg vector (1 more for final 0) typedef const char *Ccharp; Ccharp *argv; argv = (Ccharp *)malloc((m_argv.size() + 1) * sizeof(char *)); if (nullptr == argv) { LOGERR("ExecCmd::doexec: malloc() failed. errno " << errno << "\n"); return; } // Fill up argv int i = 0; for (const auto& arg : m_argv) { argv[i++] = arg.c_str(); } argv[i] = nullptr; execvp(m_argv[0].c_str(), (char *const*)argv); } recoll-1.36.1/utils/fstreewalk.cpp0000644000175000017500000004126214427373216014034 00000000000000/* Copyright (C) 2004-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include "cstr.h" #include "log.h" #include "pathut.h" #include "fstreewalk.h" using namespace std; bool FsTreeWalker::o_useFnmPathname = true; string FsTreeWalker::o_nowalkfn; const int FsTreeWalker::FtwTravMask = FtwTravNatural| FtwTravBreadth|FtwTravFilesThenDirs|FtwTravBreadthThenDepth; #ifndef _WIN32 // dev/ino means nothing on Windows. It seems that FileId could replace it // but we only use this for cycle detection which we just disable. #include class DirId { public: dev_t dev; ino_t ino; DirId(dev_t d, ino_t i) : dev(d), ino(i) {} bool operator<(const DirId& r) const { return dev < r.dev || (dev == r.dev && ino < r.ino); } }; #endif class FsTreeWalker::Internal { public: Internal(int opts) : options(opts), depthswitch(4), maxdepth(-1), errors(0) { } int options; int depthswitch; int maxdepth; int basedepth; stringstream reason; vector skippedNames; vector onlyNames; vector skippedPaths; // When doing Breadth or FilesThenDirs traversal, we keep a list // of directory paths to be processed, and we do not recurse. deque dirs; int errors; #ifndef _WIN32 set donedirs; #endif void logsyserr(const char *call, const string ¶m) { errors++; reason << call << "(" << param << ") : " << errno << " : " << strerror(errno) << endl; } }; FsTreeWalker::FsTreeWalker(int opts) { data = new Internal(opts); } FsTreeWalker::~FsTreeWalker() { delete data; } void FsTreeWalker::setOpts(int opts) { if (data) { data->options = opts; } } int FsTreeWalker::getOpts() { if (data) { return data->options; } else { return 0; } } void FsTreeWalker::setDepthSwitch(int ds) { if (data) { data->depthswitch = ds; } } void FsTreeWalker::setMaxDepth(int md) { if (data) { data->maxdepth = md; } } string FsTreeWalker::getReason() { string reason = data->reason.str(); data->reason.str(string()); data->errors = 0; return reason; } int FsTreeWalker::getErrCnt() { return data->errors; } bool FsTreeWalker::addSkippedName(const string& pattern) { if (find(data->skippedNames.begin(), data->skippedNames.end(), pattern) == data->skippedNames.end()) data->skippedNames.push_back(pattern); return true; } bool FsTreeWalker::setSkippedNames(const vector &patterns) { data->skippedNames = patterns; return true; } bool FsTreeWalker::inSkippedNames(const string& name) { for (const auto& pattern : data->skippedNames) { if (fnmatch(pattern.c_str(), name.c_str(), 0) == 0) { return true; } } return false; } bool FsTreeWalker::setOnlyNames(const vector &patterns) { data->onlyNames = patterns; return true; } bool FsTreeWalker::inOnlyNames(const string& name) { if (data->onlyNames.empty()) { // Not set: all match return true; } for (const auto& pattern : data->onlyNames) { if (fnmatch(pattern.c_str(), name.c_str(), 0) == 0) { return true; } } return false; } bool FsTreeWalker::addSkippedPath(const string& ipath) { string path = (data->options & FtwNoCanon) ? ipath : path_canon(ipath); if (find(data->skippedPaths.begin(), data->skippedPaths.end(), path) == data->skippedPaths.end()) data->skippedPaths.push_back(path); return true; } bool FsTreeWalker::setSkippedPaths(const vector &paths) { data->skippedPaths = paths; for (vector::iterator it = data->skippedPaths.begin(); it != data->skippedPaths.end(); it++) if (!(data->options & FtwNoCanon)) *it = path_canon(*it); return true; } bool FsTreeWalker::inSkippedPaths(const string& path, bool ckparents) { int fnmflags = o_useFnmPathname ? FNM_PATHNAME : 0; #ifdef FNM_LEADING_DIR if (ckparents) fnmflags |= FNM_LEADING_DIR; #endif for (vector::const_iterator it = data->skippedPaths.begin(); it != data->skippedPaths.end(); it++) { #ifndef FNM_LEADING_DIR if (ckparents) { string mpath = path; while (mpath.length() > 2) { if (fnmatch(it->c_str(), mpath.c_str(), fnmflags) == 0) return true; mpath = path_getfather(mpath); } } else #endif /* FNM_LEADING_DIR */ if (fnmatch(it->c_str(), path.c_str(), fnmflags) == 0) { return true; } } return false; } static inline int slashcount(const string& p) { return std::count(p.begin(), p.end(), '/'); } FsTreeWalker::Status FsTreeWalker::walk(const string& _top, FsTreeWalkerCB& cb) { string top = (data->options & FtwNoCanon) ? _top : path_canon(_top); if ((data->options & FtwTravMask) == 0) { data->options |= FtwTravNatural; } data->basedepth = slashcount(top); // Only used for breadthxx struct PathStat st; // We always follow symlinks at this point. Makes more sense. if (path_fileprops(top, &st) == -1) { // Note that we do not return an error if the stat call // fails. A temp file may have gone away. data->logsyserr("stat", top); return errno == ENOENT ? FtwOk : FtwError; } // Recursive version, using the call stack to store state. iwalk // will process files and recursively descend into subdirs in // physical order of the current directory. if ((data->options & FtwTravMask) == FtwTravNatural) { return iwalk(top, st, cb); } // Breadth first of filesThenDirs semi-depth first order // Managing queues of directories to be visited later, in breadth or // depth order. Null marker are inserted in the queue to indicate // father directory changes (avoids computing parents all the time). data->dirs.push_back(top); Status status; while (!data->dirs.empty()) { string dir, nfather; if (data->options & (FtwTravBreadth|FtwTravBreadthThenDepth)) { // Breadth first, pop and process an older dir at the // front of the queue. This will add any child dirs at the // back dir = data->dirs.front(); data->dirs.pop_front(); if (dir.empty()) { // Father change marker. if (data->dirs.empty()) break; dir = data->dirs.front(); data->dirs.pop_front(); nfather = path_getfather(dir); if (data->options & FtwTravBreadthThenDepth) { // Check if new depth warrants switch to depth first // traversal (will happen on next loop iteration). int curdepth = slashcount(dir) - data->basedepth; if (curdepth >= data->depthswitch) { //fprintf(stderr, "SWITCHING TO DEPTH FIRST\n"); data->options &= ~FtwTravMask; data->options |= FtwTravFilesThenDirs; } } } } else { // Depth first, pop and process latest dir dir = data->dirs.back(); data->dirs.pop_back(); if (dir.empty()) { // Father change marker. if (data->dirs.empty()) break; dir = data->dirs.back(); data->dirs.pop_back(); nfather = path_getfather(dir); } } // If changing parent directory, advise our user. if (!nfather.empty()) { if (path_fileprops(nfather, &st) == -1) { data->logsyserr("stat", nfather); return errno == ENOENT ? FtwOk : FtwError; } if (!(data->options & FtwOnlySkipped)) { status = cb.processone(nfather, FtwDirReturn, st); if (status & (FtwStop|FtwError)) { return status; } } } if (path_fileprops(dir, &st) == -1) { data->logsyserr("stat", dir); return errno == ENOENT ? FtwOk : FtwError; } // iwalk will not recurse in this case, just process file entries // and append subdir entries to the queue. status = iwalk(dir, st, cb); if (status != FtwOk) { return status; } } return FtwOk; } // Note that the 'norecurse' flag is handled as part of the directory read. // This means that we always go into the top 'walk()' parameter if it is a // directory, even if norecurse is set. Bug or Feature ? FsTreeWalker::Status FsTreeWalker::iwalk( const string &top, const struct PathStat& stp, FsTreeWalkerCB& cb) { Status status = FtwOk; bool nullpush = false; // Tell user to process the top entry itself if (stp.pst_type == PathStat::PST_DIR) { if (!(data->options & FtwOnlySkipped)) { status = cb.processone(top, FtwDirEnter, stp); if (status & (FtwStop|FtwError)) { return status; } } } else if (stp.pst_type == PathStat::PST_REGULAR) { if (!(data->options & FtwOnlySkipped)) { return cb.processone(top, FtwRegular, stp); } else { return status; } } else { return status; } int curdepth = slashcount(top) - data->basedepth; if (data->maxdepth >= 0 && curdepth >= data->maxdepth) { LOGDEB1("FsTreeWalker::iwalk: Maxdepth reached: [" << (top) << "]\n" ); return status; } // This is a directory, read it and process entries: #ifndef _WIN32 // Detect if directory already seen. This could just be several // symlinks pointing to the same place (if FtwFollow is set), it // could also be some other kind of cycle. In any case, there is // no point in entering again. // For now, we'll ignore the "other kind of cycle" part and only monitor // this is FtwFollow is set if (data->options & FtwFollow) { DirId dirid(stp.pst_dev, stp.pst_ino); if (data->donedirs.find(dirid) != data->donedirs.end()) { LOGINFO("Not processing [" << top << "] (already seen as other path)\n"); return status; } data->donedirs.insert(dirid); } #endif PathDirContents dc(top); if (!dc.opendir()) { data->logsyserr("opendir", top); switch (errno) { case EPERM: case EACCES: case ENOENT: #ifdef _WIN32 // We get this quite a lot, don't know why. To be checked. case EINVAL: #endif // No error set: indexing will continue in other directories goto out; default: status = FtwError; goto out; } } const struct PathDirContents::Entry *ent; while (errno = 0, ((ent = dc.readdir()) != nullptr)) { string fn; struct PathStat st; const string& dname{ent->d_name}; if (dname.empty()) { // ??? continue; } // Maybe skip dotfiles if ((data->options & FtwSkipDotFiles) && dname[0] == '.') continue; // Skip . and .. if (dname == "." || dname == "..") continue; // Skipped file names match ? if (!data->skippedNames.empty()) { if (inSkippedNames(dname)) { cb.processone(path_cat(top, dname), FtwSkipped); continue; } } fn = path_cat(top, dname); // Skipped file paths match ? if (!data->skippedPaths.empty()) { // We do not check the ancestors. This means that you can have // a topdirs member under a skippedPath, to index a portion of // an ignored area. This is the way it had always worked, but // this was broken by 1.13.00 and the systematic use of // FNM_LEADING_DIR if (inSkippedPaths(fn, false)) { cb.processone(fn, FtwSkipped); continue; } } int statret = path_fileprops(fn.c_str(), &st, data->options&FtwFollow); if (statret == -1) { data->logsyserr("stat", fn); #ifdef _WIN32 int rc = GetLastError(); LOGERR("stat failed: LastError " << rc << endl); if (rc == ERROR_NETNAME_DELETED) { status = FtwError; goto out; } #endif continue; } if (st.pst_type == PathStat::PST_DIR) { if (!o_nowalkfn.empty() && path_exists(path_cat(fn, o_nowalkfn))) { continue; } if (data->options & FtwNoRecurse) { if (!(data->options & FtwOnlySkipped)) { status = cb.processone(fn, FtwDirEnter, st); } else { status = FtwOk; } } else { if (data->options & FtwTravNatural) { status = iwalk(fn, st, cb); } else { // If first subdir, push marker to separate // from entries for other dir. This is to help // with generating DirReturn callbacks if (!nullpush) { if (!data->dirs.empty() && !data->dirs.back().empty()) { data->dirs.push_back(cstr_null); } nullpush = true; } data->dirs.push_back(fn); continue; } } // Note: only recursive case gets here. if (status & (FtwStop|FtwError)) goto out; if (!(data->options & FtwNoRecurse)) if (!(data->options & FtwOnlySkipped)) { status = cb.processone(top, FtwDirReturn, st); if (status & (FtwStop|FtwError)) { goto out; } } } else if (st.pst_type == PathStat::PST_REGULAR || st.pst_type == PathStat::PST_SYMLINK) { // Filtering patterns match ? if (!data->onlyNames.empty()) { if (!inOnlyNames(dname)) continue; } if (!(data->options & FtwOnlySkipped)) { status = cb.processone(fn, FtwRegular, st); if (status & (FtwStop|FtwError)) { goto out; } } } // We ignore other file types (devices etc...) } // readdir loop if (errno) { // Actual readdir error, not eof. data->logsyserr("readdir", top); #ifdef _WIN32 int rc = GetLastError(); LOGERR("Readdir failed: LastError " << rc << endl); if (rc == ERROR_NETNAME_DELETED) { status = FtwError; goto out; } #endif } out: return status; } int64_t fsTreeBytes(const string& topdir) { class bytesCB : public FsTreeWalkerCB { public: FsTreeWalker::Status processone( const string &, FsTreeWalker::CbFlag flg, const struct PathStat& st) override { if (flg == FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwRegular) { #ifdef _WIN32 totalbytes += st.pst_size; #else totalbytes += st.pst_blocks * 512; #endif } return FsTreeWalker::FtwOk; } int64_t totalbytes{0}; }; FsTreeWalker walker; bytesCB cb; FsTreeWalker::Status status = walker.walk(topdir, cb); if (status != FsTreeWalker::FtwOk) { LOGERR("fsTreeBytes: walker failed: " << walker.getReason() << endl); return -1; } return cb.totalbytes; } recoll-1.36.1/utils/pathut.h0000644000175000017500000002247514477607330012646 00000000000000/* Copyright (C) 2004-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _PATHUT_H_INCLUDED_ #define _PATHUT_H_INCLUDED_ // Miscellaneous pathname-related utility functions, some actually accessing the filesystem, some // purely textual. Work with Posix or Windows paths. All properly handle UTF-8 encoded non-ASCII // paths on Windows, which is their reason for existing in many cases. #include #include #include #include #include #include #ifndef _WIN32 #include #endif namespace MedocUtils { // Must be called in main thread before starting other threads extern void pathut_init_mt(); /// Add a / at the end if none there yet. extern void path_catslash(std::string& s); /// Concatenate 2 paths extern std::string path_cat(const std::string& s1, const std::string& s2); /// Concatenate 2 or more paths extern std::string path_cat(const std::string& s1, std::initializer_list pathelts); /// Get the simple file name (get rid of any directory path prefix extern std::string path_getsimple(const std::string& s); /// Simple file name + optional suffix stripping extern std::string path_basename(const std::string& s, const std::string& suff = std::string()); /// Component after last '.' extern std::string path_suffix(const std::string& s); /// Get the father directory extern std::string path_getfather(const std::string& s); /// Test if path is absolute extern bool path_isabsolute(const std::string& s); /// Test if path is root (x:/). root is defined by root/.. == root extern bool path_isroot(const std::string& p); /// Test if sub is a subdirectory of top. This is a textual test, /// links not allowed. Uses path_canon to clean up paths. extern bool path_isdesc(const std::string& top, const std::string& sub); /// Check if path looks like a (slashized) Windows UNC path, and extract the //server/volume extern bool path_isunc(const std::string& p, std::string& volume); /// Clean up path by removing duplicated / and resolving ../ + make it absolute. /// Except for possibly obtaining the current directory, the processing /// is purely textual and does not deal with symbolic link or file existence. extern std::string path_canon(const std::string& s, const std::string *cwd = nullptr); /// Check that path refers to same file. Uses dev/ino on Linux, /// textual comparison on Windows. bool path_samefile(const std::string& p1, const std::string& p2); /// Get the current user's home directory extern std::string path_home(); /// Get the top location for cached data extern std::string path_cachedir(); /// Expand ~ at the beginning of std::string extern std::string path_tildexpand(const std::string& s); /// Use getcwd() to make absolute path if needed. Beware: ***this can fail*** /// we return an empty path in this case. extern std::string path_absolute(const std::string& s); /// Stat parameter and check if it's a directory extern bool path_isdir(const std::string& path, bool follow = false); /// Stat parameter and check if it's a regular file extern bool path_isfile(const std::string& path, bool follow = false); /// Retrieve file size extern long long path_filesize(const std::string& path); /// Check that path is traversable and last element exists /// Returns true if last elt could be checked to exist. False may mean that /// the file/dir does not exist or that an error occurred. bool path_exists(const std::string& path); /// Same but must be readable bool path_readable(const std::string& path); #ifdef _WIN32 // Constants for _waccess() # ifndef R_OK # define R_OK 4 # endif # ifndef W_OK # define W_OK 2 # endif # ifndef X_OK // Not useful/supported on Windows. Define as R_OK # define X_OK R_OK # endif # ifndef F_OK # define F_OK 0 # endif // Conversion between utf-8 and wide char file names. bool wchartoutf8(const wchar_t *in, std::string& out, size_t len = 0); std::string wchartoutf8(const wchar_t *in, size_t len = 0); bool utf8towchar(const std::string& in, wchar_t *out, size_t obytescap); std::unique_ptr utf8towchar(const std::string& in); // Convert between slash and backslash separators. void path_slashize(std::string& s); void path_backslashize(std::string& s); extern std::string path_shortpath(const std::string& path); #else // !_WIN32 -> #define path_shortpath(path) (path) #ifndef O_BINARY #define O_BINARY 0 #endif #endif /* !_WIN32 */ /// access() or _waccess() bool path_access(const std::string& path, int mode); /// Retrieve essential file attributes. This is used rather than a /// bare stat() to ensure consistent use of the time fields (on /// windows, we set ctime=mtime as ctime is actually the creation /// time, for which we have no use). /// st_btime (birth time) is only really set on Ux/Linux if statx() is available and the file system /// supports it. Else it is set to st_ctime. On Windows it is set to the creation time. /// Only st_mtime, st_ctime, st_size, st_mode (file type bits) are set on /// all systems. st_dev and st_ino are set for special posix usage. /// The rest is zeroed. /// @ret 0 for success struct PathStat { enum PstType {PST_REGULAR, PST_SYMLINK, PST_DIR, PST_OTHER, PST_INVALID}; PstType pst_type{PST_INVALID}; int64_t pst_size; uint64_t pst_mode; int64_t pst_mtime; int64_t pst_ctime; uint64_t pst_ino; uint64_t pst_dev; uint64_t pst_blocks; uint64_t pst_blksize; int64_t pst_btime; }; extern int path_fileprops(const std::string path, struct PathStat *stp, bool follow = true); /// Return separator for PATH environment variable extern const std::string& path_PATHsep(); /// Directory reading interface. UTF-8 on Windows. class PathDirContents { public: PathDirContents(const std::string& dirpath); ~PathDirContents(); PathDirContents(const PathDirContents&) = delete; PathDirContents& operator=(const PathDirContents&) = delete; bool opendir(); struct Entry { std::string d_name; }; const struct Entry* readdir(); void rewinddir(); private: class Internal; Internal *m{nullptr}; }; /// Dump directory extern bool listdir(const std::string& dir, std::string& reason, std::set& entries); /** A small wrapper around statfs et al, to return percentage of disk occupation @param[output] pc percent occupied @param[output] avmbs Mbs available to non-superuser. Mb=1024*1024 */ bool fsocc(const std::string& path, int *pc, long long *avmbs = nullptr); /// mkdir -p extern bool path_makepath(const std::string& path, int mode); /// bool path_chdir(const std::string& path); std::string path_cwd(); bool path_unlink(const std::string& path); bool path_rmdir(const std::string& path); // Setting file times. Windows defines timeval in winsock2.h but it seems safer to use local def // Also on Windows, we use _wutime and ignore the tv_usec part. typedef struct path_timeval { int64_t tv_sec; int64_t tv_usec; } path_timeval; bool path_utimes(const std::string& path, struct path_timeval times[2]); /// Open, path is utf-8 and we do the right thing on Windows. int path_open(const std::string& path, int flags, int mode = 0); /* Open file, trying to do the right thing with non-ASCII paths on * Windows, where it only works with MSVC at the moment if the path is * not ASCII, because it uses fstream(wchar_t*), which is an MSVC * extension. On other OSes, just builds the fstream. We'd need to * find a way to make this work with g++. It would be easier in this * case to use a FILE (_openw(), then fdopen()), but conftree really * depends on std::iostream. * * @param path an utf-8 file path. * @param mode is an std::fstream mode (ios::in etc.) */ extern bool path_streamopen(const std::string& path, int mode, std::fstream& outstream); /// Lock/pid file class. This is quite close to the pidfile_xxx /// utilities in FreeBSD with a bit more encapsulation. I'd have used /// the freebsd code if it was available elsewhere class Pidfile { public: Pidfile(const std::string& path) : m_path(path), m_fd(-1) {} ~Pidfile(); Pidfile(const Pidfile&) = delete; Pidfile& operator=(const Pidfile&) = delete; /// Open/create the pid file. /// @return 0 if ok, > 0 for pid of existing process, -1 for other error. int open(); /// Write pid into the pid file /// @return 0 ok, -1 error int write_pid(); /// Close the pid file (unlocks) int close(); /// Delete the pid file int remove(); const std::string& getreason() { return m_reason; } private: std::string m_path; int m_fd; std::string m_reason; int read_pid(); int flopen(); }; } // End namespace MedocUtils using namespace MedocUtils; #endif /* _PATHUT_H_INCLUDED_ */ recoll-1.36.1/utils/appformime.h0000644000175000017500000000573714427373216013500 00000000000000/* Copyright (C) 2014 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _APPFORMIME_H_INCLUDED_ #define _APPFORMIME_H_INCLUDED_ #include #include #include /** * Rather strangely, I could not find a reasonably simple piece of * code which would parse /usr/share/applications to return a list of * apps for a given mime type. So here goes. Note that the implementation * is very primitive for now (no use of cache file, no updating once built). * Also, this is not thread-safe, but could be made so quite easily. */ class DesktopDb { public: class AppDef { public: AppDef(const std::string& nm, const std::string& cmd) : name(nm), command(cmd) {} AppDef() {} std::string name; std::string command; }; /** Build/Get the db for the standard fdo directory */ static DesktopDb* getDb(); /** Constructor for a db based on a non-standard location */ DesktopDb(const std::string& dir); /** In case of error: what happened ? */ const std::string& getReason(); /** * Get a list of applications able to process a given MIME type. * @param mime MIME type we want the apps for * @param[output] apps appropriate applications * @param[output] reason if we fail, an explanation ? * @return true for no error (apps may still be empty). false if a serious * problem was detected. */ bool appForMime(const std::string& mime, std::vector *apps, std::string *reason = nullptr); /** * Get all applications defs: * @param[output] apps applications * @return true */ bool allApps(std::vector *apps); /** * Get app with given name */ bool appByName(const std::string& nm, AppDef& app); typedef std::map > AppMap; private: /** This is used by getDb() and builds a db for the standard location */ DesktopDb(); void build(const std::string& dir); DesktopDb(const DesktopDb &); DesktopDb& operator=(const DesktopDb &); AppMap m_appMap; std::string m_reason; bool m_ok; }; /** Helper function: is MIME type a simple image ? */ bool mimeIsImage(const std::string& tp); #endif /* _APPFORMIME_H_INCLUDED_ */ recoll-1.36.1/utils/zlibut.h0000644000175000017500000000102214410615043012616 00000000000000#ifndef _ZLIBUT_H_INCLUDED_ #define _ZLIBUT_H_INCLUDED_ #include class ZLibUtBuf { public: ZLibUtBuf(); ~ZLibUtBuf(); ZLibUtBuf(const ZLibUtBuf&) = delete; ZLibUtBuf& operator=(const ZLibUtBuf&) = delete; char *getBuf() const; char *takeBuf(); size_t getCnt(); class Internal; Internal *m; }; bool inflateToBuf(const void* inp, unsigned int inlen, ZLibUtBuf& buf); bool deflateToBuf(const void* inp, unsigned int inlen, ZLibUtBuf& buf); #endif /* _ZLIBUT_H_INCLUDED_ */ recoll-1.36.1/utils/netcon.h0000644000175000017500000003155214426500174012613 00000000000000#ifndef _NETCON_H_ #define _NETCON_H_ /* Copyright (C) 2002 Jean-Francois Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #endif #include #include #include /// A set of classes to manage client-server communication over a /// connection-oriented network, or a pipe. /// /// The listening/connection-accepting code currently only uses /// TCP. The classes include client-side and server-side (accepting) /// endpoints. Netcon also has server-side static code to handle a set /// of client connections in parallel. This should be moved to a /// friend class. /// /// The client data transfer class can also be used for /// timeout-protected/asynchronous io using a given fd (ie a pipe /// descriptor) /// Base class for all network endpoints: class Netcon; typedef std::shared_ptr NetconP; class SelectLoop; class Netcon { public: enum Event {NETCONPOLL_READ = 0x1, NETCONPOLL_WRITE = 0x2}; Netcon() : m_peer(nullptr), m_fd(-1), m_ownfd(true), m_didtimo(0), m_wantedEvents(0), m_loop(nullptr) { } virtual ~Netcon(); Netcon(const Netcon&) = delete; Netcon& operator=(const Netcon&) = delete; /// Remember whom we're talking to. We let external code do this because /// the application may have a non-dns method to find the peer name. virtual void setpeer(const char *hostname); /// Retrieve the peer's hostname. Only works if it was set before ! virtual const char *getpeer() { return m_peer ? (const char *)m_peer : "none"; } /// Set or reset the TCP_NODELAY option. virtual int settcpnodelay(int on = 1); /// Did the last receive() call time out ? Resets the flag. virtual int timedout() { int s = m_didtimo; m_didtimo = 0; return s; } /// Return string version of last syscall error virtual char *sterror(); /// Return the socket descriptor virtual int getfd() { return m_fd; } /// Close the current connection if it is open virtual void closeconn(); /// Set/reset the non-blocking flag on the underlying fd. Returns /// prev state The default is that sockets are blocking except /// when added to the selectloop, or, transparently, to handle /// connection timeout issues. virtual int set_nonblock(int onoff); /// Decide what events the connection will be looking for /// (NETCONPOLL_READ, NETCONPOLL_WRITE) int setselevents(int evs); /// Retrieve the connection's currently monitored set of events int getselevents() { return m_wantedEvents; } friend class SelectLoop; SelectLoop *getloop() { return m_loop; } /// Utility function for a simplified select() interface: check one fd /// for reading or writing, for a specified maximum number of seconds. static int select1(int fd, int secs, int writing = 0); protected: char *m_peer; // Name of the connected host int m_fd; bool m_ownfd; int m_didtimo; // Used when part of the selectloop. short m_wantedEvents; SelectLoop *m_loop; // Method called by the selectloop when something can be done with a netcon virtual int cando(Netcon::Event reason) = 0; // Called when added to loop virtual void setloop(SelectLoop *loop) { m_loop = loop; } }; /// The selectloop interface is used to implement parallel servers. // The select loop mechanism allows several netcons to be used for io // in a program without blocking as long as there is data to be read // or written. In a multithread program, if each thread needs // non-blocking IO it may make sense to have one SelectLoop active per // thread. class SelectLoop { public: SelectLoop(); SelectLoop(const SelectLoop&) = delete; SelectLoop& operator=(const SelectLoop&) = delete; ~SelectLoop(); /// Add a connection to be monitored (this will usually be called /// from the server's listen connection's accept callback) int addselcon(NetconP con, int events); /// Remove a connection from the monitored set. This is /// automatically called when EOF is detected on a connection. int remselcon(NetconP con); /// Set a function to be called periodically, or a time before return. /// @param handler the function to be called. /// - if it is 0, doLoop() will return after ms mS (and can be called /// again) /// - if it is not 0, it will be called at ms mS intervals. If its return /// value is <= 0, selectloop will return. /// @param clp client data to be passed to handler at every call. /// @param ms milliseconds interval between handler calls or /// before return. Set to 0 for no periodic handler. void setperiodichandler(int (*handler)(void *), void *clp, int ms); /// Loop waiting for events on the connections and call the /// cando() method on the object when something happens (this will in /// turn typically call the app callback set on the netcon). Possibly /// call the periodic handler (if set) at regular intervals. /// @return -1 for error. 0 if no descriptors left for i/o. 1 for periodic /// timeout (should call back in after processing) int doLoop(); /// Call from data handler: make doLoop() return @param value void loopReturn(int value); friend class Netcon; private: class Internal; Internal *m; }; /////////////////////// class NetconData; /// Class for the application callback routine (when in selectloop). /// /// This is set by the app on the NetconData by calling /// setcallback(). It is then called from the NetconData's cando() /// routine, itself called by selectloop. /// /// It would be nicer to override cando() in a subclass instead of /// setting a callback, but this can't be done conveniently because /// accept() always creates a base NetconData (another approach would /// be to pass a factory function to the listener, to create /// NetconData derived classes). class NetconWorker { public: NetconWorker() {} virtual ~NetconWorker() {} NetconWorker(const NetconWorker&) = delete; NetconWorker& operator=(const NetconWorker&) = delete; virtual int data(NetconData *con, Netcon::Event reason) = 0; }; /// Base class for connections that actually transfer data. T class NetconData : public Netcon { public: NetconData(bool cancellable = false); virtual ~NetconData(); NetconData(const NetconData&) = delete; NetconData& operator=(const NetconData&) = delete; /// Write data to the connection. /// @param buf the data buffer /// @param cnt the number of bytes we should try to send /// @param expedited send data in as 'expedited' data. /// @return the count of bytes actually transferred, -1 if an /// error occurred. virtual int send(const char *buf, int cnt, int expedited = 0); /// Read from the connection /// @param buf the data buffer /// @param cnt the number of bytes we should try to read (but we return /// as soon as we get data) /// @param timeo maximum number of seconds we should be waiting for data. /// @return the count of bytes actually read (0 for EOF), or /// TimeoutOrError (-1) for timeout or error (call timedout() to /// discriminate and reset), Cancelled (-2) if cancelled. enum RcvReason {Eof = 0, TimeoutOrError = -1, Cancelled = -2}; virtual int receive(char *buf, int cnt, int timeo = -1); virtual void cancelReceive(); /// Loop on receive until cnt bytes are actually read or a timeout occurs virtual int doreceive(char *buf, int cnt, int timeo = -1); /// Read a line of text on an ascii connection. Returns -1 or byte count /// including final 0. \n is kept virtual int getline(char *buf, int cnt, int timeo = -1); /// Set handler to be called when the connection is placed in the /// selectloop and an event occurs. virtual void setcallback(std::shared_ptr user) { m_user = user; } private: char *m_buf; // Buffer. Only used when doing getline()s char *m_bufbase; // Pointer to current 1st byte of useful data int m_bufbytes; // Bytes of data. int m_bufsize; // Total buffer size int m_wkfds[2]; std::shared_ptr m_user; virtual int cando(Netcon::Event reason); // Selectloop slot }; /// Network endpoint, client side. class NetconCli : public NetconData { public: NetconCli(bool cancellable = false) : NetconData(cancellable), m_silentconnectfailure(false) { } /// Open connection to specified host and named service. Set host /// to an absolute path name for an AF_UNIX service. serv is /// ignored in this case. int openconn(const char *host, const char *serv, int timeo = -1); /// Open connection to specified host and numeric port. port is in /// HOST byte order. Set host to an absolute path name for an /// AF_UNIX service. serv is ignored in this case. int openconn(const char *host, unsigned int port, int timeo = -1); /// Reuse existing fd. /// We DONT take ownership of the fd, and do no closin' EVEN on an /// explicit closeconn() or setconn() (use getfd(), close, /// setconn(-1) if you need to really close the fd and have no /// other copy). int setconn(int fd); /// Do not log message if openconn() fails. void setSilentFail(bool onoff) { m_silentconnectfailure = onoff; } private: bool m_silentconnectfailure; // No logging of connection failures if set }; class NetconServCon; #ifdef NETCON_ACCESSCONTROL struct intarrayparam { int len; unsigned int *intarray; }; #endif /* NETCON_ACCESSCONTROL */ /// Server listening end point. /// /// if NETCON_ACCESSCONTROL is defined during compilation, /// NetconServLis has primitive access control features: okaddrs holds /// the host addresses for the hosts which we allow to connect to /// us. okmasks holds the masks to be used for comparison. okmasks /// can be shorter than okaddrs, in which case we use the last entry /// for all addrs beyond the masks array length. Both arrays are /// retrieved from the configuration file when we create the endpoint /// the key is either based on the service name (ex: cdpathdb_okaddrs, /// cdpathdb_okmasks), or "default" if the service name is not found /// (ex: default_okaddrs, default_okmasks) class NetconServLis : public Netcon { public: NetconServLis() { #ifdef NETCON_ACCESSCONTROL permsinit = 0; okaddrs.len = okmasks.len = 0; okaddrs.intarray = okmasks.intarray = 0; #endif /* NETCON_ACCESSCONTROL */ } ~NetconServLis(); NetconServLis(const NetconServLis&) = delete; NetconServLis& operator=(const NetconServLis&) = delete; /// Open named service. Used absolute pathname to create an /// AF_UNIX path-based socket instead of an IP one. int openservice(const char *serv, int backlog = 10); /// Open service by port number. int openservice(int port, int backlog = 10); /// Wait for incoming connection. Returned connected Netcon NetconServCon *accept(int timeo = -1); protected: /// This should be overriden in a derived class to handle incoming /// connections. It will usually call NetconServLis::accept(), and /// insert the new connection in the selectloop. virtual int cando(Netcon::Event reason); // Empty if port was numeric, else service name or socket path std::string m_serv; private: #ifdef NETCON_ACCESSCONTROL int permsinit; struct intarrayparam okaddrs; struct intarrayparam okmasks; int initperms(const char *servicename); int initperms(int port); int checkperms(void *cli, int clilen); #endif /* NETCON_ACCESSCONTROL */ }; /// Server-side accepted client connection. The only specific code /// allows closing the listening endpoint in the child process (in the /// case of a forking server) class NetconServCon : public NetconData { public: NetconServCon(int newfd, Netcon* lis = nullptr) { m_liscon = lis; m_fd = newfd; } /// This is for forked servers that want to get rid of the main socket void closeLisCon() { if (m_liscon) { m_liscon->closeconn(); } } private: Netcon* m_liscon; }; #endif /* _NETCON_H_ */ recoll-1.36.1/utils/closefrom.h0000644000175000017500000000211414410615043013301 00000000000000#ifndef _closefrom_h_included_ #define _closefrom_h_included_ /* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Close all descriptors >= fd */ extern int libclf_closefrom(int fd); /* Retrieve max open fd. This might be the actual max open one (not thread-safe) or a system max value. */ extern int libclf_maxfd(int flags=0); #endif /* _closefrom_h_included_ */ recoll-1.36.1/utils/copyfile.cpp0000644000175000017500000001251614410615043013464 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "copyfile.h" #include #include #include "safefcntl.h" #include #include "safesysstat.h" #include "safeunistd.h" #ifndef _WIN32 #include #include #endif #include #include "pathut.h" #include "log.h" using namespace std; #define CPBSIZ 8192 bool copyfile(const char *src, const char *dst, string &reason, int flags) { int sfd = -1; int dfd = -1; bool ret = false; char buf[CPBSIZ]; int oflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY; LOGDEB("copyfile: " << (src) << " to " << (dst) << "\n" ); if ((sfd = ::open(src, O_RDONLY, 0)) < 0) { reason += string("open ") + src + ": " + strerror(errno); goto out; } if (flags & COPYFILE_EXCL) { oflags |= O_EXCL; } if ((dfd = ::open(dst, oflags, 0644)) < 0) { reason += string("open/creat ") + dst + ": " + strerror(errno); // If we fail because of an open/truncate error, we do not // want to unlink the file, we might succeed... flags |= COPYFILE_NOERRUNLINK; goto out; } for (;;) { int didread; didread = ::read(sfd, buf, CPBSIZ); if (didread < 0) { reason += string("read src ") + src + ": " + strerror(errno); goto out; } if (didread == 0) break; if (::write(dfd, buf, didread) != didread) { reason += string("write dst ") + src + ": " + strerror(errno); goto out; } } ret = true; out: if (ret == false && !(flags©FILE_NOERRUNLINK)) path_unlink(dst); if (sfd >= 0) ::close(sfd); if (dfd >= 0) ::close(dfd); return ret; } bool stringtofile(const string& dt, const char *dst, string& reason, int flags) { LOGDEB("stringtofile:\n" ); int dfd = -1; bool ret = false; int oflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY; LOGDEB("stringtofile: " << ((unsigned int)dt.size()) << " bytes to " << (dst) << "\n" ); if (flags & COPYFILE_EXCL) { oflags |= O_EXCL; } if ((dfd = ::open(dst, oflags, 0644)) < 0) { reason += string("open/creat ") + dst + ": " + strerror(errno); // If we fail because of an open/truncate error, we do not // want to unlink the file, we might succeed... flags |= COPYFILE_NOERRUNLINK; goto out; } if (::write(dfd, dt.c_str(), size_t(dt.size())) != ssize_t(dt.size())) { reason += string("write dst ") + ": " + strerror(errno); goto out; } ret = true; out: if (ret == false && !(flags©FILE_NOERRUNLINK)) path_unlink(dst); if (dfd >= 0) ::close(dfd); return ret; } bool renameormove(const char *src, const char *dst, string &reason) { #ifdef _WIN32 // Windows refuses to rename to an existing file. It appears that // there are workarounds (See MoveFile, MoveFileTransacted), but // anyway we are not expecting atomicity here. path_unlink(dst); #endif // First try rename(2). If this succeeds we're done. If this fails // with EXDEV, try to copy. Unix really should have a library function // for this. if (rename(src, dst) == 0) { return true; } if (errno != EXDEV) { reason += string("rename(2) failed: ") + strerror(errno); return false; } struct stat st; if (stat(src, &st) < 0) { reason += string("Can't stat ") + src + " : " + strerror(errno); return false; } if (!copyfile(src, dst, reason)) return false; struct stat st1; if (stat(dst, &st1) < 0) { reason += string("Can't stat ") + dst + " : " + strerror(errno); return false; } #ifndef _WIN32 // Try to preserve modes, owner, times. This may fail for a number // of reasons if ((st1.st_mode & 0777) != (st.st_mode & 0777)) { if (chmod(dst, st.st_mode&0777) != 0) { reason += string("Chmod ") + dst + "Error : " + strerror(errno); } } if (st.st_uid != st1.st_uid || st.st_gid != st1.st_gid) { if (chown(dst, st.st_uid, st.st_gid) != 0) { reason += string("Chown ") + dst + "Error : " + strerror(errno); } } #endif struct path_timeval times[2]; times[0].tv_sec = st.st_atime; times[0].tv_usec = 0; times[1].tv_sec = st.st_mtime; times[1].tv_usec = 0; path_utimes(dst, times); // All ok, get rid of origin if (!path_unlink(src)) { reason += string("Can't unlink ") + src + "Error : " + strerror(errno); } return true; } recoll-1.36.1/utils/mimeparse.cpp0000644000175000017500000006263614475321370013654 00000000000000/* Copyright (C) 2004-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include "mimeparse.h" #include "base64.h" #include "transcode.h" #include "smallut.h" using namespace std; //#define DEBUG_MIMEPARSE #ifdef DEBUG_MIMEPARSE #define DPRINT(X) fprintf X #else #define DPRINT(X) #endif // Parsing a header value. Only content-type and content-disposition // have parameters, but others are compatible with content-type // syntax, only, parameters are not used. So we can parse all like: // // headertype: value [; paramname=paramvalue] ... // // Value and paramvalues can be quoted strings, and there can be // comments too. Note that RFC2047 is explicitly forbidden for // parameter values (RFC2231 must be used), but I have seen it used // anyway (ie: thunderbird 1.0) // // Ref: RFC2045/6/7 (MIME) RFC2183/2231 (content-disposition and encodings) /** Decode a MIME parameter value encoded according to rfc2231 * * Example input withs input charset == "": * [iso-8859-1'french'RE%A0%3A_Smoke_Tests%20bla] * Or (if charset is set) : RE%A0%3A_Smoke_Tests%20bla * * @param in input string, ascii with rfc2231 markup * @param out output string * @param charset if empty: decode string like 'charset'lang'more%20stuff, * else just do the %XX part * @return out output string encoded in utf-8 */ bool rfc2231_decode(const string &in, string &out, string &charset) { string::size_type pos1, pos2=0; if (charset.empty()) { if ((pos1 = in.find("'")) == string::npos) return false; charset = in.substr(0, pos1); // fprintf(stderr, "Charset: [%s]\n", charset.c_str()); pos1++; if ((pos2 = in.find("'", pos1)) == string::npos) return false; // We have no use for lang for now // string lang = in.substr(pos1, pos2-pos1); // fprintf(stderr, "Lang: [%s]\n", lang.c_str()); pos2++; } string raw; qp_decode(in.substr(pos2), raw, '%'); // fprintf(stderr, "raw [%s]\n", raw.c_str()); if (!transcode(raw, out, charset, "UTF-8")) return false; return true; } ///////////////////////////////////////// /// Decoding of MIME fields values and parameters // The lexical token returned by find_next_token class Lexical { public: enum kind {none, token, separator}; kind what; string value; string error; char quote; Lexical() : what(none), quote(0) {} void reset() {what = none; value.erase(); error.erase();quote = 0;} }; // Skip mime comment. This must be called with in[start] == '(' static string::size_type skip_comment(const string &in, string::size_type start, Lexical &lex) { int commentlevel = 0; for (; start < in.size(); start++) { if (in[start] == '\\') { // Skip escaped char. if (start+1 < in.size()) { start++; continue; } else { lex.error.append("\\ at end of string "); return in.size(); } } if (in[start] == '(') commentlevel++; if (in[start] == ')') { if (--commentlevel == 0) break; } } if (start == in.size() && commentlevel != 0) { lex.error.append("Unclosed comment "); return in.size(); } return start; } // Skip initial whitespace and (possibly nested) comments. static string::size_type skip_whitespace_and_comment(const string &in, string::size_type start, Lexical &lex) { while (1) { if ((start = in.find_first_not_of(" \t\r\n", start)) == string::npos) return in.size(); if (in[start] == '(') { if ((start = skip_comment(in, start, lex)) == string::npos) return string::npos; } else { break; } } return start; } /// Find next token in mime header value string. /// @return the next starting position in string, string::npos for error /// @param in the input string /// @param start the starting position /// @param lex the returned token and its description /// @param delims separators we should look for static string::size_type find_next_token(const string &in, string::size_type start, Lexical &lex, string delims = ";=") { char oquot, cquot; start = skip_whitespace_and_comment(in, start, lex); if (start == string::npos || start == in.size()) return in.size(); // Begins with separator ? return it. string::size_type delimi = delims.find_first_of(in[start]); if (delimi != string::npos) { lex.what = Lexical::separator; lex.value = delims[delimi]; return start+1; } // Check for start of quoted string oquot = in[start]; switch (oquot) { case '<': cquot = '>';break; case '"': cquot = '"';break; default: cquot = 0; break; } if (cquot != 0) { // Quoted string parsing string::size_type end; start++; // Skip quote character for (end = start;end < in.size() && in[end] != cquot; end++) { if (in[end] == '\\') { // Skip escaped char. if (end+1 < in.size()) { end++; } else { // backslash at end of string: error lex.error.append("\\ at end of string "); return string::npos; } } } if (end == in.size()) { // Found end of string before closing quote character: error lex.error.append("Unclosed quoted string "); return string::npos; } lex.what = Lexical::token; lex.value = in.substr(start, end-start); lex.quote = oquot; return ++end; } else { string::size_type end = in.find_first_of(delims + "\r\n \t(", start); lex.what = Lexical::token; lex.quote = 0; if (end == string::npos) { end = in.size(); lex.value = in.substr(start); } else { lex.value = in.substr(start, end-start); } return end; } } // Classes for handling rfc2231 value continuations class Chunk { public: Chunk() : decode(false) {} bool decode; string value; }; class Chunks { public: vector chunks; }; void stringtolower(string &out, const string& in) { for (string::size_type i = 0; i < in.size(); i++) out.append(1, char(tolower(in[i]))); } // Parse MIME field value. Should look like: // somevalue ; param1=val1;param2=val2 bool parseMimeHeaderValue(const string& value, MimeHeaderValue& parsed) { parsed.value.erase(); parsed.params.clear(); Lexical lex; string::size_type start = 0; // Get the field value start = find_next_token(value, start, lex); if (start == string::npos || lex.what != Lexical::token) return false; parsed.value = lex.value; map rawparams; // Look for parameters for (;;) { string paramname, paramvalue; lex.reset(); start = find_next_token(value, start, lex); if (start == value.size()) break; if (start == string::npos) { //fprintf(stderr, "Find_next_token error(1)\n"); return false; } if (lex.what == Lexical::separator && lex.value[0] == ';') continue; if (lex.what != Lexical::token) return false; stringtolower(paramname, lex.value); start = find_next_token(value, start, lex); if (start == string::npos || lex.what != Lexical::separator || lex.value[0] != '=') { //fprintf(stderr, "Find_next_token error (2)\n"); return false; } start = find_next_token(value, start, lex); if (start == string::npos || lex.what != Lexical::token) { //fprintf(stderr, "Parameter has no value!"); return false; } paramvalue = lex.value; rawparams[paramname] = paramvalue; //fprintf(stderr, "RAW: name [%s], value [%s]\n", paramname.c_str(), // paramvalue.c_str()); } // fprintf(stderr, "Number of raw params %d\n", rawparams.size()); // RFC2231 handling: // - if a parameter name ends in * it must be decoded // - If a parameter name looks line name*ii[*] it is a // partial value, and must be concatenated with other such. map chunks; for (map::const_iterator it = rawparams.begin(); it != rawparams.end(); it++) { string nm = it->first; // fprintf(stderr, "NM: [%s]\n", nm.c_str()); if (nm.empty()) // ?? continue; Chunk chunk; if (nm[nm.length()-1] == '*') { nm.erase(nm.length() - 1); chunk.decode = true; } else chunk.decode = false; // fprintf(stderr, "NM1: [%s]\n", nm.c_str()); chunk.value = it->second; // Look for another asterisk in nm. If none, assign index 0 string::size_type aster; int idx = 0; if ((aster = nm.rfind("*")) != string::npos) { string num = nm.substr(aster+1); //fprintf(stderr, "NUM: [%s]\n", num.c_str()); nm.erase(aster); idx = atoi(num.c_str()); } Chunks empty; if (chunks.find(nm) == chunks.end()) chunks[nm] = empty; chunks[nm].chunks.resize(idx+1); chunks[nm].chunks[idx] = chunk; //fprintf(stderr, "CHNKS: nm [%s], idx %d, decode %d, value [%s]\n", // nm.c_str(), idx, int(chunk.decode), chunk.value.c_str()); } // For each parameter name, concatenate its chunks and possibly // decode Note that we pass the whole concatenated string to // decoding if the first chunk indicates that decoding is needed, // which is not right because there might be uncoded chunks // according to the rfc. for (map::const_iterator it = chunks.begin(); it != chunks.end(); it++) { if (it->second.chunks.empty()) continue; string nm = it->first; // Create the name entry if (parsed.params.find(nm) == parsed.params.end()) parsed.params[nm].clear(); // Concatenate all chunks and decode the whole if the first one needs // to. Yes, this is not quite right. string value; for (vector::const_iterator vi = it->second.chunks.begin(); vi != it->second.chunks.end(); vi++) { value += vi->value; } if (it->second.chunks[0].decode) { string charset; rfc2231_decode(value, parsed.params[nm], charset); } else { // rfc2047 MUST NOT but IS used by some agents rfc2047_decode(value, parsed.params[nm]); } //fprintf(stderr, "FINAL: nm [%s], value [%s]\n", //nm.c_str(), parsed.params[nm].c_str()); } return true; } // Decode a string encoded with quoted-printable encoding. // we reuse the code for rfc2231 % encoding, even if the eol // processing is not useful in this case bool qp_decode(const string& in, string &out, char esc) { out.reserve(in.length()); string::size_type ii; for (ii = 0; ii < in.length(); ii++) { if (in[ii] == esc) { ii++; // Skip '=' or '%' if(ii >= in.length() - 1) { // Need at least 2 more chars break; } else if (in[ii] == '\r' && in[ii+1] == '\n') { // Soft nl, skip ii++; } else if (in[ii] != '\n' && in[ii] != '\r') { // decode char c = in[ii]; char co; if(c >= 'A' && c <= 'F') { co = char((c - 'A' + 10) * 16); } else if (c >= 'a' && c <= 'f') { co = char((c - 'a' + 10) * 16); } else if (c >= '0' && c <= '9') { co = char((c - '0') * 16); } else { return false; } if(++ii >= in.length()) break; c = in[ii]; if (c >= 'A' && c <= 'F') { co += char(c - 'A' + 10); } else if (c >= 'a' && c <= 'f') { co += char(c - 'a' + 10); } else if (c >= '0' && c <= '9') { co += char(c - '0'); } else { return false; } out += co; } } else { out += in[ii]; } } return true; } // Decode an word encoded as quoted printable or base 64 static bool rfc2047_decodeParsed(const std::string& charset, const std::string& encoding, const std::string& value, std::string &utf8) { DPRINT((stderr, "DecodeParsed: charset [%s] enc [%s] val [%s]\n", charset.c_str(), encoding.c_str(), value.c_str())); utf8.clear(); string decoded; if (!stringlowercmp("b", encoding)) { if (!base64_decode(value, decoded)) return false; DPRINT((stderr, "FromB64: [%s]\n", decoded.c_str())); } else if (!stringlowercmp("q", encoding)) { if (!qp_decode(value, decoded)) return false; // Need to translate _ to ' ' here string temp; for (string::size_type pos = 0; pos < decoded.length(); pos++) if (decoded[pos] == '_') temp += ' '; else temp += decoded[pos]; decoded = temp; DPRINT((stderr, "FromQP: [%s]\n", decoded.c_str())); } else { DPRINT((stderr, "Bad encoding [%s]\n", encoding.c_str())); return false; } if (!transcode(decoded, utf8, charset, "UTF-8")) { DPRINT((stderr, "Transcode failed\n")); return false; } return true; } // Parse a mail header value encoded according to RFC2047. // This is not supposed to be used for MIME parameter values, but it // happens. // Bugs: // - We should turn off decoding while inside quoted strings // typedef enum {rfc2047ready, rfc2047open_eq, rfc2047charset, rfc2047encoding, rfc2047value, rfc2047close_q} Rfc2047States; bool rfc2047_decode(const std::string& in, std::string &out) { DPRINT((stderr, "rfc2047_decode: [%s]\n", in.c_str())); Rfc2047States state = rfc2047ready; string encoding, charset, value, utf8; out.clear(); for (string::size_type ii = 0; ii < in.length(); ii++) { char ch = in[ii]; switch (state) { case rfc2047ready: { DPRINT((stderr, "STATE: ready, ch %c\n", ch)); switch (ch) { // Whitespace: stay ready case ' ': case '\t': value += ch;break; // '=' -> forward to next state case '=': state = rfc2047open_eq; break; DPRINT((stderr, "STATE: open_eq\n")); // Other: go back to sleep default: value += ch; state = rfc2047ready; } } break; case rfc2047open_eq: { DPRINT((stderr, "STATE: open_eq, ch %c\n", ch)); switch (ch) { case '?': { // Transcode current (unencoded part) value: // we sometimes find 8-bit chars in // there. Interpret as Iso8859. if (value.length() > 0) { transcode(value, utf8, "ISO-8859-1", "UTF-8"); out += utf8; value.clear(); } state = rfc2047charset; } break; default: state = rfc2047ready; value += '='; value += ch;break; } } break; case rfc2047charset: { DPRINT((stderr, "STATE: charset, ch %c\n", ch)); switch (ch) { case '?': state = rfc2047encoding; break; default: charset += ch; break; } } break; case rfc2047encoding: { DPRINT((stderr, "STATE: encoding, ch %c\n", ch)); switch (ch) { case '?': state = rfc2047value; break; default: encoding += ch; break; } } break; case rfc2047value: { DPRINT((stderr, "STATE: value, ch %c\n", ch)); switch (ch) { case '?': state = rfc2047close_q; break; default: value += ch;break; } } break; case rfc2047close_q: { DPRINT((stderr, "STATE: close_q, ch %c\n", ch)); switch (ch) { case '=': { DPRINT((stderr, "End of encoded area. Charset %s, Encoding %s\n", charset.c_str(), encoding.c_str())); string utf8; state = rfc2047ready; if (!rfc2047_decodeParsed(charset, encoding, value, utf8)) { return false; } out += utf8; charset.clear(); encoding.clear(); value.clear(); } break; default: state = rfc2047value; value += '?';value += ch;break; } } break; default: // ?? DPRINT((stderr, "STATE: default ?? ch %c\n", ch)); return false; } } if (value.length() > 0) { transcode(value, utf8, "CP1252", "UTF-8"); out += utf8; value.clear(); } if (state != rfc2047ready) return false; return true; } #define DEBUGDATE 0 #if DEBUGDATE #define DATEDEB(X) fprintf X #else #define DATEDEB(X) #endif // Convert rfc822 date to unix time. A date string normally looks like: // Mon, 3 Jul 2006 09:51:58 +0200 // But there are many close common variations // And also hopeless things like: Fri Nov 3 13:13:33 2006 time_t rfc2822DateToUxTime(const string& dt) { // Strip everything up to first comma if any, we don't need weekday, // then break into tokens vector toks; string::size_type idx; if ((idx = dt.find_first_of(",")) != string::npos) { if (idx == dt.length() - 1) { DATEDEB((stderr, "Bad rfc822 date format (short1): [%s]\n", dt.c_str())); return (time_t)-1; } string date = dt.substr(idx+1); stringToTokens(date, toks, " \t:"); } else { // No comma. Enter strangeland stringToTokens(dt, toks, " \t:"); // Test for date like: Sun Nov 19 06:18:41 2006 // 0 1 2 3 4 5 6 // and change to: 19 Nov 2006 06:18:41 if (toks.size() == 7) { if (toks[0].length() == 3 && toks[0].find_first_of("0123456789") == string::npos) { swap(toks[0], toks[2]); swap(toks[6], toks[2]); toks.pop_back(); } } } #if DEBUGDATE for (list::iterator it = toks.begin(); it != toks.end(); it++) { DATEDEB((stderr, "[%s] ", it->c_str())); } DATEDEB((stderr, "\n")); #endif if (toks.size() < 6) { DATEDEB((stderr, "Bad rfc822 date format (toks cnt): [%s]\n", dt.c_str())); return (time_t)-1; } if (toks.size() == 6) { // Probably no timezone, sometimes happens toks.push_back("+0000"); } struct tm tm; memset(&tm, 0, sizeof(tm)); // Load struct tm with appropriate tokens, possibly converting // when needed vector::iterator it = toks.begin(); // Day of month: no conversion needed tm.tm_mday = atoi(it->c_str()); it++; // Month. Only Jan-Dec are legal. January, February do happen // though. Convert to 0-11 if (*it == "Jan" || *it == "January") tm.tm_mon = 0; else if (*it == "Feb" || *it == "February") tm.tm_mon = 1; else if (*it == "Mar" || *it == "March") tm.tm_mon = 2; else if (*it == "Apr" || *it == "April") tm.tm_mon = 3; else if (*it == "May") tm.tm_mon = 4; else if (*it == "Jun" || *it == "June") tm.tm_mon = 5; else if (*it == "Jul" || *it == "July") tm.tm_mon = 6; else if (*it == "Aug" || *it == "August") tm.tm_mon = 7; else if (*it == "Sep" || *it == "September") tm.tm_mon = 8; else if (*it == "Oct" || *it == "October") tm.tm_mon = 9; else if (*it == "Nov" || *it == "November") tm.tm_mon = 10; else if (*it == "Dec" || *it == "December") tm.tm_mon = 11; else { DATEDEB((stderr, "Bad rfc822 date format (month): [%s]\n", dt.c_str())); return (time_t)-1; } it++; // Year. Struct tm counts from 1900. 2 char years are quite rare // but do happen. I've seen 00 happen so count small values from 2000 tm.tm_year = atoi(it->c_str()); if (it->length() == 2) { if (tm.tm_year < 10) tm.tm_year += 2000; else tm.tm_year += 1900; } if (tm.tm_year > 1900) tm.tm_year -= 1900; it++; // Hour minute second need no adjustments tm.tm_hour = atoi(it->c_str()); it++; tm.tm_min = atoi(it->c_str()); it++; tm.tm_sec = atoi(it->c_str()); it++; // Timezone is supposed to be either +-XYZT or a zone name int zonesecs = 0; if (it->length() < 1) { DATEDEB((stderr, "Bad rfc822 date format (zlen): [%s]\n", dt.c_str())); return (time_t)-1; } if (it->at(0) == '-' || it->at(0) == '+') { // Note that +xy:zt (instead of +xyzt) sometimes happen, we // may want to process it one day if (it->length() < 5) { DATEDEB((stderr, "Bad rfc822 date format (zlen1): [%s]\n", dt.c_str())); goto nozone; } zonesecs = 3600*((it->at(1)-'0') * 10 + it->at(2)-'0')+ (it->at(3)-'0')*10 + it->at(4)-'0'; zonesecs = it->at(0) == '+' ? -1 * zonesecs : zonesecs; } else { int hours; if (*it == "A") hours= 1; else if (*it == "B") hours= 2; else if (*it == "C") hours= 3; else if (*it == "D") hours= 4; else if (*it == "E") hours= 5; else if (*it == "F") hours= 6; else if (*it == "G") hours= 7; else if (*it == "H") hours= 8; else if (*it == "I") hours= 9; else if (*it == "K") hours= 10; else if (*it == "L") hours= 11; else if (*it == "M") hours= 12; else if (*it == "N") hours= -1; else if (*it == "O") hours= -2; else if (*it == "P") hours= -3; else if (*it == "Q") hours= -4; else if (*it == "R") hours= -5; else if (*it == "S") hours= -6; else if (*it == "T") hours= -7; else if (*it == "U") hours= -8; else if (*it == "V") hours= -9; else if (*it == "W") hours= -10; else if (*it == "X") hours= -11; else if (*it == "Y") hours= -12; else if (*it == "Z") hours= 0; else if (*it == "UT") hours= 0; else if (*it == "GMT") hours= 0; else if (*it == "EST") hours= 5; else if (*it == "EDT") hours= 4; else if (*it == "CST") hours= 6; else if (*it == "CDT") hours= 5; else if (*it == "MST") hours= 7; else if (*it == "MDT") hours= 6; else if (*it == "PST") hours= 8; else if (*it == "PDT") hours= 7; // Non standard names // Standard Time (or Irish Summer Time?) is actually +5.5 else if (*it == "CET") hours= -1; else if (*it == "JST") hours= -9; else if (*it == "IST") hours= -5; else if (*it == "WET") hours= 0; else if (*it == "MET") hours= -1; else { DATEDEB((stderr, "Bad rfc822 date format (zname): [%s]\n", dt.c_str())); // Forget tz goto nozone; } zonesecs = 3600 * hours; } DATEDEB((stderr, "Tz: [%s] -> %d\n", it->c_str(), zonesecs)); nozone: // Compute the UTC Unix time value #ifndef sun time_t tim = timegm(&tm); #else // No timegm on Sun. Use mktime, then correct for local timezone time_t tim = mktime(&tm); // altzone and timezone hold the difference in seconds between UTC // and local. They are negative for places east of greenwich // // mktime takes our buffer to be local time, so it adds timezone // to the conversion result (if timezone is < 0 it's currently // earlier in greenwhich). // // We have to substract it back (hey! hopefully! maybe we have to // add it). Who can really know? tim -= timezone; #endif // And add in the correction from the email's Tz tim += zonesecs; DATEDEB((stderr, "Date: %s uxtime %ld \n", ctime(&tim), tim)); return tim; } recoll-1.36.1/utils/dlib.cpp0000644000175000017500000000367414410615043012571 00000000000000/* Copyright (C) 2017-2019 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #endif #include "dlib.h" #include "pathut.h" #include "smallut.h" #ifdef _WIN32 #include "safewindows.h" #elif defined(HAVE_DLOPEN) #include #else #error dlib.cpp not ported on this system #endif void *dlib_open(const std::string& libname, int) { #ifdef _WIN32 return LoadLibraryA(libname.c_str()); #elif defined(HAVE_DLOPEN) return dlopen(libname.c_str(), RTLD_LAZY); #else return nullptr; #endif } void *dlib_sym(void *handle, const char *name) { #ifdef _WIN32 return (void *)::GetProcAddress((HMODULE)handle, name); #elif defined(HAVE_DLOPEN) return dlsym(handle, name); #else return nullptr; #endif } void dlib_close(void *handle) { #ifdef _WIN32 ::FreeLibrary((HMODULE)handle); #elif defined(HAVE_DLOPEN) dlclose(handle); #endif } const char *dlib_error() { #ifdef _WIN32 int error = GetLastError(); static std::string errorstring; errorstring = std::string("dlopen/dlsym error: ") + lltodecstr(error); return errorstring.c_str(); #elif defined(HAVE_DLOPEN) return dlerror(); #else return "??? dlib not ported"; #endif } recoll-1.36.1/utils/smallut.cpp0000644000175000017500000011156114520415746013345 00000000000000/* Copyright (C) 2006-2022 J.F.Dockes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "smallut.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Older compilers don't support stdc++ regex, but Windows does not have the Linux one. Have a // simple class to solve the simple cases. #if defined(_WIN32) #define USE_STD_REGEX #define strncasecmp _strnicmp #define strcasecmp _stricmp #define localtime_r(a,b) localtime_s(b,a) #else #define USE_LINUX_REGEX #endif #ifdef USE_STD_REGEX #include #else #include #endif using namespace std::placeholders; namespace MedocUtils { int stringicmp(const std::string& s1, const std::string& s2) { return strcasecmp(s1.c_str(), s2.c_str()); } void stringtolower(std::string& io) { std::transform(io.begin(), io.end(), io.begin(), [](unsigned char c) {return std::tolower(c);}); } std::string stringtolower(const std::string& i) { std::string o = i; stringtolower(o); return o; } void stringtoupper(std::string& io) { std::transform(io.begin(), io.end(), io.begin(), [](unsigned char c) { return std::toupper(c); }); } std::string stringtoupper(const std::string& i) { std::string o = i; stringtoupper(o); return o; } // s1 is already lowercase int stringlowercmp(const std::string& s1, const std::string& s2) { std::string::const_iterator it1 = s1.begin(); std::string::const_iterator it2 = s2.begin(); std::string::size_type size1 = s1.length(), size2 = s2.length(); char c2; if (size1 < size2) { while (it1 != s1.end()) { c2 = ::tolower(*it2); if (*it1 != c2) { return *it1 > c2 ? 1 : -1; } ++it1; ++it2; } return size1 == size2 ? 0 : -1; } while (it2 != s2.end()) { c2 = ::tolower(*it2); if (*it1 != c2) { return *it1 > c2 ? 1 : -1; } ++it1; ++it2; } return size1 == size2 ? 0 : 1; } // s1 is already uppercase int stringuppercmp(const std::string& s1, const std::string& s2) { std::string::const_iterator it1 = s1.begin(); std::string::const_iterator it2 = s2.begin(); std::string::size_type size1 = s1.length(), size2 = s2.length(); char c2; if (size1 < size2) { while (it1 != s1.end()) { c2 = ::toupper(*it2); if (*it1 != c2) { return *it1 > c2 ? 1 : -1; } ++it1; ++it2; } return size1 == size2 ? 0 : -1; } while (it2 != s2.end()) { c2 = ::toupper(*it2); if (*it1 != c2) { return *it1 > c2 ? 1 : -1; } ++it1; ++it2; } return size1 == size2 ? 0 : 1; } bool beginswith(const std::string& big, const std::string& small) { return big.compare(0, small.size(), small) == 0; } template bool stringToStrings(const std::string& s, T& tokens, const std::string& addseps) { std::string current; tokens.clear(); enum states {SPACE, TOKEN, INQUOTE, ESCAPE}; states state = SPACE; for (char i : s) { switch (i) { case '"': switch (state) { case SPACE: state = INQUOTE; continue; case TOKEN: current += '"'; continue; case INQUOTE: tokens.insert(tokens.end(), current); current.clear(); state = SPACE; continue; case ESCAPE: current += '"'; state = INQUOTE; continue; } break; case '\\': switch (state) { case SPACE: case TOKEN: current += '\\'; state = TOKEN; continue; case INQUOTE: state = ESCAPE; continue; case ESCAPE: current += '\\'; state = INQUOTE; continue; } break; case ' ': case '\t': case '\n': case '\r': switch (state) { case SPACE: continue; case TOKEN: tokens.insert(tokens.end(), current); current.clear(); state = SPACE; continue; case INQUOTE: case ESCAPE: current += i; continue; } break; default: if (!addseps.empty() && addseps.find(i) != std::string::npos) { switch (state) { case ESCAPE: state = INQUOTE; break; case INQUOTE: break; case SPACE: tokens.insert(tokens.end(), std::string(1, i)); continue; case TOKEN: tokens.insert(tokens.end(), current); current.erase(); tokens.insert(tokens.end(), std::string(1, i)); state = SPACE; continue; } } else switch (state) { case ESCAPE: state = INQUOTE; break; case SPACE: state = TOKEN; break; case TOKEN: case INQUOTE: break; } current += i; } } switch (state) { case SPACE: break; case TOKEN: tokens.insert(tokens.end(), current); break; case INQUOTE: case ESCAPE: return false; } return true; } template void stringsToString(const T& tokens, std::string& s) { if (tokens.empty()) return; for (const auto& tok : tokens) { if (tok.empty()) { s.append("\"\" "); continue; } bool hasblanks = tok.find_first_of(" \t\n") != std::string::npos; if (hasblanks) { s.append(1, '"'); } for (auto car : tok) { if (car == '"') { s.append(1, '\\'); s.append(1, car); } else { s.append(1, car); } } if (hasblanks) { s.append(1, '"'); } s.append(1, ' '); } s.resize(s.size()-1); } template std::string stringsToString(const T& tokens) { std::string out; stringsToString(tokens, out); return out; } template void stringsToCSV(const T& tokens, std::string& s, char sep) { s.erase(); for (const auto& tok : tokens) { bool needquotes = false; if (tok.empty() || tok.find_first_of(std::string(1, sep) + "\"\n") != std::string::npos) { needquotes = true; } if (needquotes) { s.append(1, '"'); } for (auto&& car : tok) { if (car == '"') { s.append(2, '"'); } else { s.append(1, car); } } if (needquotes) { s.append(1, '"'); } s.append(1, sep); } // Remove last separator. if (!s.empty()) s.pop_back(); } template std::string commonprefix(const T& values) { if (values.empty()) return {}; if (values.size() == 1) return *values.begin(); unsigned int i = 0; for (;;i++) { auto it = values.begin(); if (it->size() <= i) { goto out; } auto val = (*it)[i]; it++; for (;it < values.end(); it++) { if (it->size() <= i || (*it)[i] != val) { goto out; } } } out: return values.begin()->substr(0, i); } #ifdef SMALLUT_EXTERNAL_INSTANTIATIONS #include "smallut_instantiate.h" #else template bool stringToStrings>( const std::string&, std::list&, const std::string&); template bool stringToStrings>(const std::string&, std::vector&, const std::string&); template bool stringToStrings>(const std::string&, std::set&, const std::string&); template bool stringToStrings> (const std::string&, std::unordered_set&, const std::string&); template void stringsToString>(const std::list&, std::string&); template void stringsToString>( const std::vector&, std::string&); template void stringsToString>(const std::set&, std::string&); template void stringsToString>( const std::unordered_set&, std::string&); template std::string stringsToString>(const std::list&); template std::string stringsToString>(const std::vector&); template std::string stringsToString>(const std::set&); template std::string stringsToString>( const std::unordered_set&); template void stringsToCSV>( const std::list&, std::string&, char); template void stringsToCSV>( const std::vector&, std::string&, char); template std::string commonprefix>(const std::vector&values); #endif void stringToTokens(const std::string& str, std::vector& tokens, const std::string& delims, bool skipinit, bool allowempty) { std::string::size_type startPos = 0, pos; // Skip initial delims, return empty if this eats all. if (skipinit && (startPos = str.find_first_not_of(delims, 0)) == std::string::npos) { return; } while (startPos < str.size()) { // Find next delimiter or end of string (end of token) pos = str.find_first_of(delims, startPos); // Add token to the vector and adjust start if (pos == std::string::npos) { tokens.push_back(str.substr(startPos)); break; } if (pos == startPos) { // Dont' push empty tokens after first if (allowempty || tokens.empty()) { tokens.emplace_back(); } startPos = ++pos; } else { tokens.push_back(str.substr(startPos, pos - startPos)); startPos = ++pos; } } } void stringSplitString(const std::string& str, std::vector& tokens, const std::string& sep) { if (str.empty() || sep.empty()) return; std::string::size_type startPos = 0, pos; while (startPos < str.size()) { // Find next delimiter or end of string (end of token) pos = str.find(sep, startPos); // Add token to the vector and adjust start if (pos == std::string::npos) { tokens.push_back(str.substr(startPos)); break; } if (pos == startPos) { // Initial or consecutive separators tokens.emplace_back(); } else { tokens.push_back(str.substr(startPos, pos - startPos)); } startPos = pos + sep.size(); } } bool stringToBool(const std::string& s) { if (s.empty()) { return false; } if (isdigit(s[0])) { int val = atoi(s.c_str()); return val != 0; } return s.find_first_of("yYtT") == 0; } std::string& trimstring(std::string& s, const char *ws) { rtrimstring(s, ws); ltrimstring(s, ws); return s; } std::string& rtrimstring(std::string& s, const char *ws) { std::string::size_type pos = s.find_last_not_of(ws); if (pos == std::string::npos) { s.clear(); } else if (pos != s.length() - 1) { s.erase(pos + 1); } return s; } std::string& ltrimstring(std::string& s, const char *ws) { std::string::size_type pos = s.find_first_not_of(ws); s.erase(0, pos); return s; } // Remove some chars and replace them with spaces std::string neutchars(const std::string& str, const std::string& chars, char rep) { std::string out; neutchars(str, out, chars, rep); return out; } void neutchars(const std::string& str, std::string& out, const std::string& chars, char rep) { std::string::size_type startPos, pos; for (pos = 0;;) { // Skip initial chars, break if this eats all. if ((startPos = str.find_first_not_of(chars, pos)) == std::string::npos) { break; } // Find next delimiter or end of string (end of token) pos = str.find_first_of(chars, startPos); // Add token to the output. Note: token cant be empty here if (pos == std::string::npos) { out += str.substr(startPos); } else { out += str.substr(startPos, pos - startPos) + rep; } } } /* Truncate a string to a given maxlength, avoiding cutting off midword * if reasonably possible. Note: we could also use textsplit, stopping when * we have enough, this would be cleanly utf8-aware but would remove * punctuation */ static const std::string cstr_SEPAR = " \t\n\r-:.;,/[]{}"; std::string truncate_to_word(const std::string& input, std::string::size_type maxlen) { std::string output; if (input.length() <= maxlen) { output = input; } else { output = input.substr(0, maxlen); std::string::size_type space = output.find_last_of(cstr_SEPAR); // Original version only truncated at space if space was found after // maxlen/2. But we HAVE to truncate at space, else we'd need to do // utf8 stuff to avoid truncating at multibyte char. In any case, // not finding space means that the text probably has no value. // Except probably for Asian languages, so we may want to fix this // one day if (space == std::string::npos) { output.erase(); } else { output.erase(space); } } return output; } // Escape things that would look like markup std::string escapeHtml(const std::string& in) { std::string out; for (char pos : in) { switch(pos) { case '<': out += "<"; break; case '>': out += ">"; break; case '&': out += "&"; break; case '"': out += """; break; default: out += pos; break; } } return out; } std::string escapeShell(const std::string& in) { std::string out; out += "\""; for (char pos : in) { switch (pos) { case '$': out += "\\$"; break; case '`': out += "\\`"; break; case '"': out += "\\\""; break; case '\n': out += "\\\n"; break; case '\\': out += "\\\\"; break; default: out += pos; } } out += "\""; return out; } // Escape value to be suitable as C++ source double-quoted string (for // generating a c++ program std::string makeCString(const std::string& in) { std::string out; out += "\""; for (char pos : in) { switch (pos) { case '"': out += "\\\""; break; case '\n': out += "\\n"; break; case '\r': out += "\\r"; break; case '\\': out += "\\\\"; break; default: out += pos; } } out += "\""; return out; } // Substitute printf-like percent cmds inside a string bool pcSubst(const std::string& in, std::string& out, const std::map& subs) { std::string::const_iterator it; for (it = in.begin(); it != in.end(); it++) { if (*it == '%') { if (++it == in.end()) { out += '%'; break; } if (*it == '%') { out += '%'; continue; } auto tr = subs.find(*it); if (tr != subs.end()) { out += tr->second; } else { out += std::string("%") + *it; } } else { out += *it; } } return true; } bool pcSubst(const std::string& in, std::string& out, const std::function& mapper) { out.erase(); std::string::size_type i; for (i = 0; i < in.size(); i++) { if (in[i] == '%') { if (++i == in.size()) { out += '%'; break; } if (in[i] == '%') { out += '%'; continue; } std::string key; if (in[i] == '(') { if (++i == in.size()) { out += std::string("%("); break; } std::string::size_type j = in.find_first_of(')', i); if (j == std::string::npos) { // ??concatenate remaining part and stop out += in.substr(i - 2); break; } key = in.substr(i, j - i); i = j; } else { key = in[i]; } out += mapper(key); } else { out += in[i]; } } return true; } class PcSubstMapMapper { public: explicit PcSubstMapMapper(const std::map& subs) : m_subs(subs) {} std::string domap(const std::string& key) { auto it = m_subs.find(key); if (it != m_subs.end()) return it->second; return std::string("%") +(key.size() == 1 ? key : std::string("(") + key + std::string(")")); } const std::map& m_subs; }; bool pcSubst(const std::string& in, std::string& out, const std::map& subs) { PcSubstMapMapper mapper(subs); return pcSubst(in, out, std::bind(&PcSubstMapMapper::domap, &mapper, _1)); } void ulltodecstr(uint64_t val, std::string& buf) { buf.clear(); if (val == 0) { buf = "0"; return; } char rbuf[30]; int idx=29; rbuf[idx--] = 0; do { rbuf[idx--] = '0' + val % 10; val /= 10; } while (val); buf.assign(&rbuf[idx+1]); } void lltodecstr(int64_t val, std::string& buf) { buf.clear(); if (val == 0) { buf = "0"; return; } bool neg = val < 0; if (neg) { val = -val; } char rbuf[30]; int idx=29; rbuf[idx--] = 0; do { rbuf[idx--] = '0' + val % 10; val /= 10; } while (val); if (neg) { rbuf[idx--] = '-'; } buf.assign(&rbuf[idx+1]); } std::string lltodecstr(int64_t val) { std::string buf; lltodecstr(val, buf); return buf; } std::string ulltodecstr(uint64_t val) { std::string buf; ulltodecstr(val, buf); return buf; } // Convert byte count into unit (KB/MB...) appropriate for display std::string displayableBytes(int64_t size) { const char *unit; double roundable = 0; if (size < 1000) { unit = " B "; roundable = double(size); } else if (size < 1E6) { unit = " KB "; roundable = double(size) / 1E3; } else if (size < 1E9) { unit = " MB "; roundable = double(size) / 1E6; } else { unit = " GB "; roundable = double(size) / 1E9; } size = int64_t(std::round(roundable)); return lltodecstr(size).append(unit); } std::string breakIntoLines(const std::string& in, unsigned int ll, unsigned int maxlines) { std::string query = in; std::string oq; unsigned int nlines = 0; while (query.length() > 0) { std::string ss = query.substr(0, ll); if (ss.length() == ll) { std::string::size_type pos = ss.find_last_of(' '); if (pos == std::string::npos) { pos = query.find_first_of(' '); if (pos != std::string::npos) { ss = query.substr(0, pos + 1); } else { ss = query; } } else { ss = ss.substr(0, pos + 1); } } // This cant happen, but anyway. Be very sure to avoid an infinite loop if (ss.length() == 0) { oq = query; break; } oq += ss + "\n"; if (nlines++ >= maxlines) { oq += " ... \n"; break; } query = query.substr(ss.length()); } return oq; } // Date is Y[-M[-D]] static bool parsedate(std::vector::const_iterator& it, std::vector::const_iterator end, DateInterval *dip) { dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0; if (it->length() > 4 || !it->length() || it->find_first_not_of("0123456789") != std::string::npos) { return false; } if (it == end || sscanf(it++->c_str(), "%d", &dip->y1) != 1) { return false; } if (it == end || *it == "/") { return true; } if (*it++ != "-") { return false; } if (it->length() > 2 || !it->length() || it->find_first_not_of("0123456789") != std::string::npos) { return false; } if (it == end || sscanf(it++->c_str(), "%d", &dip->m1) != 1) { return false; } if (it == end || *it == "/") { return true; } if (*it++ != "-") { return false; } if (it->length() > 2 || !it->length() || it->find_first_not_of("0123456789") != std::string::npos) { return false; } if (it == end || sscanf(it++->c_str(), "%d", &dip->d1) != 1) { return false; } return true; } // Called with the 'P' already processed. Period ends at end of string // or at '/'. We dont' do a lot effort at validation and will happily // accept 10Y1Y4Y (the last wins) static bool parseperiod(std::vector::const_iterator& it, std::vector::const_iterator end, DateInterval *dip) { dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0; while (it != end) { int value; if (it->find_first_not_of("0123456789") != std::string::npos) { return false; } if (sscanf(it++->c_str(), "%d", &value) != 1) { return false; } if (it == end || it->empty()) { return false; } switch (it->at(0)) { case 'Y': case 'y': dip->y1 = value; break; case 'M': case 'm': dip->m1 = value; break; case 'D': case 'd': dip->d1 = value; break; default: return false; } it++; if (it == end) { return true; } if (*it == "/") { return true; } } return true; } #ifdef _WIN32 static int setenv(const char* name, const char* value, int overwrite) { if (!overwrite) { const char *cp = getenv(name); if (cp) { return -1; } } return _putenv_s(name, value); } static void unsetenv(const char* name) { _putenv_s(name, ""); } #endif time_t portable_timegm(struct tm *tm) { time_t ret; char *tz; tz = getenv("TZ"); setenv("TZ", "", 1); tzset(); ret = mktime(tm); if (tz) { setenv("TZ", tz, 1); } else { unsetenv("TZ"); } tzset(); return ret; } #if 0 static void cerrdip(const std::string& s, DateInterval *dip) { cerr << s << dip->y1 << "-" << dip->m1 << "-" << dip->d1 << "/" << dip->y2 << "-" << dip->m2 << "-" << dip->d2 << endl; } #endif // Compute date + period. Won't work out of the unix era. // or pre-1970 dates. Just convert everything to unixtime and // seconds (with average durations for months/years), add and convert // back static bool addperiod(DateInterval *dp, DateInterval *pp) { // Create a struct tm with possibly non normalized fields and let // timegm sort it out struct tm tm = {}; tm.tm_year = dp->y1 - 1900 + pp->y1; tm.tm_mon = dp->m1 + pp->m1 - 1; tm.tm_mday = dp->d1 + pp->d1; time_t tres = mktime(&tm); localtime_r(&tres, &tm); dp->y1 = tm.tm_year + 1900; dp->m1 = tm.tm_mon + 1; dp->d1 = tm.tm_mday; //cerrdip("Addperiod return", dp); return true; } int monthdays(int mon, int year) { switch (mon) { // We are returning a few too many 29 days februaries, no problem case 2: return (year % 4) == 0 ? 29 : 28; case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; default: return 30; } } bool parsedateinterval(const std::string& s, DateInterval *dip) { std::vector vs; dip->y1 = dip->m1 = dip->d1 = dip->y2 = dip->m2 = dip->d2 = 0; DateInterval p1, p2, d1, d2; p1 = p2 = d1 = d2 = *dip; bool hasp1 = false, hasp2 = false, hasd1 = false, hasd2 = false, hasslash = false; if (!stringToStrings(s, vs, "PYMDpymd-/")) { return false; } if (vs.empty()) { return false; } auto it = vs.cbegin(); if (*it == "P" || *it == "p") { it++; if (!parseperiod(it, vs.end(), &p1)) { return false; } hasp1 = true; //cerrdip("p1", &p1); p1.y1 = -p1.y1; p1.m1 = -p1.m1; p1.d1 = -p1.d1; } else if (*it == "/") { hasslash = true; goto secondelt; } else { if (!parsedate(it, vs.end(), &d1)) { return false; } hasd1 = true; } // Got one element and/or / secondelt: if (it != vs.end()) { if (*it != "/") { return false; } hasslash = true; it++; if (it == vs.end()) { // ok } else if (*it == "P" || *it == "p") { it++; if (!parseperiod(it, vs.end(), &p2)) { return false; } hasp2 = true; } else { if (!parsedate(it, vs.end(), &d2)) { return false; } hasd2 = true; } } // 2 periods dont' make sense if (hasp1 && hasp2) { return false; } // Nothing at all doesn't either if (!hasp1 && !hasd1 && !hasp2 && !hasd2) { return false; } // Empty part means today IF other part is period, else means // forever (stays at 0) time_t now = time(nullptr); struct tm *tmnow = gmtime(&now); if ((!hasp1 && !hasd1) && hasp2) { d1.y1 = 1900 + tmnow->tm_year; d1.m1 = tmnow->tm_mon + 1; d1.d1 = tmnow->tm_mday; hasd1 = true; } else if ((!hasp2 && !hasd2) && hasp1) { d2.y1 = 1900 + tmnow->tm_year; d2.m1 = tmnow->tm_mon + 1; d2.d1 = tmnow->tm_mday; hasd2 = true; } // Incomplete dates have different meanings depending if there is // a period or not (actual or infinite indicated by a / + empty) // // If there is no explicit period, an incomplete date indicates a // period of the size of the uncompleted elements. Ex: 1999 // actually means 1999/P12M // // If there is a period, the incomplete date should be extended // to the beginning or end of the unspecified portion. Ex: 1999/ // means 1999-01-01/ and /1999 means /1999-12-31 if (hasd1) { if (!(hasslash || hasp2)) { if (d1.m1 == 0) { p2.m1 = 12; d1.m1 = 1; d1.d1 = 1; } else if (d1.d1 == 0) { d1.d1 = 1; p2.d1 = monthdays(d1.m1, d1.y1); } hasp2 = true; } else { if (d1.m1 == 0) { d1.m1 = 1; d1.d1 = 1; } else if (d1.d1 == 0) { d1.d1 = 1; } } } // if hasd2 is true we had a / if (hasd2) { if (d2.m1 == 0) { d2.m1 = 12; d2.d1 = 31; } else if (d2.d1 == 0) { d2.d1 = monthdays(d2.m1, d2.y1); } } if (hasp1) { // Compute d1 d1 = d2; if (!addperiod(&d1, &p1)) { return false; } } else if (hasp2) { // Compute d2 d2 = d1; if (!addperiod(&d2, &p2)) { return false; } } dip->y1 = d1.y1; dip->m1 = d1.m1; dip->d1 = d1.d1; dip->y2 = d2.y1; dip->m2 = d2.m1; dip->d2 = d2.d1; return true; } std::string hexprint(const std::string& in, char separ) { std::string out; out.reserve(separ ? (3 *in.size()) : (2 * in.size())); static const char hex[]="0123456789abcdef"; auto cp = reinterpret_cast(in.c_str()); for (unsigned int i = 0; i < in.size(); i++) { out.append(1, hex[cp[i] >> 4]); out.append(1, hex[cp[i] & 0x0f]); if (separ && i != in.size() - 1) out.append(1, separ); } return out; } [[maybe_unused]] static char* _check_strerror_r(int, char* errbuf) { return errbuf; } [[maybe_unused]] static char* _check_strerror_r(char* cp, char*) { return cp; } void catstrerror(std::string *reason, const char *what, int _errno) { if (!reason) { return; } if (what) { reason->append(what); } reason->append(": errno: "); char nbuf[20]; sprintf(nbuf, "%d", _errno); reason->append(nbuf); reason->append(" : "); #if defined(sun) || defined(_WIN32) // Note: sun strerror is noted mt-safe ?? reason->append(strerror(_errno)); #else // There are 2 versions of strerror_r. // - The GNU one returns a pointer to the message (maybe // static storage or supplied buffer). // - The POSIX one always stores in supplied buffer and // returns 0 on success. As the possibility of error and // error code are not specified, we're basically doomed // cause we can't use a test on the 0 value to know if we // were returned a pointer... // Also couldn't find an easy way to disable the gnu version without // changing the cxxflags globally, so forget it. Recent gnu lib versions // normally default to the posix version. (not !) // The feature defines tests are too complicated and seem unreliable. // In short it's a mess, but thanks to c++ function overloading and smart // people, we have a solution: // https://www.zverovich.net/2015/03/13/reliable-detection-of-strerror-variants.html char errbuf[200]; errbuf[0] = 0; reason->append(_check_strerror_r( strerror_r(_errno, errbuf, sizeof(errbuf)), errbuf)); #endif } #ifndef SMALLUT_NO_REGEX #ifdef USE_STD_REGEX class SimpleRegexp::Internal { public: Internal(const std::string& exp, int flags, int nm) : expr(exp, std::basic_regex::flag_type( std::regex_constants::extended | ((flags&SRE_ICASE) ? int(std::regex_constants::icase) : 0) | ((flags&SRE_NOSUB) ? int(std::regex_constants::nosubs) : 0) )), ok(true), nmatch(nm) { } std::regex expr; std::smatch res; bool ok; int nmatch; }; bool SimpleRegexp::simpleMatch(const std::string& val) const { if (!ok()) return false; return regex_search(val, m->res, m->expr); } // Substitute one instance of regular expression std::string SimpleRegexp::simpleSub( const std::string& in, const std::string& repl) { if (!ok()) { return std::string(); } return regex_replace(in, m->expr, repl, std::regex_constants::format_first_only); } std::string SimpleRegexp::getMatch(const std::string&, int i) const { return m->res.str(i); } #else // -> !WIN32 class SimpleRegexp::Internal { public: Internal(const std::string& exp, int flags, int nm) : nmatch(nm) { ok = regcomp(&expr, exp.c_str(), REG_EXTENDED | ((flags & SRE_ICASE) ? REG_ICASE : 0) | ((flags & SRE_NOSUB) ? REG_NOSUB : 0)) == 0; matches.resize(nmatch+1); } ~Internal() { regfree(&expr); } bool ok; regex_t expr; int nmatch; std::vector matches; }; // Substitute one instance of regular expression std::string SimpleRegexp::simpleSub( const std::string& in, const std::string& repl) { if (!ok()) { return {}; } int err; if ((err = regexec(&m->expr, in.c_str(), m->nmatch + 1, &m->matches[0], 0))) { #if SIMPLESUB_DBG const int ERRSIZE = 200; char errbuf[ERRSIZE + 1]; regerror(err, &expr, errbuf, ERRSIZE); std::cerr << "simpleSub: regexec(" << sexp << ") failed: " << errbuf << "\n"; #endif return in; } if (m->matches[0].rm_so == -1) { // No match return in; } std::string out = in.substr(0, m->matches[0].rm_so); out += repl; out += in.substr(m->matches[0].rm_eo); return out; } bool SimpleRegexp::simpleMatch(const std::string& val) const { if (!ok()) return false; return regexec(&m->expr, val.c_str(), m->nmatch + 1, &m->matches[0], 0) == 0; } std::string SimpleRegexp::getMatch(const std::string& val, int i) const { if (i > m->nmatch) { return {}; } return val.substr(m->matches[i].rm_so, m->matches[i].rm_eo - m->matches[i].rm_so); } #endif // !windows, using C regexps SimpleRegexp::SimpleRegexp(const std::string& exp, int flags, int nmatch) : m(std::make_unique(exp, flags, nmatch)) { } SimpleRegexp::~SimpleRegexp() = default; bool SimpleRegexp::ok() const { return m->ok; } bool SimpleRegexp::operator() (const std::string& val) const { return simpleMatch(val); } #endif // SMALLUT_NO_REGEX std::string flagsToString(const std::vector& flags, unsigned int val) { const char *s; std::string out; for (const auto& flag : flags) { if ((val & flag.value) == flag.value) { s = flag.yesname; } else { s = flag.noname; } if (s && *s) { /* We have something to write */ if (out.length()) { // If not first, add '|' separator out.append("|"); } out.append(s); } } return out; } std::string valToString(const std::vector& flags, unsigned int val) { std::string out; for (const auto& flag : flags) { if (flag.value == val) { out = flag.yesname; return out; } } { char mybuf[100]; sprintf(mybuf, "Unknown Value 0x%x", val); out = mybuf; } return out; } // Decode %-encoded URL. No encoding method is defined here because the problem is too complex and // ambiguous (at least 3 RFCs + comments). Decoding however is unambiguous. static inline int h2d(int c) { if ('0' <= c && c <= '9') return c - '0'; else if ('A' <= c && c <= 'F') return 10 + c - 'A'; else if ('a' <= c && c <= 'f') return 10 + c - 'a'; else return -1; } std::string url_decode(const std::string &in) { if (in.size() <= 2) return in; std::string out; out.reserve(in.size()); const char *cp = in.c_str(); std::string::size_type i = 0; for (; i < in.size() - 2; i++) { if (cp[i] == '%') { int d1 = h2d(cp[i+1]); int d2 = h2d(cp[i+2]); if (d1 != -1 && d2 != -1) { out += (d1 << 4) + d2; } else { out += '%'; out += cp[i+1]; out += cp[i+2]; } i += 2; } else { out += cp[i]; } } while (i < in.size()) { out += cp[i++]; } return out; } // Initialization for static stuff to be called from main thread before going // multiple void smallut_init_mt() { } } // End namespace MedocUtils recoll-1.36.1/utils/dlib.h0000644000175000017500000000215714410615043012231 00000000000000/* Copyright (C) 2017-2019 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _DLIB_H_INCLUDED_ #define _DLIB_H_INCLUDED_ /** Dynamic library functions */ #include extern void *dlib_open(const std::string& libname, int flags = 0); extern void *dlib_sym(void *handle, const char *name); extern void dlib_close(void *handle); extern const char *dlib_error(); #endif /* _DLIB_H_INCLUDED_ */ recoll-1.36.1/utils/pxattr.h0000644000175000017500000001131714506737501012651 00000000000000#ifndef _pxattr_h_included_ #define _pxattr_h_included_ /* @(#$Id: pxattr.h,v 1.5 2009-01-20 13:48:34 dockes Exp $ (C) 2009 J.F.Dockes Copyright (c) 2009 Jean-Francois Dockes Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include /** * Provide a uniform C++ API for extended file attributes on Linux/FreeBSD * and MacOSX. * * We only deal with user attributes. Other namespaces are very * system-specific and would be difficult to use in a portable way. * * Linux and FreeBSD treat differently the attributes name space * segmentation: Linux uses the first name segment ("user.", "system.", ...), * FreeBSD uses an enumeration. * * We handle this by using only domain-internal names in the interface: * that is, the caller specifies the names as, ie, 'org.myapp.somename' * not 'user.org.myapp.somename'. pxattr will deal with adding/removing * the 'user.' part as needed. * * MacOsX does not segment the attribute name space. * * In order to avoid conflicts, it is recommended that attributes * names be chosen in a "reverse dns" fashion, ie: * org.recoll.indexing.status * * The interface provided should work the same way on all 3 systems, * it papers over such differences as the "list" output format, * the existence of CREATE/UPDATE distinctions, etc. * * Diagnostics: all functions return false on error, and preserve the errno * value or set it as appropriate. * * For path-based interfaces, the PXATTR_NOFOLLOW flag can be set to decide if * symbolic links will be acted on or followed. */ namespace pxattr { /** nspace might be used in the future if we support multiple namespaces.*/ enum nspace { /** User name space */ PXATTR_USER }; /** Flags can modify the behaviour of some methods */ enum flags {PXATTR_NONE=0, /** Act on link instead of following it */ PXATTR_NOFOLLOW = 1, /** Fail if existing */ PXATTR_CREATE=2, /** Fail if new */ PXATTR_REPLACE=4 }; /** * Retrieve the named attribute from path. */ bool get(const std::string& path, const std::string& name, std::string* value, flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); /** * Retrieve the named attribute from open file. */ bool get(int fd, const std::string& name, std::string* value, flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); /** * Set the named attribute on path. */ bool set(const std::string& path, const std::string& name, const std::string& value, flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); /** * Set the named attribute on open file. */ bool set(int fd, const std::string& name, const std::string& value, flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); /** * Delete the named attribute from path. */ bool del(const std::string& path, const std::string& name, flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); /** * Delete the named attribute from open file. */ bool del(int fd, const std::string& name, flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); /** * List attribute names from path. */ bool list(const std::string& path, std::vector* names, flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); /** * List attribute names from open file. */ bool list(int fd, std::vector* names, flags flags = PXATTR_NONE, nspace dom = PXATTR_USER); /** * Compute actual/system attribute name from external name * (ie: myattr->user.myattr) */ bool sysname(nspace dom, const std::string& pname, std::string* sname); /** * Compute external name from actual/system name * (ie: user.myattr->myattr) */ bool pxname(nspace dom, const std::string& sname, std::string* pname); } // namespace pxattr #endif /* _pxattr_h_included_ */ recoll-1.36.1/utils/circache.cpp0000644000175000017500000014374714444307651013440 00000000000000/* Copyright (C) 2009 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "circache.h" #include #include #include #include "safefcntl.h" #include #include "safesysstat.h" #include "safeunistd.h" #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #else struct iovec { void *iov_base; size_t iov_len; }; static ssize_t writev(int fd, const struct iovec *iov, int iovcnt) { ssize_t tot = 0; for (int i = 0; i < iovcnt; i++) { ssize_t ret = ::write(fd, iov[i].iov_base, iov[i].iov_len); if (ret > 0) { tot += ret; } if (ret != (ssize_t)iov[i].iov_len) { return ret == -1 ? -1 : tot; } } return tot; } #endif #include "chrono.h" #include "conftree.h" #include "copyfile.h" #include "cstr.h" #include "log.h" #include "md5.h" #include "pathut.h" #include "smallut.h" #include "wipedir.h" #include "zlibut.h" using namespace std; using namespace std::placeholders; /* * File structure: * - Starts with a 1-KB header block, with a param dictionary. * - Stored items follow. Each item has a header and 2 segments for * the metadata and the data. * The segment sizes are stored in the ascii header/marker: * circacheSizes = xxx yyy zzz * xxx bytes of metadata * yyy bytes of data * zzz bytes of padding up to next object (only one entry has non zero) * * There is a write position, which can be at eof while the file is growing, or inside the file if * we are recycling. This is stored in the header (oheadoffs), together with the maximum size. See * the Internal class for more detailed comments. * * If we are recycling, we have to take care to compute the size of the * possible remaining area from the last object invalidated by the write, * pad it with neutral data and store the size in the new header. To help with * this, the address for the last object written is also kept in the header * (nheadoffs, npadsize) */ // First block size #define CIRCACHE_FIRSTBLOCK_SIZE 1024 // Entry header. // 2x32 1x64 bits ints as hex integers + 1 x 16 bits flag + at least 1 zero // 15 + 2x9 + 17 + 3 + 1 = 54 static const char *headerformat = "circacheSizes = %x %x %llx %hx"; #define CIRCACHE_HEADER_SIZE 64 class EntryHeaderData { public: EntryHeaderData() : dicsize(0), datasize(0), padsize(0), flags(0) {} unsigned int dicsize; unsigned int datasize; uint64_t padsize; unsigned short flags; }; enum EntryFlags {EFNone = 0, EFDataCompressed = 1}; // A callback class for the header-hopping function. class CCScanHook { public: virtual ~CCScanHook() {} enum status {Stop, Continue, Error, Eof}; virtual status takeone(int64_t offs, const string& udi, const EntryHeaderData& d) = 0; }; // We have an auxiliary in-memory multimap of hashed-udi -> offset to // speed things up. This is created the first time the file is scanned // (on the first get), and not saved to disk. // The map key: hashed udi. As a very short hash seems sufficient, // maybe we could find something faster/simpler than md5? #define UDIHLEN 4 class UdiH { public: unsigned char h[UDIHLEN]; UdiH(const string& udi) { MD5_CTX ctx; MD5Init(&ctx); MD5Update(&ctx, (const unsigned char*)udi.c_str(), udi.length()); unsigned char md[16]; MD5Final(md, &ctx); memcpy(h, md, UDIHLEN); } string asHexString() const { static const char hex[] = "0123456789abcdef"; string out; for (int i = 0; i < UDIHLEN; i++) { out.append(1, hex[h[i] >> 4]); out.append(1, hex[h[i] & 0x0f]); } return out; } bool operator==(const UdiH& r) const { for (int i = 0; i < UDIHLEN; i++) if (h[i] != r.h[i]) { return false; } return true; } bool operator<(const UdiH& r) const { for (int i = 0; i < UDIHLEN; i++) { if (h[i] < r.h[i]) { return true; } if (h[i] > r.h[i]) { return false; } } return false; } }; typedef multimap kh_type; typedef multimap::value_type kh_value_type; class CirCacheInternal { public: int m_fd; ////// These are cache persistent state and written to the first block: // Maximum file size, after which we begin reusing old space int64_t m_maxsize; // Offset of the oldest header, or max file offset (file size) // while the file is growing. This is the next write position. int64_t m_oheadoffs; // Offset of last write (newest header) int64_t m_nheadoffs; // Pad size for newest entry. int64_t m_npadsize; // Keep history or only last entry bool m_uniquentries; ///////////////////// End header entries // A place to hold data when reading char *m_buffer; size_t m_bufsiz; // Error messages ostringstream m_reason; // State for rewind/next/getcurrent operation. This could/should // be moved to a separate iterator. int64_t m_itoffs; EntryHeaderData m_ithd; // Offset cache kh_type m_ofskh; bool m_ofskhcplt; // Has cache been fully read since open? // Add udi->offset translation to map bool khEnter(const string& udi, int64_t ofs) { UdiH h(udi); LOGDEB2("Circache::khEnter: h " << h.asHexString() << " offs " << ofs << " udi [" << udi << "]\n"); pair p = m_ofskh.equal_range(h); if (p.first != m_ofskh.end() && p.first->first == h) { for (kh_type::iterator it = p.first; it != p.second; it++) { LOGDEB2("Circache::khEnter: col h " << it->first.asHexString() << ", ofs " << it->second << "\n"); if (it->second == ofs) { // (h,offs) already there. Happens LOGDEB2("Circache::khEnter: already there\n"); return true; } } } m_ofskh.insert(kh_value_type(h, ofs)); LOGDEB2("Circache::khEnter: inserted\n"); return true; } void khDump() { for (const auto& e : m_ofskh) { LOGDEB("Circache::KHDUMP: " << e.first.asHexString() << " " << e.second << "\n"); } } // Return vector of candidate offsets for udi (possibly several // because there may be hash collisions, and also multiple // instances). bool khFind(const string& udi, vector& ofss) { ofss.clear(); UdiH h(udi); LOGDEB2("Circache::khFind: h " << h.asHexString() << " udi [" << udi << "]\n"); pair p = m_ofskh.equal_range(h); #if 0 if (p.first == m_ofskh.end()) { LOGDEB("KHFIND: FIRST END()\n"); } if (p.second == m_ofskh.end()) { LOGDEB("KHFIND: SECOND END()\n"); } if (!(p.first->first == h)) LOGDEB("KHFIND: NOKEY: " << p.first->first.asHexString() << " " << p.second->first.asHexString() << "\n"); #endif if (p.first == m_ofskh.end() || !(p.first->first == h)) { return false; } for (kh_type::iterator it = p.first; it != p.second; it++) { ofss.push_back(it->second); } return true; } // Clear entry for udi/offs bool khClear(const pair& ref) { UdiH h(ref.first); pair p = m_ofskh.equal_range(h); if (p.first != m_ofskh.end() && (p.first->first == h)) { for (kh_type::iterator it = p.first; it != p.second;) { kh_type::iterator tmp = it++; if (tmp->second == ref.second) { m_ofskh.erase(tmp); } } } return true; } // Clear entries for vector of udi/offs bool khClear(const vector >& udis) { for (const auto& udioffs : udis) { khClear(udioffs); } return true; } // Clear all entries for udi bool khClear(const string& udi) { UdiH h(udi); pair p = m_ofskh.equal_range(h); if (p.first != m_ofskh.end() && (p.first->first == h)) { for (kh_type::iterator it = p.first; it != p.second;) { kh_type::iterator tmp = it++; m_ofskh.erase(tmp); } } return true; } CirCacheInternal() : m_fd(-1), m_maxsize(-1), m_oheadoffs(-1), m_nheadoffs(0), m_npadsize(0), m_uniquentries(false), m_buffer(nullptr), m_bufsiz(0), m_ofskhcplt(false) { } ~CirCacheInternal() { if (m_fd >= 0) { close(m_fd); } if (m_buffer) { free(m_buffer); } } char *buf(size_t sz) { if (m_bufsiz >= sz) { return m_buffer; } if ((m_buffer = (char *)realloc(m_buffer, sz))) { m_bufsiz = sz; } else { m_reason << "CirCache:: realloc(" << sz << ") failed"; m_bufsiz = 0; } return m_buffer; } // Name for the cache file string datafn(const string& d) { return path_cat(d, "circache.crch"); } bool writefirstblock() { if (m_fd < 0) { m_reason << "writefirstblock: not open "; return false; } ostringstream s; s << "maxsize = " << m_maxsize << "\n" << "oheadoffs = " << m_oheadoffs << "\n" << "nheadoffs = " << m_nheadoffs << "\n" << "npadsize = " << m_npadsize << "\n" << "unient = " << m_uniquentries << "\n" << " " << " " << " " << "\0"; int sz = int(s.str().size()); assert(sz < CIRCACHE_FIRSTBLOCK_SIZE); lseek(m_fd, 0, 0); if (write(m_fd, s.str().c_str(), sz) != sz) { m_reason << "writefirstblock: write() failed: errno " << errno; return false; } return true; } bool readfirstblock() { if (m_fd < 0) { m_reason << "readfirstblock: not open "; return false; } char bf[CIRCACHE_FIRSTBLOCK_SIZE]; lseek(m_fd, 0, 0); if (read(m_fd, bf, CIRCACHE_FIRSTBLOCK_SIZE) != CIRCACHE_FIRSTBLOCK_SIZE) { m_reason << "readfirstblock: read() failed: errno " << errno; return false; } string s(bf, CIRCACHE_FIRSTBLOCK_SIZE); ConfSimple conf(s, 1); m_maxsize = conf.getInt("maxsize", -1); if (m_maxsize == -1) { m_reason << "readfirstblock: conf get maxsize failed"; return false; } m_oheadoffs = conf.getInt("oheadoffs", -1); if (m_oheadoffs == -1) { m_reason << "readfirstblock: conf get oheadoffs failed"; return false; } m_nheadoffs = conf.getInt("nheadoffs", -1); if (m_nheadoffs == -1) { m_reason << "readfirstblock: conf get nheadoffs failed"; return false; } m_npadsize = conf.getInt("npadsize", -1); if (m_npadsize == -1) { m_reason << "readfirstblock: conf get npadsize failed"; return false; } m_uniquentries = conf.getBool("unient", false); return true; } bool writeEntryHeader(int64_t offset, const EntryHeaderData& d, bool eraseData = false) { if (m_fd < 0) { m_reason << "writeEntryHeader: not open "; return false; } char bf[CIRCACHE_HEADER_SIZE]; memset(bf, 0, CIRCACHE_HEADER_SIZE); snprintf(bf, CIRCACHE_HEADER_SIZE, headerformat, d.dicsize, d.datasize, d.padsize, d.flags); if (lseek(m_fd, offset, 0) != offset) { m_reason << "CirCache::weh: lseek(" << offset << ") failed: errno " << errno; return false; } if (write(m_fd, bf, CIRCACHE_HEADER_SIZE) != CIRCACHE_HEADER_SIZE) { m_reason << "CirCache::weh: write failed. errno " << errno; return false; } if (eraseData == true) { if (d.dicsize || d.datasize) { m_reason << "CirCache::weh: erase requested but not empty"; return false; } string buf((size_t)d.padsize, ' '); if (write(m_fd, buf.c_str(), (size_t)d.padsize) != (ssize_t)d.padsize) { m_reason << "CirCache::weh: write failed. errno " << errno; return false; } } return true; } CCScanHook::status readEntryHeader(int64_t offset, EntryHeaderData& d) { if (m_fd < 0) { m_reason << "readEntryHeader: not open "; return CCScanHook::Error; } if (lseek(m_fd, offset, 0) != offset) { m_reason << "readEntryHeader: lseek(" << offset << ") failed: errno " << errno; return CCScanHook::Error; } char bf[CIRCACHE_HEADER_SIZE]; int ret = read(m_fd, bf, CIRCACHE_HEADER_SIZE); if (ret == 0) { // Eof m_reason << " Eof "; return CCScanHook::Eof; } if (ret != CIRCACHE_HEADER_SIZE) { m_reason << " readheader: read failed errno " << errno; return CCScanHook::Error; } if (sscanf(bf, headerformat, &d.dicsize, &d.datasize, &d.padsize, &d.flags) != 4) { m_reason << " readEntryHeader: bad header at " << offset << " [" << bf << "]"; return CCScanHook::Error; } LOGDEB2("Circache:readEntryHeader: dcsz " << d.dicsize << " dtsz " << d.datasize << " pdsz " << d.padsize << " flgs " <takeone(startoffset, udi, d); switch (a) { case CCScanHook::Continue: break; default: return a; } startoffset += CIRCACHE_HEADER_SIZE + d.dicsize + d.datasize + d.padsize; } } bool readHUdi(int64_t hoffs, EntryHeaderData& d, string& udi) { if (readEntryHeader(hoffs, d) != CCScanHook::Continue) { return false; } string dic; if (!readDicData(hoffs, d, dic, nullptr)) { return false; } if (d.dicsize == 0) { // This is an erased entry udi.erase(); return true; } ConfSimple conf(dic); if (!conf.get("udi", udi)) { m_reason << "Bad file: no udi in dic"; return false; } return true; } bool readDicData(int64_t hoffs, EntryHeaderData& hd, string& dic, string* data) { int64_t offs = hoffs + CIRCACHE_HEADER_SIZE; // This syscall could be avoided in some cases if we saved the offset // at each seek. In most cases, we just read the header and we are // at the right position if (lseek(m_fd, offs, 0) != offs) { m_reason << "CirCache::get: lseek(" << offs << ") failed: " << errno; return false; } char *bf = nullptr; if (hd.dicsize) { bf = buf(hd.dicsize); if (nullptr == bf) { return false; } if (read(m_fd, bf, hd.dicsize) != int(hd.dicsize)) { m_reason << "CirCache::get: read() failed: errno " << errno; return false; } dic.assign(bf, hd.dicsize); } else { dic.erase(); } if (nullptr == data) { return true; } if (hd.datasize) { bf = buf(hd.datasize); if (nullptr == bf) { return false; } if (read(m_fd, bf, hd.datasize) != int(hd.datasize)) { m_reason << "CirCache::get: read() failed: errno " << errno; return false; } if (hd.flags & EFDataCompressed) { LOGDEB1("Circache:readdicdata: data compressed\n"); ZLibUtBuf buf; if (!inflateToBuf(bf, hd.datasize, buf)) { m_reason << "CirCache: decompression failed "; return false; } data->assign(buf.getBuf(), buf.getCnt()); } else { LOGDEB1("Circache:readdicdata: data NOT compressed\n"); data->assign(bf, hd.datasize); } } else { data->erase(); } return true; } }; CirCache::CirCache(const string& dir) : m_dir(dir) { m_d = new CirCacheInternal; LOGDEB0("CirCache: [" << m_dir << "]\n"); } CirCache::~CirCache() { delete m_d; m_d = nullptr; } string CirCache::getReason() { return m_d ? m_d->m_reason.str() : "Not initialized"; } // A scan callback which just records the last header offset and // padsize seen. This is used with a scan(nofold) to find the last // physical record in the file class CCScanHookRecord : public CCScanHook { public: int64_t headoffs; int64_t padsize; CCScanHookRecord() : headoffs(0), padsize(0) { } virtual status takeone(int64_t offs, const string&, const EntryHeaderData& d) { headoffs = offs; padsize = d.padsize; LOGDEB2("CCScanHookRecord::takeone: offs " << headoffs << " padsize " << padsize << "\n"); return Continue; } }; string CirCache::getpath() const { return m_d->datafn(m_dir); } bool CirCache::create(int64_t maxsize, int flags) { LOGDEB("CirCache::create: [" << m_dir << "] maxsz " << maxsize << " flags 0x" << std::hex << flags <m_reason << "CirCache::create: mkdir(" << m_dir << ") failed. errno: " << errno; return false; } } else { // If the file exists too, and truncate is not set, switch // to open-mode. Still may need to update header params. if (path_exists(m_d->datafn(m_dir)) && !(flags & CC_CRTRUNCATE)) { if (!this->open(CC_OPWRITE)) { return false; } if (maxsize == m_d->m_maxsize && ((flags & CC_CRUNIQUE) != 0) == m_d->m_uniquentries) { LOGDEB("Header unchanged, no rewrite\n"); return true; } // If the new maxsize is bigger than current size, we need // to stop recycling if this is what we are doing. if (maxsize > m_d->m_maxsize && maxsize > st.st_size) { // Scan the file to find the last physical record. The // ohead is set at physical eof, and nhead is the last // scanned record CCScanHookRecord rec; m_d->scan(CIRCACHE_FIRSTBLOCK_SIZE, &rec, false); m_d->m_oheadoffs = lseek(m_d->m_fd, 0, SEEK_END); m_d->m_nheadoffs = rec.headoffs; m_d->m_npadsize = rec.padsize; } m_d->m_maxsize = maxsize; m_d->m_uniquentries = ((flags & CC_CRUNIQUE) != 0); LOGDEB2("CirCache::create: rewriting header with maxsize " << m_d->m_maxsize << " oheadoffs " << m_d->m_oheadoffs << " nheadoffs " << m_d->m_nheadoffs << " npadsize " << m_d->m_npadsize << " unient " << m_d->m_uniquentries <<"\n"); return m_d->writefirstblock(); } // Else fallthrough to create file } if ((m_d->m_fd = path_open(m_d->datafn(m_dir), O_CREAT | O_RDWR | O_TRUNC | O_BINARY, 0666)) < 0) { m_d->m_reason << "CirCache::create: open/creat(" << m_d->datafn(m_dir) << ") failed " << "errno " << errno; return false; } m_d->m_maxsize = maxsize; m_d->m_oheadoffs = CIRCACHE_FIRSTBLOCK_SIZE; m_d->m_uniquentries = ((flags & CC_CRUNIQUE) != 0); char buf[CIRCACHE_FIRSTBLOCK_SIZE]; memset(buf, 0, CIRCACHE_FIRSTBLOCK_SIZE); if (::write(m_d->m_fd, buf, CIRCACHE_FIRSTBLOCK_SIZE) != CIRCACHE_FIRSTBLOCK_SIZE) { m_d->m_reason << "CirCache::create: write header failed, errno " << errno; return false; } return m_d->writefirstblock(); } bool CirCache::open(OpMode mode) { if (nullptr == m_d) { LOGERR("CirCache::open: null data\n"); return false; } if (m_d->m_fd >= 0) { ::close(m_d->m_fd); } if ((m_d->m_fd = path_open(m_d->datafn(m_dir), mode == CC_OPREAD ? O_RDONLY | O_BINARY : O_RDWR | O_BINARY)) < 0) { m_d->m_reason << "CirCache::open: open(" << m_d->datafn(m_dir) << ") failed " << "errno " << errno; return false; } return m_d->readfirstblock(); } int64_t CirCache::size() const { if (nullptr == m_d) { LOGERR("CirCache::open: null data\n"); return -1; } int64_t sz = -1; struct PathStat st; if (m_d->m_fd < 0) { if (path_fileprops(m_d->datafn(m_dir), &st) < 0) { m_d->m_reason << "CirCache::size: stat(" << m_d->datafn(m_dir) << ") failed " << "errno " << errno; return -1; } sz = st.pst_size; } else { struct stat st; if (fstat(m_d->m_fd, &st) < 0) { m_d->m_reason << "CirCache::open: fstat(" << m_d->datafn(m_dir) << ") failed " << "errno " << errno; return -1; } sz = st.st_size; } return sz; } int64_t CirCache::maxsize() const { if (nullptr == m_d) { LOGERR("CirCache::open: null data\n"); return -1; } return m_d->m_maxsize; } int64_t CirCache::writepos() const { if (nullptr == m_d) { LOGERR("CirCache::open: null data\n"); return -1; } return m_d->m_oheadoffs; } int64_t CirCache::nheadpos() const { if (nullptr == m_d) { LOGERR("CirCache::open: null data\n"); return -1; } return m_d->m_nheadoffs; } bool CirCache::uniquentries() const { if (nullptr == m_d) { LOGERR("CirCache::open: null data\n"); return false; } return m_d->m_uniquentries; } class CCScanHookDump : public CCScanHook { public: virtual status takeone(int64_t offs, const string& udi, const EntryHeaderData& d) { cout << "Scan: offs " << offs << " dicsize " << d.dicsize << " datasize " << d.datasize << " padsize " << d.padsize << " flags " << d.flags << " udi [" << udi << "]" << endl; return Continue; } }; bool CirCache::dump() { CCScanHookDump dumper; // Start at oldest header. This is eof while the file is growing, scan will // fold to bot at once. int64_t start = m_d->m_oheadoffs; switch (m_d->scan(start, &dumper, true)) { case CCScanHook::Stop: cout << "Scan returns Stop??" << endl; return false; case CCScanHook::Continue: cout << "Scan returns Continue ?? " << CCScanHook::Continue << " " << getReason() << endl; return false; case CCScanHook::Error: cout << "Scan returns Error: " << getReason() << endl; return false; case CCScanHook::Eof: cout << "Scan returns Eof (ok)" << endl; return true; default: cout << "Scan returns Unknown ??" << endl; return false; } } class CCScanHookGetter : public CCScanHook { public: string m_udi; int m_targinstance; int m_instance; int64_t m_offs; EntryHeaderData m_hd; CCScanHookGetter(const string& udi, int ti) : m_udi(udi), m_targinstance(ti), m_instance(0), m_offs(0) {} virtual status takeone(int64_t offs, const string& udi, const EntryHeaderData& d) { LOGDEB1("Circache:Scan: off " << offs << " udi [" << udi << "] dcsz " << d.dicsize << " dtsz " << d.datasize << " pdsz " << d.padsize << " flgs " << d.flags << " previnst " << m_instance << "\n"); if (!m_udi.compare(udi)) { m_instance++; m_offs = offs; m_hd = d; if (m_instance == m_targinstance) { return Stop; } } return Continue; } }; bool CirCache::get(const string& udi, string& dic, string *data, int instance) { Chrono chron; if (m_d->m_fd < 0) { m_d->m_reason << "CirCache::get: no data or not open"; return false; } LOGDEB0("CirCache::get: udi [" << udi << "], instance " << instance << "\n"); // If memory map is up to date, use it: if (m_d->m_ofskhcplt) { LOGDEB1("CirCache::get: using ofskh\n"); //m_d->khDump(); vector ofss; if (m_d->khFind(udi, ofss)) { LOGDEB1("Circache::get: h found, colls " << ofss.size() << "\n"); int finst = 1; EntryHeaderData d_good; int64_t o_good = 0; for (const auto& offset : ofss) { LOGDEB1("Circache::get: trying offs " << offset << "\n"); EntryHeaderData d; string fudi; if (!m_d->readHUdi(offset, d, fudi)) { return false; } if (!fudi.compare(udi)) { // Found one, memorize offset. Done if instance // matches, else go on. If instance is -1 need to // go to the end anyway d_good = d; o_good = offset; if (finst == instance) { break; } else { finst++; } } } // Did we read an appropriate entry ? if (o_good != 0 && (instance == -1 || instance == finst)) { bool ret = m_d->readDicData(o_good, d_good, dic, data); LOGDEB0("Circache::get: hfound, " << chron.millis() << " mS\n"); return ret; } // Else try to scan anyway. } } CCScanHookGetter getter(udi, instance); int64_t start = m_d->m_oheadoffs; CCScanHook::status ret = m_d->scan(start, &getter, true); if (ret == CCScanHook::Eof) { if (getter.m_instance == 0) { return false; } } else if (ret != CCScanHook::Stop) { return false; } bool bret = m_d->readDicData(getter.m_offs, getter.m_hd, dic, data); LOGDEB0("Circache::get: scanfound, " << chron.millis() << " mS\n"); return bret; } // It would be possible to have an option to only erase if this is the // last entry in the file, by comparing the offsets from khFind() with // m_oheadoffs. Read the last offset < m_oheadoffs and check that // offset+sizes == oheadoffs bool CirCache::erase(const string& udi, bool reallyclear) { if (nullptr == m_d) { LOGERR("CirCache::erase: null data\n"); return false; } if (m_d->m_fd < 0) { m_d->m_reason << "CirCache::erase: no data or not open"; return false; } LOGDEB0("CirCache::erase: udi [" << udi << "]\n"); // If the mem cache is not up to date, update it, we're too lazy // to do a scan if (!m_d->m_ofskhcplt) { string dic; get("nosuchudi probably exists", dic); if (!m_d->m_ofskhcplt) { LOGERR("CirCache::erase : cache not updated after get\n"); return false; } } vector ofss; if (!m_d->khFind(udi, ofss)) { // Udi not in there, erase ok LOGDEB("CirCache::erase: khFind returns none\n"); return true; } for (const auto& offset : ofss) { LOGDEB2("CirCache::erase: reading at " << offset << "\n"); EntryHeaderData d; string fudi; if (!m_d->readHUdi(offset, d, fudi)) { return false; } LOGDEB2("CirCache::erase: found fudi [" << fudi << "]\n"); if (!fudi.compare(udi)) { EntryHeaderData nd; nd.padsize = d.dicsize + d.datasize + d.padsize; LOGDEB2("CirCache::erase: rewrite at " << offset << "\n"); if (offset == m_d->m_nheadoffs) { m_d->m_npadsize = nd.padsize; } if (!m_d->writeEntryHeader(offset, nd, reallyclear)) { LOGERR("CirCache::erase: write header failed\n"); return false; } } } m_d->khClear(udi); return true; } // Used to scan the file ahead until we accumulated enough space for the new // entry. class CCScanHookSpacer : public CCScanHook { public: int64_t sizewanted; int64_t sizeseen; vector > squashed_udis; CCScanHookSpacer(int64_t sz) : sizewanted(sz), sizeseen(0) { assert(sz > 0); } virtual status takeone(int64_t offs, const string& udi, const EntryHeaderData& d) { LOGDEB2("Circache:ScanSpacer:off " << offs << " dcsz " << d.dicsize << " dtsz " << d.datasize << " pdsz " << d.padsize << " udi[" << udi << "]\n"); sizeseen += CIRCACHE_HEADER_SIZE + d.dicsize + d.datasize + d.padsize; squashed_udis.push_back(make_pair(udi, offs)); if (sizeseen >= sizewanted) { return Stop; } return Continue; } }; bool CirCache::put(const string& udi, const ConfSimple *iconf, const string& data, unsigned int iflags) { if (nullptr == m_d) { LOGERR("CirCache::put: null data\n"); return false; } if (m_d->m_fd < 0) { m_d->m_reason << "CirCache::put: no data or not open"; return false; } // We need the udi in input metadata string dic; if (!iconf || !iconf->get("udi", dic) || dic.empty() || dic.compare(udi)) { m_d->m_reason << "No/bad 'udi' entry in input dic"; LOGERR("Circache::put: no/bad udi: DIC:[" << dic << "] UDI [" << udi << "]\n"); return false; } // Possibly erase older entries. Need to do this first because we may be // able to reuse the space if the same udi was last written if (m_d->m_uniquentries && !erase(udi)) { LOGERR("CirCache::put: can't erase older entries\n"); return false; } ostringstream s; iconf->write(s); dic = s.str(); // Data compression ? const char *datap = data.c_str(); size_t datalen = data.size(); unsigned short flags = 0; ZLibUtBuf buf; if (!(iflags & NoCompHint)) { if (deflateToBuf(data.c_str(), data.size(), buf)) { // If compression succeeds, and the ratio makes sense, store compressed if (float(buf.getCnt()) < 0.9 * float(data.size())) { datap = buf.getBuf(); datalen = buf.getCnt(); flags |= EFDataCompressed; } } } struct stat st; if (fstat(m_d->m_fd, &st) < 0) { m_d->m_reason << "CirCache::put: fstat failed. errno " << errno; return false; } // Characteristics for the new entry. int64_t nsize = CIRCACHE_HEADER_SIZE + dic.size() + datalen; int64_t nwriteoffs = m_d->m_oheadoffs; int64_t npadsize = 0; bool extending = false; LOGDEB("CirCache::put: nsz " << nsize << " oheadoffs " << m_d->m_oheadoffs << "\n"); // Check if we can recover some pad space from the (physically) previous // entry. int64_t recovpadsize = m_d->m_oheadoffs == CIRCACHE_FIRSTBLOCK_SIZE ? 0 : m_d->m_npadsize; if (recovpadsize != 0) { // Need to read the latest entry's header, to rewrite it with a // zero pad size EntryHeaderData pd; if (m_d->readEntryHeader(m_d->m_nheadoffs, pd) != CCScanHook::Continue) { return false; } if (int(pd.padsize) != m_d->m_npadsize) { m_d->m_reason << "CirCache::put: logic error: bad padsize "; return false; } if (pd.dicsize == 0) { // erased entry. Also recover the header space, no need to rewrite // the header, we're going to write on it. recovpadsize += CIRCACHE_HEADER_SIZE; } else { LOGDEB("CirCache::put: recov. prev. padsize " << pd.padsize << "\n"); pd.padsize = 0; if (!m_d->writeEntryHeader(m_d->m_nheadoffs, pd)) { return false; } // If we fail between here and the end, the file is broken. } nwriteoffs = m_d->m_oheadoffs - recovpadsize; } if (nsize <= recovpadsize) { // If the new entry fits entirely in the pad area from the // latest one, no need to recycle stuff LOGDEB("CirCache::put: new fits in old padsize " << recovpadsize <<"\n"); npadsize = recovpadsize - nsize; } else if (st.st_size < m_d->m_maxsize) { // Still growing the file. npadsize = 0; extending = true; } else { // Scan the file until we have enough space for the new entry, // and determine the pad size up to the 1st preserved entry int64_t scansize = nsize - recovpadsize; LOGDEB("CirCache::put: scanning for size " << scansize << " from offs " << m_d->m_oheadoffs << "\n"); CCScanHookSpacer spacer(scansize); switch (m_d->scan(m_d->m_oheadoffs, &spacer)) { case CCScanHook::Stop: LOGDEB("CirCache::put: Scan ok, sizeseen " << spacer.sizeseen<<"\n"); npadsize = spacer.sizeseen - scansize; break; case CCScanHook::Eof: npadsize = 0; extending = true; break; case CCScanHook::Continue: case CCScanHook::Error: return false; } // Take the recycled entries off the multimap m_d->khClear(spacer.squashed_udis); } LOGDEB("CirCache::put: writing " << nsize << " at " << nwriteoffs << " padsize " << npadsize << "\n"); if (lseek(m_d->m_fd, nwriteoffs, 0) != nwriteoffs) { m_d->m_reason << "CirCache::put: lseek failed: " << errno; return false; } char head[CIRCACHE_HEADER_SIZE]; memset(head, 0, CIRCACHE_HEADER_SIZE); snprintf(head, CIRCACHE_HEADER_SIZE, headerformat, dic.size(), datalen, npadsize, flags); struct iovec vecs[3]; vecs[0].iov_base = head; vecs[0].iov_len = CIRCACHE_HEADER_SIZE; vecs[1].iov_base = (void *)dic.c_str(); vecs[1].iov_len = dic.size(); vecs[2].iov_base = (void *)datap; vecs[2].iov_len = datalen; if (writev(m_d->m_fd, vecs, 3) != nsize) { m_d->m_reason << "put: write failed. errno " << errno; if (extending) if (ftruncate(m_d->m_fd, m_d->m_oheadoffs) == -1) { m_d->m_reason << "put: ftruncate failed. errno " << errno; } return false; } m_d->khEnter(udi, nwriteoffs); // Update first block information m_d->m_nheadoffs = nwriteoffs; m_d->m_npadsize = npadsize; // New oldest header is the one just after the one we just wrote. m_d->m_oheadoffs = nwriteoffs + nsize + npadsize; if (nwriteoffs + nsize >= m_d->m_maxsize) { // Max size or top of file reached, next write at BOT. m_d->m_oheadoffs = CIRCACHE_FIRSTBLOCK_SIZE; } return m_d->writefirstblock(); } bool CirCache::rewind(bool& eof) { if (nullptr == m_d) { LOGERR("CirCache::rewind: null data\n"); return false; } eof = false; int64_t fsize = lseek(m_d->m_fd, 0, SEEK_END); if (fsize == (int64_t) - 1) { LOGERR("CirCache::rewind: seek to EOF failed\n"); return false; } // Read oldest header. This is either at the position pointed to // by oheadoffs, or after the first block if the file is still // growing. if (m_d->m_oheadoffs == fsize) { m_d->m_itoffs = CIRCACHE_FIRSTBLOCK_SIZE; } else { m_d->m_itoffs = m_d->m_oheadoffs; } CCScanHook::status st = m_d->readEntryHeader(m_d->m_itoffs, m_d->m_ithd); switch (st) { case CCScanHook::Eof: eof = true; return false; case CCScanHook::Continue: return true; default: return false; } } bool CirCache::next(bool& eof) { if (nullptr == m_d) { LOGERR("CirCache::next: null data\n"); return false; } eof = false; // Skip to next header, using values stored from previous one m_d->m_itoffs += CIRCACHE_HEADER_SIZE + m_d->m_ithd.dicsize + m_d->m_ithd.datasize + m_d->m_ithd.padsize; // Looped back ? if (m_d->m_itoffs == m_d->m_oheadoffs) { eof = true; return false; } // Read. If we hit physical eof, fold. CCScanHook::status st = m_d->readEntryHeader(m_d->m_itoffs, m_d->m_ithd); if (st == CCScanHook::Eof) { m_d->m_itoffs = CIRCACHE_FIRSTBLOCK_SIZE; if (m_d->m_itoffs == m_d->m_oheadoffs) { // Then the file is not folded yet (still growing) eof = true; return false; } st = m_d->readEntryHeader(m_d->m_itoffs, m_d->m_ithd); } if (st == CCScanHook::Continue) { return true; } return false; } bool CirCache::getCurrentUdi(string& udi) { if (nullptr == m_d) { LOGERR("CirCache::getCurrentUdi: null data\n"); return false; } if (!m_d->readHUdi(m_d->m_itoffs, m_d->m_ithd, udi)) { return false; } return true; } bool CirCache::getCurrent(string& udi, string& dic, string *data) { if (nullptr == m_d) { LOGERR("CirCache::getCurrent: null data\n"); return false; } if (!m_d->readDicData(m_d->m_itoffs, m_d->m_ithd, dic, data)) { return false; } ConfSimple conf(dic, 1); conf.get("udi", udi, cstr_null); return true; } // Send all entries from occ to callback. occ is already open. static bool copyall(std::shared_ptr occ, std::function cb, int& nentries, ostringstream& msg) { bool eof = false; if (!occ->rewind(eof)) { if (!eof) { msg << "Initial rewind failed" << endl; return false; } } nentries = 0; while (!eof) { string udi, sdic, data; if (!occ->getCurrent(udi, sdic, &data)) { msg << "getCurrent failed: " << occ->getReason() << endl; return false; } // Shouldn't getcurrent deal with this ? if (sdic.size() == 0) { //cerr << "Skip empty entry" << endl; occ->next(eof); continue; } ConfSimple dic(sdic); if (!dic.ok()) { msg << "Could not parse entry attributes dic" << endl; return false; } //cerr << "UDI: " << udi << endl; if (!cb(udi, &dic, data)) { string err; catstrerror(&err, "", errno); msg << "put failed: errno " << err << " for [" << sdic << "]" << endl; return false; } nentries++; occ->next(eof); } return true; } int CirCache::appendCC(const string& ddir, const string& sdir, string *reason) { ostringstream msg; // Open source file std::shared_ptr occ(new CirCache(sdir)); if (!occ->open(CirCache::CC_OPREAD)) { if (reason) { msg << "Open failed in " << sdir << " : " << occ->getReason() << endl; *reason = msg.str(); } return -1; } // Possibly resize dest. If the dest is currently recycling, it // will keep on. This only avoids erasing entries in dest if it is // currently writing at EOF (growing), which will be the case if // we are using this to compact existing file (the dest was just // created for the purpose). int64_t dstavail{0}, dstmaxsize{0}; bool isunique; // Check dest size { std::shared_ptr ncc(new CirCache(ddir)); if (!ncc->open(CirCache::CC_OPREAD)) { if (reason) { msg << "Open failed in " << ddir << " : " << ncc->getReason() << endl; *reason = msg.str(); } return -1; } dstmaxsize = ncc->m_d->m_maxsize; dstavail = dstmaxsize - ncc->m_d->m_nheadoffs; isunique = ncc->m_d->m_uniquentries; } if (dstavail < occ->size()) { std::shared_ptr ncc(new CirCache(ddir)); auto nsize = dstmaxsize + (occ->size() - dstavail) + 5*1000*1000; LOGDEB1("CirCache::appendCC: Dstmaxsize " << displayableBytes(dstmaxsize) << " dstavail "<< displayableBytes(dstavail) << " org size " << displayableBytes(occ->size()) << " nsize " << displayableBytes(nsize) << "\n"); if (!ncc->create(nsize, isunique ? CC_CRUNIQUE : CC_CRNONE)) { if (reason) { msg << "Open failed in " << ddir << " : " << ncc->getReason() << endl; *reason = msg.str(); } return -1; } } // Open dest file std::shared_ptr ncc(new CirCache(ddir)); if (!ncc->open(CirCache::CC_OPWRITE)) { if (reason) { msg << "Open failed in " << ddir << " : " << ncc->getReason() << endl; *reason = msg.str(); } return -1; } int nentries; std::function cb = std::bind(&CirCache::put, ncc, _1, _2, _3, 0); if (!copyall(occ, cb, nentries, msg)) { msg << " " << ncc->getReason() << "\n"; LOGERR(msg.str()); if (reason) { *reason = msg.str(); } return -1; } return nentries; } bool CirCache::compact(const std::string& dir, std::string *reason) { ostringstream msg; msg << "CirCache::compact: "; // Open source file std::shared_ptr occ(new CirCache(dir)); if (!occ->open(CirCache::CC_OPREAD)) { msg << "open failed in " << dir << " : " << occ->getReason() << "\n"; LOGERR(msg.str()); if (reason) { *reason = msg.str(); } return false; } long long avmbs; if (fsocc(dir, nullptr, &avmbs) && avmbs * 1024 * 1024 < 1.2 * occ->size()) { msg << "not enough space on file system"; LOGERR(msg.str() <<"\n"); if (reason) { *reason = msg.str(); } return false; } std::string ndir = path_cat(dir, "tmpcopy"); if (!path_makepath(dir, 0700)) { msg << "path_makepath failed with errno " << errno; LOGERR(msg.str() << "\n"); if (reason) { *reason = msg.str(); } return false; } std::shared_ptr ncc(new CirCache(ndir)); if (!ncc->create(occ->size(), occ->uniquentries() ? CC_CRUNIQUE : CC_CRNONE)) { msg << "open failed in " << ndir << " : " << ncc->getReason(); LOGERR(msg.str() << "\n"); if (reason) { *reason = msg.str(); } return false; } int nentries; std::function cb = std::bind(&CirCache::put, ncc, _1, _2, _3, 0); if (!copyall(occ, cb, nentries, msg)) { msg << " " << ncc->getReason(); LOGERR(msg.str() << "\n"); if (reason) { *reason = msg.str(); } return false; } // Close both occ.reset(); ncc.reset(); // Rename new to old std::string r; std::string nfile = path_cat(ndir, "circache.crch").c_str(); std::string ofile = path_cat(dir, "circache.crch").c_str(); if (!renameormove(nfile.c_str(), ofile.c_str(), r)) { msg << "rename: " << r; LOGERR(msg.str() << "\n"); if (reason) { *reason = msg.str(); } return false; } // Cleanup wipedir(ndir, true); return true; } class CCDataToFile { public: CCDataToFile(const std::string dd) : m_dir(dd) {} bool putFile(const std::string& udi, const ConfSimple *dicp, const std::string& data); std::string& getReason() {return m_reason;} private: std::string m_dir; std::string m_reason; }; bool CCDataToFile::putFile(const std::string& udi, const ConfSimple *dicp, const std::string& data) { #if 0 std::ostringstream deb; dicp->write(deb); LOGDEB("CCDataToFile::putFile: udi " << udi << " dic " << deb.str() << " datasize " << data.size() << "\n"); #endif std::string hash = MD5Hex(udi); std::string dsuff; std::string mt; dicp->get("mimetype", mt); if (mt == "text/html") { dsuff = ".html"; } else if (mt == "application/pdf") { dsuff = ".pdf"; } else { dsuff = ".xxx"; } int dupnum = 1; std::string fn; for (;;) { fn = path_cat(m_dir, "circache-" + hash + "-" + lltodecstr(dupnum) + dsuff); if (path_exists(fn)) { dupnum++; } else { break; } } if (!stringtofile(data, fn.c_str(), m_reason)) { return false; } // Try to reset the original mtime on the data file. std::string sfmtime; if (dicp->get("fmtime", sfmtime)) { time_t fmtime = atoll(sfmtime.c_str()); if (fmtime) { struct path_timeval tv[2]; tv[0] = tv[1] = {fmtime, 0}; path_utimes(fn, tv); } } fn = path_cat(m_dir, "circache-" + hash + "-" + lltodecstr(dupnum) + ".dic"); std::ostringstream str; dicp->write(str); if (!stringtofile(str.str(), fn.c_str(), m_reason)) { return false; } return true; } bool CirCache::burst(const std::string& ccdir, const std::string destdir, std::string *reason) { ostringstream msg; msg << "CirCache::burst: "; // Open source file std::shared_ptr occ(new CirCache(ccdir)); if (!occ->open(CirCache::CC_OPREAD)) { msg << "open failed in " << ccdir << " : " << occ->getReason() << "\n"; LOGERR(msg.str()); if (reason) { *reason = msg.str(); } return false; } // This test may now actually be very optimistic because of compression ? long long avmbs; if (fsocc(destdir, nullptr, &avmbs) && avmbs * 1024 * 1024 < 1.2 * occ->size()) { msg << "not enough space on file system"; LOGERR(msg.str() <<"\n"); if (reason) { *reason = msg.str(); } return false; } if (!path_makepath(destdir, 0700)) { msg << "path_makepath failed with errno " << errno; LOGERR(msg.str() << "\n"); if (reason) { *reason = msg.str(); } return false; } int nentries; CCDataToFile copier(destdir); std::function cb = std::bind(&CCDataToFile::putFile, copier, _1, _2, _3); if (!copyall(occ, cb, nentries, msg)) { msg << " " << copier.getReason(); LOGERR(msg.str() << "\n"); if (reason) { *reason = msg.str(); } return false; } return true; } recoll-1.36.1/utils/cancelcheck.cpp0000644000175000017500000000162114410615043014070 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "cancelcheck.h" CancelCheck& CancelCheck::instance() { static CancelCheck ck; return ck; } recoll-1.36.1/utils/x11mon.h0000644000175000017500000000165714410615043012446 00000000000000#ifndef _X11MON_H_INCLUDED_ #define _X11MON_H_INCLUDED_ /* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** Poll X11 server status and connectivity */ extern bool x11IsAlive(); #endif /* _X11MON_H_INCLUDED_ */ recoll-1.36.1/utils/appformime.cpp0000644000175000017500000001170314427373216014021 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include using namespace std; #include "pathut.h" #include "smallut.h" #include "appformime.h" static const string topappsdir("/usr/share/applications"); static const string desktopext("desktop"); static DesktopDb *theDb; class FstCb : public FsTreeWalkerCB { public: FstCb(DesktopDb::AppMap *appdefs) : m_appdefs(appdefs) {} virtual FsTreeWalker::Status processone(const string &, FsTreeWalker::CbFlag, const struct PathStat&) override; DesktopDb::AppMap *m_appdefs; }; FsTreeWalker::Status FstCb::processone( const string& fn, FsTreeWalker::CbFlag flg, const struct PathStat&) { if (flg != FsTreeWalker::FtwRegular) return FsTreeWalker::FtwOk; if (path_suffix(fn).compare(desktopext)) { //cerr << fn << " does not end with .desktop" << endl; return FsTreeWalker::FtwOk; } ConfSimple dt(fn.c_str(), true); if (!dt.ok()) { cerr << fn << " cant parse" << endl; return FsTreeWalker::FtwOk; } string tp, nm, cmd, mt; if (!dt.get("Type", tp, "Desktop Entry")) { //cerr << fn << " no Type" << endl; return FsTreeWalker::FtwOk; } if (tp.compare("Application")) { //cerr << fn << " wrong Type " << tp << endl; return FsTreeWalker::FtwOk; } if (!dt.get("Exec", cmd, "Desktop Entry")) { //cerr << fn << " no Exec" << endl; return FsTreeWalker::FtwOk; } if (!dt.get("Name", nm, "Desktop Entry")) { //cerr << fn << " no Name" << endl; nm = path_basename(fn, desktopext); } if (!dt.get("MimeType", mt, "Desktop Entry")) { //cerr << fn << " no MimeType" << endl; return FsTreeWalker::FtwOk; } DesktopDb::AppDef appdef(nm, cmd); // Breakup mime type list, and push app to mime entries vector mimes; stringToTokens(mt, mimes, ";"); for (vector::const_iterator it = mimes.begin(); it != mimes.end(); it++) { (*m_appdefs)[*it].push_back(appdef); } return FsTreeWalker::FtwOk; } DesktopDb* DesktopDb::getDb() { if (nullptr == theDb) { theDb = new DesktopDb(); } if (theDb && theDb->m_ok) return theDb; return nullptr; } void DesktopDb::build(const string& dir) { FstCb procapp(&m_appMap); FsTreeWalker walker; if (walker.walk(dir, procapp) != FsTreeWalker::FtwOk) { m_ok = false; m_reason = walker.getReason(); } m_ok = true; } DesktopDb::DesktopDb() { build(topappsdir); } DesktopDb::DesktopDb(const string& dir) { build(dir); } bool DesktopDb::appForMime(const string& mime, vector *apps, string *reason) { AppMap::const_iterator it = m_appMap.find(mime); if (it == m_appMap.end()) { if (reason) *reason = string("No application found for ") + mime; return false; } *apps = it->second; return true; } bool DesktopDb::allApps(vector *apps) { map allaps; for (AppMap::const_iterator it = m_appMap.begin(); it != m_appMap.end(); it++) { for (vector::const_iterator it1 = it->second.begin(); it1 != it->second.end(); it1++) { allaps.insert(pair (it1->name, AppDef(it1->name, it1->command))); } } for (map::const_iterator it = allaps.begin(); it != allaps.end(); it++) { apps->push_back(it->second); } return true; } bool DesktopDb::appByName(const string& nm, AppDef& app) { for (AppMap::const_iterator it = m_appMap.begin(); it != m_appMap.end(); it++) { for (vector::const_iterator it1 = it->second.begin(); it1 != it->second.end(); it1++) { if (!nm.compare(it1->name)) { app.name = it1->name; app.command = it1->command; return true; } } } return false; } const string& DesktopDb::getReason() { return m_reason; } bool mimeIsImage(const std::string& tp) { return !tp.compare(0, 6, "image/") && tp.compare("image/vnd.djvu") && tp.compare("image/svg+xml"); } recoll-1.36.1/utils/strmatcher.h0000644000175000017500000000564614410615043013501 00000000000000/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _STRMATCHER_H_INCLUDED_ #define _STRMATCHER_H_INCLUDED_ #include #include #include "smallut.h" // Encapsulating simple wildcard/regexp string matching. // Matcher class. Interface to either wildcard or regexp yes/no matcher class StrMatcher { public: StrMatcher(const std::string& exp) : m_sexp(exp) {} virtual ~StrMatcher() {}; StrMatcher(const StrMatcher&) = delete; StrMatcher& operator=(const StrMatcher&) = delete; virtual bool match(const std::string &val) const = 0; virtual std::string::size_type baseprefixlen() const = 0; virtual bool setExp(const std::string& newexp) { m_sexp = newexp; return true; } virtual bool ok() const { return true; } virtual const std::string& exp() const { return m_sexp; } virtual StrMatcher *clone() const = 0; const std::string& getreason() const { return m_reason; } protected: std::string m_sexp; std::string m_reason; }; class StrWildMatcher : public StrMatcher { public: StrWildMatcher(const std::string& exp) : StrMatcher(exp) {} virtual ~StrWildMatcher() {} StrWildMatcher(const StrWildMatcher&) = delete; StrWildMatcher& operator=(const StrWildMatcher&) = delete; virtual bool match(const std::string& val) const override; virtual std::string::size_type baseprefixlen() const override; virtual StrWildMatcher *clone() const override { return new StrWildMatcher(m_sexp); } }; class StrRegexpMatcher : public StrMatcher { public: StrRegexpMatcher(const std::string& exp); virtual bool setExp(const std::string& newexp) override; virtual ~StrRegexpMatcher() {}; StrRegexpMatcher(const StrRegexpMatcher&) = delete; StrRegexpMatcher& operator=(const StrRegexpMatcher&) = delete; virtual bool match(const std::string& val) const override; virtual std::string::size_type baseprefixlen() const override; virtual bool ok() const override; virtual StrRegexpMatcher *clone() const override { return new StrRegexpMatcher(m_sexp); } private: std::unique_ptr m_re; }; #endif /* _STRMATCHER_H_INCLUDED_ */ recoll-1.36.1/utils/strmatcher.cpp0000644000175000017500000000422314426500174014027 00000000000000/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "strmatcher.h" #include #include #include #include #include "cstr.h" #include "log.h" #include "rclutil.h" using namespace std; bool StrWildMatcher::match(const string& val) const { LOGDEB2("StrWildMatcher::match: in: ["<< val << "] expr: [" << m_sexp << "]\n"); int ret = fnmatch(m_sexp.c_str(), val.c_str(), FNM_NOESCAPE); switch (ret) { case 0: return true; case FNM_NOMATCH: return false; default: LOGINFO("StrWildMatcher::match:err: e [" << m_sexp << "] s [" << val << "] (" << url_encode(val) << ") ret " << ret << "\n"); return false; } } string::size_type StrWildMatcher::baseprefixlen() const { return m_sexp.find_first_of(cstr_wildSpecStChars); } StrRegexpMatcher::StrRegexpMatcher(const string& exp) : StrMatcher(exp), m_re(new SimpleRegexp(exp, SimpleRegexp::SRE_NOSUB)) { } bool StrRegexpMatcher::setExp(const string& exp) { m_re = std::unique_ptr(new SimpleRegexp(exp, SimpleRegexp::SRE_NOSUB)); return ok(); } bool StrRegexpMatcher::match(const string& val) const { if (ok()) return false; return (*m_re)(val); } string::size_type StrRegexpMatcher::baseprefixlen() const { return m_sexp.find_first_of(cstr_regSpecStChars); } bool StrRegexpMatcher::ok() const { return m_re && m_re->ok(); } recoll-1.36.1/utils/workqueue.h0000644000175000017500000003120114426500174013343 00000000000000/* Copyright (C) 2006-2016 J.F.Dockes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef _WORKQUEUE_H_INCLUDED_ #define _WORKQUEUE_H_INCLUDED_ #include #if HAVE_STD_FUTURE #include #endif #include #include #include #include #include #ifdef MDU_INCLUDE_LOG #include MDU_INCLUDE_LOG #else #include "log.h" #endif /** * A WorkQueue manages the synchronisation around a queue of work items, * where a number of client threads queue tasks and a number of worker * threads take and execute them. The goal is to introduce some level * of parallelism between the successive steps of a previously single * threaded pipeline. For example data extraction / data preparation / index * update, but this could have other uses. * * There is no individual task status return. In case of fatal error, * the client or worker sets an end condition on the queue. A second * queue could conceivably be used for returning individual task * status. * * The strange thread functions argument and return values * comes from compatibility with an earlier pthread-based * implementation. */ template class WorkQueue { public: /** Create a WorkQueue * @param name for message printing * @param hi number of tasks on queue before clients blocks. Default 0 * meaning no limit. hi == -1 means that the queue is disabled. * @param lo minimum count of tasks before worker starts. Default 1. */ WorkQueue(const std::string& name, size_t hi = 0, size_t lo = 1) : m_name(name), m_high(hi), m_low(lo), m_workers_exited(0), m_ok(true), m_clients_waiting(0), m_workers_waiting(0), m_tottasks(0), m_nowake(0), m_workersleeps(0), m_clientsleeps(0) { } ~WorkQueue() { if (!m_worker_threads.empty()) { setTerminateAndWait(); } } WorkQueue(const WorkQueue&) = delete; WorkQueue& operator=(const WorkQueue&) = delete; /** Task deleter * If put() is called with the flush option, and the tasks allocate memory, * you need to set this function, which will be called on each task popped * from the queue. Tasks which go through normally must be freed by the * worker function. */ void setTaskFreeFunc(void (*func)(T&)) { m_taskfreefunc = func; } /// Forbid inputting new tasks. This is mostly useful for abnormal terminations as some data will /// probably be lost, depending on how the upstream handles the put() error. void closeShop() { m_openforbusiness = false; } /** Start the worker threads. * * @param nworkers number of threads copies to start. * @param start_routine thread function. It should loop * taking (QueueWorker::take()) and executing tasks. * @param arg initial parameter to thread function. * @return true if ok. */ bool start(int nworkers, void *(workproc)(void *), void *arg) { std::unique_lock lock(m_mutex); for (int i = 0; i < nworkers; i++) { Worker w; #if HAVE_STD_FUTURE std::packaged_task task(workproc); w.res = task.get_future(); w.thr = std::thread(std::move(task), arg); #else w.thr = std::thread(workproc, arg); #endif m_worker_threads.push_back(std::move(w)); } return true; } /** Add item to work queue, called from client. * * Sleeps if there are already too many. */ bool put(T t, bool flushprevious = false) { std::unique_lock lock(m_mutex); if (!ok() || !m_openforbusiness) { LOGERR("WorkQueue::put: " << m_name << ": ok: " << ok() << " openforbusiness " << m_openforbusiness << "\n"); return false; } LOGDEB2("WorkQueue::put: " << m_name << "\n"); while (ok() && m_high > 0 && m_queue.size() >= m_high) { m_clientsleeps++; // Keep the order: we test ok() AFTER the sleep... m_clients_waiting++; m_ccond.wait(lock); if (!ok()) { m_clients_waiting--; return false; } m_clients_waiting--; } if (flushprevious) { while (!m_queue.empty()) { if (m_taskfreefunc) { T& d = m_queue.front(); m_taskfreefunc(d); } m_queue.pop(); } } m_queue.push(t); if (m_workers_waiting > 0) { // Just wake one worker, there is only one new task. m_wcond.notify_one(); } else { m_nowake++; } return true; } /** Wait until the queue is inactive. Called from client. * * Waits until the task queue is empty and the workers are all * back sleeping (or exited). Used by the client to wait for all current work * to be completed, when it needs to perform work that couldn't be * done in parallel with the worker's tasks, or before shutting * down. Work can be resumed after calling this. Note that the * only thread which can call it safely is the client just above * (which can control the task flow), else there could be * tasks in the intermediate queues. * To rephrase: there is no warranty on return that the queue is actually * idle EXCEPT if the caller knows that no jobs are still being created. * It would be possible to transform this into a safe call if some kind * of suspend condition was set on the queue by waitIdle(), to be reset by * some kind of "resume" call. Not currently the case. */ bool waitIdle() { std::unique_lock lock(m_mutex); // We're not done while: // - the queue is not empty and we have some workers left // - OR some workers are working (not exited or back waiting for a task). while (((m_queue.size() > 0 && m_workers_exited < m_worker_threads.size()) || (m_workers_waiting + m_workers_exited) < m_worker_threads.size())) { LOGDEB0("waitIdle: " << m_name << " qsz " << m_queue.size() << " wwaiting " << m_workers_waiting << " wexit " << m_workers_exited << " nthr " << m_worker_threads.size() << "\n"); m_clients_waiting++; m_ccond.wait(lock); m_clients_waiting--; } return ok(); } /** Tell the workers to exit, and wait for them. * * Does not bother about tasks possibly remaining on the queue, so * should be called after waitIdle() for an orderly shutdown. */ void *setTerminateAndWait() { std::unique_lock lock(m_mutex); LOGDEB("setTerminateAndWait:" << m_name << "\n"); if (m_worker_threads.empty()) { // Already called ? return (void*)0; } // Wait for all worker threads to have called workerExit() m_ok = false; while (m_workers_exited < m_worker_threads.size()) { m_wcond.notify_all(); m_clients_waiting++; m_ccond.wait(lock); m_clients_waiting--; } LOGDEB("" << m_name << ": tasks " << m_tottasks << " nowakes " << m_nowake << " wsleeps " << m_workersleeps << " csleeps " << m_clientsleeps << "\n"); // Perform the thread joins and compute overall status // Workers return (void*)1 if ok void *statusall = (void*)1; while (!m_worker_threads.empty()) { #if HAVE_STD_FUTURE void *status = m_worker_threads.front().res.get(); #else void *status = (void*) 1; #endif m_worker_threads.front().thr.join(); if (status == (void *)0) { statusall = status; } m_worker_threads.pop_front(); } // Reset to start state. m_workers_exited = m_clients_waiting = m_workers_waiting = m_tottasks = m_nowake = m_workersleeps = m_clientsleeps = 0; m_ok = true; LOGDEB("setTerminateAndWait:" << m_name << " done\n"); return statusall; } /** Take task from queue. Called from worker. * * Sleeps if there are not enough. Signal if we go to sleep on empty * queue: client may be waiting for our going idle. */ bool take(T* tp, size_t *szp = nullptr) { std::unique_lock lock(m_mutex); if (!ok()) { LOGDEB("WorkQueue::take:" << m_name << ": not ok\n"); return false; } while (ok() && m_queue.size() < m_low) { m_workersleeps++; m_workers_waiting++; if (m_queue.empty()) { m_ccond.notify_all(); } m_wcond.wait(lock); if (!ok()) { // !ok is a normal condition when shutting down m_workers_waiting--; return false; } m_workers_waiting--; } m_tottasks++; *tp = m_queue.front(); if (szp) { *szp = m_queue.size(); } m_queue.pop(); if (m_clients_waiting > 0) { // No reason to wake up more than one client thread m_ccond.notify_one(); } else { m_nowake++; } return true; } bool waitminsz(size_t sz) { std::unique_lock lock(m_mutex); if (!ok()) { return false; } while (ok() && m_queue.size() < sz) { m_workersleeps++; m_workers_waiting++; if (m_queue.empty()) { m_ccond.notify_all(); } m_wcond.wait(lock); if (!ok()) { m_workers_waiting--; return false; } m_workers_waiting--; } return true; } /** Advertise exit and abort queue. Called from worker * * This would happen after an unrecoverable error, or when * the queue is terminated by the client. Workers never exit normally, * except when the queue is shut down (at which point m_ok is set to * false by the shutdown code anyway). The thread must return/exit * immediately after calling this. */ void workerExit() { LOGDEB("workerExit:" << m_name << "\n"); std::unique_lock lock(m_mutex); m_workers_exited++; m_ok = false; m_ccond.notify_all(); } size_t qsize() { std::unique_lock lock(m_mutex); return m_queue.size(); } private: bool ok() { bool isok = m_ok && m_workers_exited == 0 && !m_worker_threads.empty(); if (!isok) { LOGDEB("WorkQueue:ok:" << m_name << ": not ok m_ok " << m_ok << " m_workers_exited " << m_workers_exited << " m_worker_threads size " << m_worker_threads.size() << "\n"); } return isok; } struct Worker { std::thread thr; #if HAVE_STD_FUTURE std::future res; #endif }; void (*m_taskfreefunc)(T&){nullptr}; // Configuration std::string m_name; size_t m_high; size_t m_low; // Worker threads having called exit. Used to decide when we're done unsigned int m_workers_exited; // Status bool m_ok; // Accepting new tasks bool m_openforbusiness{true}; // Our threads. std::list m_worker_threads; // Jobs input queue std::queue m_queue; // Synchronization std::condition_variable m_ccond; std::condition_variable m_wcond; std::mutex m_mutex; // Client/Worker threads currently waiting for a job unsigned int m_clients_waiting; unsigned int m_workers_waiting; // Statistics unsigned int m_tottasks; unsigned int m_nowake; unsigned int m_workersleeps; unsigned int m_clientsleeps; }; #endif /* _WORKQUEUE_H_INCLUDED_ */ recoll-1.36.1/utils/base64.h0000644000175000017500000000237614505743670012423 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _BASE64_H_INCLUDED_ #define _BASE64_H_INCLUDED_ #include void base64_encode(const std::string& in, std::string& out); bool base64_decode(const std::string& in, std::string& out); inline std::string base64_encode(const std::string& in) { std::string o; base64_encode(in, o); return o; } inline std::string base64_decode(const std::string& in) { std::string o; if (base64_decode(in, o)) return o; return std::string(); } #endif /* _BASE64_H_INCLUDED_ */ recoll-1.36.1/utils/circache.h0000644000175000017500000001533514444307651013074 00000000000000/* Copyright (C) 2009 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _circache_h_included_ #define _circache_h_included_ /** * A data cache implemented as a circularly managed file. * * A single file is used to serially store objects. The file grows to a specified maximum size, then * is rewritten from the start, overwriting older entries. * * The file begins with a descriptive block, containing both static attributes (e.g. maximum storage * size, store duplicate entries or not), and state information (e.g. position of the write pointer * for the next entry). * * The objects inside the cache each have two consecutive parts: an attribute (metadata) dictionary * and a data segment. They are named using an externally chosen unique identifier set when storing * them. Recoll uses the document UDI for this purpose. * * The unique identifiers are stored inside the entry's dictionary under the key "udi". * * It is assumed that the dictionaries are small (they are routinely read/parsed) */ #include #include class ConfSimple; class CirCacheInternal; class CirCache { public: /// Constructor: only performs initialization, it does not create or access the storage. CirCache(const std::string& dir); /// Destructor: memory cleanup, no file access except for closing the fd. virtual ~CirCache(); CirCache(const CirCache&) = delete; CirCache& operator=(const CirCache&) = delete; /// Get an explanatory message for the last error. virtual std::string getReason(); enum CreateFlags {CC_CRNONE = 0, // Unique entries: erase older instances when same udi is stored. CC_CRUNIQUE = 1, // Truncate file (restart from scratch). CC_CRTRUNCATE = 2 }; virtual bool create(int64_t maxsize, int flags); /// Open the storage file in the prescribed mode, and read the first (attributes) block. enum OpMode {CC_OPREAD, CC_OPWRITE}; virtual bool open(OpMode mode); /// Return the current file size (as from the storage file st_size) virtual int64_t size() const; /// Return the set maximum storage size in bytes. virtual int64_t maxsize() const; /// Return the position of the start of the newest entry (nheadoffs). virtual int64_t nheadpos() const; /// Return the current write position. This is the EOF if the file is not full, or the start of /// the oldest entry (or possibly EOF) else. virtual int64_t writepos() const; /// Return the 'unique' attribute, determining if we store duplicate UDIs. virtual bool uniquentries() const; /// Return the storage file path. virtual std::string getpath() const; /// Get data and attributes for a given udi. /// @param udi the object identifier. /// @param dic the std::string where to store the metadata dictionary. /// @param data std::string pointer where to store the data. Set to nullptr if you just want /// the header. /// @param instance Specific instance to retrieve (if storing duplicates). Set to -1 to get /// latest. Otherwise specify from 1+ virtual bool get(const std::string& udi, std::string& dic, std::string *data = nullptr, int instance = -1); /// Store a data object. /// Note: the dicp MUST have an udi entry. enum PutFlags { NoCompHint = 1, // Do not attempt compression. }; virtual bool put(const std::string& udi, const ConfSimple *dicp, const std::string& data, unsigned int flags = 0); /// Erase all instances for given udi. If reallyclear is set, actually overwrite the contents, /// else just logically erase. virtual bool erase(const std::string& udi, bool reallyclear = false); /** Walk the archive. * * Maybe we'll have separate iterators one day, but this is good * enough for now. No put() operations should be performed while * using these. */ /** Back to oldest */ virtual bool rewind(bool& eof); /** Get entry under cursor */ virtual bool getCurrent(std::string& udi, std::string& dic, std::string *data = nullptr); /** Get current entry udi only. Udi can be empty (erased empty), caller * should call again */ virtual bool getCurrentUdi(std::string& udi); /** Skip to next. (false && !eof) -> error, (false&&eof)->EOF. */ virtual bool next(bool& eof); /** Write global header and all entry headers to stdout. Debug. */ virtual bool dump(); /** Utility: append all entries from sdir to ddir. * * ddir must already exist. It will be appropriately resized if needed to avoid recycling while * writing the new entries. * * ** Note: if dest is not currently growing, this action will recycle old dest entries * between the current write point and EOF (or up to wherever we need to write to store * the source data) ** * * Also note that if the objective is just to compact (reuse the erased entries space) you * should first create the new circache with the same maxsize as the old one, else the new * maxsize will be the current file size (current erased+active entries, with available space * corresponding to the old erased entries). * * @param ddir destination circache (must exist) * @param sdir source circache * @return number of entries copied or -1 */ static int appendCC(const std::string& ddir, const std::string& sdir, std::string *reason = nullptr); /** Utility: rewrite the cache so that the space wasted to erased entries is recovered. * This may need to temporarily use twice the current cache disk space. */ static bool compact(const std::string& dir, std::string *reason = nullptr); /** Utility: extract all entries as metadata/data file pairs */ static bool burst( const std::string& ccdir, const std::string destdir, std::string *reason = nullptr); protected: CirCacheInternal *m_d{nullptr}; std::string m_dir; }; #endif /* _circache_h_included_ */ recoll-1.36.1/utils/miniz.cpp0000644000175000017500000114525414410615043013007 00000000000000/************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #include "miniz.h" typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API's */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } /* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ #if 0 mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } #else /* Faster, but larger CPU cache footprint. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; while (buf_len >= 4) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; pByte_buf += 4; buf_len -= 4; } while (buf_len) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; ++pByte_buf; --buf_len; } return ~crc32; } #endif void mz_free(void *p) { MZ_FREE(p); } void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } void miniz_def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } #ifndef MINIZ_NO_ZLIB_APIS int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for (;;) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; /* Can't make forward progress without some input. */ } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflate(mz_streamp pStream, int flush) { inflate_state *pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state *)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } /* flush != MZ_FINISH then we must assume there's more input. */ if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for (;;) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ else if (flush == MZ_FINISH) { /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = { { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } }; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif /*MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression (independent from all decompression API's) */ /* Purposely making these tables static for faster init and thread safety. */ static const mz_uint16 s_tdefl_len_sym[256] = { 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 }; static const mz_uint8 s_tdefl_len_extra[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 }; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 }; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 }; /* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32 *pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq *t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } /* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next = 1; next < n - 1; next++) { if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key; if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); } A[n - 2].m_key = 0; for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; avbl = 1; used = dpth = 0; root = n - 2; next = n - 1; while (avbl > 0) { while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; } while (avbl > used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2 * used; dpth++; used = 0; } } /* Limits canonical Huffman code table's max code size. */ enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) \ do \ { \ mz_uint bits = b; \ mz_uint len = l; \ MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); \ d->m_bits_in += len; \ while (d->m_bits_in >= 8) \ { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } \ MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() \ { \ if (rle_repeat_count) \ { \ if (rle_repeat_count < 3) \ { \ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) \ packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } \ else \ { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 16; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ } \ rle_repeat_count = 0; \ } \ } #define TDEFL_RLE_ZERO_CODE_SIZE() \ { \ if (rle_z_count) \ { \ if (rle_z_count < 3) \ { \ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ while (rle_z_count--) \ packed_code_sizes[num_packed_code_sizes++] = 0; \ } \ else if (rle_z_count <= 10) \ { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 17; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ } \ else \ { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 18; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ } \ rle_z_count = 0; \ } \ } static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) \ { \ bit_buffer |= (((mz_uint64)(b)) << bits_in); \ bits_in += (l); \ } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; *(mz_uint64 *)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #ifdef MINIZ_UNALIGNED_USE_MEMCPY static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) #define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) #endif static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16 *)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) continue; p = s; probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static mz_bool tdefl_compress_fast(tdefl_compressor *d) { /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; /* Simple lazy/greedy parsing state machine. */ len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } /* Move the lookahead forward by len_to_move bytes. */ d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); /* Check if it's time to flush the current LZ codes to the internal output buffer. */ if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8 *)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; /* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ #endif /* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } /* write dummy header */ for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); /* compress image data */ tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } /* write real header */ *pLen_out = out_buf.m_size - 41; { static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x44, 0x41, 0x54 }; pnghdr[18] = (mz_uint8)(w >> 8); pnghdr[19] = (mz_uint8)w; pnghdr[22] = (mz_uint8)(h >> 8); pnghdr[23] = (mz_uint8)h; pnghdr[25] = chans[num_chans]; pnghdr[33] = (mz_uint8)(*pLen_out >> 24); pnghdr[34] = (mz_uint8)(*pLen_out >> 16); pnghdr[35] = (mz_uint8)(*pLen_out >> 8); pnghdr[36] = (mz_uint8)*pLen_out; c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); for (i = 0; i < 4; ++i, c <<= 8) ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); memcpy(out_buf.m_pBuf, pnghdr, 41); } /* write footer (IDAT CRC-32, followed by IEND chunk) */ if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); for (i = 0; i < 4; ++i, c <<= 8) (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); /* compute final size of file, grab compressed data buffer and return */ *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tdefl_compressor *tdefl_compressor_alloc() { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } void tdefl_compressor_free(tdefl_compressor *pComp) { MZ_FREE(pComp); } #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __cplusplus } #endif /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Decompression (completely independent from all compression API's) */ #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN \ switch (r->m_state) \ { \ case 0: #define TINFL_CR_RETURN(state_index, result) \ do \ { \ status = result; \ r->m_state = state_index; \ goto common_exit; \ case state_index:; \ } \ MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) \ do \ { \ for (;;) \ { \ TINFL_CR_RETURN(state_index, result); \ } \ } \ MZ_MACRO_END #define TINFL_CR_FINISH } #define TINFL_GET_BYTE(state_index, c) \ do \ { \ while (pIn_buf_cur >= pIn_buf_end) \ { \ TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ } \ c = *pIn_buf_cur++; \ } \ MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) \ do \ { \ mz_uint c; \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) \ do \ { \ if (num_bits < (mz_uint)(n)) \ { \ TINFL_NEED_BITS(state_index, n); \ } \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) \ do \ { \ if (num_bits < (mz_uint)(n)) \ { \ TINFL_NEED_BITS(state_index, n); \ } \ b = bit_buf & ((1 << (n)) - 1); \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END /* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ /* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do \ { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) \ { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } \ else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ } \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < 15); /* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ /* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ /* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ #define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ do \ { \ int temp; \ mz_uint code_len, c; \ if (num_bits < 15) \ { \ if ((pIn_buf_end - pIn_buf_cur) < 2) \ { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } \ else \ { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ pIn_buf_cur += 2; \ num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ bit_buf >>= code_len; \ num_bits -= code_len; \ } \ MZ_MACRO_END tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) { mz_uint8 *pSrc; for (;;) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif while(counter>2) { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; counter -= 3; } if (counter > 0) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ TINFL_SKIP_BITS(32, num_bits & 7); while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) { while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } } r->m_num_bits = num_bits; r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } /* Higher level helper functions. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for (;;) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; tinfl_init(&decomp); for (;;) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } tinfl_decompressor *tinfl_decompressor_alloc() { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) tinfl_init(pDecomp); return pDecomp; } void tinfl_decompressor_free(tinfl_decompressor *pDecomp) { MZ_FREE(pDecomp); } #ifdef __cplusplus } #endif /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * Copyright 2016 Martin Raiber * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- .ZIP archive reading */ #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #if defined(_MSC_VER) || defined(__MINGW64__) #if defined(__MINGW64__) static FILE *mz_fopen(const char *pFilename, const char *pMode) { FILE *pFile = NULL; fopen_s(&pFile, pFilename, pMode); return pFile; } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { FILE *pFile = NULL; if (freopen_s(&pFile, pPath, pMode, pStream)) return NULL; return pFile; } #endif /* MINGW64 */ #ifndef MINIZ_NO_TIME #include #endif #if defined(__MINGW64__) #define MZ_FOPENREAD mz_fopen #define MZ_FOPEN mz_fopen #else /*->msc*/ #define MZ_FOPENREAD(f, m) _wfopen(f, m) #define MZ_FOPEN(f, m) fopen(f, m) #endif #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #elif defined(__MINGW32__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPENREAD(f, m) _wfopen(f, m) #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPENREAD(f, m) fopen(f, m) #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__GNUC__) && _LARGEFILE64_SOURCE #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FOPENREAD(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #elif defined(__APPLE__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FOPENREAD(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen(p, m, s) #define MZ_DELETE_FILE remove #else #pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPENREAD(f, m) fopen(f, m) #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #ifdef __STRICT_ANSI__ #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #else #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #endif #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #endif /* #ifdef _MSC_VER */ #endif /* #ifdef MINIZ_NO_STDIO */ #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) /* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ enum { /* ZIP archive identifiers and record sizes */ MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, /* ZIP64 archive identifier and record sizes */ MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, /* Central directory header record offsets */ MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, /* Local directory header offsets */ MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, /* End of central directory offsets */ MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, /* ZIP64 End of central directory locator offsets */ MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ /* ZIP64 End of central directory header offsets */ MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; /* The flags passed in when the archive is initially opened. */ uint32_t m_init_flags; /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ mz_bool m_zip64; /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ mz_bool m_zip64_has_extended_info_fields; /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ MZ_FILE *m_pFile; mz_uint64 m_file_archive_start_ofs; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size #if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) { MZ_ASSERT(index < pArray->m_size); return index; } #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] #else #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] #endif static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) { memset(pArray, 0, sizeof(mz_zip_array)); pArray->m_element_size = element_size; } static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif /* #ifdef _MSC_VER */ *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifndef MINIZ_NO_STDIO #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) { struct MZ_FILE_STAT_STRUCT file_stat; /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; *pTime = file_stat.st_mtime; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) { struct utimbuf t; memset(&t, 0, sizeof(t)); t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_TIME */ static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) { if (pZip) pZip->m_last_error = err_num; return MZ_FALSE; } static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; pZip->m_last_error = MZ_ZIP_NO_ERROR; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_init_flags = flags; pZip->m_pState->m_zip64 = MZ_FALSE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; pZip->m_zip_mode = MZ_ZIP_MODE_READING; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) \ do \ { \ mz_uint32 t = a; \ a = b; \ b = t; \ } \ MZ_MACRO_END /* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices; mz_uint32 start, end; const mz_uint32 size = pZip->m_total_files; if (size <= 1U) return; pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); start = (size - 2U) >> 1U; for (;;) { mz_uint64 child, root = start; for (;;) { if ((child = (root << 1U) + 1U) >= size) break; child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } if (!start) break; start--; } end = size - 1; while (end > 0) { mz_uint64 child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for (;;) { if ((child = (root << 1U) + 1U) >= end) break; child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) { mz_int64 cur_file_ofs; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; /* Basic sanity checks - reject files which are too small */ if (pZip->m_archive_size < record_size) return MZ_FALSE; /* Find the record by scanning the file from the end towards the beginning. */ cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for (;;) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) { mz_uint s = MZ_READ_LE32(pBuf + i); if (s == record_sig) { if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) break; } } if (i >= 0) { cur_file_ofs += i; break; } /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } *pOfs = cur_file_ofs; return MZ_TRUE; } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) { mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; mz_uint64 cdir_ofs = 0; mz_int64 cur_file_ofs = 0; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; mz_uint64 zip64_end_of_central_dir_ofs = 0; /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); /* Read and verify the end of central directory record. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { pZip->m_pState->m_zip64 = MZ_TRUE; } } } } } pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if (pZip->m_pState->m_zip64) { mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (zip64_total_num_of_disks != 1U) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); /* Check for miniz's practical limits */ if (zip64_cdir_total_entries > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ if (zip64_size_of_central_directory > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); cdir_size = (mz_uint32)zip64_size_of_central_directory; num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); } if (pZip->m_total_files != cdir_entries_on_this_disk) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); /* Now create an index into the central directory file records, do some basic sanity checking on each record */ p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; mz_uint64 comp_size, decomp_size, local_header_ofs; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && (ext_data_size) && (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) { /* Attempt to find zip64 extended information field in the entry's extra data */ mz_uint32 extra_size_remaining = ext_data_size; if (extra_size_remaining) { const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ pZip->m_pState->m_zip64 = MZ_TRUE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); } } /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (comp_size != MZ_UINT32_MAX) { if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } void mz_zip_zero_struct(mz_zip_archive *pZip) { if (pZip) MZ_CLEAR_OBJ(*pZip); } static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_bool status = MZ_TRUE; if (!pZip) return MZ_FALSE; if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) { if (set_last_error) pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { return mz_zip_reader_end_internal(pZip, MZ_TRUE); } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) { if ((!pZip) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) { if (!pMem) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pNeeds_keepalive = NULL; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const WCHAR_TYPE *pFilename, mz_uint32 flags) { return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); } mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const WCHAR_TYPE *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) { mz_uint64 file_size; MZ_FILE *pFile; if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); #ifdef _WIN32 pFile = MZ_FOPENREAD(pFilename, L"rb"); #else pFile = MZ_FOPENREAD(pFilename, "rb"); #endif if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); file_size = archive_size; if (!file_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); } file_size = MZ_FTELL64(pFile); } /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_zip_type = MZ_ZIP_TYPE_FILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) { mz_uint64 cur_file_ofs; if ((!pZip) || (!pFile)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); cur_file_ofs = MZ_FTELL64(pFile); if (!archive_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); archive_size = MZ_FTELL64(pFile) - cur_file_ofs; if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = archive_size; pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; } mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) { mz_uint bit_flag; mz_uint method; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if ((method != 0) && (method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); return MZ_FALSE; } if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); return MZ_FALSE; } if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, attribute_mapping_id, external_attr; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ /* FIXME: Remove this check? Is it necessary - we already check the filename. */ attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; (void)attribute_mapping_id; external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) { return MZ_TRUE; } return MZ_FALSE; } static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) { mz_uint n; const mz_uint8 *p = pCentral_dir_header; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_FALSE; if ((!p) || (!pStat)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Extract fields from the central directory record. */ pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); /* Copy as much of the filename and comment as possible. */ n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; /* Set some flags for convienance */ pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); /* See if we need to read any zip64 extended information fields. */ /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) { /* Attempt to find zip64 extended information field in the entry's extra data */ mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if (extra_size_remaining) { const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; mz_uint32 field_data_remaining = field_data_size; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_TRUE; if (pStat->m_uncomp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_uncomp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_comp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_comp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_local_header_ofs == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); } } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const uint32_t size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); if (pIndex) *pIndex = 0; if (size) { /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ mz_int64 l = 0, h = (mz_int64)size - 1; while (l <= h) { mz_int64 m = l + ((h - l) >> 1); uint32_t file_index = pIndices[(uint32_t)m]; int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } else if (comp < 0) l = m + 1; else h = m - 1; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint32 index; if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) return -1; else return (int)index; } mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) { mz_uint file_index; size_t name_len, comment_len; if (pIndex) *pIndex = 0; if ((!pZip) || (!pZip->m_pState) || (!pName)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* See if we can use a binary search */ if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) { return mz_zip_locate_file_binary_search(pZip, pName, pIndex); } /* Locate the entry by scanning the entire central directory */ name_len = strlen(pName); if (name_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); comment_len = pComment ? strlen(pComment) : 0; if (comment_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Ensure supplied output buffer is large enough. */ needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); /* Read and parse the local directory entry. */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) { if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); } #endif return MZ_TRUE; } /* Decompress the file either directly from memory or from a file input buffer. */ tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { /* Read directly from the archive in memory. */ pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { /* Use a user provided read buffer. */ if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { /* Temporarily allocate a read buffer. */ read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); status = TINFL_STATUS_FAILED; } #endif } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_uint64 comp_size, uncomp_size, alloc_size; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); void *pBuf; if (pSize) *pSize = 0; if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return NULL; } comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); return NULL; } if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); /* Decompress the file either directly from memory or from a file input buffer. */ if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pState->m_pMem) { if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; } else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); #endif } cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); } #endif if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); status = TINFL_STATUS_FAILED; } else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); #endif if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (file_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; } #endif } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_reader_extract_iter_state *pState; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; /* Argument sanity check */ if ((!pZip) || (!pZip->m_pState)) return NULL; /* Allocate an iterator status structure */ pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); if (!pState) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } /* Fetch file details */ if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Encryption and patch files are not supported. */ if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Init state - save args */ pState->pZip = pZip; pState->flags = flags; /* Init state - reset variables to defaults */ pState->status = TINFL_STATUS_DONE; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS pState->file_crc32 = MZ_CRC32_INIT; #endif pState->read_buf_ofs = 0; pState->out_buf_ofs = 0; pState->pRead_buf = NULL; pState->pWrite_buf = NULL; pState->out_blk_remain = 0; /* Read and parse the local directory entry. */ pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Decompress the file either directly from memory or from a file input buffer. */ if (pZip->m_pState->m_pMem) { pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; pState->comp_remaining = pState->file_stat.m_comp_size; } else { if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, therefore intermediate read buffer required */ pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } else { /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ pState->read_buf_size = 0; } pState->read_buf_avail = 0; pState->comp_remaining = pState->file_stat.m_comp_size; } if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, init decompressor */ tinfl_init( &pState->inflator ); /* Allocate write buffer */ if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pState->pRead_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } return pState; } mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_uint32 file_index; /* Locate file index by name */ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return NULL; /* Construct iterator */ return mz_zip_reader_extract_iter_new(pZip, file_index, flags); } size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) { size_t copied_to_caller = 0; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) return 0; if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data, calc amount to return. */ copied_to_caller = MZ_MIN( buf_size, pState->comp_remaining ); /* Zip is in memory....or requires reading from a file? */ if (pState->pZip->m_pState->m_pMem) { /* Copy data to caller's buffer */ memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; } else { /* Read directly into caller's buffer */ if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) { /* Failed to read all that was asked for, flag failure and alert user */ mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; copied_to_caller = 0; } } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Compute CRC if not returning compressed data only */ if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); #endif /* Advance offsets, dec counters */ pState->cur_file_ofs += copied_to_caller; pState->out_buf_ofs += copied_to_caller; pState->comp_remaining -= copied_to_caller; } else { do { /* Calc ptr to write buffer - given current output pos and block size */ mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); /* Calc max output size - given current output pos and block size */ size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if (!pState->out_blk_remain) { /* Read more data from file if none available (and reading from file) */ if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) { /* Calc read size */ pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) { mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Advance offsets, dec counters */ pState->cur_file_ofs += pState->read_buf_avail; pState->comp_remaining -= pState->read_buf_avail; pState->read_buf_ofs = 0; } /* Perform decompression */ in_buf_size = (size_t)pState->read_buf_avail; pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); pState->read_buf_avail -= in_buf_size; pState->read_buf_ofs += in_buf_size; /* Update current output block size remaining */ pState->out_blk_remain = out_buf_size; } if (pState->out_blk_remain) { /* Calc amount to return. */ size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); /* Copy data to caller's buffer */ memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Perform CRC */ pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); #endif /* Decrement data consumed from block */ pState->out_blk_remain -= to_copy; /* Inc output offset, while performing sanity check */ if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Increment counter of data copied to caller */ copied_to_caller += to_copy; } } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); } /* Return how many bytes were copied into user buffer */ return copied_to_caller; } mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) { int status; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) return MZ_FALSE; /* Was decompression completed and requested? */ if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); pState->status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (pState->file_crc32 != pState->file_stat.m_crc32) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; } #endif } /* Free buffers */ if (!pState->pZip->m_pState->m_pMem) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); if (pState->pWrite_buf) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); /* Save status */ status = pState->status; /* Free context */ pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); return status == TINFL_STATUS_DONE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) { if (status) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) { mz_zip_archive_file_stat file_stat; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); } mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); } #endif /* #ifndef MINIZ_NO_STDIO */ static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_uint32 *p = (mz_uint32 *)pOpaque; (void)file_ofs; *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); return n; } mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_archive_file_stat file_stat; mz_zip_internal_state *pState; const mz_uint8 *pCentral_dir_header; mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint64 local_header_ofs = 0; mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; mz_bool has_data_descriptor; mz_uint32 local_header_bit_flags; mz_zip_array file_data_array; mz_zip_array_init(&file_data_array, 1); if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (file_index > pZip->m_total_files) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_is_encrypted) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports stored and deflate. */ if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); if (!file_stat.m_is_supported) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); /* Read and parse the local directory entry. */ local_header_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); has_data_descriptor = (local_header_bit_flags & 8) != 0; if (local_header_filename_len != strlen(file_stat.m_filename)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (local_header_filename_len) { if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_uint32 extra_size_remaining = local_header_extra_len; const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) { mz_uint8 descriptor_buf[32]; mz_bool has_id; const mz_uint8 *pSrc; mz_uint32 file_crc32; mz_uint64 comp_size = 0, uncomp_size = 0; mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; file_crc32 = MZ_READ_LE32(pSrc); if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); } else { comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); } if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } else { if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } mz_zip_array_clear(pZip, &file_data_array); if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) { if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) return MZ_FALSE; /* 1 more check to be sure, although the extract checks too. */ if (uncomp_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); return MZ_FALSE; } } return MZ_TRUE; handle_failure: mz_zip_array_clear(pZip, &file_data_array); return MZ_FALSE; } mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) { mz_zip_internal_state *pState; uint32_t i; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Basic sanity checks */ if (!pState->m_zip64) { if (pZip->m_total_files > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (pZip->m_archive_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } else { if (pZip->m_total_files >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } for (i = 0; i < pZip->m_total_files; i++) { if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) { mz_uint32 found_index; mz_zip_archive_file_stat stat; if (!mz_zip_reader_file_stat(pZip, i, &stat)) return MZ_FALSE; if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) return MZ_FALSE; /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ if (found_index != i) return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); } if (!mz_zip_validate_file(pZip, i, flags)) return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if ((!pMem) || (!size)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_validate_file_archive(const WCHAR_TYPE *pFilename, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if (!pFilename) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #endif /* #ifndef MINIZ_NO_STDIO */ /* ------------------- .ZIP archive writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) { mz_write_le32(p, (mz_uint32)v); mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) #define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); if (!n) return 0; /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); return 0; } if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return 0; } pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) { mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) { if (!pZip->m_pRead) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (pZip->m_file_offset_alignment) { /* Ensure user specified file offset alignment is a power of 2. */ if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_zip64 = zip64; pZip->m_pState->m_zip64_has_extended_info_fields = zip64; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { return mz_zip_writer_init_v2(pZip, existing_size, 0); } mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) { mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); return 0; } return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); } mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } pZip->m_pState->m_pFile = pFile; pZip->m_zip_type = MZ_ZIP_TYPE_FILE; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) { pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, 0, flags)) return MZ_FALSE; pZip->m_pState->m_pFile = pFile; pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ if (!pZip->m_pState->m_zip64) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* No sense in trying to write to an archive that's already at the support max size */ if (pZip->m_pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO (void)pFilename; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); #else if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (!pFilename) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ mz_zip_reader_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } } pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; #endif /* #ifdef MINIZ_NO_STDIO */ } else if (pState->m_pMem) { /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; } /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ else if (!pZip->m_pWrite) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Start writing new files at the archive's current central directory location. */ /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_central_directory_file_ofs = 0; /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ /* TODO: We could easily maintain the sorted central directory offsets. */ mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); } /* TODO: pArchive_name is a terrible name here! */ mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } #define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) #define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) { mz_uint8 *pDst = pBuf; mz_uint32 field_size = 0; MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); MZ_WRITE_LE16(pDst + 2, 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { MZ_WRITE_LE64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pComp_size) { MZ_WRITE_LE64(pDst, *pComp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pLocal_header_ofs) { MZ_WRITE_LE64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } MZ_WRITE_LE16(pBuf + 2, field_size); return (mz_uint32)(pDst - pBuf); } static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; if (!pZip->m_pState->m_zip64) { if (local_header_ofs > 0xFFFFFFFF) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size + user_extra_data_len, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { /* Try to resize the central directory array back into its original state. */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ if (*pArchive_name == '/') return MZ_FALSE; while (*pArchive_name) { if ((*pArchive_name == '\\') || (*pArchive_name == ':')) return MZ_FALSE; pArchive_name++; } return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); } mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint16 bit_flags = 0; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); #ifndef MINIZ_NO_TIME if (last_modified != NULL) { mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); } else { MZ_TIME_T cur_time; time(&cur_time); mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); } #endif /* #ifndef MINIZ_NO_TIME */ if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { /* Set DOS Subdirectory attribute bit. */ ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; /* Subdirectories cannot contain data. */ if ((buf_size) || (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes; MZ_CLEAR_OBJ(local_dir_header); if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { method = MZ_DEFLATED; } if (pState->m_zip64) { if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pExtra_data != NULL) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += buf_size; comp_size = buf_size; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; if (uncomp_size) { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_zip_internal_state *pState; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) { /* Source file is too large for non-zip64 */ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ pState->m_zip64 = MZ_TRUE; } /* We could support this, but why? */ if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } #ifndef MINIZ_NO_TIME if (pFile_time) { mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); } #endif if (uncomp_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_archive_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (uncomp_size && level) { method = MZ_DEFLATED; } MZ_CLEAR_OBJ(local_dir_header); if (pState->m_zip64) { if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (uncomp_size) { mz_uint64 uncomp_remaining = uncomp_size; void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!level) { while (uncomp_remaining) { mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); uncomp_remaining -= n; cur_archive_file_ofs += n; } comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); } for (;;) { size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); tdefl_status status; tdefl_flush flush = TDEFL_NO_FLUSH; if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); break; } uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); uncomp_remaining -= in_buf_size; if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) flush = TDEFL_FULL_FLUSH; status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) { mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); break; } } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { MZ_FILE *pSrc_file = NULL; mz_uint64 uncomp_size = 0; MZ_TIME_T file_modified_time; MZ_TIME_T *pFile_time = NULL; mz_bool status; memset(&file_modified_time, 0, sizeof(file_modified_time)); #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) pFile_time = &file_modified_time; if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); #endif pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); MZ_FCLOSE(pSrc_file); return status; } #endif /* #ifndef MINIZ_NO_STDIO */ static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { /* + 64 should be enough for any new zip64 data */ if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) { mz_uint8 new_ext_block[64]; mz_uint8 *pDst = new_ext_block; mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); mz_write_le16(pDst + sizeof(mz_uint16), 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { mz_write_le64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); } if (pComp_size) { mz_write_le64(pDst, *pComp_size); pDst += sizeof(mz_uint64); } if (pLocal_header_ofs) { mz_write_le64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); } if (pDisk_start) { mz_write_le32(pDst, *pDisk_start); pDst += sizeof(mz_uint32); } mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if ((pExt) && (ext_len)) { mz_uint32 extra_size_remaining = ext_len; const mz_uint8 *pExtra_data = pExt; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } return MZ_TRUE; } /* TODO: This func is now pretty freakin complex due to zip64, split it up? */ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; mz_zip_archive_file_stat src_file_stat; mz_uint32 src_filename_len, src_comment_len, src_ext_len; mz_uint32 local_header_filename_size, local_header_extra_len; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Get pointer to the source central dir header and crack it */ if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); if (!pState->m_zip64) { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) return MZ_FALSE; cur_src_file_ofs = src_file_stat.m_local_header_ofs; cur_dst_file_ofs = pZip->m_archive_size; /* Read the source archive's local dir header */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Compute the total size we need to copy (filename+extra data+compressed data) */ local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; /* Try to find a zip64 extended information field */ if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_zip_array file_data_array; const mz_uint8 *pExtra_data; mz_uint32 extra_size_remaining = local_header_extra_len; mz_zip_array_init(&file_data_array, 1); if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } pExtra_data = (const mz_uint8 *)file_data_array.m_p; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); mz_zip_array_clear(pZip, &file_data_array); } if (!pState->m_zip64) { /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ /* We also check when the archive is finalized so this doesn't need to be perfect. */ mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; if (approx_new_archive_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } /* Write dest archive padding */ if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); while (src_archive_bytes_remaining) { n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_dst_file_ofs += n; src_archive_bytes_remaining -= n; } /* Now deal with the optional data descriptor */ bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { /* Copy data descriptor */ if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { /* src is zip64, dest must be zip64 */ /* name uint32_t's */ /* id 1 (optional in zip64?) */ /* crc 1 */ /* comp_size 2 */ /* uncomp_size 2 */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); } else { /* src is NOT zip64 */ mz_bool has_id; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); if (pZip->m_pState->m_zip64) { /* dest is zip64, so upgrade the data descriptor */ const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); const mz_uint32 src_crc32 = pSrc_descriptor[0]; const mz_uint64 src_comp_size = pSrc_descriptor[1]; const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); n = sizeof(mz_uint32) * 6; } else { /* dest is NOT zip64, just copy it as-is */ n = sizeof(mz_uint32) * (has_id ? 4 : 3); } } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); /* Finally, add the new central dir header */ orig_central_dir_size = pState->m_central_dir.m_size; memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); if (pState->m_zip64) { /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; mz_zip_array new_ext_block; mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) { mz_zip_array_clear(pZip, &new_ext_block); return MZ_FALSE; } MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) { mz_zip_array_clear(pZip, &new_ext_block); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } mz_zip_array_clear(pZip, &new_ext_block); } else { /* sanity checks */ if (cur_dst_file_ofs > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (local_dir_header_ofs >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } } /* This shouldn't trigger unless we screwed up during the initial sanity checks */ if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) { /* TODO: Support central dirs >= 32-bits in size */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); } n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[256]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { /* Write central directory */ central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += central_dir_size; } if (pState->m_zip64) { /* Write zip64 end of central directory header */ mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; /* Write zip64 end of central directory locator */ MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; } /* Write end of central directory record */ MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) { if ((!ppBuf) || (!pSize)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); *ppBuf = NULL; *pSize = 0; if ((!pZip) || (!pZip->m_pState)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_pWrite != mz_zip_heap_write_func) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *ppBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { return mz_zip_writer_end_internal(pZip, MZ_TRUE); } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); } mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; mz_zip_zero_struct(&zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (!mz_zip_writer_validate_archive_name(pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_FILENAME; return MZ_FALSE; } /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { /* Create a new archive. */ if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } created_new_archive = MZ_TRUE; } else { /* Append to an existing archive. */ if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); actual_err = zip_archive.m_last_error; /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ if (!mz_zip_writer_finalize_archive(&zip_archive)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if (!mz_zip_writer_end_internal(&zip_archive, status)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if ((!status) && (created_new_archive)) { /* It's a new archive and something went wrong, so just delete it. */ int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } if (pErr) *pErr = actual_err; return status; } void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) { mz_uint32 file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return NULL; } mz_zip_zero_struct(&zip_archive); if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return NULL; } if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) { p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); } mz_zip_reader_end_internal(&zip_archive, p != NULL); if (pErr) *pErr = zip_archive.m_last_error; return p; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ /* ------------------- Misc utils */ mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; } mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; } mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = err_num; return prev_err; } mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) { if (!pZip) return MZ_ZIP_INVALID_PARAMETER; return pZip->m_last_error; } mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) { return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); } mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = MZ_ZIP_NO_ERROR; return prev_err; } const char *mz_zip_get_error_string(mz_zip_error mz_err) { switch (mz_err) { case MZ_ZIP_NO_ERROR: return "no error"; case MZ_ZIP_UNDEFINED_ERROR: return "undefined error"; case MZ_ZIP_TOO_MANY_FILES: return "too many files"; case MZ_ZIP_FILE_TOO_LARGE: return "file too large"; case MZ_ZIP_UNSUPPORTED_METHOD: return "unsupported method"; case MZ_ZIP_UNSUPPORTED_ENCRYPTION: return "unsupported encryption"; case MZ_ZIP_UNSUPPORTED_FEATURE: return "unsupported feature"; case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: return "failed finding central directory"; case MZ_ZIP_NOT_AN_ARCHIVE: return "not a ZIP archive"; case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: return "invalid header or archive is corrupted"; case MZ_ZIP_UNSUPPORTED_MULTIDISK: return "unsupported multidisk archive"; case MZ_ZIP_DECOMPRESSION_FAILED: return "decompression failed or archive is corrupted"; case MZ_ZIP_COMPRESSION_FAILED: return "compression failed"; case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: return "unexpected decompressed size"; case MZ_ZIP_CRC_CHECK_FAILED: return "CRC-32 check failed"; case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: return "unsupported central directory size"; case MZ_ZIP_ALLOC_FAILED: return "allocation failed"; case MZ_ZIP_FILE_OPEN_FAILED: return "file open failed"; case MZ_ZIP_FILE_CREATE_FAILED: return "file create failed"; case MZ_ZIP_FILE_WRITE_FAILED: return "file write failed"; case MZ_ZIP_FILE_READ_FAILED: return "file read failed"; case MZ_ZIP_FILE_CLOSE_FAILED: return "file close failed"; case MZ_ZIP_FILE_SEEK_FAILED: return "file seek failed"; case MZ_ZIP_FILE_STAT_FAILED: return "file stat failed"; case MZ_ZIP_INVALID_PARAMETER: return "invalid parameter"; case MZ_ZIP_INVALID_FILENAME: return "invalid filename"; case MZ_ZIP_BUF_TOO_SMALL: return "buffer too small"; case MZ_ZIP_INTERNAL_ERROR: return "internal error"; case MZ_ZIP_FILE_NOT_FOUND: return "file not found"; case MZ_ZIP_ARCHIVE_TOO_LARGE: return "archive is too large"; case MZ_ZIP_VALIDATION_FAILED: return "validation failed"; case MZ_ZIP_WRITE_CALLBACK_FAILED: return "write calledback failed"; default: break; } return "unknown error"; } /* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return MZ_FALSE; return pZip->m_pState->m_zip64; } size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_central_dir.m_size; } mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) { if (!pZip) return 0; return pZip->m_archive_size; } mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_file_archive_start_ofs; } MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_pFile; } size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); } mz_bool mz_zip_end(mz_zip_archive *pZip) { if (!pZip) return MZ_FALSE; if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) return mz_zip_reader_end(pZip); #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) return mz_zip_writer_end(pZip); #endif return MZ_FALSE; } #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ recoll-1.36.1/utils/mimeparse.h0000644000175000017500000000720414410615043013277 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _MIME_H_INCLUDED_ #define _MIME_H_INCLUDED_ /* Mime definitions RFC to 4-9-2006: 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies. N. Freed, N. Borenstein. November 1996. (Format: TXT=72932 bytes) (Obsoletes RFC1521, RFC1522, RFC1590) (Updated by RFC2184, RFC2231) (Status: DRAFT STANDARD) 2046 Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types. N. Freed, N. Borenstein. November 1996. (Format: TXT=105854 bytes) (Obsoletes RFC1521, RFC1522, RFC1590) (Updated by RFC2646, RFC3798) (Status: DRAFT STANDARD) 2047 MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text. K. Moore. November 1996. (Format: TXT=33262 bytes) (Obsoletes RFC1521, RFC1522, RFC1590) (Updated by RFC2184, RFC2231) (Status: DRAFT STANDARD) 2183 Communicating Presentation Information in Internet Messages: The Content-Disposition Header Field. R. Troost, S. Dorner, K. Moore, Ed.. August 1997. (Format: TXT=23150 bytes) (Updates RFC1806) (Updated by RFC2184, RFC2231) (Status: PROPOSED STANDARD) 2231 MIME Parameter Value and Encoded Word Extensions: Character Sets, Languages, and Continuations. N. Freed, K. Moore. November 1997. (Format: TXT=19280 bytes) (Obsoletes RFC2184) (Updates RFC2045, RFC2047, RFC2183) (Status: PROPOSED STANDARD) */ #include #include #include #include "base64.h" /** A class to represent a MIME header value with parameters */ class MimeHeaderValue { public: std::string value; std::map params; }; /** * Parse MIME Content-type and Content-disposition value * * @param in the input string should be like: value; pn1=pv1; pn2=pv2. * Example: text/plain; charset="iso-8859-1" */ extern bool parseMimeHeaderValue(const std::string& in, MimeHeaderValue& psd); /** * Quoted Printable decoding. * * Doubles up as rfc2231 decoder, with the help of the hence the @param esc * parameter. * RFC2045 Quoted Printable uses '=' , RFC2331 uses '%'. The two encodings are * otherwise similar. */ extern bool qp_decode(const std::string& in, std::string &out, char esc = '='); /** Decode an Internet mail field value encoded according to rfc2047 * * Example input: Some words =?iso-8859-1?Q?RE=A0=3A_Smoke_Tests?= more input * * Note that MIME parameter values are explicitly NOT to be encoded with * this encoding which is only for headers like Subject:, To:. But it * is sometimes used anyway... * * @param in input string, ascii with rfc2047 markup * @return out output string encoded in utf-8 */ extern bool rfc2047_decode(const std::string& in, std::string &out); /** Decode RFC2822 date to unix time (gmt secs from 1970) * * @param dt date string (the part after Date: ) * @return unix time */ time_t rfc2822DateToUxTime(const std::string& dt); #endif /* _MIME_H_INCLUDED_ */ recoll-1.36.1/utils/utf8iter.cpp0000644000175000017500000000664614473577747013466 00000000000000/* Copyright (C) 2017-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "utf8iter.h" #include #include #include void utf8truncate(std::string& s, int maxlen, int flags, const std::string& ellipsis, const std::string& ws) { if (s.size() <= std::string::size_type(maxlen)) { return; } std::unordered_set wss; if (flags & UTF8T_ATWORD) { Utf8Iter iter(ws); for (; !iter.eof(); iter++) { unsigned int c = *iter; wss.insert(c); } } if (flags & UTF8T_ELLIPSIS) { size_t ellen = utf8len(ellipsis); maxlen = std::max(0, maxlen - int(ellen)); } std::string::size_type pos = 0; std::string::size_type lastwspos = 0; { Utf8Iter iter(s); for (; !iter.eof(); iter++) { unsigned int c = *iter; if (iter.getBpos() < std::string::size_type(maxlen)) { pos = iter.getBpos() + iter.getBlen(); if ((flags & UTF8T_ATWORD) && wss.find(c) != wss.end()) { lastwspos = pos; } } else { break; } } } if (flags & UTF8T_ATWORD) { s.erase(lastwspos); for (;;) { Utf8Iter iter(s); unsigned int c = 0; for (; !iter.eof(); iter++) { c = *iter; pos = iter.getBpos(); } if (wss.find(c) == wss.end()) { break; } s.erase(pos); } } else { s.erase(pos); } if (flags & UTF8T_ELLIPSIS) { s += ellipsis; } } size_t utf8len(const std::string& s) { size_t len = 0; Utf8Iter iter(s); while (iter++ != std::string::npos) { len++; } return len; } static const std::string replchar{"\xef\xbf\xbd"}; // Check utf-8 encoding, replacing errors with the ? char above int utf8check(const std::string& in, bool fixit, std::string *out, int maxrepl) { int cnt = 0; Utf8Iter it(in); for (;!it.eof(); it++) { if (it.error()) { if (!fixit) { return -1; } *out += replchar; ++cnt; for (; cnt < maxrepl; cnt++) { it.retryfurther(); if (it.eof()) return cnt; if (!it.error()) break; *out += replchar; } if (it.error()) { return -1; } } // We have reached a good char and eof is false if (fixit) { it.appendchartostring(*out); } } return cnt; } recoll-1.36.1/utils/conftree.h0000644000175000017500000005743114516676107013150 00000000000000/* Copyright (C) 2006-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _CONFTREE_H_ #define _CONFTREE_H_ /** * Classes for managing ini-type data, with 'name = value' lines, and supporting subsections * with lines like '[subkey]' * * Lines like '[subkey]' in the file define subsections, with independant * configuration namespaces. Only subsections holding at least one variable are * significant (empty subsections may be deleted during an update, or not). * * Whitespace around name and value is ignored (trailing white space may be optionally preserved). * * The names are case-sensitive by default, but there are separate options for ignoring case in * names and subkeys. * * Values can be queried for, or set. * * Any line without a '=', or beginning with '[ \t]*#' is a comment. * * A configuration object can be created empty or by reading from a file or a string. * * All 'set' calls normally cause an immediate rewrite of the backing * object if any (file or string). This can be prevented with holdWrites(). * * The ConfTree derived class interprets the subkeys as file paths and * lets subdir keys hierarchically inherit the properties from parents. * * The ConfStack class stacks several Con(Simple/Tree) objects so that * parameters from the top of the stack override the values from lower * (useful to have central/personal config files). */ #include #include #include #include #include #include #include "pathut.h" /** Internal class used for storing presentation information */ class ConfLine { public: enum Kind {CFL_COMMENT, CFL_SK, CFL_VAR, CFL_VARCOMMENT}; Kind m_kind; // For a comment or varcomment line, m_data is the full line // For an SK or a VAR, m_data is the name (sk or var name) std::string m_data; // For a VAR m_value is the original value. Unset for other types. Unchanged // if the variable is updated or erased. Only (barely) used with the XML comments stuff. // For normal rewriting, the value is extracted with a regular get() std::string m_value; // Only used for VARCOMMENT lines (commented out variable assignation). Holds the variable name // in this case. Used if the variable is set, to group the comment and actual assignment. std::string m_aux; ConfLine(Kind k, const std::string& d, std::string a = std::string()) : m_kind(k), m_data(d), m_aux(a) { } bool operator==(const ConfLine& o) { return o.m_kind == m_kind && o.m_data == m_data; } }; /** * Virtual base class used to define the interface, and a few helper methods. */ class ConfNull { public: enum StatusCode {STATUS_ERROR = 0, STATUS_RO = 1, STATUS_RW = 2}; ConfNull() = default; virtual ~ConfNull() = default; ConfNull(const ConfNull&) = delete; ConfNull &operator=(const ConfNull &) = delete; virtual int get(const std::string& name, std::string& value, const std::string& sk = std::string()) const = 0; virtual int set(const std::string& nm, const std::string& val, const std::string& sk = std::string()) = 0; virtual long long getInt(const std::string& name, long long dflt, const std::string& sk = std::string()); virtual double getFloat(const std::string& name, double dflt, const std::string& sk = std::string()); virtual bool getBool(const std::string& name, bool dflt, const std::string& sk = std::string()); virtual bool ok() const = 0; virtual std::vector getNames(const std::string& sk, const char* = nullptr) const = 0; virtual bool hasNameAnywhere(const std::string& nm) const = 0; virtual int erase(const std::string&, const std::string&) = 0; virtual int eraseKey(const std::string&) = 0; virtual std::vector getSubKeys() const = 0; virtual std::vector getSubKeys(bool) const = 0; virtual bool holdWrites(bool) = 0; virtual bool sourceChanged() const = 0; virtual bool write(std::ostream&) const {return true;}; }; struct CaseComparator { CaseComparator(bool nocase = false) : m_nocase(nocase) {} bool operator()(const std::string& a, const std::string& b) const { if (!m_nocase) return a < b; return std::lexicographical_compare( a.begin(), a.end(), b.begin(), b.end(), [](char ch1, char ch2) { return std::tolower(ch1) < std::tolower(ch2); }); } bool m_nocase; }; /** * Manage simple configuration data with subsections. */ class ConfSimple : public ConfNull { public: /** * Build the object by reading content from file. * @param filename file to open * @param readonly if true open readonly, else rw * @param tildexp try tilde (home dir) expansion for subkey values * @param trimvalues remove trailing white space from values */ ConfSimple(const char *fname, int readonly = 0, bool tildexp = false, bool trimvalues = true); /** * Build the object by reading content from a string * @param data points to the data to parse. This is used as input only. Use write() to * get the data back after updating the object. * @param readonly if true open readonly, else rw * @param tildexp try tilde (home dir) expansion for subsection names * @param trimvalues remove trailing white space from values */ ConfSimple(const std::string& data, int readonly = 0, bool tildexp = false, bool trimvalues = true); /** * Build an empty object. This will be memory only, with no backing store. * @param readonly if true open read only, else rw * @param tildexp try tilde (home dir) expansion for subsection names * @param trimvalues remove trailing white space from values */ ConfSimple(int readonly = 0, bool tildexp = false, bool trimvalues = true); /** * Build any kind of ConfSimple, depending on flags. * @param flags a bitfield where the following flags can be set: * CFSF_RO: if set, the object can't be modified after initialisation * CFSF_FROMSTRING: if set, @param dataorfn is the data to parse, else it is a file name. * CFSF_TILDEXP: perform tilde expansion on subsection names * CFSF_NOTRIMVALUES: do not trim white space at the end of values. * CFSF_SUBMAPNOCASE: use case-insensitive comparisons for submap names. * CFSF_KEYNOCASE: use case-insensitive comparisons for keys. * CFSF_NOCASE: both keys and sections are case-insensitive. * @param dataorfn input data or file name depending on flags. */ enum Flag {CFSF_NONE = 0, CFSF_RO = 1, CFSF_TILDEXP = 2, CFSF_NOTRIMVALUES = 4, CFSF_SUBMAPNOCASE = 8, CFSF_KEYNOCASE = 0x10, CFSF_FROMSTRING = 0x20, CFSF_NOCASE = CFSF_SUBMAPNOCASE | CFSF_KEYNOCASE, }; ConfSimple(int flags, const std::string& dataorfn); virtual ~ConfSimple() = default; /** Origin file changed. Only makes sense if we read the data from a file */ virtual bool sourceChanged() const override; /** * Decide if we actually rewrite the backing-store after modifying the * tree. */ virtual bool holdWrites(bool on) override { m_holdWrites = on; if (on == false) { return write(); } else { return true; } } /** Clear, then reparse from string */ void reparse(const std::string& in); /** * Get string value for named parameter, from specified subsection (looks * in global space if sk is empty). * @return 0 if name not found, 1 else */ virtual int get(const std::string& name, std::string& value, const std::string& sk = std::string()) const override; /** * Set value for named string parameter in specified subsection (or global) * @return 0 for error, 1 else */ virtual int set(const std::string& nm, const std::string& val, const std::string& sk = std::string()) override; /** * Set value for named integer parameter in specified subsection (or global) * @return 0 for error, 1 else */ virtual int set(const std::string& nm, long long val, const std::string& sk = std::string()); /** * Remove name and value from config */ virtual int erase(const std::string& name, const std::string& sk) override; /** * Erase all names under given subkey (and subkey itself) */ virtual int eraseKey(const std::string& sk) override; /** Clear all content */ virtual int clear(); virtual StatusCode getStatus() const; virtual bool ok() const override { return getStatus() != STATUS_ERROR; } /** * Walk the configuration values, calling function for each. * The function is called with a null nm when changing subsections (the * value is then the new subsection name) * @return WALK_STOP when/if the callback returns WALK_STOP, * WALK_CONTINUE else (got to end of config) */ enum WalkerCode {WALK_STOP, WALK_CONTINUE}; virtual WalkerCode sortwalk( WalkerCode (*wlkr)(void *cldata, const std::string& nm, const std::string& val), void *clidata) const; /** Return all names in given submap. On win32, the pattern thing only works in recoll builds */ virtual std::vector getNames( const std::string& sk, const char *pattern = nullptr) const override; /** Check if name is present in any submap. This is relatively expensive * but useful for saving further processing sometimes */ virtual bool hasNameAnywhere(const std::string& nm) const override; /** * Return all subkeys */ virtual std::vector getSubKeys(bool) const override { return getSubKeys(); } virtual std::vector getSubKeys() const override; /** Return subkeys in file order. BEWARE: only for the original from the * file: the data is not duplicated to further copies */ virtual std::vector getSubKeys_unsorted(bool = false) const { return m_subkeys_unsorted; } /** Test for subkey existence */ virtual bool hasSubKey(const std::string& sk) const { return m_submaps.find(sk) != m_submaps.end(); } virtual std::string getFilename() const { return m_filename; } /** Used with config files with specially formatted, xml-like comments. * Extract the comments as text */ virtual bool commentsAsXML(std::ostream& out); /** !! Note that assignment and copy constructor do not copy the auxiliary data (m_order and subkeys_unsorted). */ /** * Copy constructor. Expensive but less so than a full rebuild */ ConfSimple(const ConfSimple& rhs) : ConfNull() { if ((status = rhs.status) == STATUS_ERROR) { return; } dotildexpand = rhs.dotildexpand; trimvalues = rhs.trimvalues; m_flags = rhs.m_flags; m_filename = rhs.m_filename; m_submaps = rhs.m_submaps; } /** * Assignement. This is expensive */ ConfSimple& operator=(const ConfSimple& rhs) { if (this != &rhs && (status = rhs.status) != STATUS_ERROR) { dotildexpand = rhs.dotildexpand; trimvalues = rhs.trimvalues; m_flags = rhs.m_flags; m_filename = rhs.m_filename; m_submaps = rhs.m_submaps; } return *this; } /** * Write in file format to out. If the object was normally constructed from a file or string, * this will respect the original comments and declarations order. If this object was copied * from another, this will just output the significant content (submaps and parameters values). */ bool write(std::ostream& out) const override; /** Give access to semi-parsed file contents */ const std::vector& getlines() const { return m_order; } protected: bool dotildexpand; bool trimvalues; StatusCode status; private: int m_flags{0}; // Set if we're working with a file std::string m_filename; int64_t m_fmtime{0}; // Configuration data submaps (one per subkey, the main data has a // null subkey) std::map, CaseComparator> m_submaps; // Presentation data. We keep the comments, empty lines and variable and subkey ordering // information in there (for rewriting the file while keeping hand-edited information). // ** This is not copied by the copy constructor or assignment operator, and is only valid // for an object directly constructed from data. ** std::vector m_order; std::vector m_subkeys_unsorted; // Control if we're writing to the backing store after each set() bool m_holdWrites{false}; // Comparators to use for names and keys comparisons. One is passed to the maps constructors, // depending on our construction flags CaseComparator m_casecomp; CaseComparator m_nocasecomp{true}; void parseinput(std::istream& input); bool write(); bool content_write(std::ostream& out) const; // Internal version of set: no RW checking virtual int i_set(const std::string& nm, const std::string& val, const std::string& sk, bool init = false); bool i_changed(bool upd); void openfile(int readonly, std::fstream& input); }; /** * This is a configuration class which attaches tree-like signification to the * submap names. * * If a given variable is not found in the specified section, it will be * looked up the tree of section names, and in the global space. * * submap names should be '/' separated paths (ie: /sub1/sub2). No checking * is done, but else the class adds no functionality to ConfSimple. * * NOTE: contrary to common behaviour, the global or root space is NOT * designated by '/' but by '' (empty subkey). A '/' subkey will not * be searched at all. * * Note: getNames() : uses ConfSimple method, this does *not* inherit * names from englobing submaps. */ class ConfTree : public ConfSimple { public: /* The constructors just call ConfSimple's, asking for key tilde expansion */ ConfTree(const char *fname, int readonly = 0, bool trimvalues=true) : ConfSimple(fname, readonly, true, trimvalues) {} ConfTree(const std::string& data, int readonly = 0, bool trimvalues=true) : ConfSimple(data, readonly, true, trimvalues) {} ConfTree(int readonly = 0, bool trimvalues=true) : ConfSimple(readonly, true, trimvalues) {} ConfTree(int flags, const std::string& dataorfn) : ConfSimple(flags|ConfSimple::CFSF_TILDEXP, dataorfn) {} virtual ~ConfTree() = default; ConfTree(const ConfTree& r) : ConfSimple(r) {}; ConfTree& operator=(const ConfTree& r) { ConfSimple::operator=(r); return *this; } /** * Get value for named parameter, from specified subsection, or its * parents. * @return 0 if name not found, 1 else */ virtual int get(const std::string& name, std::string& value, const std::string& sk) const override; }; /** * Use several config files, trying to get values from each in order. * * Enables having a central/default config, with possible overrides * from more specific (e.g. personal) ones. * * Notes: it's ok for some of the files not to exist, but the last * (bottom) one must or we generate an error. We open all trees * readonly, except the topmost one if requested. All writes go to the * topmost file. Note that erase() won't work except for parameters * only defined in the topmost file (it erases only from there). */ template class ConfStack : public ConfNull { public: /// Construct from configuration file names. The earlier files in have priority when fetching /// values. /// Only the first file will be updated by set(). ConfStack(int flags, const std::vector& fns) { ConfStack::construct(flags, fns); } // Old call, compat. ConfStack(const std::vector& fns, bool ro) : ConfStack((ro ? ConfSimple::CFSF_RO : ConfSimple::CFSF_NONE), fns) {} /// Construct out of single file name and multiple directories ConfStack(const std::string& nm, const std::vector& dirs, bool ro) { std::vector fns; for (const auto& dir : dirs) { fns.push_back(path_cat(dir, nm)); } ConfStack::construct((ro ? ConfSimple::CFSF_RO : ConfSimple::CFSF_NONE), fns); } /// Construct out of single file name and multiple directories ConfStack(int flags, const std::string& nm, const std::vector& dirs) { std::vector fns; for (const auto& dir : dirs) { fns.push_back(path_cat(dir, nm)); } ConfStack::construct(flags, fns); } ConfStack(const ConfStack& rhs) : ConfNull() { init_from(rhs); } virtual ~ConfStack() { clear(); m_ok = false; } ConfStack& operator=(const ConfStack& rhs) { if (this != &rhs) { clear(); m_ok = rhs.m_ok; if (m_ok) { init_from(rhs); } } return *this; } virtual bool sourceChanged() const override { for (const auto& conf : m_confs) { if (conf->sourceChanged()) { return true; } } return false; } virtual int get(const std::string& name, std::string& value, const std::string& sk, bool shallow) const { for (const auto& conf : m_confs) { if (conf->get(name, value, sk)) { return true; } if (shallow) { break; } } return false; } virtual int get(const std::string& name, std::string& value, const std::string& sk) const override { return get(name, value, sk, false); } virtual bool hasNameAnywhere(const std::string& nm) const override { for (const auto& conf : m_confs) { if (conf->hasNameAnywhere(nm)) { return true; } } return false; } virtual int set(const std::string& nm, const std::string& val, const std::string& sk = std::string()) override { if (!m_ok) { return 0; } //LOGDEB2(("ConfStack::set [%s]:[%s] -> [%s]\n", sk.c_str(), //nm.c_str(), val.c_str())); // Avoid adding unneeded entries: if the new value matches the // one out from the deeper configs, erase or dont add it // from/to the topmost file auto it = m_confs.begin(); it++; while (it != m_confs.end()) { std::string value; if ((*it)->get(nm, value, sk)) { // This file has value for nm/sk. If it is the same as the new // one, no need for an entry in the topmost file. Else, stop // looking and add the new entry if (value == val) { m_confs.front()->erase(nm, sk); return true; } else { break; } } it++; } return m_confs.front()->set(nm, val, sk); } virtual int erase(const std::string& nm, const std::string& sk) override { return m_confs.front()->erase(nm, sk); } virtual int eraseKey(const std::string& sk) override { return m_confs.front()->eraseKey(sk); } virtual bool holdWrites(bool on) override { return m_confs.front()->holdWrites(on); } /** Return all names in given submap. On win32, the pattern thing only works in recoll builds */ virtual std::vector getNames( const std::string& sk, const char *pattern = 0) const override { return getNames1(sk, pattern, false); } virtual std::vector getNamesShallow( const std::string& sk, const char *patt = 0) const { return getNames1(sk, patt, true); } virtual std::vector getNames1( const std::string& sk, const char *pattern, bool shallow) const { std::vector nms; bool skfound = false; for (const auto& conf : m_confs) { if (conf->hasSubKey(sk)) { skfound = true; std::vector lst = conf->getNames(sk, pattern); nms.insert(nms.end(), lst.begin(), lst.end()); } if (shallow && skfound) { break; } } sort(nms.begin(), nms.end()); std::vector::iterator uit = unique(nms.begin(), nms.end()); nms.resize(uit - nms.begin()); return nms; } virtual std::vector getSubKeys() const override { return getSubKeys(false); } virtual std::vector getSubKeys(bool shallow) const override { std::vector sks; for (const auto& conf : m_confs) { std::vector lst; lst = conf->getSubKeys(); sks.insert(sks.end(), lst.begin(), lst.end()); if (shallow) { break; } } sort(sks.begin(), sks.end()); std::vector::iterator uit = unique(sks.begin(), sks.end()); sks.resize(uit - sks.begin()); return sks; } virtual bool ok() const override { return m_ok; } private: bool m_ok; std::vector m_confs; /// Reset to pristine void clear() { for (auto& conf : m_confs) { delete(conf); } m_confs.clear(); } /// Common code to initialize from existing object void init_from(const ConfStack& rhs) { if ((m_ok = rhs.m_ok)) { for (const auto& conf : rhs.m_confs) { m_confs.push_back(new T(*conf)); } } } /// Common construct from file names. /// Fail if any fails, except for missing files in all but the bottom location, or the /// top one in rw mode. void construct(int flags, const std::vector& fns) { bool ok{true}; for (unsigned int i = 0; i < fns.size(); i++) { const auto& fn = fns[i]; T* p = new T(flags, fn); if (p && p->ok()) { m_confs.push_back(p); } else { delete p; // We accept missing files in all but the bottom/ directory. // In rw mode, the topmost file must be present. if (!path_exists(fn)) { // !ro can only be true for i==0 if (!(flags & ConfSimple::CFSF_RO) || (i == fns.size() - 1)) { ok = false; break; } } } // Only the first file is opened rw flags |= ConfSimple::CFSF_RO; } m_ok = ok; } }; #endif /*_CONFTREE_H_ */ recoll-1.36.1/utils/idfile.cpp0000644000175000017500000001353214426500174013112 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TEST_IDFILE #include "autoconfig.h" #include #include #include #include #include #include "idfile.h" #include "log.h" using namespace std; // Bogus code to avoid bogus valgrind mt warnings about the // initialization of treat_mbox_... which I can't even remember the // use of (it's not documented or ever set) static int treat_mbox_as_rfc822; class InitTMAR { public: InitTMAR() { treat_mbox_as_rfc822 = getenv("RECOLL_TREAT_MBOX_AS_RFC822") ? 1 : -1; } }; static InitTMAR initTM; /** * This code is currently ONLY used to identify mbox and mail message files * which are badly handled by standard mime type identifiers * There is a very old (circa 1990) mbox format using blocks of ^A (0x01) chars * to separate messages, that we don't recognize currently */ // Mail headers we compare to: static const char *mailhs[] = {"From: ", "Received: ", "Message-Id: ", "To: ", "Date: ", "Subject: ", "Status: ", "In-Reply-To: "}; static const int mailhsl[] = {6, 10, 12, 4, 6, 9, 8, 13}; static const int nmh = sizeof(mailhs) / sizeof(char *); const int wantnhead = 3; // fn is for message printing static string idFileInternal(istream& input, const char *fn) { bool line1HasFrom = false; bool gotnonempty = false; int lookslikemail = 0; // emacs VM sometimes inserts very long lines with continuations or // not (for folder information). This forces us to look at many // lines and long ones int lnum = 1; for (int loop = 1; loop < 200; loop++, lnum++) { #define LL 2*1024 char cline[LL+1]; cline[LL] = 0; input.getline(cline, LL-1); if (input.fail()) { if (input.bad()) { LOGERR("idfile: error while reading [" << (fn) << "]\n" ); return string(); } // Must be eof ? break; } // gcount includes the \n std::streamsize ll = input.gcount() - 1; if (ll > 0) gotnonempty = true; LOGDEB2("idfile: lnum " << (lnum) << " ll " << ((unsigned int)ll) << ": [" << (cline) << "]\n" ); // Check for a few things that can't be found in a mail file, // (optimization to get a quick negative) // Empty lines if (ll <= 0) { // Accept a few empty lines at the beginning of the file, // otherwise this is the end of headers if (gotnonempty || lnum > 10) { LOGDEB2("Got empty line\n" ); break; } else { // Don't increment the line counter for initial empty lines. lnum--; continue; } } // emacs vm can insert VERY long header lines. if (ll > LL - 20) { LOGDEB2("idFile: Line too long\n" ); return string(); } // Check for mbox 'From ' line if (lnum == 1 && !strncmp("From ", cline, 5)) { if (treat_mbox_as_rfc822 == -1) { line1HasFrom = true; LOGDEB2("idfile: line 1 has From_\n" ); } continue; } // Except for a possible first line with 'From ', lines must // begin with whitespace or have a colon // (hope no one comes up with a longer header name ! // Take care to convert to unsigned char because ms ctype does // like negative values if (!isspace((unsigned char)cline[0])) { char *cp = strchr(cline, ':'); if (nullptr == cp || (cp - cline) > 70) { LOGDEB2("idfile: can't be mail header line: [" << (cline) << "]\n" ); break; } } // Compare to known headers for (int i = 0; i < nmh; i++) { if (!strncasecmp(mailhs[i], cline, mailhsl[i])) { //fprintf(stderr, "Got [%s]\n", mailhs[i]); lookslikemail++; break; } } if (lookslikemail >= wantnhead) break; } if (line1HasFrom) lookslikemail++; if (lookslikemail >= wantnhead) return line1HasFrom ? string("text/x-mail") : string("message/rfc822"); return string(); } string idFile(const char *fn) { ifstream input; input.open(fn, ios::in); if (!input.is_open()) { LOGERR("idFile: could not open [" << (fn) << "]\n" ); return string(); } return idFileInternal(input, fn); } string idFileMem(const string& data) { stringstream s(data, stringstream::in); return idFileInternal(s, ""); } #else #include #include #include #include #include using namespace std; #include "log.h" #include "idfile.h" int main(int argc, char **argv) { if (argc < 2) { cerr << "Usage: idfile filename" << endl; exit(1); } DebugLog::getdbl()->setloglevel(DEBDEB1); DebugLog::setfilename("stderr"); for (int i = 1; i < argc; i++) { string mime = idFile(argv[i]); cout << argv[i] << " : " << mime << endl; } exit(0); } #endif recoll-1.36.1/utils/copyfile.h0000644000175000017500000000320514410615043013124 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _COPYFILE_H_INCLUDED_ #define _COPYFILE_H_INCLUDED_ #include enum CopyfileFlags {COPYFILE_NONE = 0, COPYFILE_NOERRUNLINK = 1, COPYFILE_EXCL = 2, }; /** Copy src to dst. * * We destroy an existing dst except if COPYFILE_EXCL is set (or we if * have no permission...). * A partially copied dst is normally removed, except if COPYFILE_NOERRUNLINK * is set. */ extern bool copyfile(const char *src, const char *dst, std::string &reason, int flags = 0); /** Save c++ string to file */ extern bool stringtofile(const std::string& dt, const char *dst, std::string& reason, int flags = 0); /** Try to rename src. If this fails (different devices) copy then unlink src */ extern bool renameormove(const char *src, const char *dst, std::string &reason); #endif /* _COPYFILE_H_INCLUDED_ */ recoll-1.36.1/utils/chrono.cpp0000644000175000017500000001512714426500174013150 00000000000000/* Copyright (C) 2014 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TEST_CHRONO #include "autoconfig.h" #include "chrono.h" #include #include using namespace std; #ifndef CLOCK_REALTIME typedef int clockid_t; #define CLOCK_REALTIME 1 #endif #define SECONDS(TS1, TS2) \ (double((TS2).tv_sec - (TS1).tv_sec) + \ double((TS2).tv_nsec - (TS1).tv_nsec) * 1e-9) #define MILLIS(TS1, TS2) \ ((int64_t)((TS2).tv_sec - (TS1).tv_sec) * 1000LL + \ ((TS2).tv_nsec - (TS1).tv_nsec) / 1000000) #define MICROS(TS1, TS2) \ ((int64_t)((TS2).tv_sec - (TS1).tv_sec) * 1000000LL + \ ((TS2).tv_nsec - (TS1).tv_nsec) / 1000) #define NANOS(TS1, TS2) \ ((int64_t)((TS2).tv_sec - (TS1).tv_sec) * 1000000000LL + \ ((TS2).tv_nsec - (TS1).tv_nsec)) // Using clock_gettime() is nice because it gives us ns res and it helps with // computing threads work times, but it's also a pita because it forces linking // with -lrt. So keep it non-default, special development only. // #define USE_CLOCK_GETTIME // And wont' bother with gettime() on these. #if defined(__APPLE__) || defined(_WIN32) #undef USE_CLOCK_GETTIME #endif #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN #include #include // portable: uint64_t MSVC: __int64 // MSVC defines this in winsock2.h!? typedef struct timeval { long tv_sec; long tv_usec; } timeval; int gettimeofday(struct timeval * tp, struct timezone * /*tzp*/) { // Note: some broken versions only have 8 trailing zero's, the // correct epoch has 9 trailing zero's static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); SYSTEMTIME system_time; FILETIME file_time; uint64_t time; GetSystemTime( &system_time ); SystemTimeToFileTime( &system_time, &file_time ); time = ((uint64_t)file_time.dwLowDateTime ) ; time += ((uint64_t)file_time.dwHighDateTime) << 32; tp->tv_sec = (long) ((time - EPOCH) / 10000000L); tp->tv_usec = (long) (system_time.wMilliseconds * 1000); return 0; } #else // -> Not _WIN32 #ifndef USE_CLOCK_GETTIME // Using gettimeofday then, needs struct timeval #include #endif #endif // We use gettimeofday instead of clock_gettime for now and get only // uS resolution, because clock_gettime is more configuration trouble // than it's worth static void gettime(int #ifdef USE_CLOCK_GETTIME clk_id #endif , Chrono::TimeSpec *ts) { #ifdef USE_CLOCK_GETTIME struct timespec mts; clock_gettime(clk_id, &mts); ts->tv_sec = mts.tv_sec; ts->tv_nsec = mts.tv_nsec; #else struct timeval tv; gettimeofday(&tv, nullptr); ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec * 1000; #endif } ///// End system interface // Note: this not protected against multithread access and not // reentrant, but this is mostly debug code, and it won't crash, just // show bad results. Also the frozen thing is not used that much Chrono::TimeSpec Chrono::o_now; void Chrono::refnow() { gettime(CLOCK_REALTIME, &o_now); } Chrono::Chrono() { restart(); } // Reset and return value before rest in milliseconds int64_t Chrono::restart() { TimeSpec now; gettime(CLOCK_REALTIME, &now); int64_t ret = MILLIS(m_orig, now); m_orig = now; return ret; } int64_t Chrono::urestart() { TimeSpec now; gettime(CLOCK_REALTIME, &now); int64_t ret = MICROS(m_orig, now); m_orig = now; return ret; } // Get current timer value, milliseconds int64_t Chrono::millis(bool frozen) { if (frozen) { return MILLIS(m_orig, o_now); } else { TimeSpec now; gettime(CLOCK_REALTIME, &now); return MILLIS(m_orig, now); } } // int64_t Chrono::micros(bool frozen) { if (frozen) { return MICROS(m_orig, o_now); } else { TimeSpec now; gettime(CLOCK_REALTIME, &now); return MICROS(m_orig, now); } } int64_t Chrono::amicros() const { TimeSpec ts; ts.tv_sec = 0; ts.tv_nsec = 0; return MICROS(ts, m_orig); } // int64_t Chrono::nanos(bool frozen) { if (frozen) { return NANOS(m_orig, o_now); } else { TimeSpec now; gettime(CLOCK_REALTIME, &now); return NANOS(m_orig, now); } } double Chrono::secs(bool frozen) { if (frozen) { return SECONDS(m_orig, o_now); } else { TimeSpec now; gettime(CLOCK_REALTIME, &now); return SECONDS(m_orig, now); } } #else ///////////////////// test driver #include #include #include #include #include #include "chrono.h" using namespace std; static char *thisprog; static void Usage(void) { fprintf(stderr, "Usage : %s \n", thisprog); exit(1); } Chrono achrono; Chrono rchrono; void showsecs(long msecs) { fprintf(stderr, "%3.5f S", ((float)msecs) / 1000.0); } void sigint(int sig) { signal(SIGINT, sigint); signal(SIGQUIT, sigint); fprintf(stderr, "Absolute interval: "); showsecs(achrono.millis()); fprintf(stderr, ". Relative interval: "); showsecs(rchrono.restart()); cerr << " Abs micros: " << achrono.amicros() << " Relabs micros: " << rchrono.amicros() - 1430477861905884LL << endl; fprintf(stderr, ".\n"); if (sig == SIGQUIT) { exit(0); } } int main(int argc, char **argv) { thisprog = argv[0]; argc--; argv++; if (argc != 0) { Usage(); } for (int i = 0; i < 50000000; i++); cerr << "Start secs: " << achrono.secs() << endl; fprintf(stderr, "Type ^C for intermediate result, ^\\ to stop\n"); signal(SIGINT, sigint); signal(SIGQUIT, sigint); achrono.restart(); rchrono.restart(); while (1) { pause(); } } #endif /*TEST_CHRONO*/ recoll-1.36.1/utils/readfile.h0000644000175000017500000001011214426500174013065 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _READFILE_H_INCLUDED_ #define _READFILE_H_INCLUDED_ #include #include class FileScanUpstream; /** Data sink for the file reader. */ class FileScanDo { public: FileScanDo() {} virtual ~FileScanDo() {} FileScanDo(const FileScanDo&) = delete; FileScanDo& operator=(const FileScanDo&) = delete; /* Initialize and allocate. * @param size if set, lower bound of data size. * @param reason[output] set to error message in case of error. * @return false for error (file_scan will return), true if ok. */ virtual bool init(int64_t size, std::string *reason) = 0; /* Process chunk of data * @param buf the data buffer. * @param cnt byte count. * @param reason[output] set to error message in case of error. * @return false for error (file_scan will return), true if ok. */ virtual bool data(const char *buf, int cnt, std::string *reason) = 0; virtual void setUpstream(FileScanUpstream*) {} }; /** Open and read file, calling the FileScanDo data() method for each chunk. * * @param filename File name. Use empty value for stdin * @param doer the data processor. The init() method will be called * initially witht a lower bound of the data size (may be used to * reserve a buffer), or with a 0 size if nothing is known about the * size. The data() method will be called for every chunk of data * read. * @param offs Start offset. If not zero, will disable decompression * (set to -1 to start at 0 with no decompression). * @param cnt Max bytes in output. Set cnt to -1 for no limit. * @param[output] md5p If not null, points to a string to store the hex ascii * md5 of the uncompressed data. * @param[output] reason If not null, points to a string for storing an * error message if the return value is false. * @return true if the operation ended normally, else false. */ bool file_scan(const std::string& fn, FileScanDo* doer, int64_t startoffs, int64_t cnttoread, std::string *reason #ifdef READFILE_ENABLE_MD5 , std::string *md5p #endif ); /** Same as above, not offset/cnt/md5 */ bool file_scan(const std::string& filename, FileScanDo* doer, std::string *reason); /** Same as file_scan, from a memory buffer. No libz processing */ bool string_scan(const char *data, size_t cnt, FileScanDo* doer, std::string *reason #ifdef READFILE_ENABLE_MD5 , std::string *md5p #endif ); #if defined(READFILE_ENABLE_MINIZ) /* Process a zip archive member */ bool file_scan(const std::string& filename, const std::string& membername, FileScanDo* doer, std::string *reason); bool string_scan(const char* data, size_t cnt, const std::string& membername, FileScanDo* doer, std::string *reason); #endif /** * Read file into string. * @return true for ok, false else */ bool file_to_string(const std::string& filename, std::string& data, std::string *reason = nullptr); /** Read file chunk into string. Set cnt to -1 for going to * eof, offs to -1 for going from the start without decompression */ bool file_to_string(const std::string& filename, std::string& data, int64_t offs, size_t cnt, std::string *reason = nullptr); #endif /* _READFILE_H_INCLUDED_ */ recoll-1.36.1/utils/hldata.cpp0000644000175000017500000003015114426500174013107 00000000000000/* Copyright (C) 2017-2019 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "hldata.h" #include #include #include "log.h" #include "smallut.h" using std::string; using std::unordered_map; using std::vector; using std::pair; #undef DEBUGGROUPS #ifdef DEBUGGROUPS #define LOGRP LOGINF #else #define LOGRP LOGDEB1 #endif // Combined position list for or'd terms struct OrPList { void addplist(const string& term, const vector* pl) { terms.push_back(term); plists.push_back(pl); indexes.push_back(0); totalsize += pl->size(); } // Returns -1 for eof, else the next smallest value in the // combined lists, according to the current indexes. int value() { int minval = INT_MAX; int minidx = -1; for (unsigned ii = 0; ii < indexes.size(); ii++) { const vector& pl(*plists[ii]); if (indexes[ii] >= pl.size()) continue; // this list done if (pl[indexes[ii]] < minval) { minval = pl[indexes[ii]]; minidx = ii; } } if (minidx != -1) { LOGRP("OrPList::value() -> " << minval << " for " << terms[minidx] << "\n"); currentidx = minidx; return minval; } else { LOGRP("OrPList::value(): EOL for " << stringsToString(terms)<<"\n"); return -1; } } int next() { if (currentidx != -1) { indexes[currentidx]++; } return value(); } int size() const { return totalsize; } void rewind() { for (auto& idx : indexes) { idx = 0; } currentidx = -1; } vector*> plists; vector indexes; vector terms; int currentidx{-1}; int totalsize{0}; }; static inline void setWinMinMax(int pos, int& sta, int& sto) { if (pos < sta) { sta = pos; } if (pos > sto) { sto = pos; } } /* * @param window the total width for the "near" area, in positions. * @param plists the position vectors for the terms. The array is * sorted shorted first for optimization. The function does a * recursive call on the next array if the match is still possible * after dealing with the current one * @param plist_idx the index for the position list we will work with. * @param min, max the current minimum and maximum term positions. * @param[output] sp, ep, the start and end positions of the found match. * @param minpos Highest end of a found match. While looking for * further matches, we don't want the search to extend before * this, because it does not make sense for highlight regions to * overlap. * @param isphrase if true, the position lists are in term order, and * we only look for the next match beyond the current window top. */ static bool do_proximity_test( const int window, vector& plists, unsigned int plist_idx, int min, int max, int *sp, int *ep, int minpos, bool isphrase) { // Overlap interdiction: possibly adjust window start by input minpos int actualminpos = isphrase ? max + 1 : max + 1 - window; if (actualminpos < minpos) actualminpos = minpos; LOGRP("do_prox_test: win " << window << " plist_idx " << plist_idx << " min " << min << " max " << max << " minpos " << minpos << " isphrase " << isphrase << " actualminpos " << actualminpos << "\n"); // Find 1st position bigger than window start. A previous call may // have advanced the index, so we begin by retrieving the current // value. int nextpos = plists[plist_idx].value(); while (nextpos != -1 && nextpos < actualminpos) nextpos = plists[plist_idx].next(); // Look for position inside window. If not found, no match. If // found: if this is the last list we're done, else recurse on // next list after adjusting the window while (nextpos != -1) { if (nextpos > min + window - 1) { return false; } if (plist_idx + 1 == plists.size()) { // We already checked pos > min, now we also have pos < // max, and we are the last list: done: set return values. setWinMinMax(nextpos, *sp, *ep); return true; } setWinMinMax(nextpos, min, max); if (do_proximity_test(window, plists, plist_idx + 1, min, max, sp, ep, minpos, isphrase)) { return true; } nextpos = plists[plist_idx].next(); } return false; } // Find matches for one group of terms bool matchGroup(const HighlightData& hldata, unsigned int grpidx, const unordered_map>& inplists, const unordered_map>& gpostobytes, vector& tboffs) { const auto& tg(hldata.index_term_groups[grpidx]); bool isphrase = tg.kind == HighlightData::TermGroup::TGK_PHRASE; #ifdef DEBUGGROUPS string allplterms; for (const auto& entry:inplists) { allplterms += entry.first + " "; } LOGRP("matchGroup: isphrase " << isphrase << ". Have plists for [" << allplterms << "]\n"); //LOGRP("matchGroup: hldata: " << hldata.toString() << std::endl); #endif int window = int(tg.orgroups.size() + tg.slack); // The position lists we are going to work with. We extract them from the // (string->plist) map vector orplists; // Find the position list for each term in the group and build the combined lists for the term // or groups (each group is the result of the exansion of one user term). It is possible that // this particular group was not actually matched by the search, so that some terms are not // found, in which case we bail out. for (const auto& group : tg.orgroups) { orplists.push_back(OrPList()); for (const auto& term : group) { const auto pl = inplists.find(term); if (pl == inplists.end()) { LOGRP("TextSplitPTR::matchGroup: term [" << term << "] not found in plists\n"); continue; } orplists.back().addplist(pl->first, &(pl->second)); } if (orplists.back().plists.empty()) { LOGRP("No positions list found for OR group [" << stringsToString(group) << "] : input has no group match, returning false\n"); return false; } else { LOGRP("Created OrPList has " << orplists.back().plists.size() << " members\n"); } } // I think this can't actually happen, was useful when we used to // prune the groups, but doesn't hurt. if (orplists.size() < 2) { LOGRP("TextSplitPTR::matchGroup: no actual groups found\n"); return false; } if (!isphrase) { // Sort the positions lists so that the shorter is first std::sort(orplists.begin(), orplists.end(), [](const OrPList& a, const OrPList& b) -> bool { return a.size() < b.size(); } ); } // Minpos is the highest end of a found match. While looking for // further matches, we don't want the search to extend before // this, because it does not make sense for highlight regions to // overlap int minpos = 0; // Walk the shortest plist and look for matches int pos; while ((pos = orplists[0].next()) != -1) { int sta = INT_MAX, sto = 0; LOGDEB2("MatchGroup: Testing at pos " << pos << "\n"); if (do_proximity_test( window, orplists, 1, pos, pos, &sta, &sto, minpos, isphrase)) { setWinMinMax(pos, sta, sto); LOGRP("TextSplitPTR::matchGroup: MATCH termpos [" << sta << "," << sto << "]\n"); minpos = sto + 1; // Translate the position window into a byte offset window auto i1 = gpostobytes.find(sta); auto i2 = gpostobytes.find(sto); if (i1 != gpostobytes.end() && i2 != gpostobytes.end()) { LOGDEB2("TextSplitPTR::matchGroup: pushing bpos " << i1->second.first << " " << i2->second.second << "\n"); tboffs.push_back(GroupMatchEntry(i1->second.first, i2->second.second, grpidx)); } else { LOGDEB0("matchGroup: no bpos found for " << sta << " or " << sto << "\n"); } } else { LOGRP("matchGroup: no group match found at this position\n"); } } return !tboffs.empty(); } vector kindflags { CHARFLAGENTRY(HighlightData::TermGroup::TGK_TERM), CHARFLAGENTRY(HighlightData::TermGroup::TGK_NEAR), CHARFLAGENTRY(HighlightData::TermGroup::TGK_PHRASE), }; string HighlightData::toString() const { string out; out.append("\nUser terms (orthograph): "); for (const auto& term : uterms) { out.append(" [").append(term).append("]"); } out.append("\nUser terms to Query terms:"); for (const auto& entry: terms) { out.append("[").append(entry.first).append("]->["); out.append(entry.second).append("] "); } out.append("\nGroups: "); char cbuf[200]; sprintf(cbuf, "index_term_groups size %d ugroups size %d", int(index_term_groups.size()), int(ugroups.size())); out.append(cbuf); size_t ugidx = (size_t) - 1; for (HighlightData::TermGroup tg : index_term_groups) { if (ugidx != tg.grpsugidx) { ugidx = tg.grpsugidx; out.append("\n("); for (unsigned int j = 0; j < ugroups[ugidx].size(); j++) { out.append("[").append(ugroups[ugidx][j]).append("] "); } out.append(") ->"); } if (tg.kind == HighlightData::TermGroup::TGK_TERM) { out.append(" <").append(tg.term).append(">"); } else { out.append(" {"); for (unsigned int j = 0; j < tg.orgroups.size(); j++) { out.append(" {"); for (unsigned int k = 0; k < tg.orgroups[j].size(); k++) { out.append("[").append(tg.orgroups[j][k]).append("]"); } out.append("}"); } out.append(" "); out.append(valToString(kindflags, tg.kind)).append("-").append(lltodecstr(tg.slack)); out.append(" }"); } } out.append("\n"); out.append("\nTerms generated by spelling approximation:"); out.append(stringsToString(spellexpands)); out.append("\n"); return out; } void HighlightData::append(const HighlightData& hl) { uterms.insert(hl.uterms.begin(), hl.uterms.end()); terms.insert(hl.terms.begin(), hl.terms.end()); size_t ugsz0 = ugroups.size(); ugroups.insert(ugroups.end(), hl.ugroups.begin(), hl.ugroups.end()); size_t itgsize = index_term_groups.size(); index_term_groups.insert(index_term_groups.end(), hl.index_term_groups.begin(), hl.index_term_groups.end()); // Adjust the grpsugidx values for the newly inserted entries for (unsigned int idx = itgsize; idx < index_term_groups.size(); idx++) { index_term_groups[idx].grpsugidx += ugsz0; } spellexpands.insert(spellexpands.end(), hl.spellexpands.begin(), hl.spellexpands.end()); } recoll-1.36.1/utils/idfile.h0000644000175000017500000000226014410615043012546 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _IDFILE_H_INCLUDED_ #define _IDFILE_H_INCLUDED_ #include // Look at data inside file or string, and return mime type or empty string. // // The system's file utility does a bad job on mail folders. idFile // only looks for mail file types for now, but this may change extern std::string idFile(const char *fn); extern std::string idFileMem(const std::string& data); #endif /* _IDFILE_H_INCLUDED_ */ recoll-1.36.1/utils/readfile.cpp0000644000175000017500000004506214410615043013427 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #ifndef PRETEND_USE #define PRETEND_USE(expr) ((void)(expr)) #endif #endif #include "readfile.h" #include #include #ifdef _WIN32 #include "safefcntl.h" #include "safesysstat.h" #include "safeunistd.h" #define OPEN _wopen #else #include #include #include #define OPEN open #endif #include #include "smallut.h" #include "pathut.h" #ifdef READFILE_ENABLE_MD5 #include "md5.h" #endif #ifdef MDU_INCLUDE_LOG #include MDU_INCLUDE_LOG #else #include "log.h" #endif using namespace std; /////////////// // Implementation of basic interface: read whole file to memory buffer class FileToString : public FileScanDo { public: FileToString(string& data) : m_data(data) {} // Note: the fstat() + reserve() (in init()) calls divide cpu // usage almost by 2 on both linux i586 and macosx (compared to // just append()) Also tried a version with mmap, but it's // actually slower on the mac and not faster on linux. virtual bool init(int64_t size, string *) { if (size > 0) { m_data.reserve((size_t)size); } return true; } virtual bool data(const char *buf, int cnt, string *reason) { try { m_data.append(buf, cnt); } catch (...) { catstrerror(reason, "append", errno); return false; } return true; } string& m_data; }; bool file_to_string(const string& fn, string& data, int64_t offs, size_t cnt, string *reason) { FileToString accum(data); return file_scan(fn, &accum, offs, cnt, reason #ifdef READFILE_ENABLE_MD5 , nullptr #endif ); } bool file_to_string(const string& fn, string& data, string *reason) { return file_to_string(fn, data, 0, size_t(-1), reason); } ///////////// // Callback/filtering interface // Abstract class base for both source (origin) and filter // (midstream). Both have a downstream class FileScanUpstream { public: virtual void setDownstream(FileScanDo *down) { m_down = down; } virtual FileScanDo *out() { return m_down; } protected: FileScanDo *m_down{nullptr}; }; // Source element. class FileScanSource : public FileScanUpstream { public: FileScanSource(FileScanDo *down) { setDownstream(down); } virtual bool scan() = 0; }; // Inside element of a transformation pipe. The idea is that elements // which don't recognize the data get themselves out of the pipe // (pop()). Typically, only one of the decompression modules // (e.g. gzip/bzip2/xz...) would remain. For now there is only gzip, // it pops itself if the data does not have the right magic number class FileScanFilter : public FileScanDo, public FileScanUpstream { public: virtual void insertAtSink(FileScanDo *sink, FileScanUpstream *upstream) { setDownstream(sink); if (m_down) { m_down->setUpstream(this); } setUpstream(upstream); if (m_up) { m_up->setDownstream(this); } } // Remove myself from the pipe. virtual void pop() { if (m_down) { m_down->setUpstream(m_up); } if (m_up) { m_up->setDownstream(m_down); } } virtual void setUpstream(FileScanUpstream *up) override { m_up = up; } private: FileScanUpstream *m_up{nullptr}; }; #if defined(READFILE_ENABLE_ZLIB) #include #include static const vector inflateErrors{ CHARFLAGENTRY(Z_OK), CHARFLAGENTRY(Z_STREAM_END), CHARFLAGENTRY(Z_NEED_DICT), CHARFLAGENTRY(Z_ERRNO), CHARFLAGENTRY(Z_STREAM_ERROR), CHARFLAGENTRY(Z_DATA_ERROR), CHARFLAGENTRY(Z_MEM_ERROR), CHARFLAGENTRY(Z_BUF_ERROR), CHARFLAGENTRY(Z_VERSION_ERROR), }; class GzFilter : public FileScanFilter { public: virtual ~GzFilter() { if (m_initdone) { inflateEnd(&m_stream); } } virtual bool init(int64_t size, string *reason) override { LOGDEB1("GzFilter::init\n"); if (out()) { return out()->init(size, reason); } return true; } virtual bool data(const char *buf, int cnt, string *reason) override { LOGDEB1("GzFilter::data: cnt " << cnt << endl); int error; m_stream.next_in = (Bytef*)buf; m_stream.avail_in = cnt; if (m_initdone == false) { // We do not support a first read cnt < 2. This quite // probably can't happen with a compressed file (size>2) // except if we're reading a tty which is improbable. So // assume this is a regular file. const unsigned char *ubuf = (const unsigned char *)buf; if ((cnt < 2) || ubuf[0] != 0x1f || ubuf[1] != 0x8b) { LOGDEB1("GzFilter::data: not gzip. out() is " << out() << "\n"); pop(); if (out()) { return out()->data(buf, cnt, reason); } else { return false; } } m_stream.opaque = nullptr; m_stream.zalloc = alloc_func; m_stream.zfree = free_func; m_stream.next_out = (Bytef*)m_obuf; m_stream.avail_out = m_obs; if ((error = inflateInit2(&m_stream, 15+32)) != Z_OK) { LOGERR("inflateInit2 error: " << error << endl); if (reason) { *reason += " Zlib inflateinit failed"; if (m_stream.msg && *m_stream.msg) { *reason += string(": ") + m_stream.msg; } } return false; } m_initdone = true; } while (m_stream.avail_in != 0) { m_stream.next_out = (Bytef*)m_obuf; m_stream.avail_out = m_obs; error = inflate(&m_stream, Z_SYNC_FLUSH); if (error != Z_OK && !(error == Z_STREAM_END && m_stream.avail_in == 0)) { // Note that Z_STREAM_END with avail_in!=0 is an error, // we still have data: something is wrong with the file. LOGERR("inflate error: " << valToString(inflateErrors, error) << " remaining bytes: " << m_stream.avail_in << endl); if (reason) { *reason += " Zlib inflate failed"; if (m_stream.msg && *m_stream.msg) { *reason += string(": ") + m_stream.msg; } } return false; } if (out() && !out()->data(m_obuf, m_obs - m_stream.avail_out, reason)) { return false; } } return true; } static voidpf alloc_func(voidpf, uInt items, uInt size) { return malloc(items * size); } static void free_func(voidpf, voidpf address) { free(address); } bool m_initdone{false}; z_stream m_stream; char m_obuf[10000]; const int m_obs{10000}; }; #endif // GZ #ifdef READFILE_ENABLE_MD5 class FileScanMd5 : public FileScanFilter { public: FileScanMd5(string& d) : digest(d) {} virtual bool init(int64_t size, string *reason) override { LOGDEB1("FileScanMd5: init\n"); MD5Init(&ctx); if (out()) { return out()->init(size, reason); } return true; } virtual bool data(const char *buf, int cnt, string *reason) override { LOGDEB1("FileScanMd5: data. cnt " << cnt << endl); MD5Update(&ctx, (const unsigned char*)buf, cnt); if (out() && !out()->data(buf, cnt, reason)) { return false; } return true; } bool finish() { LOGDEB1("FileScanMd5: finish\n"); MD5Final(digest, &ctx); return true; } string &digest; MD5_CTX ctx; }; #endif // MD5 // Source taking data from a regular file class FileScanSourceFile : public FileScanSource { public: FileScanSourceFile(FileScanDo *next, const string& fn, int64_t startoffs, int64_t cnttoread, string *reason) : FileScanSource(next), m_fn(fn), m_startoffs(startoffs), m_cnttoread(cnttoread), m_reason(reason) { } virtual bool scan() { LOGDEB1("FileScanSourceFile: reading " << m_fn << " offs " << m_startoffs<< " cnt " << m_cnttoread << " out " << out() << endl); const int RDBUFSZ = 8192; bool ret = false; bool noclosing = true; int fd = 0; struct stat st; // Initialize st_size: if fn.empty() , the fstat() call won't happen. st.st_size = 0; // If we have a file name, open it, else use stdin. if (!m_fn.empty()) { #ifdef _WIN32 auto buf = utf8towchar(m_fn); auto realpath = buf.get(); #else auto realpath = m_fn.c_str(); #endif fd = OPEN(realpath, O_RDONLY | O_BINARY); if (fd < 0 || fstat(fd, &st) < 0) { catstrerror(m_reason, "open/stat", errno); return false; } noclosing = false; } #if defined O_NOATIME && O_NOATIME != 0 if (fcntl(fd, F_SETFL, O_NOATIME) < 0) { // perror("fcntl"); } #endif if (out()) { if (m_cnttoread != -1 && m_cnttoread) { out()->init(m_cnttoread + 1, m_reason); } else if (st.st_size > 0) { out()->init(st.st_size + 1, m_reason); } else { out()->init(0, m_reason); } } int64_t curoffs = 0; if (m_startoffs > 0 && !m_fn.empty()) { if (lseek(fd, m_startoffs, SEEK_SET) != m_startoffs) { catstrerror(m_reason, "lseek", errno); return false; } curoffs = m_startoffs; } char buf[RDBUFSZ]; int64_t totread = 0; for (;;) { size_t toread = RDBUFSZ; if (m_startoffs > 0 && curoffs < m_startoffs) { toread = size_t(MIN(RDBUFSZ, m_startoffs - curoffs)); } if (m_cnttoread != -1) { toread = (size_t)MIN(toread, (uint64_t)(m_cnttoread - totread)); } ssize_t n = static_cast(read(fd, buf, toread)); if (n < 0) { catstrerror(m_reason, "read", errno); goto out; } if (n == 0) { break; } curoffs += n; if (curoffs - n < m_startoffs) { continue; } if (!out()->data(buf, n, m_reason)) { goto out; } totread += n; if (m_cnttoread > 0 && totread >= m_cnttoread) { break; } } ret = true; out: if (fd >= 0 && !noclosing) { close(fd); } return ret; } protected: string m_fn; int64_t m_startoffs; int64_t m_cnttoread; string *m_reason; }; #if defined(READFILE_ENABLE_MINIZ) #include "miniz.h" // Source taking data from a ZIP archive member class FileScanSourceZip : public FileScanSource { public: FileScanSourceZip(FileScanDo *next, const string& fn, const string& member, string *reason) : FileScanSource(next), m_fn(fn), m_member(member), m_reason(reason) {} FileScanSourceZip(const char *data, size_t cnt, FileScanDo *next, const string& member, string *reason) : FileScanSource(next), m_data(data), m_cnt(cnt), m_member(member), m_reason(reason) {} virtual bool scan() { bool ret = false; mz_zip_archive zip; mz_zip_zero_struct(&zip); void *opaque = this; bool ret1; if (m_fn.empty()) { ret1 = mz_zip_reader_init_mem(&zip, m_data, m_cnt, 0); } else { #ifdef _WIN32 auto buf = utf8towchar(m_fn); auto realpath = buf.get(); #else auto realpath = m_fn.c_str(); #endif ret1 = mz_zip_reader_init_file(&zip, realpath, 0); } if (!ret1) { if (m_reason) { *m_reason += "mz_zip_reader_init_xx() failed: "; *m_reason += string(mz_zip_get_error_string(zip.m_last_error)); } return false; } mz_uint32 file_index; if (mz_zip_reader_locate_file_v2(&zip, m_member.c_str(), NULL, 0, &file_index) < 0) { if (m_reason) { *m_reason += "mz_zip_reader_locate_file() failed: "; *m_reason += string(mz_zip_get_error_string(zip.m_last_error)); } goto out; } mz_zip_archive_file_stat zstat; if (!mz_zip_reader_file_stat(&zip, file_index, &zstat)) { if (m_reason) { *m_reason += "mz_zip_reader_file_stat() failed: "; *m_reason += string(mz_zip_get_error_string(zip.m_last_error)); } goto out; } if (out()) { if (!out()->init(zstat.m_uncomp_size, m_reason)) { goto out; } } if (!mz_zip_reader_extract_to_callback( &zip, file_index, write_cb, opaque, 0)) { if (m_reason) { *m_reason += "mz_zip_reader_extract_to_callback() failed: "; *m_reason += string(mz_zip_get_error_string(zip.m_last_error)); } goto out; } ret = true; out: mz_zip_reader_end(&zip); return ret; } static size_t write_cb(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { const char *cp = (const char*)pBuf; PRETEND_USE(file_ofs); LOGDEB1("write_cb: ofs " << file_ofs << " cnt " << n << " data: " << string(cp, n) << endl); FileScanSourceZip *ths = (FileScanSourceZip *)pOpaque; if (ths->out()) { if (!ths->out()->data(cp, n, ths->m_reason)) { return (size_t)-1; } } return n; } protected: const char *m_data; size_t m_cnt; string m_fn; string m_member; string *m_reason; }; bool file_scan(const std::string& filename, const std::string& membername, FileScanDo* doer, std::string *reason) { if (membername.empty()) { return file_scan(filename, doer, 0, -1, reason #ifdef READFILE_ENABLE_MD5 , nullptr #endif ); } else { FileScanSourceZip source(doer, filename, membername, reason); return source.scan(); } } bool string_scan(const char *data, size_t cnt, const std::string& membername, FileScanDo* doer, std::string *reason) { if (membername.empty()) { return string_scan(data, cnt, doer, reason #ifdef READFILE_ENABLE_MD5 , nullptr #endif ); } else { FileScanSourceZip source(data, cnt, doer, membername, reason); return source.scan(); } } #endif // READFILE_ENABLE_ZIP bool file_scan(const string& fn, FileScanDo* doer, int64_t startoffs, int64_t cnttoread, string *reason #ifdef READFILE_ENABLE_MD5 , string *md5p #endif ) { LOGDEB1("file_scan: doer " << doer << endl); #if defined(READFILE_ENABLE_ZLIB) bool nodecomp = startoffs != 0; #endif if (startoffs < 0) { startoffs = 0; } FileScanSourceFile source(doer, fn, startoffs, cnttoread, reason); FileScanUpstream *up = &source; PRETEND_USE(up); #if defined(READFILE_ENABLE_ZLIB) GzFilter gzfilter; if (!nodecomp) { gzfilter.insertAtSink(doer, up); up = &gzfilter; } #endif #ifdef READFILE_ENABLE_MD5 // We compute the MD5 on the uncompressed data, so insert this // right at the source (after the decompressor). string digest; FileScanMd5 md5filter(digest); if (md5p) { md5filter.insertAtSink(doer, up); up = &md5filter; } #endif bool ret = source.scan(); #ifdef READFILE_ENABLE_MD5 if (md5p) { md5filter.finish(); MD5HexPrint(digest, *md5p); } #endif return ret; } bool file_scan(const string& fn, FileScanDo* doer, string *reason) { return file_scan(fn, doer, 0, -1, reason #ifdef READFILE_ENABLE_MD5 , nullptr #endif ); } class FileScanSourceBuffer : public FileScanSource { public: FileScanSourceBuffer(FileScanDo *next, const char *data, size_t cnt, string *reason) : FileScanSource(next), m_data(data), m_cnt(cnt), m_reason(reason) {} virtual bool scan() { if (out()) { if (!out()->init(m_cnt, m_reason)) { return false; } return out()->data(m_data, m_cnt, m_reason); } else { return true; } } protected: const char *m_data{nullptr}; size_t m_cnt{0}; string *m_reason{nullptr}; }; bool string_scan(const char *data, size_t cnt, FileScanDo* doer, std::string *reason #ifdef READFILE_ENABLE_MD5 , std::string *md5p #endif ) { FileScanSourceBuffer source(doer, data, cnt, reason); FileScanUpstream *up = &source; PRETEND_USE(up); #ifdef READFILE_ENABLE_MD5 string digest; FileScanMd5 md5filter(digest); if (md5p) { md5filter.insertAtSink(doer, up); up = &md5filter; } #endif bool ret = source.scan(); #ifdef READFILE_ENABLE_MD5 if (md5p) { md5filter.finish(); MD5HexPrint(digest, *md5p); } #endif return ret; } recoll-1.36.1/utils/netcon.cpp0000644000175000017500000011510414427373216013150 00000000000000/* Copyright (C) 2002 J.F. Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Wrapper classes for the socket interface #ifdef BUILDING_RECOLL #include "autoconfig.h" #else #include "config.h" #endif #include "netcon.h" #include #include #include #include #include #ifdef _AIX #include #endif // _AIX #include #include #include #include #include #include #include #include #ifdef HAVE_KQUEUE #include #include #include #endif #include #include #ifdef MDU_INCLUDE_LOG #include MDU_INCLUDE_LOG #else #include "log.h" #endif using namespace std; #ifndef PRETEND_USE #define PRETEND_USE(expr) ((void)(expr)) #endif #ifndef SOCKLEN_T #define SOCKLEN_T socklen_t #endif // Size of path buffer in sockaddr_un (AF_UNIX socket // addr). Mysteriously it is 108 (explicit value) under linux, no // define accessible. Let's take a little margin as it appears that // some systems use 92. I believe we could also malloc a variable size // struct but why bother. #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 90 #endif // Need &one, &zero for setsockopt... static const int one = 1; static const int zero = 0; #ifndef LOGSYSERR #define LOGSYSERR(who, call, spar) \ LOGERR(who << ": " << call << "(" << spar << ") errno " << \ errno << " (" << strerror(errno) << ")\n") #endif #ifndef freeZ #define freeZ(X) if (X) {free(X);X=nullptr;} #endif #define MILLIS(OLD, NEW) ( (uint64_t((NEW).tv_sec) - (OLD).tv_sec) * 1000 + \ ((NEW).tv_usec - (OLD).tv_usec) / 1000 ) // Static method // Simplified interface to 'select()'. Only use one fd, for either // reading or writing. This is only used when not using the // selectloop() style of network i/o. // Note that timeo == 0 does NOT mean wait forever but no wait at all. int Netcon::select1(int fd, int timeo, int write) { int ret; struct timeval tv; fd_set rd; tv.tv_sec = timeo; tv.tv_usec = 0; FD_ZERO(&rd); FD_SET(fd, &rd); if (write) { ret = select(fd + 1, nullptr, &rd, nullptr, &tv); } else { ret = select(fd + 1, &rd, nullptr, nullptr, &tv); } if (!FD_ISSET(fd, &rd)) { LOGDEB2("Netcon::select1: fd " << fd << " timeout\n"); } return ret; } /////////////////////////////////////////// // SelectLoop class SelectLoop::Internal { public: Internal() { #ifdef HAVE_KQUEUE if ((kq = kqueue()) == -1) { LOGSYSERR("Netcon::selectloop", "kqueue", ""); } #endif } ~Internal() { #ifdef HAVE_KQUEUE if (kq >= 0) close(kq); #endif } // Set by client callback to tell selectloop to return. bool selectloopDoReturn{false}; int selectloopReturnValue{0}; int placetostart{0}; // Map of NetconP indexed by fd map polldata; #ifdef HAVE_KQUEUE int kq{-1}; #endif // The last time we did the periodic thing. Initialized by setperiodic() struct timeval lasthdlcall; // The call back function and its parameter int (*periodichandler)(void *){0}; void *periodicparam{0}; // The periodic interval int periodicmillis{0}; void periodictimeout(struct timeval *tv); void periodictimeout(struct timespec *ts); int maybecallperiodic(); int setselevents(int fd, int events); int setselevents(NetconP& con, int events); }; SelectLoop::SelectLoop() { m = new Internal; } SelectLoop::~SelectLoop() { delete m; } void SelectLoop::loopReturn(int value) { m->selectloopDoReturn = true; m->selectloopReturnValue = value; } void SelectLoop::setperiodichandler(int (*handler)(void *), void *p, int ms) { m->periodichandler = handler; m->periodicparam = p; m->periodicmillis = ms; if (m->periodicmillis > 0) { gettimeofday(&m->lasthdlcall, nullptr); } } // Compute the appropriate timeout so that the select call returns in // time to call the periodic routine. void SelectLoop::Internal::periodictimeout(struct timeval *tv) { // If periodic not set, the select call times out and we loop // after a very long time (we'd need to pass NULL to select for an // infinite wait, and I'm too lazy to handle it) if (periodicmillis <= 0) { tv->tv_sec = 10000; tv->tv_usec = 0; return; } struct timeval mtv; gettimeofday(&mtv, nullptr); int millis = periodicmillis - MILLIS(lasthdlcall, mtv); // millis <= 0 means we should have already done the thing. *dont* set the // tv to 0, which means no timeout at all ! if (millis <= 0) { millis = 1; } tv->tv_sec = millis / 1000; tv->tv_usec = (millis % 1000) * 1000; } void SelectLoop::Internal::periodictimeout(struct timespec *ts) { struct timeval tv; periodictimeout(&tv); ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec * 1000; } // Check if it's time to call the handler. selectloop will return to // caller if either we or the handler return 0 int SelectLoop::Internal::maybecallperiodic() { if (periodicmillis <= 0) { return 1; } struct timeval mtv; gettimeofday(&mtv, nullptr); int millis = periodicmillis - MILLIS(lasthdlcall, mtv); if (millis <= 0) { lasthdlcall = mtv; if (periodichandler) { return periodichandler(periodicparam); } else { return 0; } } return 1; } #ifndef HAVE_KQUEUE int SelectLoop::doLoop() { for (;;) { if (m->selectloopDoReturn) { m->selectloopDoReturn = false; LOGDEB("Netcon::selectloop: returning on request\n"); return m->selectloopReturnValue; } int nfds; fd_set rd, wd; FD_ZERO(&rd); FD_ZERO(&wd); // Walk the netcon map and set up the read and write fd_sets // for select() nfds = 0; for (auto& entry : m->polldata) { NetconP& pll = entry.second; int fd = entry.first; LOGDEB2("Selectloop: fd " << fd << " flags 0x" << pll->m_wantedEvents << "\n"); if (pll->m_wantedEvents & Netcon::NETCONPOLL_READ) { FD_SET(fd, &rd); nfds = std::max(nfds, fd + 1); } if (pll->m_wantedEvents & Netcon::NETCONPOLL_WRITE) { FD_SET(fd, &wd); nfds = std::max(nfds, fd + 1); } } if (nfds == 0) { // This should never happen in a server as we should at least // always monitor the main listening server socket. For a // client, it's up to client code to avoid or process this // condition. // Just in case there would still be open fds in there // (with no r/w flags set). Should not be needed, but safer m->polldata.clear(); LOGDEB1("Netcon::selectloop: no fds\n"); return 0; } LOGDEB2("Netcon::selectloop: selecting, nfds = " << nfds << "\n"); // Compute the next timeout according to what might need to be // done apart from waiting for data struct timeval tv; m->periodictimeout(&tv); // Wait for something to happen int ret = select(nfds, &rd, &wd, nullptr, &tv); LOGDEB2("Netcon::selectloop: nfds " << nfds << " select returns " << ret << "\n"); if (ret < 0) { LOGSYSERR("Netcon::selectloop", "select", ""); return -1; } if (m->periodicmillis > 0 && m->maybecallperiodic() <= 0) { return 1; } // Timeout, do it again. if (ret == 0) { continue; } // Select returned > 0: at least one fd must be ready. Sweep the fd // table and act on the ready ones. // We don't start the fd sweep at 0, else some fds would be advantaged. // Note that we do an fd sweep, not a map sweep. This is // inefficient because the fd array may be very sparse. Otoh, the // map may change between 2 sweeps, so that we'd have to be smart // with the iterator. As the cost per unused fd is low (just 2 bit // flag tests), we keep it like this for now if (m->placetostart >= nfds) { m->placetostart = 0; } int i, fd; int activefds = 0; for (i = 0, fd = m->placetostart; i < nfds; i++, fd++) { if (fd >= nfds) { fd = 0; } int canread = FD_ISSET(fd, &rd); int canwrite = FD_ISSET(fd, &wd); bool none = !canread && !canwrite; LOGDEB2("Netcon::selectloop: fd " << fd << " " << (none ? "blocked" : "can") << " " << (canread ? "read" : "") << " " << (canwrite ? "write" : "") << "\n"); if (none) { continue; } auto it = m->polldata.find(fd); if (it == m->polldata.end()) { // This should never happen, because we only set our // own fds in the mask ! LOGERR("Netcon::selectloop: fd " << fd << " not found\n"); continue; } activefds++; // Next start will be one beyond last serviced (modulo nfds) m->placetostart = fd + 1; NetconP& pll = it->second; if (canread && pll->cando(Netcon::NETCONPOLL_READ) <= 0) { pll->m_wantedEvents &= ~Netcon::NETCONPOLL_READ; } if (canwrite && pll->cando(Netcon::NETCONPOLL_WRITE) <= 0) { pll->m_wantedEvents &= ~Netcon::NETCONPOLL_WRITE; } if (!(pll->m_wantedEvents & (Netcon::NETCONPOLL_WRITE | Netcon::NETCONPOLL_READ))) { LOGDEB0("Netcon::selectloop: fd " << it->first << " has 0x" << it->second->m_wantedEvents << " mask, erasing\n"); m->polldata.erase(it); } } // fd sweep if (ret > 0 && activefds != ret) { LOGERR("Select returned " << ret << " not equal to " << activefds << " active fd found\n"); return -1; } } // forever loop LOGERR("SelectLoop::doLoop: got out of loop !\n"); return -1; } #else // -> Using kqueue: use select() int SelectLoop::doLoop() { for (;;) { if (m->selectloopDoReturn) { m->selectloopDoReturn = false; LOGDEB("Netcon::selectloop: returning on request\n"); return m->selectloopReturnValue; } // Check that we do have something to wait for. int nfds = 0; for (auto& entry : m->polldata) { NetconP& pll = entry.second; if (pll->m_wantedEvents & Netcon::NETCONPOLL_READ) { nfds++; } else if (pll->m_wantedEvents & Netcon::NETCONPOLL_WRITE) { nfds++; } } if (nfds == 0) { // This should never happen in a server as we should at least // always monitor the main listening server socket. For a // client, it's up to client code to avoid or process this // condition. // Just in case there would still be open fds in there // (with no r/w flags set). Should not be needed, but safer m->polldata.clear(); LOGDEB1("Netcon::selectloop: no fds\n"); return 0; } // Compute the next timeout according to what might need to be // done apart from waiting for data struct timespec ts; m->periodictimeout(&ts); // Wait for something to happen vector events; events.resize(nfds); LOGDEB1("Netcon::selectloop: kevent(), nfds = " << nfds << "\n"); int ret = kevent(m->kq, 0, 0, &events[0], events.size(), &ts); LOGDEB1("Netcon::selectloop: nfds " << nfds << " kevent returns " << ret << "\n"); if (ret < 0) { LOGSYSERR("Netcon::selectloop", "kevent", ""); return -1; } if (m->periodicmillis > 0 && m->maybecallperiodic() <= 0) { return 1; } if (ret == 0) { // Timeout, do it again. continue; } for (int i = 0; i < ret; i++) { struct kevent& ev = events[i]; if (ev.flags & EV_ERROR) { LOGSYSERR("Netcon::selectLoop", "kevent", ""); LOGERR("Netcon::selectLoop: event error: " << strerror(ev.data)); return -1; } int canread = ev.filter == EVFILT_READ; int canwrite = ev.filter == EVFILT_WRITE; bool none = !canread && !canwrite; LOGDEB1("Netcon::selectloop: fd " << int(ev.ident) << " " << (none ? "blocked" : "can") << " " << (canread ? "read" : "") << " " << (canwrite ? "write" : "") << "\n"); if (none) { LOGERR("Kevent returned unknown filter " << ev.filter <polldata.find(int(ev.ident)); if (it == m->polldata.end()) { LOGERR("Netcon::selectloop: fd " << int(ev.ident) << " not found\n"); continue; } NetconP& pll = it->second; if (canread && pll->cando(Netcon::NETCONPOLL_READ) <= 0) { pll->setselevents(pll->getselevents() & ~Netcon::NETCONPOLL_READ); } if (canwrite && pll->cando(Netcon::NETCONPOLL_WRITE) <= 0) { pll->setselevents(pll->getselevents() & ~Netcon::NETCONPOLL_WRITE); } if (!(pll->getselevents() & (Netcon::NETCONPOLL_WRITE | Netcon::NETCONPOLL_READ))) { LOGDEB0("Netcon::selectloop: fd " << it->first << " has 0x" << it->second->getselevents() << " mask, erasing\n"); m->polldata.erase(it); } } // fd sweep } // forever loop LOGERR("SelectLoop::doLoop: got out of loop !\n"); return -1; } #endif // kqueue version int SelectLoop::Internal::setselevents(int fd, int events) { #ifdef HAVE_KQUEUE auto it = polldata.find(fd); if (it == polldata.end()) { return -1; } return setselevents(it->second, events); #endif return 0; } int SelectLoop::Internal::setselevents(NetconP& con, int events) { #ifdef HAVE_KQUEUE struct kevent event; if (events & Netcon::NETCONPOLL_READ) { EV_SET(&event, con->m_fd, EVFILT_READ, EV_ADD, 0, 0, 0); if(kevent(kq, &event, 1, 0, 0, 0) < 0) { LOGSYSERR("SelectLoop::addselcon", "kevent", ""); return -1; } } else { EV_SET(&event, con->m_fd, EVFILT_READ, EV_DELETE, 0, 0, 0); kevent(kq, &event, 1, 0, 0, 0); } if (events & Netcon::NETCONPOLL_WRITE) { EV_SET(&event, con->m_fd, EVFILT_WRITE, EV_ADD, 0, 0, 0); if(kevent(kq, &event, 1, 0, 0, 0) < 0) { LOGSYSERR("SelectLoop::addselcon", "kevent", ""); return -1; } } else { EV_SET(&event, con->m_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0); kevent(kq, &event, 1, 0, 0, 0); } #endif return 0; } // Add a connection to the monitored set. This can be used to change // the event flags too (won't add duplicates) int SelectLoop::addselcon(NetconP con, int events) { if (!con) { return -1; } LOGDEB1("Netcon::addselcon: fd " << con->m_fd << "\n"); con->set_nonblock(1); con->m_wantedEvents = events; m->polldata[con->m_fd] = con; con->setloop(this); return m->setselevents(con, events); } // Remove a connection from the monitored set. int SelectLoop::remselcon(NetconP con) { if (!con) { return -1; } LOGDEB1("Netcon::remselcon: fd " << con->m_fd << "\n"); m->setselevents(con, 0); auto it = m->polldata.find(con->m_fd); if (it == m->polldata.end()) { LOGDEB1("Netcon::remselcon: con not found for fd " << con->m_fd << "\n"); return -1; } con->setloop(nullptr); m->polldata.erase(it); return 0; } ////////////////////////////////////////////////////////// // Base class (Netcon) methods Netcon::~Netcon() { closeconn(); if (m_peer) { free(m_peer); m_peer = nullptr; } } void Netcon::closeconn() { if (m_ownfd && m_fd >= 0) { close(m_fd); } m_fd = -1; m_ownfd = true; } char *Netcon::sterror() { return strerror(errno); } void Netcon::setpeer(const char *hostname) { if (m_peer) { free(m_peer); } m_peer = strdup(hostname); } int Netcon::settcpnodelay(int on) { LOGDEB2("Netcon::settcpnodelay\n"); if (m_fd < 0) { LOGERR("Netcon::settcpnodelay: connection not opened\n"); return -1; } char *cp = on ? (char *)&one : (char *)&zero; if (setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, cp, sizeof(one)) < 0) { LOGSYSERR("NetconCli::settcpnodelay", "setsockopt", "TCP_NODELAY"); return -1; } return 0; } // Set/reset non-blocking flag on fd int Netcon::set_nonblock(int onoff) { int flags = fcntl(m_fd, F_GETFL, 0); if (flags != -1) { int newflags = onoff ? flags | O_NONBLOCK : flags & ~O_NONBLOCK; if (newflags != flags) if (fcntl(m_fd, F_SETFL, newflags) < 0) { return -1; } } return flags; } int Netcon::setselevents(int events) { m_wantedEvents = events; if (m_loop) { m_loop->m->setselevents(m_fd, events); } return m_wantedEvents; } ///////////////////////////////////////////////////////////////////// // Data socket (NetconData) methods NetconData::NetconData(bool cancellable) : m_buf(nullptr), m_bufbase(nullptr), m_bufbytes(0), m_bufsize(0), m_wkfds{-1,-1} { if (cancellable) { if (pipe(m_wkfds) < 0) { LOGSYSERR("NetconData::NetconData", "pipe", ""); m_wkfds[0] = m_wkfds[1] = -1; } LOGDEB2("NetconData:: m_wkfds[0] " << m_wkfds[0] << " m_wkfds[1] " << m_wkfds[1] << endl); for (int i = 0; i < 2; i++) { int flags = fcntl(m_wkfds[i], F_GETFL, 0); fcntl(m_wkfds[i], F_SETFL, flags | O_NONBLOCK); } } } NetconData::~NetconData() { freeZ(m_buf); m_bufbase = nullptr; m_bufbytes = m_bufsize = 0; for (int i = 0; i < 2; i++) { if (m_wkfds[i] >= 0) { close(m_wkfds[i]); } } } int NetconData::send(const char *buf, int cnt, int expedited) { LOGDEB2("NetconData::send: fd " << m_fd << " cnt " << cnt << " expe " << expedited << "\n"); int flag = 0; if (m_fd < 0) { LOGERR("NetconData::send: connection not opened\n"); return -1; } if (expedited) { LOGDEB2("NetconData::send: expedited data, count " <= 0) { LOGDEB2("NetconData::cancelReceive: writing to " << m_wkfds[1] << endl); // We can't do a thing about the ::write return value, the // following nonsense is for cancelling warnings int ret = ::write(m_wkfds[1], "!", 1); PRETEND_USE(ret); } } // Receive at most cnt bytes (maybe less) int NetconData::receive(char *buf, int cnt, int timeo) { LOGDEB2("NetconData::receive: cnt " << cnt << " timeo " << timeo << " m_buf 0x" << m_buf << " m_bufbytes " << m_bufbytes << "\n"); if (m_fd < 0) { LOGERR("NetconData::receive: connection not opened\n"); return -1; } int fromibuf = 0; // Get whatever might have been left in the buffer by a previous // getline, except if we're called to fill the buffer of course if (m_buf && m_bufbytes > 0 && (buf < m_buf || buf > m_buf + m_bufsize)) { fromibuf = std::min(m_bufbytes, cnt); memcpy(buf, m_bufbase, fromibuf); m_bufbytes -= fromibuf; m_bufbase += fromibuf; cnt -= fromibuf; LOGDEB2("NetconData::receive: got " << fromibuf << " from mbuf\n"); if (cnt <= 0) { return fromibuf; } } if (timeo > 0) { struct timeval tv; tv.tv_sec = timeo; tv.tv_usec = 0; fd_set rd; FD_ZERO(&rd); FD_SET(m_fd, &rd); bool cancellable = (m_wkfds[0] >= 0); if (cancellable) { LOGDEB2("NetconData::receive: cancel fd " << m_wkfds[0] << endl); FD_SET(m_wkfds[0], &rd); } int nfds = std::max(m_fd, m_wkfds[0]) + 1; int ret = select(nfds, &rd, nullptr, nullptr, &tv); LOGDEB2("NetconData::receive: select returned " << ret << endl); if (cancellable && FD_ISSET(m_wkfds[0], &rd)) { char b[100]; // We can't do a thing about the return value, the // following nonsense is for cancelling warnings int ret = ::read(m_wkfds[0], b, 100); PRETEND_USE(ret); return Cancelled; } if (!FD_ISSET(m_fd, &rd)) { m_didtimo = 1; return TimeoutOrError; } if (ret < 0) { LOGSYSERR("NetconData::receive", "select", ""); m_didtimo = 0; return TimeoutOrError; } } m_didtimo = 0; if ((cnt = read(m_fd, buf + fromibuf, cnt)) < 0) { LOGSYSERR("NetconData::receive", "read", m_fd); return -1; } LOGDEB2("NetconData::receive: normal return, fromibuf " << fromibuf << " cnt " << cnt << "\n"); return fromibuf + cnt; } // Receive exactly cnt bytes (except for timeout) int NetconData::doreceive(char *buf, int cnt, int timeo) { int got, cur; LOGDEB2("Netcon::doreceive: cnt " << cnt << ", timeo " << timeo << "\n"); cur = 0; while (cnt > cur) { got = receive(buf, cnt - cur, timeo); LOGDEB2("Netcon::doreceive: got " << got << "\n"); if (got < 0) { return got; } if (got == 0) { return cur; } cur += got; buf += got; } return cur; } // Read data until cnt-1 characters are read or a newline is found. Add // null char at end of buffer and return. // As we don't know where the newline will be and it would be inefficient to // read a character at a time, we use a buffer // Unlike fgets, we return an integer status: // >0: number of characters returned, not including the final 0 // 0: EOF reached, no chars transferred // -1: error static const int defbufsize = 200; int NetconData::getline(char *buf, int cnt, int timeo) { LOGDEB2("NetconData::getline: cnt " << cnt << ", timeo " << timeo << "\n"); if (nullptr == m_buf) { if ((m_buf = (char *)malloc(defbufsize)) == nullptr) { LOGSYSERR("NetconData::getline: Out of mem", "malloc", ""); return -1; } m_bufsize = defbufsize; m_bufbase = m_buf; m_bufbytes = 0; } char *cp = buf; for (;;) { // Transfer from buffer. Have to take a lot of care to keep counts and // pointers consistant in all end cases int maxtransf = std::min(m_bufbytes, cnt - 1); int nn = maxtransf; LOGDEB2("Before loop, bufbytes " << m_bufbytes << ", maxtransf " << maxtransf << ", nn: " << nn << "\n"); for (nn = maxtransf; nn > 0;) { // This is not pretty but we want nn to be decremented for // each byte copied (even newline), and not become -1 if // we go to the end. Better ways welcome! nn--; if ((*cp++ = *m_bufbase++) == '\n') { break; } } // Update counts maxtransf -= nn; // Actual count transferred m_bufbytes -= maxtransf; cnt -= maxtransf; LOGDEB2("After transfer: actual transf " << maxtransf << " cnt " << cnt << ", m_bufbytes " << m_bufbytes << "\n"); // Finished ? if (cnt <= 1 || (cp > buf && cp[-1] == '\n')) { *cp = 0; return cp - buf; } // Transfer from net m_bufbase = m_buf; m_bufbytes = receive(m_buf, m_bufsize, timeo); if (m_bufbytes == 0) { // EOF *cp = 0; return cp - buf; } if (m_bufbytes < 0) { m_bufbytes = 0; *cp = 0; return -1; } } } // Called when selectloop detects that data can be read or written on // the connection. The user callback would normally have been set // up. If it is, call it and return. Else, perform housecleaning: read // and discard. int NetconData::cando(Netcon::Event reason) { LOGDEB2("NetconData::cando\n"); if (m_user) { return m_user->data(this, reason); } // No user callback. Clean up by ourselves if (reason & NETCONPOLL_READ) { #define BS 200 char buf[BS]; int n; if ((n = receive(buf, BS)) < 0) { LOGSYSERR("NetconData::cando", "receive", ""); return -1; } if (n == 0) { // EOF return 0; } } m_wantedEvents &= ~NETCONPOLL_WRITE; return 1; } /////////////////////////////////////////////////////////////////////// // Methods for a client connection (NetconCli) int NetconCli::openconn(const char *host, unsigned int port, int timeo) { int ret = -1; LOGDEB2("Netconcli::openconn: host " << host << ", port " << port << "\n"); closeconn(); struct sockaddr *saddr; socklen_t addrsize; struct sockaddr_in ip_addr; struct sockaddr_un unix_addr; if (host[0] != '/') { memset(&ip_addr, 0, sizeof(ip_addr)); ip_addr.sin_family = AF_INET; ip_addr.sin_port = htons(port); // Server name may be host name or IP address int addr; if ((addr = inet_addr(host)) != -1) { memcpy(&ip_addr.sin_addr, &addr, sizeof(addr)); } else { struct hostent *hp; if ((hp = gethostbyname(host)) == nullptr) { LOGERR("NetconCli::openconn: gethostbyname(" << host << ") failed\n"); return -1; } memcpy(&ip_addr.sin_addr, hp->h_addr, hp->h_length); } if ((m_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { LOGSYSERR("NetconCli::openconn", "socket", ""); return -1; } addrsize = sizeof(ip_addr); saddr = (sockaddr*)&ip_addr; } else { memset(&unix_addr, 0, sizeof(unix_addr)); unix_addr.sun_family = AF_UNIX; if (strlen(host) > UNIX_PATH_MAX - 1) { LOGERR("NetconCli::openconn: name too long: " << host << "\n"); return -1; } strcpy(unix_addr.sun_path, host); if ((m_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { LOGSYSERR("NetconCli::openconn", "socket", ""); return -1; } addrsize = sizeof(unix_addr); saddr = (sockaddr*)&unix_addr; } if (timeo > 0) { set_nonblock(1); } if (connect(m_fd, saddr, addrsize) < 0) { if (timeo > 0) { if (errno != EINPROGRESS) { goto out; } if (select1(m_fd, timeo, 1) == 1) { goto connectok; } } if (m_silentconnectfailure == 0) { LOGSYSERR("NetconCli", "connect", ""); } goto out; } connectok: if (timeo > 0) { set_nonblock(0); } LOGDEB2("NetconCli::connect: setting keepalive\n"); if (setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one)) < 0) { LOGSYSERR("NetconCli::connect", "setsockopt", "KEEPALIVE"); } setpeer(host); LOGDEB2("NetconCli::openconn: connection opened ok\n"); ret = 0; out: if (ret < 0) { closeconn(); } return ret; } // Same as previous, but get the port number from services int NetconCli::openconn(const char *host, const char *serv, int timeo) { LOGDEB2("Netconcli::openconn: host " << host << ", serv " << serv << "\n"); if (host[0] != '/') { struct servent *sp; if ((sp = getservbyname(serv, "tcp")) == nullptr) { LOGERR("NetconCli::openconn: getservbyname failed for " << serv << "\n"); return -1; } // Callee expects the port number in host byte order return openconn(host, ntohs(sp->s_port), timeo); } else { return openconn(host, (unsigned int)0, timeo); } } int NetconCli::setconn(int fd) { LOGDEB2("Netconcli::setconn: fd " << fd << "\n"); closeconn(); m_fd = fd; m_ownfd = false; setpeer(""); return 0; } /////////////////////////////////////////////////////////////////////// // Methods for the main (listening) server connection NetconServLis::~NetconServLis() { #ifdef NETCON_ACCESSCONTROL freeZ(okaddrs.intarray); freeZ(okmasks.intarray); #endif } #if 0 // code for dumping a struct servent static void dump_servent(struct servent *servp) { fprintf(stderr, "Official name %s\n", servp->s_name); for (char **cpp = servp->s_aliases; *cpp; cpp++) { fprintf(stderr, "Nickname %s\n", *cpp); } fprintf(stderr, "Port %d\n", (int)ntohs((short)servp->s_port)); fprintf(stderr, "Proto %s\n", servp->s_proto); } #endif // Set up service. int NetconServLis::openservice(const char *serv, int backlog) { int port; struct servent *servp; if (!serv) { LOGERR("NetconServLis::openservice: null serv??\n"); return -1; } LOGDEB1("NetconServLis::openservice: serv " << serv << "\n"); #ifdef NETCON_ACCESSCONTROL if (initperms(serv) < 0) { return -1; } #endif m_serv = serv; if (serv[0] != '/') { if ((servp = getservbyname(serv, "tcp")) == nullptr) { LOGERR("NetconServLis::openservice: getservbyname failed for " << serv << "\n"); return -1; } port = (int)ntohs((short)servp->s_port); return openservice(port, backlog); } else { if (strlen(serv) > UNIX_PATH_MAX - 1) { LOGERR("NetconServLis::openservice: too long for AF_UNIX: " << serv << "\n"); return -1; } int ret = -1; struct sockaddr_un addr; if ((m_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { LOGSYSERR("NetconServLis", "socket", ""); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, serv); if (::bind(m_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { LOGSYSERR("NetconServLis", "bind", ""); goto out; } if (listen(m_fd, backlog) < 0) { LOGSYSERR("NetconServLis", "listen", ""); goto out; } LOGDEB1("NetconServLis::openservice: service opened ok\n"); ret = 0; out: if (ret < 0 && m_fd >= 0) { close(m_fd); m_fd = -1; } return ret; } } // Port is a natural host integer value int NetconServLis::openservice(int port, int backlog) { LOGDEB1("NetconServLis::openservice: port " << port << "\n"); #ifdef NETCON_ACCESSCONTROL if (initperms(port) < 0) { return -1; } #endif int ret = -1; struct sockaddr_in ipaddr; if ((m_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { LOGSYSERR("NetconServLis", "socket", ""); return -1; } (void) setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); #ifdef SO_REUSEPORT (void) setsockopt(m_fd, SOL_SOCKET, SO_REUSEPORT, (char *)&one, sizeof(one)); #endif /*SO_REUSEPORT*/ memset(&ipaddr, 0, sizeof(ipaddr)); ipaddr.sin_family = AF_INET; ipaddr.sin_addr.s_addr = htonl(INADDR_ANY); ipaddr.sin_port = htons((short)port); if (::bind(m_fd, (struct sockaddr *)&ipaddr, sizeof(ipaddr)) < 0) { LOGSYSERR("NetconServLis", "bind", ""); goto out; } if (listen(m_fd, backlog) < 0) { LOGSYSERR("NetconServLis", "listen", ""); goto out; } LOGDEB1("NetconServLis::openservice: service opened ok\n"); ret = 0; out: if (ret < 0 && m_fd >= 0) { close(m_fd); m_fd = -1; } return ret; } #ifdef NETCON_ACCESSCONTROL int NetconServLis::initperms(int port) { if (permsinit) { return 0; } char sport[30]; sprintf(sport, "%d", port); return initperms(sport); } // Get authorized address lists from parameter file. This is disabled for now int NetconServLis::initperms(const char *serv) { if (permsinit) { return 0; } if (serv == 0 || *serv == 0 || strlen(serv) > 80) { LOGERR("NetconServLis::initperms: bad service name " << serv << "\n"); return -1; } char keyname[100]; sprintf(keyname, "%s_okaddrs", serv); if (genparams->getparam(keyname, &okaddrs, 1) < 0) { serv = "default"; sprintf(keyname, "%s_okaddrs", serv); if (genparams->getparam(keyname, &okaddrs) < 0) { LOGERR("NetconServLis::initperms: no okaddrs found in config file\n"); return -1; } } sprintf(keyname, "%s_okmasks", serv); if (genparams->getparam(keyname, &okmasks)) { LOGERR("NetconServLis::initperms: okmasks not found\n"); return -1; } if (okaddrs.len == 0 || okmasks.len == 0) { LOGERR("NetconServLis::initperms: len 0 for okmasks or okaddrs\n"); return -1; } permsinit = 1; return 0; } #endif /* NETCON_ACCESSCONTROL */ // Sample cando routine for server master connection: delete newly // accepted connection. What else ? // This is to be overriden by a derived class method for an application // using the selectloop thing int NetconServLis::cando(Netcon::Event reason) { PRETEND_USE(reason); delete accept(); return 1; } NetconServCon * NetconServLis::accept(int timeo) { LOGDEB("NetconServLis::accept\n"); if (timeo > 0) { int ret = select1(m_fd, timeo); if (ret == 0) { LOGDEB2("NetconServLis::accept timed out\n"); m_didtimo = 1; return nullptr; } if (ret < 0) { LOGSYSERR("NetconServLis::accept", "select", ""); return nullptr; } } m_didtimo = 0; NetconServCon *con = nullptr; int newfd = -1; struct sockaddr_in who; struct sockaddr_un uwho; if (m_serv.empty() || m_serv[0] != '/') { SOCKLEN_T clilen = (SOCKLEN_T)sizeof(who); if ((newfd = ::accept(m_fd, (struct sockaddr *)&who, &clilen)) < 0) { LOGSYSERR("NetconServCon::accept", "accept", ""); goto out; } #ifdef NETCON_ACCESSCONTROL if (checkperms(&who, clilen) < 0) { goto out; } #endif } else { SOCKLEN_T clilen = (SOCKLEN_T)sizeof(uwho); if ((newfd = ::accept(m_fd, (struct sockaddr *)&uwho, &clilen)) < 0) { LOGSYSERR("NetconServCon::accept", "accept", ""); goto out; } } con = new NetconServCon(newfd); if (nullptr == con) { LOGERR("NetconServLis::accept: new NetconServCon failed\n"); goto out; } // Retrieve peer's host name. Errors are non fatal if (m_serv.empty() || m_serv[0] != '/') { struct hostent *hp; if ((hp = gethostbyaddr((char *) & (who.sin_addr), sizeof(struct in_addr), AF_INET)) == nullptr) { LOGERR("NetconServLis::accept: gethostbyaddr failed for addr 0x" << who.sin_addr.s_addr << "\n"); con->setpeer(inet_ntoa(who.sin_addr)); } else { con->setpeer(hp->h_name); } } else { con->setpeer(m_serv.c_str()); } LOGDEB2("NetconServLis::accept: setting keepalive\n"); if (setsockopt(newfd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one)) < 0) { LOGSYSERR("NetconServLis::accept", "setsockopt", "KEEPALIVE"); } LOGDEB2("NetconServLis::accept: got connect from " << con->getpeer() << "\n"); out: if (nullptr == con && newfd >= 0) { close(newfd); } return con; } #ifdef NETCON_ACCESSCONTROL int NetconServLis::checkperms(void *cl, int) { // If okmasks and addrs were not initialized, the default is allow to all if (okmasks.len <= 0 || okaddrs.len <= 0) { return 0; } struct sockaddr *addr = (struct sockaddr *)cl; unsigned long ip_addr; if (addr->sa_family != AF_INET) { LOGERR("NetconServLis::checkperms: connection from non-INET addr !\n"); return -1; } ip_addr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); LOGDEB2("checkperms: ip_addr: 0x" << ip_addr << "\n"); for (int i = 0; i < okaddrs.len; i++) { unsigned int mask; if (i < okmasks.len) { mask = okmasks.intarray[i]; } else { mask = okmasks.intarray[okmasks.len - 1]; } LOGDEB2("checkperms: trying okaddr 0x" << okaddrs.intarray[i] << ", mask 0x" << mask << "\n"); if ((ip_addr & mask) == (okaddrs.intarray[i] & mask)) { return (0); } } LOGERR("NetconServLis::checkperm: connection from bad address 0x" << ip_addr << "\n"); return -1; } #endif /* NETCON_ACCESSCONTROL */ recoll-1.36.1/utils/damlev.h0000644000175000017500000000602014474037766012604 00000000000000/* Copyright (C) 2022 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Damerau-Levenshtein distance between two strings. // Implements the algorithm from: // https://en.wikipedia.org/wiki/Damerau–Levenshtein_distance // "Distance with adjacent transpositions" // // The function is usable with regular std::string or any class implementing operator[] and size(), // such as some kind of int array to be used after a conversion to utf-32 (the algorithm will NOT // work with UTF-8 because of the variable length multi-char8 characters). #include #include #include #include namespace MedocUtils { // Two-dimensional array with configurable lower bounds class Mat2 { public: Mat2(int w, int h, int xs = 0, int ys = 0) : m_w(w), m_xs(xs), m_ys(ys) { ds = (int*)malloc(sizeof(int) * w * h); } ~Mat2() { if (ds) free(ds); } int& operator()(int x, int y) { return ds[(y - m_ys) * m_w + (x - m_xs)]; } private: int m_w, m_xs, m_ys; int *ds{nullptr}; }; // https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance#Algorithm template int DLDistance(const T& str1, const T& str2) { // This replaces an array of the size of the alphabet, initialized to 0. std::map da; int size1 = str1.size(); int size2 = str2.size(); Mat2 d(size1 + 2, size2 + 2, -1, -1); int maxdist = size1 + size2; d(-1,-1) = maxdist; for (int x = 0; x <= size1; x++) { d(x, -1) = maxdist; d(x, 0) = x; } for (int y = 0; y <= size2; y++) { d(-1, y) = maxdist; d(0, y) = y; } // The strings in the algo are 1-indexed, so we adjust accessses (e.g. a[x-1]) for (int x = 1; x <= size1; x++) { int db = 0; for (int y = 1; y <= size2; y++) { auto it = da.find(str2[y-1]); int k = it == da.end() ? 0 : da[str2[y-1]]; int l = db; int cost; if (str1[x-1] == str2[y-1]) { cost = 0; db = y; } else { cost = 1; } d(x, y) = std::min({d(x-1, y-1) + cost, d(x, y-1) + 1, d(x-1, y) + 1, d(k-1, l-1) + (x-k-1) + 1 + (y-l-1)}); } da[str1[x-1]] = x; } return d(size1, size2); } } recoll-1.36.1/utils/wipedir.cpp0000644000175000017500000000432114426500174013315 00000000000000/* Copyright (C) 2004-2019 J.F.Dockes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "wipedir.h" #include #include "log.h" #include "pathut.h" int wipedir(const std::string& dir, bool selfalso, bool recurse) { int ret = -1; if (!path_isdir(dir)) { LOGERR("wipedir: " << dir << " not a directory\n"); return -1; } if (!path_access(dir, R_OK|W_OK|X_OK)) { LOGSYSERR("wipedir", "access", dir); return -1; } PathDirContents dc(dir); if (!dc.opendir()) { LOGSYSERR("wipedir", "opendir", dir); return -1; } int remaining = 0; const struct PathDirContents::Entry *ent; while ((ent = dc.readdir()) != nullptr) { const std::string& dname{ent->d_name}; if (dname == "." || dname == "..") continue; std::string fn = path_cat(dir, dname); if (path_isdir(fn)) { if (recurse) { int rr = wipedir(fn, true, true); if (rr == -1) goto out; else remaining += rr; } else { remaining++; } } else { if (!path_unlink(fn)) { LOGSYSERR("wipedir", "unlink", fn); goto out; } } } ret = remaining; if (selfalso && ret == 0) { if (!path_rmdir(dir)) { LOGSYSERR("wipedir", "rmdir", dir); ret = -1; } } out: return ret; } recoll-1.36.1/utils/wipedir.h0000644000175000017500000000211214410615043012751 00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _FILEUT_H_INCLUDED_ #define _FILEUT_H_INCLUDED_ /* Copyright (C) 2004 J.F.Dockes */ #include /** * Remove all files inside directory. * @return 0 if ok, count of remaining entries (ie: subdirs), or -1 for error */ int wipedir(const std::string& dirname, bool topalso = 0, bool recurse = 0); #endif /* _FILEUT_H_INCLUDED_ */ recoll-1.36.1/bincimapmime/0000755000175000017500000000000014521161751012520 500000000000000recoll-1.36.1/bincimapmime/convert.h0000644000175000017500000002171014426500174014272 00000000000000/* -*- mode:c++;c-basic-offset:2 -*- */ /* -------------------------------------------------------------------- * Filename: * src/util/convert.h * * Description: * Declaration of miscellaneous convertion functions. * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #ifndef convert_h_included #define convert_h_included #include #include #include #include #include #include #include #include namespace Binc { //---------------------------------------------------------------------- inline std::string toString(int i_in) { char intbuf[16]; snprintf(intbuf, sizeof(intbuf), "%d", i_in); return std::string(intbuf); } //---------------------------------------------------------------------- inline std::string toString(unsigned int i_in) { char intbuf[16]; snprintf(intbuf, sizeof(intbuf), "%u", i_in); return std::string(intbuf); } //---------------------------------------------------------------------- inline std::string toString(unsigned long i_in) { char longbuf[40]; snprintf(longbuf, sizeof(longbuf), "%lu", i_in); return std::string(longbuf); } //---------------------------------------------------------------------- inline std::string toString(const char *i_in) { return std::string(i_in); } //---------------------------------------------------------------------- inline int atoi(const std::string &s_in) { return ::atoi(s_in.c_str()); } //---------------------------------------------------------------------- inline std::string toHex(const std::string &s) { const char hexchars[] = "0123456789abcdef"; std::string tmp; for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { unsigned char c = (unsigned char)*i; tmp += hexchars[((c & 0xf0) >> 4)]; tmp += hexchars[c & 0x0f]; } return tmp; } //---------------------------------------------------------------------- inline std::string fromHex(const std::string &s) { const char hexchars[] = "0123456789abcdef"; std::string tmp; for (std::string::const_iterator i = s.begin(); i != s.end() && i + 1 != s.end(); i += 2) { ptrdiff_t n; unsigned char c = *i; unsigned char d = *(i + 1); const char *t; if ((t = strchr(hexchars, c)) == nullptr) return "out of range"; n = (t - hexchars) << 4; if ((t = strchr(hexchars, d)) == nullptr) return "out of range"; n += (t - hexchars); if (n >= 0 && n <= 255) tmp += (char) n; else return "out of range"; } return tmp; } //---------------------------------------------------------------------- inline std::string toImapString(const std::string &s_in) { for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { unsigned char c = (unsigned char)*i; if (c <= 31 || c >= 127 || c == '\"' || c == '\\') return "{" + toString((unsigned long)s_in.length()) + "}\r\n" + s_in; } return "\"" + s_in + "\""; } //---------------------------------------------------------------------- inline void uppercase(std::string &input) { for (std::string::iterator i = input.begin(); i != input.end(); ++i) *i = toupper(*i); } //---------------------------------------------------------------------- inline void lowercase(std::string &input) { for (std::string::iterator i = input.begin(); i != input.end(); ++i) *i = tolower(*i); } //---------------------------------------------------------------------- inline void chomp(std::string &s_in, const std::string &chars = " \t\r\n") { std::string::size_type n = s_in.length(); while (n > 1 && chars.find(s_in[n - 1]) != std::string::npos) s_in.resize(n-- - 1); } //---------------------------------------------------------------------- inline void trim(std::string &s_in, const std::string &chars = " \t\r\n") { while (s_in != "" && chars.find(s_in[0]) != std::string::npos) s_in = s_in.substr(1); chomp(s_in, chars); } //---------------------------------------------------------------------- inline const std::string unfold(const std::string &a, bool removecomment = true) { std::string tmp; bool incomment = false; bool inquotes = false; for (std::string::const_iterator i = a.begin(); i != a.end(); ++i) { unsigned char c = (unsigned char)*i; if (!inquotes && removecomment) { if (c == '(') { incomment = true; tmp += " "; } else if (c == ')') { incomment = false; } else if (c != 0x0a && c != 0x0d) { tmp += *i; } } else if (c != 0x0a && c != 0x0d) { tmp += *i; } if (!incomment) { if (*i == '\"') inquotes = !inquotes; } } trim(tmp); return tmp; } //---------------------------------------------------------------------- inline void split(const std::string &s_in, const std::string &delim, std::vector &dest, bool skipempty = true) { std::string token; for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { if (delim.find(*i) != std::string::npos) { if (!skipempty || token != "") dest.push_back(token); token.clear(); } else token += *i; } if (token != "") dest.push_back(token); } //---------------------------------------------------------------------- inline void splitAddr(const std::string &s_in, std::vector &dest, bool skipempty = true) { static const std::string delim = ","; std::string token; bool inquote = false; for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { if (inquote && *i == '\"') inquote = false; else if (!inquote && *i == '\"') inquote = true; if (!inquote && delim.find(*i) != std::string::npos) { if (!skipempty || token != "") dest.push_back(token); token.clear(); } else token += *i; } if (token != "") dest.push_back(token); } //---------------------------------------------------------------------- inline std::string toCanonMailbox(const std::string &s_in) { if (s_in.find("..") != std::string::npos) return std::string(); if (s_in.length() >= 5) { std::string a = s_in.substr(0, 5); uppercase(a); return a == "INBOX" ? a + (s_in.length() > 5 ? s_in.substr(5) : std::string()) : s_in; } return s_in; } //------------------------------------------------------------------------ inline std::string toRegex(const std::string &s_in, char delimiter) { std::string regex = "^"; for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { if (*i == '.' || *i == '[' || *i == ']' || *i == '{' || *i == '}' || *i == '(' || *i == ')' || *i == '^' || *i == '$' || *i == '?' || *i == '+' || *i == '\\') { regex += "\\"; regex += *i; } else if (*i == '*') regex += ".*?"; else if (*i == '%') { regex += "(\\"; regex += delimiter; regex += "){0,1}"; regex += "[^\\"; regex += delimiter; regex += "]*?"; } else regex += *i; } if (regex[regex.length() - 1] == '?') regex[regex.length() - 1] = '$'; else regex += "$"; return regex; } //------------------------------------------------------------------------ class BincStream { private: std::string nstr; public: //-- BincStream &operator << (std::ostream&(*)(std::ostream&)); BincStream &operator << (const std::string &t); BincStream &operator << (unsigned int t); BincStream &operator << (int t); BincStream &operator << (char t); //-- std::string popString(std::string::size_type size); //-- char popChar(void); void unpopChar(char c); void unpopStr(const std::string &s); //-- const std::string &str(void) const; //-- unsigned int getSize(void) const; //-- void clear(void); //-- BincStream(void); ~BincStream(void); }; } #endif recoll-1.36.1/bincimapmime/mime.cc0000644000175000017500000001022614426500174013677 00000000000000/* -*- mode:c++;c-basic-offset:2 -*- */ /* -------------------------------------------------------------------- * Filename: * mime.cc * * Description: * Implementation of main mime parser components * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include "mime.h" #include "convert.h" #include "mime-inputsource.h" using namespace std; //------------------------------------------------------------------------ Binc::MimeDocument::MimeDocument(void) { allIsParsed = false; headerIsParsed = false; doc_mimeSource = nullptr; } //------------------------------------------------------------------------ Binc::MimeDocument::~MimeDocument(void) { delete doc_mimeSource; doc_mimeSource = nullptr; } //------------------------------------------------------------------------ void Binc::MimeDocument::clear(void) { members.clear(); h.clear(); headerIsParsed = false; allIsParsed = false; delete doc_mimeSource; doc_mimeSource = nullptr; } //------------------------------------------------------------------------ void Binc::MimePart::clear(void) { members.clear(); h.clear(); mimeSource = nullptr; } //------------------------------------------------------------------------ Binc::MimePart::MimePart(void) { size = 0; messagerfc822 = false; multipart = false; nlines = 0; nbodylines = 0; mimeSource = nullptr; } //------------------------------------------------------------------------ Binc::MimePart::~MimePart(void) { } //------------------------------------------------------------------------ Binc::HeaderItem::HeaderItem(void) { } //------------------------------------------------------------------------ Binc::HeaderItem::HeaderItem(const string &key, const string &value) { this->key = key; this->value = value; } //------------------------------------------------------------------------ Binc::Header::Header(void) { } //------------------------------------------------------------------------ Binc::Header::~Header(void) { } //------------------------------------------------------------------------ bool Binc::Header::getFirstHeader(const string &key, HeaderItem &dest) const { string k = key; lowercase(k); for (vector::const_iterator i = content.begin(); i != content.end(); ++i) { string tmp = (*i).getKey(); lowercase(tmp); if (tmp == k) { dest = *i; return true; } } return false; } //------------------------------------------------------------------------ bool Binc::Header::getAllHeaders(const string &key, vector &dest) const { string k = key; lowercase(k); for (vector::const_iterator i = content.begin(); i != content.end(); ++i) { string tmp = (*i).getKey(); lowercase(tmp); if (tmp == k) dest.push_back(*i); } return (dest.size() != 0); } //------------------------------------------------------------------------ void Binc::Header::clear(void) { content.clear(); } //------------------------------------------------------------------------ void Binc::Header::add(const string &key, const string &value) { content.push_back(HeaderItem(key, value)); } recoll-1.36.1/bincimapmime/AUTHORS0000644000175000017500000000444714410615043013514 00000000000000The following parties have participated in writing code or otherwise contributed to the Binc IMAP project: Author: Andreas Aardal Hanssen Several users have been very helpful with bug reports and suggestions, and the author is very grateful for their contributions. Some users have also gone to the extra effort of debugging the cause of a bug, or have found a way of implementing a feature, and have either provided a very good description of what is needed, or they have actually provided a patch that has been added to Binc IMAP. While adding extra value to the discussion around the discovery of a bug or the evaluation of a new feature, these contributors also take some load of the author's back, so they deserve extra thanks. In this list are also included people who have contributed with mirrors and translations of the web pages. Henry Baragar Jrgen Botz Charlie Brady Caskey Dickson Ketil Froyn Gary Gordon Marek Gutkowski Daniel James Zak Johnson Sergei Kolobov Rafal Kupka Eivind Kvedalen HIROSHIMA Naoki Greger Stolt Nilsen John Starks Peter Stuge Gerrit Pape Jeremy Rossi Dale Woolridge If you have contributed to the Binc IMAP project but are not listed here (this happens quite often), please send a mail to andreas-binc@bincimap.org and I'll add you to the list. recoll-1.36.1/bincimapmime/mime-printbody.cc0000644000175000017500000000331214410615043015700 00000000000000/* -*- mode:c++;c-basic-offset:2 -*- */ /* -------------------------------------------------------------------- * Filename: * mime-printbody.cc * * Description: * Implementation of main mime parser components * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #include "autoconfig.h" #include "mime.h" #include "mime-utils.h" #include "mime-inputsource.h" #include using namespace ::std; void Binc::MimePart::getBody(string &s, unsigned int startoffset, unsigned int length) const { mimeSource->reset(); mimeSource->seek(bodystartoffsetcrlf + startoffset); s.reserve(length); if (startoffset + length > bodylength) length = bodylength - startoffset; char c = '\0'; for (unsigned int i = 0; i < length; ++i) { if (!mimeSource->getChar(&c)) break; s += (char)c; } } recoll-1.36.1/bincimapmime/convert.cc0000644000175000017500000000672314410615043014432 00000000000000/* -*- mode:c++;c-basic-offset:2 -*- */ /* -------------------------------------------------------------------- * Filename: * convert.cc * * Description: * Implementation of miscellaneous convertion functions. * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #include "convert.h" #include #ifndef NO_NAMESPACES using namespace ::std; using namespace Binc; #endif /* NO_NAMESPACES */ //------------------------------------------------------------------------ BincStream::BincStream(void) { } //------------------------------------------------------------------------ BincStream::~BincStream(void) { clear(); } //------------------------------------------------------------------------ string BincStream::popString(std::string::size_type size) { if (size > nstr.length()) size = nstr.length(); string tmp = nstr.substr(0, size); nstr = nstr.substr(size); return tmp; } //------------------------------------------------------------------------ char BincStream::popChar(void) { if (nstr.length() == 0) return '\0'; char c = nstr[0]; nstr = nstr.substr(1); return c; } //------------------------------------------------------------------------ void BincStream::unpopChar(char c) { nstr = c + nstr; } //------------------------------------------------------------------------ void BincStream::unpopStr(const string &s) { nstr = s + nstr; } //------------------------------------------------------------------------ const string &BincStream::str(void) const { return nstr; } //------------------------------------------------------------------------ void BincStream::clear(void) { nstr.clear(); } //------------------------------------------------------------------------ unsigned int BincStream::getSize(void) const { return (unsigned int) nstr.length(); } //------------------------------------------------------------------------ BincStream &BincStream::operator << (std::ostream&(*)(std::ostream&)) { nstr += "\r\n"; return *this; } //------------------------------------------------------------------------ BincStream &BincStream::operator << (const string &t) { nstr += t; return *this; } //------------------------------------------------------------------------ BincStream &BincStream::operator << (int t) { nstr += toString(t); return *this; } //------------------------------------------------------------------------ BincStream &BincStream::operator << (unsigned int t) { nstr += toString(t); return *this; } //------------------------------------------------------------------------ BincStream &BincStream::operator << (char t) { nstr += t; return *this; } recoll-1.36.1/bincimapmime/COPYING0000644000175000017500000004460514410615043013477 00000000000000This software is released under the GPL. Find a full copy of the GNU General Public License below. In addition, as a special exception, Andreas Aardal Hanssen, author of Binc IMAP, gives permission to link the code of this program with the OpenSSL library (or with modified versions of OpenSSL that use the same license as OpenSSL, listed in the included COPYING.OpenSSL file), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. --------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. recoll-1.36.1/bincimapmime/mime-parseonlyheader.cc0000644000175000017500000001014614410615043017056 00000000000000/* -*- mode:c++;c-basic-offset:2 -*- */ /* -------------------------------------------------------------------- * Filename: * mime-parseonlyheader.cc * * Description: * Implementation of main mime parser components * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #include "autoconfig.h" #include "mime.h" #include "mime-utils.h" #include "mime-inputsource.h" #include "convert.h" #include #include #include #include #include #include #include #include #include using namespace std; //------------------------------------------------------------------------ void Binc::MimeDocument::parseOnlyHeader(int fd) { if (allIsParsed || headerIsParsed) return; headerIsParsed = true; delete doc_mimeSource; doc_mimeSource = new MimeInputSource(fd); headerstartoffsetcrlf = 0; headerlength = 0; bodystartoffsetcrlf = 0; bodylength = 0; messagerfc822 = false; multipart = false; nlines = 0; nbodylines = 0; doParseOnlyHeader(doc_mimeSource); } void Binc::MimeDocument::parseOnlyHeader(istream& s) { if (allIsParsed || headerIsParsed) return; headerIsParsed = true; delete doc_mimeSource; doc_mimeSource = new MimeInputSourceStream(s); headerstartoffsetcrlf = 0; headerlength = 0; bodystartoffsetcrlf = 0; bodylength = 0; messagerfc822 = false; multipart = false; nlines = 0; nbodylines = 0; doParseOnlyHeader(doc_mimeSource); } //------------------------------------------------------------------------ int Binc::MimePart::doParseOnlyHeader(MimeInputSource *ms) { mimeSource = ms; string name; string content; char cqueue[4]; memset(cqueue, 0, sizeof(cqueue)); headerstartoffsetcrlf = mimeSource->getOffset(); bool quit = false; char c = '\0'; while (!quit) { // read name while (1) { if (!mimeSource->getChar(&c)) { quit = true; break; } if (c == '\n') ++nlines; if (c == ':') break; if (c == '\n') { for (int i = int(name.length()) - 1; i >= 0; --i) mimeSource->ungetChar(); quit = true; name.clear(); break; } name += c; if (name.length() == 2 && name.substr(0, 2) == "\r\n") { name.clear(); quit = true; break; } } if (name.length() == 1 && name[0] == '\r') { name.clear(); break; } if (quit) break; while (!quit) { if (!mimeSource->getChar(&c)) { quit = true; break; } if (c == '\n') ++nlines; for (int i = 0; i < 3; ++i) cqueue[i] = cqueue[i + 1]; cqueue[3] = c; if (strncmp(cqueue, "\r\n\r\n", 4) == 0) { quit = true; break; } if (cqueue[2] == '\n') { // guess the mime rfc says what can not appear on the beginning // of a line. if (!isspace(cqueue[3])) { if (content.length() > 2) content.resize(content.length() - 2); trim(content); h.add(name, content); name = c; content.clear(); break; } } content += c; } } if (name != "") { if (content.length() > 2) content.resize(content.length() - 2); h.add(name, content); } headerlength = mimeSource->getOffset() - headerstartoffsetcrlf; return 1; } recoll-1.36.1/bincimapmime/mime-inputsource.h0000644000175000017500000001304114410615043016110 00000000000000/* -------------------------------------------------------------------- * Filename: * src/mime-inputsource.h * * Description: * The base class of the MIME input source * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #ifndef mime_inputsource_h_included #define mime_inputsource_h_included // Data source for MIME parser // Note about large files: we might want to change the unsigned int // used for offsets into an off_t for intellectual satisfaction, but // in the context of recoll, we could only get into trouble if a // *single message* exceeded 2GB, which seems rather unlikely. When // parsing a mailbox files, we read each message in memory and use the // stream input source (from a memory buffer, no file offsets). When // parsing a raw message file, it's only one message. #include #include "safeunistd.h" #include namespace Binc { class MimeInputSource { public: // Note that we do NOT take ownership of fd, won't close it on delete inline MimeInputSource(int fd, unsigned int start = 0); virtual inline ~MimeInputSource(void); virtual inline ssize_t fillRaw(char *raw, size_t nbytes); virtual inline void reset(void); virtual inline bool fillInputBuffer(void); inline void seek(unsigned int offset); inline bool getChar(char *c); inline void ungetChar(void); inline int getFileDescriptor(void) const; inline unsigned int getOffset(void) const; private: int fd; char data[16384]; unsigned int offset; unsigned int tail; unsigned int head; unsigned int start; char lastChar; }; inline MimeInputSource::MimeInputSource(int fd, unsigned int start) { this->fd = fd; this->start = start; offset = 0; tail = 0; head = 0; lastChar = '\0'; memset(data, '\0', sizeof(data)); seek(start); } inline MimeInputSource::~MimeInputSource(void) { } inline ssize_t MimeInputSource::fillRaw(char *raw, size_t nbytes) { return read(fd, raw, nbytes); } inline bool MimeInputSource::fillInputBuffer(void) { char raw[4096]; ssize_t nbytes = fillRaw(raw, 4096); if (nbytes <= 0) { // FIXME: If ferror(crlffile) we should log this. return false; } for (ssize_t i = 0; i < nbytes; ++i) { const char c = raw[i]; if (c == '\r') { if (lastChar == '\r') { data[tail++ & (0x4000-1)] = '\r'; data[tail++ & (0x4000-1)] = '\n'; } } else if (c == '\n') { data[tail++ & (0x4000-1)] = '\r'; data[tail++ & (0x4000-1)] = '\n'; } else { if (lastChar == '\r') { data[tail++ & (0x4000-1)] = '\r'; data[tail++ & (0x4000-1)] = '\n'; } data[tail++ & (0x4000-1)] = c; } lastChar = c; } return true; } inline void MimeInputSource::reset(void) { offset = head = tail = 0; lastChar = '\0'; if (fd != -1) lseek(fd, 0, SEEK_SET); } inline void MimeInputSource::seek(unsigned int seekToOffset) { if (offset > seekToOffset) reset(); char c; int n = 0; while (seekToOffset > offset) { if (!getChar(&c)) break; ++n; } } inline bool MimeInputSource::getChar(char *c) { if (head == tail && !fillInputBuffer()) return false; *c = data[head++ & (0x4000-1)]; ++offset; return true; } inline void MimeInputSource::ungetChar() { --head; --offset; } inline int MimeInputSource::getFileDescriptor(void) const { return fd; } inline unsigned int MimeInputSource::getOffset(void) const { return offset; } /////////////////////////////////// class MimeInputSourceStream : public MimeInputSource { public: inline MimeInputSourceStream(std::istream& s, unsigned int start = 0); virtual inline ssize_t fillRaw(char *raw, size_t nb); virtual inline void reset(void); private: std::istream& s; }; inline MimeInputSourceStream::MimeInputSourceStream(std::istream& si, unsigned int start) : MimeInputSource(-1, start), s(si) { } inline ssize_t MimeInputSourceStream::fillRaw(char *raw, size_t nb) { // Why can't streams tell how many characters were actually read // when hitting eof ? std::streampos st = s.tellg(); s.seekg(0, std::ios::end); std::streampos lst = s.tellg(); s.seekg(st); size_t nbytes = size_t(lst - st); if (nbytes > nb) { nbytes = nb; } if (nbytes <= 0) { return (ssize_t)-1; } s.read(raw, nbytes); return static_cast(nbytes); } inline void MimeInputSourceStream::reset(void) { MimeInputSource::reset(); s.seekg(0); } } #endif recoll-1.36.1/bincimapmime/mime-utils.h0000644000175000017500000000311514410615043014671 00000000000000/* -*- mode:c++;c-basic-offset:2 -*- */ /* -------------------------------------------------------------------- * Filename: * mime.cc * * Description: * Implementation of main mime parser components * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #ifndef mime_utils_h_included #define mime_utils_h_included #include #include #include #include #ifndef NO_NAMESPACES using namespace ::std; #endif /* NO_NAMESPACES */ inline bool compareStringToQueue(const char *s_in, char *bqueue, int pos, int size) { for (int i = 0; i < size; ++i) { if (s_in[i] != bqueue[pos]) return false; if (++pos == size) pos = 0; } return true; } #endif recoll-1.36.1/bincimapmime/00README.recoll0000644000175000017500000000016314410615043014732 00000000000000Most of the code in this directory was taken from the Binc IMAP project (http://www.bincimap.org/), version 1.3.3 recoll-1.36.1/bincimapmime/mime-parsefull.cc0000644000175000017500000004220014426500174015667 00000000000000 /* -*- mode:c++;c-basic-offset:2 -*- */ /* -------------------------------------------------------------------- * Filename: * mime-parsefull.cc * * Description: * Implementation of main mime parser components * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include "mime.h" #include "mime-utils.h" #include "mime-inputsource.h" #include "convert.h" using namespace std; // #define MPF #ifdef MPF #define MPFDEB(X) fprintf X #else #define MPFDEB(X) #endif //------------------------------------------------------------------------ void Binc::MimeDocument::parseFull(int fd) { if (allIsParsed) return; allIsParsed = true; delete doc_mimeSource; doc_mimeSource = new MimeInputSource(fd); headerstartoffsetcrlf = 0; headerlength = 0; bodystartoffsetcrlf = 0; bodylength = 0; size = 0; messagerfc822 = false; multipart = false; int bsize = 0; string bound; doParseFull(doc_mimeSource, bound, bsize); // eat any trailing junk to get the correct size char c; while (doc_mimeSource->getChar(&c)); size = doc_mimeSource->getOffset(); } void Binc::MimeDocument::parseFull(istream& s) { if (allIsParsed) return; allIsParsed = true; delete doc_mimeSource; doc_mimeSource = new MimeInputSourceStream(s); headerstartoffsetcrlf = 0; headerlength = 0; bodystartoffsetcrlf = 0; bodylength = 0; size = 0; messagerfc822 = false; multipart = false; int bsize = 0; string bound; doParseFull(doc_mimeSource, bound, bsize); // eat any trailing junk to get the correct size char c; while (doc_mimeSource->getChar(&c)); size = doc_mimeSource->getOffset(); } //------------------------------------------------------------------------ bool Binc::MimePart::parseOneHeaderLine(Binc::Header *header, unsigned int *nlines) { using namespace ::Binc; char c; bool eof = false; char cqueue[4]; string name; string content; while (mimeSource->getChar(&c)) { // If we encounter a \r before we got to the first ':', then // rewind back to the start of the line and assume we're at the // start of the body. if (c == '\r') { for (int i = 0; i < (int) name.length() + 1; ++i) mimeSource->ungetChar(); return false; } // A colon marks the end of the header name if (c == ':') break; // Otherwise add to the header name name += c; } cqueue[0] = '\0'; cqueue[1] = '\0'; cqueue[2] = '\0'; cqueue[3] = '\0'; // Read until the end of the header. bool endOfHeaders = false; while (!endOfHeaders) { if (!mimeSource->getChar(&c)) { eof = true; break; } if (c == '\n') ++*nlines; for (int i = 0; i < 3; ++i) cqueue[i] = cqueue[i + 1]; cqueue[3] = c; if (strncmp(cqueue, "\r\n\r\n", 4) == 0) { endOfHeaders = true; break; } // If the last character was a newline, and the first now is not // whitespace, then rewind one character and store the current // key,value pair. if (cqueue[2] == '\n' && c != ' ' && c != '\t') { if (content.length() > 2) content.resize(content.length() - 2); trim(content); header->add(name, content); if (c != '\r') { mimeSource->ungetChar(); if (c == '\n') --*nlines; return true; } mimeSource->getChar(&c); return false; } content += c; } if (name != "") { if (content.length() > 2) content.resize(content.length() - 2); header->add(name, content); } return !(eof || endOfHeaders); } //------------------------------------------------------------------------ void Binc::MimePart::parseHeader(Binc::Header *header, unsigned int *nlines) { while (parseOneHeaderLine(header, nlines)) { } } //------------------------------------------------------------------------ void Binc::MimePart::analyzeHeader(Binc::Header *header, bool *multipart, bool *messagerfc822, string *subtype, string *boundary) { using namespace ::Binc; // Do simple parsing of headers to determine the // type of message (multipart,messagerfc822 etc) HeaderItem ctype; if (header->getFirstHeader("content-type", ctype)) { vector types; split(ctype.getValue(), ";", types); if (types.size() > 0) { // first element should describe content type string tmp = types[0]; trim(tmp); vector v; split(tmp, "/", v); string key, value; key = (v.size() > 0) ? v[0] : "text"; value = (v.size() > 1) ? v[1] : "plain"; lowercase(key); if (key == "multipart") { *multipart = true; lowercase(value); *subtype = value; } else if (key == "message") { lowercase(value); if (value == "rfc822") *messagerfc822 = true; } } for (vector::const_iterator i = types.begin(); i != types.end(); ++i) { string element = *i; trim(element); if (element.find("=") != string::npos) { string::size_type pos = element.find('='); string key = element.substr(0, pos); string value = element.substr(pos + 1); lowercase(key); trim(key); if (key == "boundary") { trim(value, " \""); *boundary = value; } } } } } void Binc::MimePart::parseMessageRFC822(vector *members, bool *foundendofpart, unsigned int *bodylength, unsigned int *nbodylines, const string &toboundary) { using namespace ::Binc; // message rfc822 means a completely enclosed mime document. we // call the parser recursively, and pass on the boundary string // that we got. when parse() finds this boundary, it returns 0. if // it finds the end boundary (boundary + "--"), it returns != 0. MimePart m; unsigned int bodystartoffsetcrlf = mimeSource->getOffset(); // parsefull returns the number of bytes that need to be removed // from the body because of the terminating boundary string. int bsize = 0; if (m.doParseFull(mimeSource, toboundary, bsize)) *foundendofpart = true; // make sure bodylength doesn't overflow *bodylength = mimeSource->getOffset(); if (*bodylength >= bodystartoffsetcrlf) { *bodylength -= bodystartoffsetcrlf; if (*bodylength >= (unsigned int) bsize) { *bodylength -= (unsigned int) bsize; } else { *bodylength = 0; } } else { *bodylength = 0; } *nbodylines += m.getNofLines(); members->push_back(m); } bool Binc::MimePart::skipUntilBoundary(const string &delimiter, unsigned int *nlines, bool *eof) { string::size_type endpos = delimiter.length(); char *delimiterqueue = nullptr; string::size_type delimiterpos = 0; const char *delimiterStr = delimiter.c_str(); if (delimiter != "") { delimiterqueue = new char[endpos]; memset(delimiterqueue, 0, endpos); } // first, skip to the first delimiter string. Anything between the // header and the first delimiter string is simply ignored (it's // usually a text message intended for non-mime clients) char c; bool foundBoundary = false; for (;;) { if (!mimeSource->getChar(&c)) { *eof = true; break; } if (c == '\n') ++*nlines; // if there is no delimiter, we just read until the end of the // file. if (!delimiterqueue) continue; delimiterqueue[delimiterpos++] = c; if (delimiterpos == endpos) delimiterpos = 0; if (compareStringToQueue(delimiterStr, delimiterqueue, delimiterpos, int(endpos))) { foundBoundary = true; break; } } delete [] delimiterqueue; delimiterqueue = nullptr; return foundBoundary; } // JFD: Things we do after finding a boundary (something like CRLF--somestring) // Need to see if this is a final one (with an additional -- at the end), // and need to check if it is immediately followed by another boundary // (in this case, we give up our final CRLF in its favour) inline void Binc::MimePart::postBoundaryProcessing(bool *eof, unsigned int *nlines, int *boundarysize, bool *foundendofpart) { // Read two more characters. This may be CRLF, it may be "--" and // it may be any other two characters. char a = '\0'; if (!mimeSource->getChar(&a)) *eof = true; if (a == '\n') ++*nlines; char b = '\0'; if (!mimeSource->getChar(&b)) *eof = true; if (b == '\n') ++*nlines; // If eof, we're done here if (*eof) return; // If we find two dashes after the boundary, then this is the end // of boundary marker, and we need to get 2 more chars if (a == '-' && b == '-') { *foundendofpart = true; *boundarysize += 2; if (!mimeSource->getChar(&a)) *eof = true; if (a == '\n') ++*nlines; if (!mimeSource->getChar(&b)) *eof = true; if (b == '\n') ++*nlines; } // If the boundary is followed by CRLF, we need to handle the // special case where another boundary line follows // immediately. In this case we consider the CRLF to be part of // the NEXT boundary. if (a == '\r' && b == '\n') { // Get 2 more if (!mimeSource->getChar(&a) || !mimeSource->getChar(&b)) { *eof = true; } else if (a == '-' && b == '-') { MPFDEB((stderr, "BINC: consecutive delimiters, giving up CRLF\n")); mimeSource->ungetChar(); mimeSource->ungetChar(); mimeSource->ungetChar(); mimeSource->ungetChar(); } else { // We unget the 2 chars, and keep our crlf (increasing our own size) MPFDEB((stderr, "BINC: keeping my CRLF\n")); mimeSource->ungetChar(); mimeSource->ungetChar(); *boundarysize += 2; } } else { // Boundary string not followed by CRLF, don't read more and let // others skip the rest. Note that this is allowed but quite uncommon mimeSource->ungetChar(); mimeSource->ungetChar(); } } void Binc::MimePart::parseMultipart(const string &boundary, const string &toboundary, bool *eof, unsigned int *nlines, int *boundarysize, bool *foundendofpart, unsigned int *bodylength, vector *members) { MPFDEB((stderr, "BINC: ParseMultipart: boundary [%s], toboundary[%s]\n", boundary.c_str(), toboundary.c_str())); using namespace ::Binc; unsigned int bodystartoffsetcrlf = mimeSource->getOffset(); // multipart parsing starts with skipping to the first // boundary. then we call parse() for all parts. the last parse() // command will return a code indicating that it found the last // boundary of this multipart. Note that the first boundary does // not have to start with CRLF. string delimiter = "--" + boundary; skipUntilBoundary(delimiter, nlines, eof); if (!eof) *boundarysize = int(delimiter.size()); postBoundaryProcessing(eof, nlines, boundarysize, foundendofpart); // read all mime parts. if (!*foundendofpart && !*eof) { bool quit = false; do { MimePart m; // If parseFull returns != 0, then it encountered the multipart's // final boundary. int bsize = 0; if (m.doParseFull(mimeSource, boundary, bsize)) { quit = true; *boundarysize = bsize; } members->push_back(m); } while (!quit); } if (!*foundendofpart && !*eof) { // multipart parsing starts with skipping to the first // boundary. then we call parse() for all parts. the last parse() // command will return a code indicating that it found the last // boundary of this multipart. Note that the first boundary does // not have to start with CRLF. string delimiter = "\r\n--" + toboundary; skipUntilBoundary(delimiter, nlines, eof); if (!*eof) *boundarysize = int(delimiter.size()); postBoundaryProcessing(eof, nlines, boundarysize, foundendofpart); } // make sure bodylength doesn't overflow *bodylength = mimeSource->getOffset(); if (*bodylength >= bodystartoffsetcrlf) { *bodylength -= bodystartoffsetcrlf; if (*bodylength >= (unsigned int) *boundarysize) { *bodylength -= (unsigned int) *boundarysize; } else { *bodylength = 0; } } else { *bodylength = 0; } MPFDEB((stderr, "BINC: ParseMultipart return\n")); } void Binc::MimePart::parseSinglePart(const string &toboundary, int *boundarysize, unsigned int *nbodylines, unsigned int *nlines, bool *eof, bool *foundendofpart, unsigned int *bodylength) { MPFDEB((stderr, "BINC: parseSinglePart, boundary [%s]\n", toboundary.c_str())); using namespace ::Binc; unsigned int bodystartoffsetcrlf = mimeSource->getOffset(); // If toboundary is empty, then we read until the end of the // file. Otherwise we will read until we encounter toboundary. string _toboundary; if (toboundary != "") { _toboundary = "\r\n--"; _toboundary += toboundary; } // if (skipUntilBoundary(_toboundary, nlines, eof)) // *boundarysize = _toboundary.length(); char *boundaryqueue = nullptr; size_t endpos = _toboundary.length(); if (toboundary != "") { boundaryqueue = new char[endpos]; memset(boundaryqueue, 0, endpos); } *boundarysize = 0; const char *_toboundaryStr = _toboundary.c_str(); string line; bool toboundaryIsEmpty = (toboundary == ""); char c; string::size_type boundarypos = 0; while (mimeSource->getChar(&c)) { if (c == '\n') { ++*nbodylines; ++*nlines; } if (toboundaryIsEmpty) continue; // find boundary boundaryqueue[boundarypos++] = c; if (boundarypos == endpos) boundarypos = 0; if (compareStringToQueue(_toboundaryStr, boundaryqueue, boundarypos, int(endpos))) { *boundarysize = static_cast(_toboundary.length()); break; } } delete [] boundaryqueue; if (toboundary != "") { postBoundaryProcessing(eof, nlines, boundarysize, foundendofpart); } else { // Recoll: in the case of a multipart body with a null // boundary (probably illegal but wtf), eof was not set and // multipart went into a loop until bad alloc. *eof = true; } // make sure bodylength doesn't overflow *bodylength = mimeSource->getOffset(); if (*bodylength >= bodystartoffsetcrlf) { *bodylength -= bodystartoffsetcrlf; if (*bodylength >= (unsigned int) *boundarysize) { *bodylength -= (unsigned int) *boundarysize; } else { *bodylength = 0; } } else { *bodylength = 0; } MPFDEB((stderr, "BINC: parseSimple ret: bodylength %d, boundarysize %d\n", *bodylength, *boundarysize)); } //------------------------------------------------------------------------ int Binc::MimePart::doParseFull(MimeInputSource *ms, const string &toboundary, int &boundarysize) { MPFDEB((stderr, "BINC: doParsefull, toboundary[%s]\n", toboundary.c_str())); mimeSource = ms; headerstartoffsetcrlf = mimeSource->getOffset(); // Parse the header of this mime part. parseHeader(&h, &nlines); // Headerlength includes the seperating CRLF. Body starts after the // CRLF. headerlength = mimeSource->getOffset() - headerstartoffsetcrlf; bodystartoffsetcrlf = mimeSource->getOffset(); MPFDEB((stderr, "BINC: doParsefull, bodystartoffsetcrlf %d\n", bodystartoffsetcrlf)); bodylength = 0; // Determine the type of mime part by looking at fields in the // header. analyzeHeader(&h, &multipart, &messagerfc822, &subtype, &boundary); bool eof = false; bool foundendofpart = false; if (messagerfc822) { parseMessageRFC822(&members, &foundendofpart, &bodylength, &nbodylines, toboundary); } else if (multipart) { parseMultipart(boundary, toboundary, &eof, &nlines, &boundarysize, &foundendofpart, &bodylength, &members); } else { parseSinglePart(toboundary, &boundarysize, &nbodylines, &nlines, &eof, &foundendofpart, &bodylength); } MPFDEB((stderr, "BINC: doParsefull ret, toboundary[%s]\n", toboundary.c_str())); return (eof || foundendofpart) ? 1 : 0; } recoll-1.36.1/bincimapmime/mime.h0000644000175000017500000001411314410615043013533 00000000000000/* -------------------------------------------------------------------- * Filename: * src/parsers/mime/mime.h * * Description: * Declaration of main mime parser components * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -------------------------------------------------------------------- */ #ifndef mime_h_included #define mime_h_included #include #include #include #include namespace Binc { class MimeInputSource; //---------------------------------------------------------------------- class HeaderItem { private: mutable std::string key; mutable std::string value; public: inline const std::string &getKey(void) const { return key; } inline const std::string &getValue(void) const { return value; } //-- HeaderItem(void); HeaderItem(const std::string &key, const std::string &value); }; //---------------------------------------------------------------------- class Header { private: mutable std::vector content; public: bool getFirstHeader(const std::string &key, HeaderItem &dest) const; bool getAllHeaders(const std::string &key, std::vector &dest) const; void add(const std::string &name, const std::string &content); void clear(void); //-- Header(void); ~Header(void); }; //---------------------------------------------------------------------- class IODevice; class MimeDocument; class MimePart { protected: public: mutable bool multipart; mutable bool messagerfc822; mutable std::string subtype; mutable std::string boundary; mutable unsigned int headerstartoffsetcrlf; mutable unsigned int headerlength; mutable unsigned int bodystartoffsetcrlf; mutable unsigned int bodylength; mutable unsigned int nlines; mutable unsigned int nbodylines; mutable unsigned int size; public: enum FetchType { FetchBody, FetchHeader, FetchMime }; mutable Header h; mutable std::vector members; inline const std::string &getSubType(void) const { return subtype; } inline bool isMultipart(void) const { return multipart; } inline bool isMessageRFC822(void) const { return messagerfc822; } inline unsigned int getSize(void) const { return bodylength; } inline unsigned int getNofLines(void) const { return nlines; } inline unsigned int getNofBodyLines(void) const { return nbodylines; } inline unsigned int getBodyLength(void) const { return bodylength; } inline unsigned int getBodyStartOffset(void) const { return bodystartoffsetcrlf; } void printBody(Binc::IODevice &output, unsigned int startoffset, unsigned int length) const; void getBody(std::string& s, unsigned int startoffset, unsigned int length) const; virtual void clear(void); virtual int doParseOnlyHeader(MimeInputSource *ms); virtual int doParseFull(MimeInputSource *ms, const std::string &toboundary, int &boundarysize); MimePart(void); virtual ~MimePart(void); private: MimeInputSource *mimeSource; bool parseOneHeaderLine(Binc::Header *header, unsigned int *nlines); bool skipUntilBoundary(const std::string &delimiter, unsigned int *nlines, bool *eof); inline void postBoundaryProcessing(bool *eof, unsigned int *nlines, int *boundarysize, bool *foundendofpart); void parseMultipart(const std::string &boundary, const std::string &toboundary, bool *eof, unsigned int *nlines, int *boundarysize, bool *foundendofpart, unsigned int *bodylength, std::vector *members); void parseSinglePart(const std::string &toboundary, int *boundarysize, unsigned int *nbodylines, unsigned int *nlines, bool *eof, bool *foundendofpart, unsigned int *bodylength); void parseHeader(Binc::Header *header, unsigned int *nlines); void analyzeHeader(Binc::Header *header, bool *multipart, bool *messagerfc822, std::string *subtype, std::string *boundary); void parseMessageRFC822(std::vector *members, bool *foundendofpart, unsigned int *bodylength, unsigned int *nbodylines, const std::string &toboundary); }; //---------------------------------------------------------------------- class MimeDocument : public MimePart { public: MimeDocument(void); ~MimeDocument(void); void parseOnlyHeader(int fd); void parseFull(int fd); void parseOnlyHeader(std::istream& s); void parseFull(std::istream& s); void clear(void); bool isHeaderParsed(void) const { return headerIsParsed; } bool isAllParsed(void) const { return allIsParsed; } private: bool headerIsParsed; bool allIsParsed; MimeInputSource *doc_mimeSource; }; }; #endif recoll-1.36.1/INSTALL0000644000175000017500000016160314410615043011041 00000000000000 More documentation can be found in the doc/ directory or at http://www.recoll.org Link: home: Recoll user manual Link: up: Recoll user manual Link: prev: 4.3. API Link: next: 5.2. Supporting packages Chapter 5. Installation and configuration Prev Next ---------------------------------------------------------------------- Chapter 5. Installation and configuration 5.1. Installing a binary copy Recoll binary copies are always distributed as regular packages for your system. They can be obtained either through the system's normal software distribution framework (e.g. Debian/Ubuntu apt, FreeBSD ports, etc.), or from some type of "backports" repository providing versions newer than the standard ones, or found on the Recoll WEB site in some cases. There used to exist another form of binary install, as pre-compiled source trees, but these are just less convenient than the packages and don't exist any more. The package management tools will usually automatically deal with hard dependencies for packages obtained from a proper package repository. You will have to deal with them by hand for downloaded packages (for example, when dpkg complains about missing dependencies). In all cases, you will have to check or install supporting applications for the file types that you want to index beyond those that are natively processed by Recoll (text, HTML, email files, and a few others). You should also maybe have a look at the configuration section (but this may not be necessary for a quick test with default parameters). Most parameters can be more conveniently set from the GUI interface. ---------------------------------------------------------------------- Prev Next 4.3. API Home 5.2. Supporting packages Link: home: Recoll user manual Link: up: Chapter 5. Installation and configuration Link: prev: Chapter 5. Installation and configuration Link: next: 5.3. Building from source 5.2. Supporting packages Prev Chapter 5. Installation and configuration Next ---------------------------------------------------------------------- 5.2. Supporting packages Recoll uses external applications to index some file types. You need to install them for the file types that you wish to have indexed (these are run-time optional dependencies. None is needed for building or running Recoll except for indexing their specific file type). After an indexing pass, the commands that were found missing can be displayed from the recoll File menu. The list is stored in the missing text file inside the configuration directory. A list of common file types which need external commands follows. Many of the handlers need the iconv command, which is not always listed as a dependency. Please note that, due to the relatively dynamic nature of this information, the most up to date version is now kept on http://www.recoll.org/features.html along with links to the home pages or best source/patches pages, and misc tips. The list below is not updated often and may be quite stale. For many Linux distributions, most of the commands listed can be installed from the package repositories. However, the packages are sometimes outdated, or not the best version for Recoll, so you should take a look at http://www.recoll.org/features.html if a file type is important to you. As of Recoll release 1.14, a number of XML-based formats that were handled by ad hoc handler code now use the xsltproc command, which usually comes with libxslt. These are: abiword, fb2 (ebooks), kword, openoffice, svg. Now for the list: o Openoffice files need unzip and xsltproc. o PDF files need pdftotext which is part of Poppler (usually comes with the poppler-utils package). Avoid the original one from Xpdf. o Postscript files need pstotext. The original version has an issue with shell character in file names, which is corrected in recent packages. See http://www.recoll.org/features.html for more detail. o MS Word needs antiword. It is also useful to have wvWare installed as it may be be used as a fallback for some files which antiword does not handle. o MS Excel and PowerPoint are processed by internal Python handlers. o MS Open XML (docx) needs xsltproc. o Wordperfect files need wpd2html from the libwpd (or libwpd-tools on Ubuntu) package. o RTF files need unrtf, which, in its older versions, has much trouble with non-western character sets. Many Linux distributions carry outdated unrtf versions. Check http://www.recoll.org/features.html for details. o TeX files need untex or detex. Check http://www.recoll.org/features.html for sources if it's not packaged for your distribution. o dvi files need dvips. o djvu files need djvutxt and djvused from the DjVuLibre package. o Audio files: Recoll releases 1.14 and later use a single Python handler based on mutagen for all audio file types. o Pictures: Recoll uses the Exiftool Perl package to extract tag information. Most image file formats are supported. Note that there may not be much interest in indexing the technical tags (image size, aperture, etc.). This is only of interest if you store personal tags or textual descriptions inside the image files. o chm: files in Microsoft help format need Python and the pychm module (which needs chmlib). o ICS: up to Recoll 1.13, iCalendar files need Python and the icalendar module. icalendar is not needed for newer versions, which use internal code. o Zip archives need Python (and the standard zipfile module). o Rar archives need Python, the rarfile Python module and the unrar utility. o Midi karaoke files need Python and the Midi module o Konqueror webarchive format with Python (uses the Tarfile module). o Mimehtml web archive format (support based on the email handler, which introduces some mild weirdness, but still usable). Text, HTML, email folders, and Scribus files are processed internally. Lyx is used to index Lyx files. Many handlers need iconv and the standard sed and awk. ---------------------------------------------------------------------- Prev Up Next Chapter 5. Installation and configuration Home 5.3. Building from source Link: home: Recoll user manual Link: up: Chapter 5. Installation and configuration Link: prev: 5.2. Supporting packages Link: next: 5.4. Configuration overview 5.3. Building from source Prev Chapter 5. Installation and configuration Next ---------------------------------------------------------------------- 5.3. Building from source 5.3.1. Prerequisites If you can install any or all of the following through the package manager for your system, all the better. Especially Qt is a very big piece of software, but you will most probably be able to find a binary package. You may have to compile Xapian but this is easy. The shopping list: o C++ compiler. Up to Recoll version 1.13.04, its absence can manifest itself by strange messages about a missing iconv_open. o Development files for Xapian core. Important If you are building Xapian for an older CPU (before Pentium 4 or Athlon 64), you need to add the --disable-sse flag to the configure command. Else all Xapian application will crash with an illegal instruction error. o Development files for Qt 4 . Recoll has not been tested with Qt 5 yet. Recoll 1.15.9 was the last version to support Qt 3. If you do not want to install or build the Qt Webkit module, Recoll has a configuration option to disable its use (see further). o Development files for X11 and zlib. o You may also need libiconv. On Linux systems, the iconv interface is part of libc and you should not need to do anything special. Check the Recoll download page for up to date version information. 5.3.2. Building Recoll has been built on Linux, FreeBSD, Mac OS X, and Solaris, most versions after 2005 should be ok, maybe some older ones too (Solaris 8 is ok). If you build on another system, and need to modify things, I would very much welcome patches. Configure options: o --without-aspell will disable the code for phonetic matching of search terms. o --with-fam or --with-inotify will enable the code for real time indexing. Inotify support is enabled by default on recent Linux systems. o --with-qzeitgeist will enable sending Zeitgeist events about the visited search results, and needs the qzeitgeist package. o --disable-webkit is available from version 1.17 to implement the result list with a Qt QTextBrowser instead of a WebKit widget if you do not or can't depend on the latter. o --disable-idxthreads is available from version 1.19 to suppress multithreading inside the indexing process. You can also use the run-time configuration to restrict recollindex to using a single thread, but the compile-time option may disable a few more unused locks. This only applies to the use of multithreading for the core index processing (data input). The Recoll monitor mode always uses at least two threads of execution. o --disable-python-module will avoid building the Python module. o --disable-xattr will prevent fetching data from file extended attributes. Beyond a few standard attributes, fetching extended attributes data can only be useful is some application stores data in there, and also needs some simple configuration (see comments in the fields configuration file). o --enable-camelcase will enable splitting camelCase words. This is not enabled by default as it has the unfortunate side-effect of making some phrase searches quite confusing: ie, "MySQL manual" would be matched by "MySQL manual" and "my sql manual" but not "mysql manual" (only inside phrase searches). o --with-file-command Specify the version of the 'file' command to use (ie: --with-file-command=/usr/local/bin/file). Can be useful to enable the gnu version on systems where the native one is bad. o --disable-qtgui Disable the Qt interface. Will allow building the indexer and the command line search program in absence of a Qt environment. o --disable-x11mon Disable X11 connection monitoring inside recollindex. Together with --disable-qtgui, this allows building recoll without Qt and X11. o --disable-pic will compile Recoll with position-dependant code. This is incompatible with building the KIO or the Python or PHP extensions, but might yield very marginally faster code. o Of course the usual autoconf configure options, like --prefix apply. Normal procedure: cd recoll-xxx ./configure make (practices usual hardship-repelling invocations) There is little auto-configuration. The configure script will mainly link one of the system-specific files in the mk directory to mk/sysconf. If your system is not known yet, it will tell you as much, and you may want to manually copy and modify one of the existing files (the new file name should be the output of uname -s). 5.3.2.1. Building on Solaris We did not test building the GUI on Solaris for recent versions. You will need at least Qt 4.4. There are some hints on an old web site page, they may still be valid. Someone did test the 1.19 indexer and Python module build, they do work, with a few minor glitches. Be sure to use GNU make and install. 5.3.3. Installation Either type make install or execute recollinstall prefix, in the root of the source tree. This will copy the commands to prefix/bin and the sample configuration files, scripts and other shared data to prefix/share/recoll. If the installation prefix given to recollinstall is different from either the system default or the value which was specified when executing configure (as in configure --prefix /some/path), you will have to set the RECOLL_DATADIR environment variable to indicate where the shared data is to be found (ie for (ba)sh: export RECOLL_DATADIR=/some/path/share/recoll). You can then proceed to configuration. ---------------------------------------------------------------------- Prev Up Next 5.2. Supporting packages Home 5.4. Configuration overview Link: home: Recoll user manual Link: up: Chapter 5. Installation and configuration Link: prev: 5.3. Building from source 5.4. Configuration overview Prev Chapter 5. Installation and configuration ---------------------------------------------------------------------- 5.4. Configuration overview Most of the parameters specific to the recoll GUI are set through the Preferences menu and stored in the standard Qt place ($HOME/.config/Recoll.org/recoll.conf). You probably do not want to edit this by hand. Recoll indexing options are set inside text configuration files located in a configuration directory. There can be several such directories, each of which defines the parameters for one index. The configuration files can be edited by hand or through the Index configuration dialog (Preferences menu). The GUI tool will try to respect your formatting and comments as much as possible, so it is quite possible to use both ways. The most accurate documentation for the configuration parameters is given by comments inside the default files, and we will just give a general overview here. By default, for each index, there are two sets of configuration files. System-wide configuration files are kept in a directory named like /usr/[local/]share/recoll/examples, and define default values, shared by all indexes. For each index, a parallel set of files defines the customized parameters. In addition (as of Recoll version 1.19.7), it is possible to specify two additional configuration directories which will be stacked before and after the user configuration directory. These are defined by the RECOLL_CONFTOP and RECOLL_CONFMID environment variables. Values from configuration files inside the top directory will override user ones, values from configuration files inside the middle directory will override system ones and be overridden by user ones. These two variables may be of use to applications which augment Recoll functionality, and need to add configuration data without disturbing the user's files. Please note that the two, currently single, values will probably be interpreted as colon-separated lists in the future: do not use colon characters inside the directory paths. The default location of the configuration is the .recoll directory in your home. Most people will only use this directory. This location can be changed, or others can be added with the RECOLL_CONFDIR environment variable or the -c option parameter to recoll and recollindex. If the .recoll directory does not exist when recoll or recollindex are started, it will be created with a set of empty configuration files. recoll will give you a chance to edit the configuration file before starting indexing. recollindex will proceed immediately. To avoid mistakes, the automatic directory creation will only occur for the default location, not if -c or RECOLL_CONFDIR were used (in the latter cases, you will have to create the directory). All configuration files share the same format. For example, a short extract of the main configuration file might look as follows: # Space-separated list of directories to index. topdirs = ~/docs /usr/share/doc [~/somedirectory-with-utf8-txt-files] defaultcharset = utf-8 There are three kinds of lines: o Comment (starts with #) or empty. o Parameter affectation (name = value). o Section definition ([somedirname]). Depending on the type of configuration file, section definitions either separate groups of parameters or allow redefining some parameters for a directory sub-tree. They stay in effect until another section definition, or the end of file, is encountered. Some of the parameters used for indexing are looked up hierarchically from the current directory location upwards. Not all parameters can be meaningfully redefined, this is specified for each in the next section. When found at the beginning of a file path, the tilde character (~) is expanded to the name of the user's home directory, as a shell would do. White space is used for separation inside lists. List elements with embedded spaces can be quoted using double-quotes. Encoding issues. Most of the configuration parameters are plain ASCII. Two particular sets of values may cause encoding issues: o File path parameters may contain non-ascii characters and should use the exact same byte values as found in the file system directory. Usually, this means that the configuration file should use the system default locale encoding. o The unac_except_trans parameter should be encoded in UTF-8. If your system locale is not UTF-8, and you need to also specify non-ascii file paths, this poses a difficulty because common text editors cannot handle multiple encodings in a single file. In this relatively unlikely case, you can edit the configuration file as two separate text files with appropriate encodings, and concatenate them to create the complete configuration. 5.4.1. Environment variables RECOLL_CONFDIR Defines the main configuration directory. RECOLL_TMPDIR, TMPDIR Locations for temporary files, in this order of priority. The default if none of these is set is to use /tmp. Big temporary files may be created during indexing, mostly for decompressing, and also for processing, e.g. email attachments. RECOLL_CONFTOP, RECOLL_CONFMID Allow adding configuration directories with priorities below and above the user directory (see above the Configuration overview section for details). RECOLL_EXTRA_DBS, RECOLL_ACTIVE_EXTRA_DBS Help for setting up external indexes. See this paragraph for explanations. RECOLL_DATADIR Defines replacement for the default location of Recoll data files, normally found in, e.g., /usr/share/recoll). RECOLL_FILTERSDIR Defines replacement for the default location of Recoll filters, normally found in, e.g., /usr/share/recoll/filters). ASPELL_PROG aspell program to use for creating the spelling dictionary. The result has to be compatible with the libaspell which Recoll is using. VARNAME Blabla 5.4.2. The main configuration file, recoll.conf recoll.conf is the main configuration file. It defines things like what to index (top directories and things to ignore), and the default character set to use for document types which do not specify it internally. The default configuration will index your home directory. If this is not appropriate, start recoll to create a blank configuration, click Cancel, and edit the configuration file before restarting the command. This will start the initial indexing, which may take some time. Most of the following parameters can be changed from the Index Configuration menu in the recoll interface. Some can only be set by editing the configuration file. 5.4.2.1. Parameters affecting what documents we index: topdirs Specifies the list of directories or files to index (recursively for directories). You can use symbolic links as elements of this list. See the followLinks option about following symbolic links found under the top elements (not followed by default). skippedNames A space-separated list of wildcard patterns for names of files or directories that should be completely ignored. The list defined in the default file is: skippedNames = #* bin CVS Cache cache* caughtspam tmp .thumbnails .svn \ *~ .beagle .git .hg .bzr loop.ps .xsession-errors \ .recoll* xapiandb recollrc recoll.conf The list can be redefined at any sub-directory in the indexed area. The top-level directories are not affected by this list (that is, a directory in topdirs might match and would still be indexed). The list in the default configuration does not exclude hidden directories (names beginning with a dot), which means that it may index quite a few things that you do not want. On the other hand, email user agents like thunderbird usually store messages in hidden directories, and you probably want this indexed. One possible solution is to have .* in skippedNames, and add things like ~/.thunderbird or ~/.evolution in topdirs. Not even the file names are indexed for patterns in this list. See the noContentSuffixes variable for an alternative approach which indexes the file names. noContentSuffixes This is a list of file name endings (not wildcard expressions, nor dot-delimited suffixes). Only the names of matching files will be indexed (no attempt at MIME type identification, no decompression, no content indexing). This can be redefined for subdirectories, and edited from the GUI. The default value is: noContentSuffixes = .md5 .map \ .o .lib .dll .a .sys .exe .com \ .mpp .mpt .vsd \ .img .img.gz .img.bz2 .img.xz .image .image.gz .image.bz2 .image.xz \ .dat .bak .rdf .log.gz .log .db .msf .pid \ ,v ~ # skippedPaths and daemSkippedPaths A space-separated list of patterns for paths of files or directories that should be skipped. There is no default in the sample configuration file, but the code always adds the configuration and database directories in there. skippedPaths is used both by batch and real time indexing. daemSkippedPaths can be used to specify things that should be indexed at startup, but not monitored. Example of use for skipping text files only in a specific directory: skippedPaths = ~/somedir/*.txt skippedPathsFnmPathname The values in the *skippedPaths variables are matched by default with fnmatch(3), with the FNM_PATHNAME flag. This means that '/' characters must be matched explicitly. You can set skippedPathsFnmPathname to 0 to disable the use of FNM_PATHNAME (meaning that /*/dir3 will match /dir1/dir2/dir3). zipSkippedNames A space-separated list of patterns for names of files or directories that should be ignored inside zip archives. This is used directly by the zip handler, and has a function similar to skippedNames, but works independently. Can be redefined for filesystem subdirectories. For versions up to 1.19, you will need to update the Zip handler and install a supplementary Python module. The details are described on the Recoll wiki. followLinks Specifies if the indexer should follow symbolic links while walking the file tree. The default is to ignore symbolic links to avoid multiple indexing of linked files. No effort is made to avoid duplication when this option is set to true. This option can be set individually for each of the topdirs members by using sections. It can not be changed below the topdirs level. indexedmimetypes Recoll normally indexes any file which it knows how to read. This list lets you restrict the indexed MIME types to what you specify. If the variable is unspecified or the list empty (the default), all supported types are processed. Can be redefined for subdirectories. excludedmimetypes This list lets you exclude some MIME types from indexing. Can be redefined for subdirectories. compressedfilemaxkbs Size limit for compressed (.gz or .bz2) files. These need to be decompressed in a temporary directory for identification, which can be very wasteful if 'uninteresting' big compressed files are present. Negative means no limit, 0 means no processing of any compressed file. Defaults to -1. textfilemaxmbs Maximum size for text files. Very big text files are often uninteresting logs. Set to -1 to disable (default 20MB). textfilepagekbs If set to other than -1, text files will be indexed as multiple documents of the given page size. This may be useful if you do want to index very big text files as it will both reduce memory usage at index time and help with loading data to the preview window. A size of a few megabytes would seem reasonable (default: 1MB). membermaxkbs This defines the maximum size in kilobytes for an archive member (zip, tar or rar at the moment). Bigger entries will be skipped. indexallfilenames Recoll indexes file names in a special section of the database to allow specific file names searches using wild cards. This parameter decides if file name indexing is performed only for files with MIME types that would qualify them for full text indexing, or for all files inside the selected subtrees, independently of MIME type. usesystemfilecommand Decide if we execute a system command (file -i by default) as a final step for determining the MIME type for a file (the main procedure uses suffix associations as defined in the mimemap file). This can be useful for files with suffix-less names, but it will also cause the indexing of many bogus "text" files. systemfilecommand Command to use for mime for mime type determination if usesystefilecommand is set. Recent versions of xdg-mime sometimes work better than file. processwebqueue If this is set, process the directory where Web browser plugins copy visited pages for indexing. webqueuedir The path to the web indexing queue. This is hard-coded in the Firefox plugin as ~/.recollweb/ToIndex so there should be no need to change it. 5.4.2.2. Parameters affecting how we generate terms: Changing some of these parameters will imply a full reindex. Also, when using multiple indexes, it may not make sense to search indexes that don't share the values for these parameters, because they usually affect both search and index operations. indexStripChars Decide if we strip characters of diacritics and convert them to lower-case before terms are indexed. If we don't, searches sensitive to case and diacritics can be performed, but the index will be bigger, and some marginal weirdness may sometimes occur. The default is a stripped index (indexStripChars = 1) for now. When using multiple indexes for a search, this parameter must be defined identically for all. Changing the value implies an index reset. maxTermExpand Maximum expansion count for a single term (e.g.: when using wildcards). The default of 10000 is reasonable and will avoid queries that appear frozen while the engine is walking the term list. maxXapianClauses Maximum number of elementary clauses we can add to a single Xapian query. In some cases, the result of term expansion can be multiplicative, and we want to avoid using excessive memory. The default of 100 000 should be both high enough in most cases and compatible with current typical hardware configurations. nonumbers If this set to true, no terms will be generated for numbers. For example "123", "1.5e6", 192.168.1.4, would not be indexed ("value123" would still be). Numbers are often quite interesting to search for, and this should probably not be set except for special situations, ie, scientific documents with huge amounts of numbers in them. This can only be set for a whole index, not for a subtree. nocjk If this set to true, specific east asian (Chinese Korean Japanese) characters/word splitting is turned off. This will save a small amount of cpu if you have no CJK documents. If your document base does include such text but you are not interested in searching it, setting nocjk may be a significant time and space saver. cjkngramlen This lets you adjust the size of n-grams used for indexing CJK text. The default value of 2 is probably appropriate in most cases. A value of 3 would allow more precision and efficiency on longer words, but the index will be approximately twice as large. indexstemminglanguages A list of languages for which the stem expansion databases will be built. See recollindex(1) or use the recollindex -l command for possible values. You can add a stem expansion database for a different language by using recollindex -s, but it will be deleted during the next indexing. Only languages listed in the configuration file are permanent. defaultcharset The name of the character set used for files that do not contain a character set definition (ie: plain text files). This can be redefined for any sub-directory. If it is not set at all, the character set used is the one defined by the nls environment ( LC_ALL, LC_CTYPE, LANG), or iso8859-1 if nothing is set. unac_except_trans This is a list of characters, encoded in UTF-8, which should be handled specially when converting text to unaccented lowercase. For example, in Swedish, the letter a with diaeresis has full alphabet citizenship and should not be turned into an a. Each element in the space-separated list has the special character as first element and the translation following. The handling of both the lowercase and upper-case versions of a character should be specified, as appartenance to the list will turn-off both standard accent and case processing. Example for Swedish: unac_except_trans = aaaa AAaa a:a: A:a: o:o: O:o: Note that the translation is not limited to a single character, you could very well have something like u:ue in the list. The default value set for unac_except_trans can't be listed here because I have trouble with SGML and UTF-8, but it only contains ligature decompositions: german ss, oe, ae, fi, fl. This parameter can't be defined for subdirectories, it is global, because there is no way to do otherwise when querying. If you have document sets which would need different values, you will have to index and query them separately. maildefcharset This can be used to define the default character set specifically for email messages which don't specify it. This is mainly useful for readpst (libpst) dumps, which are utf-8 but do not say so. localfields This allows setting fields for all documents under a given directory. Typical usage would be to set an "rclaptg" field, to be used in mimeview to select a specific viewer. If several fields are to be set, they should be separated with a semi-colon (';') character, which there is currently no way to escape. Also note the initial semi-colon. Example: localfields= ;rclaptg=gnus;other = val, then select specifier viewer with mimetype|tag=... in mimeview. testmodifusemtime If true, use mtime instead of default ctime to determine if a file has been modified (in addition to size, which is always used). Setting this can reduce re-indexing on systems where extended attributes are modified (by some other application), but not indexed (changing extended attributes only affects ctime). Notes: o This may prevent detection of change in some marginal file rename cases (the target would need to have the same size and mtime). o You should probably also set noxattrfields to 1 in this case, except if you still prefer to perform xattr indexing, for example if the local file update pattern makes it of value (as in general, there is a risk for pure extended attributes updates without file modification to go undetected). Perform a full index reset after changing the value of this parameter. noxattrfields Recoll versions 1.19 and later automatically translate file extended attributes into document fields (to be processed according to the parameters from the fields file). Setting this variable to 1 will disable the behaviour. metadatacmds This allows executing external commands for each file and storing the output in Recoll document fields. This could be used for example to index external tag data. The value is a list of field names and commands, don't forget an initial semi-colon. Example: [/some/area/of/the/fs] metadatacmds = ; tags = tmsu tags %f; otherfield = somecmd -xx %f As a specially disgusting hack brought by Recoll 1.19.7, if a "field name" begins with rclmulti, the data returned by the command is expected to contain multiple field values, in configuration file format. This allows setting several fields by executing a single command. Example: metadatacmds = ; rclmulti1 = somecmd %f If somecmd returns data in the form of: field1 = value1 field2 = value for field2 field1 and field2 will be set inside the document metadata. 5.4.2.3. Parameters affecting where and how we store things: dbdir The name of the Xapian data directory. It will be created if needed when the index is initialized. If this is not an absolute path, it will be interpreted relative to the configuration directory. The value can have embedded spaces but starting or trailing spaces will be trimmed. You cannot use quotes here. idxstatusfile The name of the scratch file where the indexer process updates its status. Default: idxstatus.txt inside the configuration directory. maxfsoccuppc Maximum file system occupation before we stop indexing. The value is a percentage, corresponding to what the "Capacity" df output column shows. The default value is 0, meaning no checking. mboxcachedir The directory where mbox message offsets cache files are held. This is normally $RECOLL_CONFDIR/mboxcache, but it may be useful to share a directory between different configurations. mboxcacheminmbs The minimum mbox file size over which we cache the offsets. There is really no sense in caching offsets for small files. The default is 5 MB. webcachedir This is only used by the web browser plugin indexing code, and defines where the cache for visited pages will live. Default: $RECOLL_CONFDIR/webcache webcachemaxmbs This is only used by the web browser plugin indexing code, and defines the maximum size for the web page cache. Default: 40 MB. Quite unfortunately, this is only taken into account when creating the cache file. You need to delete the file for a change to be taken into account. idxflushmb Threshold (megabytes of new text data) where we flush from memory to disk index. Setting this can help control memory usage. A value of 0 means no explicit flushing, letting Xapian use its own default, which is flushing every 10000 (or XAPIAN_FLUSH_THRESHOLD) documents, which gives little memory usage control, as memory usage also depends on average document size. The default value is 10, and it is probably a bit low. If your system usually has free memory, you can try higher values between 20 and 80. In my experience, values beyond 100 are always counterproductive. 5.4.2.4. Parameters affecting multithread processing The Recoll indexing process recollindex can use multiple threads to speed up indexing on multiprocessor systems. The work done to index files is divided in several stages and some of the stages can be executed by multiple threads. The stages are: 1. File system walking: this is always performed by the main thread. 2. File conversion and data extraction. 3. Text processing (splitting, stemming, etc.) 4. Xapian index update. You can also read a longer document about the transformation of Recoll indexing to multithreading. The threads configuration is controlled by two configuration file parameters. thrQSizes This variable defines the job input queues configuration. There are three possible queues for stages 2, 3 and 4, and this parameter should give the queue depth for each stage (three integer values). If a value of -1 is used for a given stage, no queue is used, and the thread will go on performing the next stage. In practise, deep queues have not been shown to increase performance. A value of 0 for the first queue tells Recoll to perform autoconfiguration (no need for the two other values in this case) - this is the default configuration. thrTCounts This defines the number of threads used for each stage. If a value of -1 is used for one of the queue depths, the corresponding thread count is ignored. It makes no sense to use a value other than 1 for the last stage because updating the Xapian index is necessarily single-threaded (and protected by a mutex). The following example would use three queues (of depth 2), and 4 threads for converting source documents, 2 for processing their text, and one to update the index. This was tested to be the best configuration on the test system (quadri-processor with multiple disks). thrQSizes = 2 2 2 thrTCounts = 4 2 1 The following example would use a single queue, and the complete processing for each document would be performed by a single thread (several documents will still be processed in parallel in most cases). The threads will use mutual exclusion when entering the index update stage. In practise the performance would be close to the precedent case in general, but worse in certain cases (e.g. a Zip archive would be performed purely sequentially), so the previous approach is preferred. YMMV... The 2 last values for thrTCounts are ignored. thrQSizes = 2 -1 -1 thrTCounts = 6 1 1 The following example would disable multithreading. Indexing will be performed by a single thread. thrQSizes = -1 -1 -1 5.4.2.5. Miscellaneous parameters: autodiacsens IF the index is not stripped, decide if we automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the D modifier to specify diacritics sensitivity. Default is no. autocasesens IF the index is not stripped, decide if we automatically trigger character case sensitivity if the search term has upper-case characters in any but the first position. Else you need to use the query language and the C modifier to specify character-case sensitivity. Default is yes. loglevel,daemloglevel Verbosity level for recoll and recollindex. A value of 4 lists quite a lot of debug/information messages. 2 only lists errors. The daemversion is specific to the indexing monitor daemon. logfilename, daemlogfilename Where the messages should go. 'stderr' can be used as a special value, and is the default. The daemversion is specific to the indexing monitor daemon. checkneedretryindexscript This defines the name for a command executed by recollindex when starting indexing. If the exit status of the command is 0, recollindex retries to index all files which previously could not be indexed because of data extraction errors. The default value is a script which checks if any of the common bin directories have changed (indicating that a helper program may have been installed). mondelaypatterns This allows specify wildcard path patterns (processed with fnmatch(3) with 0 flag), to match files which change too often and for which a delay should be observed before re-indexing. This is a space-separated list, each entry being a pattern and a time in seconds, separated by a colon. You can use double quotes if a path entry contains white space. Example: mondelaypatterns = *.log:20 "this one has spaces*:10" monixinterval Minimum interval (seconds) for processing the indexing queue. The real time monitor does not process each event when it comes in, but will wait this time for the queue to accumulate to diminish overhead and in order to aggregate multiple events to the same file. Default 30 S. monauxinterval Period (in seconds) at which the real time monitor will regenerate the auxiliary databases (spelling, stemming) if needed. The default is one hour. monioniceclass, monioniceclassdata These allow defining the ionice class and data used by the indexer (default class 3, no data). filtermaxseconds Maximum handler execution time, after which it is aborted. Some postscript programs just loop... filtermaxmbytes Recoll 1.20.7 and later. Maximum handler memory utilisation. This uses setrlimit(RLIMIT_AS) on most systems (total virtual memory space size limit). Some programs may start with 500 MBytes of mapped shared libraries, so take this into account when choosing a value. The default is a liberal 2000MB. filtersdir A directory to search for the external input handler scripts used to index some types of files. The value should not be changed, except if you want to modify one of the default scripts. The value can be redefined for any sub-directory. iconsdir The name of the directory where recoll result list icons are stored. You can change this if you want different images. idxabsmlen Recoll stores an abstract for each indexed file inside the database. The text can come from an actual 'abstract' section in the document or will just be the beginning of the document. It is stored in the index so that it can be displayed inside the result lists without decoding the original file. The idxabsmlen parameter defines the size of the stored abstract. The default value is 250 bytes. The search interface gives you the choice to display this stored text or a synthetic abstract built by extracting text around the search terms. If you always prefer the synthetic abstract, you can reduce this value and save a little space. idxmetastoredlen Maximum stored length for metadata fields. This does not affect indexing (the whole field is processed anyway), just the amount of data stored in the index for the purpose of displaying fields inside result lists or previews. The default value is 150 bytes which may be too low if you have custom fields. aspellLanguage Language definitions to use when creating the aspell dictionary. The value must match a set of aspell language definition files. You can type "aspell config" to see where these are installed (look for data-dir). The default if the variable is not set is to use your desktop national language environment to guess the value. noaspell If this is set, the aspell dictionary generation is turned off. Useful for cases where you don't need the functionality or when it is unusable because aspell crashes during dictionary generation. mhmboxquirks This allows defining location-related quirks for the mailbox handler. Currently only the tbird flag is defined, and it should be set for directories which hold Thunderbird data, as their folder format is weird. 5.4.3. The fields file This file contains information about dynamic fields handling in Recoll. Some very basic fields have hard-wired behaviour, and, mostly, you should not change the original data inside the fields file. But you can create custom fields fitting your data and handle them just like they were native ones. The fields file has several sections, which each define an aspect of fields processing. Quite often, you'll have to modify several sections to obtain the desired behaviour. We will only give a short description here, you should refer to the comments inside the default file for more detailed information. Field names should be lowercase alphabetic ASCII. [prefixes] A field becomes indexed (searchable) by having a prefix defined in this section. [stored] A field becomes stored (displayable inside results) by having its name listed in this section (typically with an empty value). [aliases] This section defines lists of synonyms for the canonical names used inside the [prefixes] and [stored] sections [queryaliases] This section also defines aliases for the canonic field names, with the difference that the substitution will only be used at query time, avoiding any possibility that the value would pick-up random metadata from documents. handler-specific sections Some input handlers may need specific configuration for handling fields. Only the email message handler currently has such a section (named [mail]). It allows indexing arbitrary email headers in addition to the ones indexed by default. Other such sections may appear in the future. Here follows a small example of a personal fields file. This would extract a specific email header and use it as a searchable field, with data displayable inside result lists. (Side note: as the email handler does no decoding on the values, only plain ascii headers can be indexed, and only the first occurrence will be used for headers that occur several times). [prefixes] # Index mailmytag contents (with the given prefix) mailmytag = XMTAG [stored] # Store mailmytag inside the document data record (so that it can be # displayed - as %(mailmytag) - in result lists). mailmytag = [queryaliases] filename = fn containerfilename = cfn [mail] # Extract the X-My-Tag mail header, and use it internally with the # mailmytag field name x-my-tag = mailmytag 5.4.3.1. Extended attributes in the fields file Recoll versions 1.19 and later process user extended file attributes as documents fields by default. Attributes are processed as fields of the same name, after removing the user prefix on Linux. The [xattrtofields] section of the fields file allows specifying translations from extended attributes names to Recoll field names. An empty translation disables use of the corresponding attribute data. 5.4.4. The mimemap file mimemap specifies the file name extension to MIME type mappings. For file names without an extension, or with an unknown one, the system's file -i command will be executed to determine the MIME type (this can be switched off inside the main configuration file). The mappings can be specified on a per-subtree basis, which may be useful in some cases. Example: gaim logs have a .txt extension but should be handled specially, which is possible because they are usually all located in one place. The recoll_noindex mimemap variable has been moved to recoll.conf and renamed to noContentSuffixes, while keeping the same function, as of Recoll version 1.21. For older Recoll versions, see the documentation for noContentSuffixes but use recoll_noindex in mimemap. 5.4.5. The mimeconf file mimeconf specifies how the different MIME types are handled for indexing, and which icons are displayed in the recoll result lists. Changing the parameters in the [index] section is probably not a good idea except if you are a Recoll developer. The [icons] section allows you to change the icons which are displayed by recoll in the result lists (the values are the basenames of the png images inside the iconsdir directory (specified in recoll.conf). 5.4.6. The mimeview file mimeview specifies which programs are started when you click on an Open link in a result list. Ie: HTML is normally displayed using firefox, but you may prefer Konqueror, your openoffice.org program might be named oofice instead of openoffice etc. Changes to this file can be done by direct editing, or through the recoll GUI preferences dialog. If Use desktop preferences to choose document editor is checked in the Recoll GUI preferences, all mimeview entries will be ignored except the one labelled application/x-all (which is set to use xdg-open by default). In this case, the xallexcepts top level variable defines a list of MIME type exceptions which will be processed according to the local entries instead of being passed to the desktop. This is so that specific Recoll options such as a page number or a search string can be passed to applications that support them, such as the evince viewer. As for the other configuration files, the normal usage is to have a mimeview inside your own configuration directory, with just the non-default entries, which will override those from the central configuration file. All viewer definition entries must be placed under a [view] section. The keys in the file are normally MIME types. You can add an application tag to specialize the choice for an area of the filesystem (using a localfields specification in mimeconf). The syntax for the key is mimetype|tag The nouncompforviewmts entry, (placed at the top level, outside of the [view] section), holds a list of MIME types that should not be uncompressed before starting the viewer (if they are found compressed, ie: mydoc.doc.gz). The right side of each assignment holds a command to be executed for opening the file. The following substitutions are performed: o %D. Document date o %f. File name. This may be the name of a temporary file if it was necessary to create one (ie: to extract a subdocument from a container). o %i. Internal path, for subdocuments of containers. The format depends on the container type. If this appears in the command line, Recoll will not create a temporary file to extract the subdocument, expecting the called application (possibly a script) to be able to handle it. o %M. MIME type o %p. Page index. Only significant for a subset of document types, currently only PDF, Postscript and DVI files. Can be used to start the editor at the right page for a match or snippet. o %s. Search term. The value will only be set for documents with indexed page numbers (ie: PDF). The value will be one of the matched search terms. It would allow pre-setting the value in the "Find" entry inside Evince for example, for easy highlighting of the term. o %u. Url. In addition to the predefined values above, all strings like %(fieldname) will be replaced by the value of the field named fieldname for the document. This could be used in combination with field customisation to help with opening the document. 5.4.7. The ptrans file ptrans specifies query-time path translations. These can be useful in multiple cases. The file has a section for any index which needs translations, either the main one or additional query indexes. The sections are named with the Xapian index directory names. No slash character should exist at the end of the paths (all comparisons are textual). An example should make things sufficiently clear [/home/me/.recoll/xapiandb] /this/directory/moved = /to/this/place [/path/to/additional/xapiandb] /server/volume1/docdir = /net/server/volume1/docdir /server/volume2/docdir = /net/server/volume2/docdir 5.4.8. Examples of configuration adjustments 5.4.8.1. Adding an external viewer for an non-indexed type Imagine that you have some kind of file which does not have indexable content, but for which you would like to have a functional Open link in the result list (when found by file name). The file names end in .blob and can be displayed by application blobviewer. You need two entries in the configuration files for this to work: o In $RECOLL_CONFDIR/mimemap (typically ~/.recoll/mimemap), add the following line: .blob = application/x-blobapp Note that the MIME type is made up here, and you could call it diesel/oil just the same. o In $RECOLL_CONFDIR/mimeview under the [view] section, add: application/x-blobapp = blobviewer %f We are supposing that blobviewer wants a file name parameter here, you would use %u if it liked URLs better. If you just wanted to change the application used by Recoll to display a MIME type which it already knows, you would just need to edit mimeview. The entries you add in your personal file override those in the central configuration, which you do not need to alter. mimeview can also be modified from the Gui. 5.4.8.2. Adding indexing support for a new file type Let us now imagine that the above .blob files actually contain indexable text and that you know how to extract it with a command line program. Getting Recoll to index the files is easy. You need to perform the above alteration, and also to add data to the mimeconf file (typically in ~/.recoll/mimeconf): o Under the [index] section, add the following line (more about the rclblob indexing script later): application/x-blobapp = exec rclblob o Under the [icons] section, you should choose an icon to be displayed for the files inside the result lists. Icons are normally 64x64 pixels PNG files which live in /usr/[local/]share/recoll/images. o Under the [categories] section, you should add the MIME type where it makes sense (you can also create a category). Categories may be used for filtering in advanced search. The rclblob handler should be an executable program or script which exists inside /usr/[local/]share/recoll/filters. It will be given a file name as argument and should output the text or html contents on the standard output. The filter programming section describes in more detail how to write an input handler. ---------------------------------------------------------------------- Prev Up 5.3. Building from source Home recoll-1.36.1/config.rpath0000755000175000017500000004421614410615043012320 00000000000000#! /bin/sh # Output a system dependent set of variables, describing how to set the # run time search path of shared libraries in an executable. # # Copyright 1996-2014 Free Software Foundation, Inc. # Taken from GNU libtool, 2001 # Originally by Gordon Matzigkeit , 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # The first argument passed to this file is the canonical host specification, # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld # should be set by the caller. # # The set of defined variables is at the end of this script. # Known limitations: # - On IRIX 6.5 with CC="cc", the run time search patch must not be longer # than 256 bytes, otherwise the compiler driver will dump core. The only # known workaround is to choose shorter directory names for the build # directory and/or the installation directory. # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a shrext=.so host="$1" host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # Code taken from libtool.m4's _LT_CC_BASENAME. for cc_temp in $CC""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` # Code taken from libtool.m4's _LT_COMPILER_PIC. wl= if test "$GCC" = yes; then wl='-Wl,' else case "$host_os" in aix*) wl='-Wl,' ;; mingw* | cygwin* | pw32* | os2* | cegcc*) ;; hpux9* | hpux10* | hpux11*) wl='-Wl,' ;; irix5* | irix6* | nonstopux*) wl='-Wl,' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in ecc*) wl='-Wl,' ;; icc* | ifort*) wl='-Wl,' ;; lf95*) wl='-Wl,' ;; nagfor*) wl='-Wl,-Wl,,' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) wl='-Wl,' ;; ccc*) wl='-Wl,' ;; xl* | bgxl* | bgf* | mpixl*) wl='-Wl,' ;; como) wl='-lopt=' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ F* | *Sun*Fortran*) wl= ;; *Sun\ C*) wl='-Wl,' ;; esac ;; esac ;; newsos6) ;; *nto* | *qnx*) ;; osf3* | osf4* | osf5*) wl='-Wl,' ;; rdos*) ;; solaris*) case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) wl='-Qoption ld ' ;; *) wl='-Wl,' ;; esac ;; sunos4*) wl='-Qoption ld ' ;; sysv4 | sysv4.2uw2* | sysv4.3*) wl='-Wl,' ;; sysv4*MP*) ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) wl='-Wl,' ;; unicos*) wl='-Wl,' ;; uts4*) ;; esac fi # Code taken from libtool.m4's _LT_LINKER_SHLIBS. hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_direct=no hardcode_minus_L=no case "$host_os" in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. # Unlike libtool, we use -rpath here, not --rpath, since the documented # option of GNU ld is called -rpath, not --rpath. hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' case "$host_os" in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no fi ;; amigaos*) case "$host_cpu" in powerpc) ;; m68k) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then : else ld_shlibs=no fi ;; haiku*) ;; interix[3-9]*) hardcode_direct=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; netbsd*) ;; solaris*) if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then ld_shlibs=no elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' else ld_shlibs=no fi ;; esac ;; sunos4*) hardcode_direct=yes ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then hardcode_libdir_flag_spec= fi else case "$host_os" in aix3*) # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac fi hardcode_direct=yes hardcode_libdir_separator=':' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && \ strings "$collect2name" | grep resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac fi # Begin _LT_AC_SYS_LIBPATH_AIX. echo 'int main () { return 0; }' > conftest.c ${CC} ${LDFLAGS} conftest.c -o conftest aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` fi if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib" fi rm -f conftest.c conftest # End _LT_AC_SYS_LIBPATH_AIX. if test "$aix_use_runtimelinking" = yes; then hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' else hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" fi fi ;; amigaos*) case "$host_cpu" in powerpc) ;; m68k) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' libext=lib ;; darwin* | rhapsody*) hardcode_direct=no if { case $cc_basename in ifort*) true;; *) test "$GCC" = yes;; esac; }; then : else ld_shlibs=no fi ;; dgux*) hardcode_libdir_flag_spec='-L$libdir' ;; freebsd2.[01]*) hardcode_direct=yes hardcode_minus_L=yes ;; freebsd* | dragonfly*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; hpux9*) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; hpux10*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no ;; *) hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; netbsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; newsos6) hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then hardcode_libdir_flag_spec='${wl}-rpath,$libdir' else case "$host_os" in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) hardcode_libdir_flag_spec='-R$libdir' ;; *) hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; osf3*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) if test "$GCC" = yes; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else # Both cc and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi hardcode_libdir_separator=: ;; solaris*) hardcode_libdir_flag_spec='-R$libdir' ;; sunos4*) hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes ;; sysv4) case $host_vendor in sni) hardcode_direct=yes # is this really true??? ;; siemens) hardcode_direct=no ;; motorola) hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac ;; sysv4.3*) ;; sysv4*MP*) if test -d /usr/nec; then ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) ;; sysv5* | sco3.2v5* | sco5v6*) hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' hardcode_libdir_separator=':' ;; uts4*) hardcode_libdir_flag_spec='-L$libdir' ;; *) ld_shlibs=no ;; esac fi # Check dynamic linker characteristics # Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER. # Unlike libtool.m4, here we don't care about _all_ names of the library, but # only about the one the linker finds when passed -lNAME. This is the last # element of library_names_spec in libtool.m4, or possibly two of them if the # linker has special search rules. library_names_spec= # the last element of library_names_spec in libtool.m4 libname_spec='lib$name' case "$host_os" in aix3*) library_names_spec='$libname.a' ;; aix[4-9]*) library_names_spec='$libname$shrext' ;; amigaos*) case "$host_cpu" in powerpc*) library_names_spec='$libname$shrext' ;; m68k) library_names_spec='$libname.a' ;; esac ;; beos*) library_names_spec='$libname$shrext' ;; bsdi[45]*) library_names_spec='$libname$shrext' ;; cygwin* | mingw* | pw32* | cegcc*) shrext=.dll library_names_spec='$libname.dll.a $libname.lib' ;; darwin* | rhapsody*) shrext=.dylib library_names_spec='$libname$shrext' ;; dgux*) library_names_spec='$libname$shrext' ;; freebsd[23].*) library_names_spec='$libname$shrext$versuffix' ;; freebsd* | dragonfly*) library_names_spec='$libname$shrext' ;; gnu*) library_names_spec='$libname$shrext' ;; haiku*) library_names_spec='$libname$shrext' ;; hpux9* | hpux10* | hpux11*) case $host_cpu in ia64*) shrext=.so ;; hppa*64*) shrext=.sl ;; *) shrext=.sl ;; esac library_names_spec='$libname$shrext' ;; interix[3-9]*) library_names_spec='$libname$shrext' ;; irix5* | irix6* | nonstopux*) library_names_spec='$libname$shrext' case "$host_os" in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; *) libsuff= shlibsuff= ;; esac ;; esac ;; linux*oldld* | linux*aout* | linux*coff*) ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) library_names_spec='$libname$shrext' ;; knetbsd*-gnu) library_names_spec='$libname$shrext' ;; netbsd*) library_names_spec='$libname$shrext' ;; newsos6) library_names_spec='$libname$shrext' ;; *nto* | *qnx*) library_names_spec='$libname$shrext' ;; openbsd*) library_names_spec='$libname$shrext$versuffix' ;; os2*) libname_spec='$name' shrext=.dll library_names_spec='$libname.a' ;; osf3* | osf4* | osf5*) library_names_spec='$libname$shrext' ;; rdos*) ;; solaris*) library_names_spec='$libname$shrext' ;; sunos4*) library_names_spec='$libname$shrext$versuffix' ;; sysv4 | sysv4.3*) library_names_spec='$libname$shrext' ;; sysv4*MP*) library_names_spec='$libname$shrext' ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) library_names_spec='$libname$shrext' ;; tpf*) library_names_spec='$libname$shrext' ;; uts4*) library_names_spec='$libname$shrext' ;; esac sed_quote_subst='s/\(["`$\\]\)/\\\1/g' escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` shlibext=`echo "$shrext" | sed -e 's,^\.,,'` escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' < #include #include #include "docseq.h" #include "dynconf.h" namespace Rcl { class Db; } /** DynConf Document history entry */ class RclDHistoryEntry : public DynConfEntry { public: RclDHistoryEntry() : unixtime(0) {} RclDHistoryEntry(time_t t, const std::string& u, const std::string& d) : unixtime(t), udi(u), dbdir(d) {} virtual ~RclDHistoryEntry() {} RclDHistoryEntry(const RclDHistoryEntry&) = default; RclDHistoryEntry& operator=(const RclDHistoryEntry&) = default; virtual bool decode(const std::string &value); virtual bool encode(std::string& value); virtual bool equal(const DynConfEntry& other); time_t unixtime; std::string udi; std::string dbdir; }; /** A DocSequence coming from the history file. * History is kept as a list of urls. This queries the db to fetch * metadata for an url key */ class DocSequenceHistory : public DocSequence { public: DocSequenceHistory(std::shared_ptr db, RclDynConf *h, const std::string &t) : DocSequence(t), m_db(db), m_hist(h) {} virtual ~DocSequenceHistory() {} DocSequenceHistory(const DocSequenceHistory&) = delete; DocSequenceHistory& operator=(const DocSequenceHistory&) = delete; virtual bool getDoc(int num, Rcl::Doc &doc, std::string *sh = nullptr); virtual int getResCnt(); virtual std::string getDescription() {return m_description;} void setDescription(const std::string& desc) {m_description = desc;} protected: virtual std::shared_ptr getDb() { return m_db; } private: std::shared_ptr m_db; RclDynConf *m_hist; time_t m_prevtime{-1}; std::string m_description; // This is just an nls translated 'doc history' std::vector m_history; }; extern bool historyEnterDoc(Rcl::Db *db, RclDynConf *dncf, const Rcl::Doc& doc); #endif /* _DOCSEQ_H_INCLUDED_ */ recoll-1.36.1/query/wasaparseaux.cpp0000644000175000017500000002002014427373216014363 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include "wasatorcl.h" #include "wasaparserdriver.h" #include "searchdata.h" #include "log.h" #include "rclconfig.h" #define YYDEBUG 1 // bison-generated file #include "wasaparse.hpp" using namespace std; using namespace Rcl; void yy::parser::error (const location_type&, const std::string& m) { d->setreason(m); } std::shared_ptr wasaStringToRcl( const RclConfig *config, const std::string& stemlang, const std::string& query, string &reason, const std::string& autosuffs) { WasaParserDriver d(config, stemlang, autosuffs); auto sd = std::shared_ptr(d.parse(query)); if (!sd) reason = d.getreason(); return sd; } SearchData *WasaParserDriver::parse(const std::string& in) { m_input = in; m_index = 0; delete m_result; m_result = 0; m_returns = stack(); yy::parser parser(this); parser.set_debug_level(0); if (parser.parse() != 0) { delete m_result; m_result = 0; } if (m_result == 0) return m_result; // Set the top level filters (types, dates, size) for (const auto& ft : m_filetypes) { m_result->addFiletype(ft); } for (const auto& ft : m_nfiletypes) { m_result->remFiletype(ft); } if (m_haveDates) { m_result->setDateSpan(&m_dates); } #ifdef EXT4_BIRTH_TIME if (m_haveBrDates) { m_result->setBrDateSpan(&m_brdates); } #endif if (m_minSize != -1) { m_result->setMinSize(m_minSize); } if (m_maxSize != -1) { m_result->setMaxSize(m_maxSize); } if (m_subSpec != Rcl::SearchData::SUBDOC_ANY) { m_result->setSubSpec(m_subSpec); } //if (m_result) m_result->dump(cout); return m_result; } int WasaParserDriver::GETCHAR() { if (!m_returns.empty()) { int c = m_returns.top(); m_returns.pop(); return c; } if (m_index < m_input.size()) return m_input[m_index++]; return 0; } void WasaParserDriver::UNGETCHAR(int c) { m_returns.push(c); } // Add clause to query, handling special pseudo-clauses for size/date // etc. (mostly determined on field name). bool WasaParserDriver::addClause(SearchData *sd, SearchDataClauseSimple* cl) { if (cl->getfield().empty()) { // Simple clause with empty field spec. // Possibly change terms found in the "autosuffs" list into "ext" // field queries if (!m_autosuffs.empty()) { vector asfv; if (stringToStrings(m_autosuffs, asfv)) { if (find_if(asfv.begin(), asfv.end(), StringIcmpPred(cl->gettext())) != asfv.end()) { cl->setfield("ext"); cl->addModifier(SearchDataClause::SDCM_NOSTEMMING); } } } return sd->addClause(cl); } const string& ofld = cl->getfield(); string fld = stringtolower(ofld); // MIME types and categories if (!fld.compare("mime") || !fld.compare("format")) { if (cl->getexclude()) { m_nfiletypes.push_back(cl->gettext()); } else { m_filetypes.push_back(cl->gettext()); } delete cl; return false; } // Filtering for standalone- or sub-documents if (!fld.compare("issub")) { m_subSpec = atoi(cl->gettext().c_str()); delete cl; return false; } if (!fld.compare("rclcat") || !fld.compare("type")) { vector mtypes; if (m_config && m_config->getMimeCatTypes(cl->gettext(), mtypes)) { for (vector::iterator mit = mtypes.begin(); mit != mtypes.end(); mit++) { if (cl->getexclude()) { m_nfiletypes.push_back(*mit); } else { m_filetypes.push_back(*mit); } } } delete cl; return false; } // Handle "date" spec if (!fld.compare("date") #ifdef EXT4_BIRTH_TIME || !fld.compare("birtime") #endif ) { DateInterval di; if (!parsedateinterval(cl->gettext(), &di)) { LOGERR("Bad date interval format: " << cl->gettext() << "\n"); m_reason = "Bad date interval format"; delete cl; return false; } LOGDEB("addClause:: date/brdate span: " << di.y1 << "-" << di.m1 << "-" << di.d1 << "/" << di.y2 << "-" << di.m2 << "-" << di.d2 << "\n"); #ifdef EXT4_BIRTH_TIME if (fld.compare("date")) { m_haveBrDates = true; m_brdates = di; } else #endif { m_haveDates = true; m_dates = di; } delete cl; return false; } // Handle "size" spec if (!fld.compare("size")) { char *cp; int64_t size = strtoll(cl->gettext().c_str(), &cp, 10); if (*cp != 0) { switch (*cp) { case 'k': case 'K': size *= 1000;break; case 'm': case 'M': size *= 1000*1000;break; case 'g': case 'G': size *= 1000*1000*1000;break; case 't': case 'T': size *= int64_t(1000)*1000*1000*1000;break; default: m_reason = string("Bad multiplier suffix: ") + *cp; delete cl; return false; } } SearchDataClause::Relation rel = cl->getrel(); delete cl; switch (rel) { case SearchDataClause::REL_EQUALS: m_maxSize = m_minSize = size; break; case SearchDataClause::REL_LT: case SearchDataClause::REL_LTE: m_maxSize = size; break; case SearchDataClause::REL_GT: case SearchDataClause::REL_GTE: m_minSize = size; break; default: m_reason = "Bad relation operator with size query. Use > < or ="; return false; } return false; } if (!fld.compare("dir")) { // dir filtering special case SearchDataClausePath *nclause = new SearchDataClausePath(cl->gettext(), cl->getexclude()); delete cl; return sd->addClause(nclause); } if (cl->getTp() == SCLT_OR || cl->getTp() == SCLT_AND) { // If this is a normal clause and the term has commas or // slashes inside, take it as a list, turn the slashes/commas // to spaces, leave unquoted. Otherwise, this would end up as // a phrase query. This is a handy way to enter multiple terms // to be searched inside a field. We interpret ',' as AND, and // '/' as OR. No mixes allowed and ',' wins. SClType tp = SCLT_FILENAME;// impossible value string ns = neutchars(cl->gettext(), ","); if (ns.compare(cl->gettext())) { // had ',' tp = SCLT_AND; } else { ns = neutchars(cl->gettext(), "/"); if (ns.compare(cl->gettext())) { // had not ',' but has '/' tp = SCLT_OR; } } if (tp != SCLT_FILENAME) { SearchDataClauseSimple *ncl = new SearchDataClauseSimple(tp, ns, ofld); delete cl; return sd->addClause(ncl); } } return sd->addClause(cl); } recoll-1.36.1/query/docseqhist.cpp0000644000175000017500000001164514427373216014042 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "docseqhist.h" #include #include #include #include "rcldb.h" #include "fileudi.h" #include "base64.h" #include "log.h" #include "smallut.h" using std::vector; using std::string; // Encode document history entry: // U + Unix time + base64 of udi // The U distinguishes udi-based entries from older fn+ipath ones bool RclDHistoryEntry::encode(string& value) { string budi, bdir; base64_encode(udi, budi); base64_encode(dbdir, bdir); value = string("V ") + lltodecstr(unixtime) + " " + budi + " " + bdir; return true; } // Decode. We support historical entries which were like "time b64fn [b64ipath]" // Previous entry format is "U time b64udi" // Current entry format "V time b64udi [b64dir]" bool RclDHistoryEntry::decode(const string &value) { vector vall; stringToStrings(value, vall); vector::const_iterator it = vall.begin(); udi.clear(); dbdir.clear(); string fn, ipath; switch (vall.size()) { case 2: // Old fn+ipath, null ipath case unixtime = atoll((*it++).c_str()); base64_decode(*it++, fn); break; case 3: if (!it->compare("U") || !it->compare("V")) { // New udi-based entry, no dir it++; unixtime = atoll((*it++).c_str()); base64_decode(*it++, udi); } else { // Old fn + ipath. We happen to know how to build an udi unixtime = atoll((*it++).c_str()); base64_decode(*it++, fn); base64_decode(*it, ipath); } break; case 4: // New udi-based entry, with directory it++; unixtime = atoll((*it++).c_str()); base64_decode(*it++, udi); base64_decode(*it++, dbdir); break; default: return false; } if (!fn.empty()) { // Old style entry found, make an udi, using the fs udi maker make_udi(fn, ipath, udi); } LOGDEB1("RclDHistoryEntry::decode: udi [" << udi << "] dbdir [" << dbdir << "]\n"); return true; } bool RclDHistoryEntry::equal(const DynConfEntry& other) { const RclDHistoryEntry& e = dynamic_cast(other); return e.udi == udi && e.dbdir == dbdir; } bool historyEnterDoc(Rcl::Db *db, RclDynConf *dncf, const Rcl::Doc& doc) { string udi; if (db && doc.getmeta(Rcl::Doc::keyudi, &udi)) { std::string dbdir = db->whatIndexForResultDoc(doc); LOGDEB("historyEnterDoc: [" << udi << ", " << dbdir << "] into " << dncf->getFilename() << "\n"); RclDHistoryEntry ne(time(nullptr), udi, dbdir); RclDHistoryEntry scratch; return dncf->insertNew(docHistSubKey, ne, scratch, 200); } else { LOGDEB("historyEnterDoc: doc has no udi\n"); } return false; } vector getDocHistory(RclDynConf* dncf) { return dncf->getEntries(docHistSubKey); } bool DocSequenceHistory::getDoc(int num, Rcl::Doc &doc, string *sh) { // Retrieve history list if (!m_hist) return false; if (m_history.empty()) m_history = getDocHistory(m_hist); if (num < 0 || num >= (int)m_history.size()) return false; // We get the history oldest first, but our users expect newest first RclDHistoryEntry& hentry = m_history[m_history.size() - 1 - num]; if (sh) { if (m_prevtime < 0 || std::abs(m_prevtime - hentry.unixtime) > 86400) { m_prevtime = hentry.unixtime; time_t t = (time_t)(hentry.unixtime); *sh = string(ctime(&t)); // Get rid of the final \n in ctime sh->erase(sh->length()-1); } else { sh->erase(); } } bool ret = m_db->getDoc(hentry.udi, hentry.dbdir, doc); if (!ret || doc.pc == -1) { doc.url = "UNKNOWN"; doc.ipath = ""; } // Ensure the snippets link won't be shown as it does not make // sense (no query terms...) doc.haspages = 0; return ret; } int DocSequenceHistory::getResCnt() { if (m_history.empty()) m_history = getDocHistory(m_hist); return int(m_history.size()); } recoll-1.36.1/query/wasaparserdriver.h0000644000175000017500000000606514427373216014725 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _WASAPARSERDRIVER_H_INCLUDED_ #define _WASAPARSERDRIVER_H_INCLUDED_ #include #include #include #include "smallut.h" #include "searchdata.h" class WasaParserDriver; namespace yy { class parser; } class RclConfig; class WasaParserDriver { public: WasaParserDriver(const RclConfig *c, const std::string sl, const std::string& as) : m_stemlang(sl), m_autosuffs(as), m_config(c) {} ~WasaParserDriver() {} WasaParserDriver(const WasaParserDriver&) = delete; WasaParserDriver& operator=(const WasaParserDriver&) = delete; Rcl::SearchData *parse(const std::string&); bool addClause(Rcl::SearchData *sd, Rcl::SearchDataClauseSimple* cl); int GETCHAR(); void UNGETCHAR(int c); std::string& qualifiers() { return m_qualifiers; } void setreason(const std::string& reason) { m_reason = reason; } const std::string& getreason() const { return m_reason; } private: friend class yy::parser; std::string m_stemlang; std::string m_autosuffs; const RclConfig *m_config; // input string. std::string m_input; // Current position in m_input unsigned int m_index{0}; // Characters pushed-back, ready for next getchar. std::stack m_returns; // Result, set by parser. Rcl::SearchData *m_result{nullptr}; // Storage for top level filters std::vector m_filetypes; std::vector m_nfiletypes; bool m_haveDates{false}; DateInterval m_dates; // Restrict to date interval #ifdef EXT4_BIRTH_TIME bool m_haveBrDates{false}; DateInterval m_brdates; // Restrict to date interval #endif int64_t m_maxSize{-1}; int64_t m_minSize{-1}; int m_subSpec{Rcl::SearchData::SUBDOC_ANY}; std::string m_reason; // Let the quoted string reader store qualifiers in there, simpler // than handling this in the parser, because their nature is // determined by the absence of white space after the closing // dquote. e.g "some term"abc. We could avoid this by making white // space a token. std::string m_qualifiers; }; #endif /* _WASAPARSERDRIVER_H_INCLUDED_ */ recoll-1.36.1/query/docseqdb.cpp0000644000175000017500000001577614427373216013471 00000000000000/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include "docseqdb.h" #include "rcldb.h" #include "log.h" #include "wasatorcl.h" using std::list; using std::string; using std::vector; DocSequenceDb::DocSequenceDb(std::shared_ptr db, std::shared_ptr q, const string &t, std::shared_ptr sdata) : DocSequence(t), m_db(db), m_q(q), m_sdata(sdata), m_fsdata(sdata) { } void DocSequenceDb::getTerms(HighlightData& hld) { m_fsdata->getTerms(hld); } string DocSequenceDb::getDescription() { return m_fsdata->getDescription(); } bool DocSequenceDb::getDoc(int num, Rcl::Doc &doc, string *sh) { std::unique_lock locker(o_dblock); if (!setQuery()) return false; if (sh) sh->erase(); return m_q->getDoc(num, doc); } int DocSequenceDb::getResCnt() { std::unique_lock locker(o_dblock); if (!setQuery()) return false; if (m_rescnt < 0) { m_rescnt= m_q->getResCnt(); } return m_rescnt; } static const string cstr_mre("[...]"); // This one only gets called to fill-up the snippets window // We ignore most abstract/snippets preferences. bool DocSequenceDb::getAbstract(Rcl::Doc &doc, PlainToRich *ptr, vector& vpabs, int maxlen, bool sortbypage) { LOGDEB("DocSequenceDb::getAbstract/pair\n"); std::unique_lock locker(o_dblock); if (!setQuery()) return false; // Have to put the limit somewhere. int ret = Rcl::ABSRES_ERROR; if (m_q->whatDb()) { ret = m_q->makeDocAbstract( doc, ptr, vpabs, maxlen, m_q->whatDb()->getAbsCtxLen() + 2, sortbypage); } LOGDEB("DocSequenceDb::getAbstract: got ret " << ret << " vpabs len " << vpabs.size() << "\n"); if (vpabs.empty()) { return true; } // If the list was probably truncated, indicate it. if (ret & Rcl::ABSRES_TRUNC) { vpabs.push_back(Rcl::Snippet(-1, cstr_mre)); } if (ret & Rcl::ABSRES_TERMMISS) { vpabs.insert(vpabs.begin(), Rcl::Snippet(-1, "(Words missing in snippets)")); } return true; } bool DocSequenceDb::getAbstract(Rcl::Doc &doc, PlainToRich *ptr, vector& vabs) { std::unique_lock locker(o_dblock); if (!setQuery()) return false; if (m_q->whatDb() && m_queryBuildAbstract && (doc.syntabs || m_queryReplaceAbstract)) { m_q->makeDocAbstract(doc, ptr, vabs); } if (vabs.empty()) vabs.push_back(doc.meta[Rcl::Doc::keyabs]); return true; } int DocSequenceDb::getFirstMatchPage(Rcl::Doc &doc, string& term) { std::unique_lock locker(o_dblock); if (!setQuery()) return false; if (m_q->whatDb()) { return m_q->getFirstMatchPage(doc, term); } return -1; } int DocSequenceDb::getFirstMatchLine(const Rcl::Doc &doc, const string& term) { std::unique_lock locker(o_dblock); if (!setQuery()) return false; if (m_q->whatDb()) { return m_q->getFirstMatchLine(doc, term); } return 1; } list DocSequenceDb::expand(Rcl::Doc &doc) { std::unique_lock locker(o_dblock); if (!setQuery()) return list(); vector v = m_q->expand(doc); return list(v.begin(), v.end()); } string DocSequenceDb::title() { string qual; if (m_isFiltered && !m_isSorted) qual = string(" (") + o_filt_trans + string(")"); else if (!m_isFiltered && m_isSorted) qual = string(" (") + o_sort_trans + string(")"); else if (m_isFiltered && m_isSorted) qual = string(" (") + o_sort_trans + string(",") + o_filt_trans + string(")"); return DocSequence::title() + qual; } bool DocSequenceDb::setFiltSpec(const DocSeqFiltSpec &fs) { LOGDEB("DocSequenceDb::setFiltSpec\n"); std::unique_lock locker(o_dblock); if (fs.isNotNull()) { // We build a search spec by adding a filtering layer to the base one. m_fsdata = std::make_shared(Rcl::SCLT_AND, m_sdata->getStemLang()); Rcl::SearchDataClauseSub *cl = new Rcl::SearchDataClauseSub(m_sdata); m_fsdata->addClause(cl); for (unsigned int i = 0; i < fs.crits.size(); i++) { switch (fs.crits[i]) { case DocSeqFiltSpec::DSFS_MIMETYPE: m_fsdata->addFiletype(fs.values[i]); break; case DocSeqFiltSpec::DSFS_QLANG: { if (!m_q) break; string reason; auto sd = wasaStringToRcl( m_q->whatDb()->getConf(), m_sdata->getStemLang(), fs.values[i], reason); if (sd) { Rcl::SearchDataClauseSub *cl1 = new Rcl::SearchDataClauseSub(sd); m_fsdata->addClause(cl1); } } break; default: break; } } m_isFiltered = true; } else { m_fsdata = m_sdata; m_isFiltered = false; } m_needSetQuery = true; return true; } bool DocSequenceDb::setSortSpec(const DocSeqSortSpec &spec) { LOGDEB("DocSequenceDb::setSortSpec: fld [" << spec.field << "] " << (spec.desc ? "desc" : "asc") << "\n"); std::unique_lock locker(o_dblock); if (spec.isNotNull()) { m_q->setSortBy(spec.field, !spec.desc); m_isSorted = true; } else { m_q->setSortBy(string(), true); m_isSorted = false; } m_needSetQuery = true; return true; } bool DocSequenceDb::setQuery() { if (!m_needSetQuery) return true; m_needSetQuery = false; m_rescnt = -1; m_lastSQStatus = m_q->setQuery(m_fsdata); if (!m_lastSQStatus) { m_reason = m_q->getReason(); LOGERR("DocSequenceDb::setQuery: rclquery::setQuery failed: " << m_reason << "\n"); } return m_lastSQStatus; } bool DocSequenceDb::docDups(const Rcl::Doc& doc, std::vector& dups) { if (m_q->whatDb()) { std::unique_lock locker(o_dblock); return m_q->whatDb()->docDups(doc, dups); } else { return false; } } recoll-1.36.1/query/recollq.cpp0000644000175000017500000004331014501255346013323 00000000000000/* Copyright (C) 2006-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Takes a query and run it, no gui, results to stdout #include "autoconfig.h" #include #include #include #include #include #include #include #include #include #include #include "rcldb.h" #include "rclquery.h" #include "rclconfig.h" #include "pathut.h" #include "rclinit.h" #include "log.h" #include "wasatorcl.h" #include "internfile.h" #include "wipedir.h" #include "transcode.h" #include "textsplit.h" #include "smallut.h" #include "chrono.h" #include "base64.h" #include "rclutil.h" #include "internfile.h" #include "plaintorich.h" #include "hldata.h" #include "rcldoc.h" #include "searchdata.h" static PlainToRich g_hiliter; static const std::string cstr_ellipsis("..."); using namespace std; bool dump_contents(RclConfig *rclconfig, Rcl::Doc& idoc) { FileInterner interner(idoc, rclconfig, FileInterner::FIF_forPreview); Rcl::Doc fdoc; std::string ipath = idoc.ipath; if (interner.internfile(fdoc, ipath)) { cout << fdoc.text << "\n"; } else { cout << "Cant turn to text:" << idoc.url << " | " << idoc.ipath << "\n"; } return true; } std::string make_abstract(Rcl::Doc& doc, Rcl::Query& query, bool asSnippets, int snipcount, bool showlines, HighlightData& hldata) { std::vector snippets; std::ostringstream str; int cnt = 0; if (query.makeDocAbstract(doc, &g_hiliter, snippets, 0, -1, true)) { for (const auto& snippet : snippets) { if (snipcount > 0 && ++cnt > snipcount) break; if (asSnippets) { str << (showlines ? snippet.line : snippet.page) << " : " << snippet.snippet << "\n"; } else { str << snippet.snippet << cstr_ellipsis; } } } if (!asSnippets) { str << "\n"; } return str.str(); } void output_fields(std::vector fields, Rcl::Doc& doc, Rcl::Query& query, Rcl::Db&, bool printnames, bool asSnippets, int snipcnt, bool showlines, HighlightData& hldata) { if (fields.empty()) { for (const auto& entry : doc.meta) { fields.push_back(entry.first); } } for (const auto& fld : fields) { std::string out; if (fld == "abstract") { base64_encode(make_abstract(doc, query, asSnippets, snipcnt, showlines, hldata), out); } else if (fld == "xdocid") { char cdocid[30]; sprintf(cdocid, "%lu", (unsigned long)doc.xdocid); base64_encode(cdocid, out); } else { base64_encode(doc.meta[fld], out); } // Before printnames existed, recollq printed a single blank for empty // fields. This is a problem when printing names and using strtok, but // have to keep the old behaviour when printnames is not set. if (!(out.empty() && printnames)) { if (printnames) { cout << fld << " "; } cout << out << " "; } } cout << "\n"; } static char *thisprog; static char usage [] = " Runs a recoll query and displays result lines. \n" " By default, the argument(s) will be interpreted as a Recoll query language\n" " string. The -q option was kept for compatibility with the GUI and is just\n" " ignored: the query *must* be specified in the non-option arguments.\n" " Query language elements:\n" " * Implicit AND, exclusion, field spec: t1 -t2 title:t3\n" " * OR has priority: t1 OR t2 t3 OR t4 means (t1 OR t2) AND (t3 OR t4)\n" " * Phrase: \"t1 t2\" (needs additional quoting on cmd line)\n" " Other query modes :\n" " -o Emulate the GUI simple search in ANY TERM mode.\n" " -a Emulate the GUI simple search in ALL TERMS mode.\n" " -f Emulate the GUI simple search in filename mode.\n" " Query and results options:\n" " -c : specify configuration directory, overriding $RECOLL_CONFDIR.\n" " -C : collapse duplicates.\n" " -d also dump file contents.\n" " -n [first-] define the result slice. The default value for [first] is 0.\n" " Without the option, the default max count is 2000. Use n=0 for no limit.\n" " -b : basic. Just output urls, no mime types or titles.\n" " -Q : no result lines, just the processed query and result count.\n" " -m : dump the whole document meta[] array for each result.\n" " -A : output the document abstracts.\n" " -p : show snippets, with page numbers instead of the\n" " concatenated abstract.\n" " -g : show snippets, with line numbers instead of the\n" " concatenated abstract.\n" " -S fld : sort by field .\n" " -D : sort descending.\n" " -s stemlang : set stemming language to use (must exist in index...).\n" " Use -s \"\" to turn off stem expansion.\n" " -T : use the parameter (Thesaurus) for word expansion.\n" " -i : additional index, several can be given.\n" " -e use url encoding (%xx) for urls.\n" " -E use exact result count instead of lower bound estimate.\n" " -F : output exactly these fields for each result.\n" " The field values are encoded in base64, output in one line and \n" " separated by one space character. This is the recommended format \n" " for use by other programs. Use a normal query with option -m to \n" " see the field names. Use -F '' to output all fields, but you probably\n" " also want option -N in this case.\n" " -N : with -F, print the (plain text) field names before the field values.\n" " --extract_to : extract the first result to filepath, which must not\n" " exist. Use a -n option with an offset to select the appropriate result.\n" " --paths-only: only print results which would have a file:// scheme, and\n" " exclude the scheme.\n" " Other non-query usages:\n" " -P: Show the date span for all the documents present in the index.\n" ; static void Usage(std::ostream &os = std::cerr) { os << "Usage: " << thisprog << " [options] [query elements]" << "\n" << usage; exit(1); } // BEWARE COMPATIBILITY WITH recoll OPTIONS letters static int op_flags; #define OPT_A 0x1 #define OPT_a 0x2 #define OPT_b 0x4 #define OPT_C 0x8 #define OPT_D 0x10 #define OPT_d 0x20 #define OPT_e 0x40 #define OPT_F 0x80 #define OPT_g 0x100 #define OPT_f 0x200 #define OPT_l 0x400 #define OPT_m 0x800 #define OPT_N 0x1000 #define OPT_o 0x2000 #define OPT_p 0x4000 #define OPT_P 0x8000 #define OPT_Q 0x10000 #define OPT_q 0x20000 #define OPT_S 0x40000 #define OPT_E 0x80000 static struct option long_options[] = { #define OPTION_EXTRACT 1000 #define OPTION_AUTOSPELL 1001 #define OPTION_AUTOSPELLMAXDIST 1002 #define OPTION_PATHS_ONLY 1003 {"extract-to", required_argument, 0, OPTION_EXTRACT}, {"autospell", no_argument, nullptr, OPTION_AUTOSPELL}, {"autospell-max-distance", required_argument, 0, OPTION_AUTOSPELLMAXDIST}, {"help", no_argument, 0, 'h'}, {"paths-only", no_argument, 0, OPTION_PATHS_ONLY}, {nullptr, 0, nullptr, 0} }; int recollq(RclConfig **cfp, int argc, char **argv) { thisprog = argv[0]; std::string a_config; std::string sortfield; std::string stemlang("english"); list extra_dbs; std::string sf; std::string syngroupsfn; int firstres = 0; int maxcount = 2000; int snipcnt = -1; std::string extractfile; bool autospell{false}; int autospellmaxdist{1}; bool paths_only{false}; int ret; while ((ret = getopt_long(argc, argv, "+AabCc:DdEefF:hi:lmNn:oPp:g:QqS:s:tT:v", long_options, NULL)) != -1) { switch (ret) { case 'A': op_flags |= OPT_A; break; // GUI: -a same case 'a': op_flags |= OPT_a; break; case 'b': op_flags |= OPT_b; break; case 'C': op_flags |= OPT_C; break; // GUI: -c same case 'c': a_config = optarg; break; case 'd': op_flags |= OPT_d; break; case 'D': op_flags |= OPT_D; break; case 'E': op_flags |= OPT_E; break; case 'e': op_flags |= OPT_e; break; case 'F': op_flags |= OPT_F; sf = optarg; break; // GUI: -f same case 'f': op_flags |= OPT_f; break; // GUI: -h same case 'h': Usage(std::cout); // Usage exits case 'i': extra_dbs.push_back(optarg); break; // GUI uses -L to set language of messages // GUI: -l specifies query language, which is the default. Accept and ignore case 'l': op_flags |= OPT_l; break; case 'm': op_flags |= OPT_m; break; case 'N': op_flags |= OPT_N; break; case 'n': { std::string rescnt = optarg; std::string::size_type dash = rescnt.find("-"); if (dash != std::string::npos) { firstres = atoi(rescnt.substr(0, dash).c_str()); if (dash < rescnt.size()-1) { maxcount = atoi(rescnt.substr(dash+1).c_str()); } } else { maxcount = atoi(rescnt.c_str()); } if (maxcount <= 0) maxcount = INT_MAX; } break; // GUI: -o same case 'o': op_flags |= OPT_o; break; case 'P': op_flags |= OPT_P; break; case 'p': op_flags |= OPT_p; goto porg; case 'g': op_flags |= OPT_g; { porg: const char *cp = optarg; char *cpe; snipcnt = strtol(cp, &cpe, 10); if (*cpe != 0) Usage(); } break; case 'Q': op_flags |= OPT_Q; break; // GUI: -q same case 'q': op_flags |= OPT_q; break; case 'S': op_flags |= OPT_S; sortfield = optarg; break; case 's': stemlang = optarg; break; // GUI: -t use command line, us: ignored case 't': break; case 'T': syngroupsfn = optarg; break; // GUI: -v same case 'v': std::cout << Rcl::version_string() << "\n"; return 0; // GUI uses -w : open minimized case OPTION_EXTRACT: extractfile = optarg;break; case OPTION_AUTOSPELL: autospell = true; break; case OPTION_AUTOSPELLMAXDIST: autospellmaxdist = atoi(optarg); break; case OPTION_PATHS_ONLY: paths_only = true; op_flags |= OPT_b; break; } } std::string reason; *cfp = recollinit(0, nullptr, nullptr, reason, &a_config); RclConfig *rclconfig = *cfp; if (!rclconfig || !rclconfig->ok()) { std::cerr << "Recoll init failed: " << reason << "\n"; exit(1); } if (argc < 1 && !(op_flags & OPT_P)) { Usage(); } std::vector fields; if (op_flags & OPT_F) { if (op_flags & (OPT_b|OPT_d|OPT_b|OPT_Q|OPT_m|OPT_A)) Usage(); stringToStrings(sf, fields); } Rcl::Db rcldb(rclconfig); if (!extra_dbs.empty()) { for (const auto& db : extra_dbs) { if (!rcldb.addQueryDb(db)) { cerr << "Can't add index: " << db << "\n"; exit(1); } } } if (!syngroupsfn.empty()) { if (!rcldb.setSynGroupsFile(syngroupsfn)) { cerr << "Can't use synonyms file: " << syngroupsfn << "\n"; exit(1); } } rcldb.setUseSpellFuzz(autospell); rcldb.setMaxSpellDist(autospellmaxdist); if (!rcldb.open(Rcl::Db::DbRO)) { cerr << "Cant open database in " << rclconfig->getDbDir() << " reason: " << rcldb.getReason() << "\n"; exit(1); } if (op_flags & OPT_P) { int minyear, maxyear; if (!rcldb.maxYearSpan(&minyear, &maxyear)) { cerr << "maxYearSpan failed: " << rcldb.getReason() << "\n"; return 1; } else { cout << "Min year " << minyear << " Max year " << maxyear << "\n"; return 0; } } if (optind >= argc) { Usage(); } std::string qs; while (optind < argc) { qs += std::string(argv[optind++]) + " "; } { std::string uq; std::string charset = rclconfig->getDefCharset(true); int ercnt; if (!transcode(qs, uq, charset, "UTF-8", &ercnt)) { std::cerr << "Can't convert command line args to utf-8\n"; return 1; } else if (ercnt) { std::cerr << ercnt << " errors while converting arguments from " << charset << "to utf-8\n"; } qs = uq; } std::shared_ptr sd; if (op_flags & (OPT_a|OPT_o|OPT_f)) { sd = std::make_shared(Rcl::SCLT_OR, stemlang); Rcl::SearchDataClause *clp; if (op_flags & OPT_f) { clp = new Rcl::SearchDataClauseFilename(qs); } else { clp = new Rcl::SearchDataClauseSimple( (op_flags & OPT_o) ? Rcl::SCLT_OR : Rcl::SCLT_AND, qs); } if (sd) sd->addClause(clp); } else { sd = wasaStringToRcl(rclconfig, stemlang, qs, reason); } if (!sd) { std::cerr << "Query string interpretation failed: " << reason << "\n"; return 1; } std::shared_ptr rq(sd); Rcl::Query query(&rcldb); if (op_flags & OPT_C) { query.setCollapseDuplicates(true); } if (op_flags & OPT_S) { query.setSortBy(sortfield, (op_flags & OPT_D) ? false : true); } Chrono chron; if (!query.setQuery(rq)) { std::cerr << "Query setup failed: " << query.getReason() << "\n"; return 1; } HighlightData hldata; sd->getTerms(hldata); int cnt; if (op_flags & OPT_E) { cnt = query.getResCnt(-1, true); } else { cnt = query.getResCnt(); } if (!(op_flags & OPT_b)) { std::cout << "Recoll query: " << rq->getDescription() << "\n"; if (firstres == 0) { if (cnt <= maxcount) std::cout << cnt << " results" << "\n"; else std::cout << cnt << " results (printing " << maxcount << " max):" << "\n"; } else { std::cout << "Printing at most " << cnt - (firstres+maxcount) << " results from first " << firstres << "\n"; } } if (op_flags & OPT_Q) { std::cout << "Query setup took " << chron.millis() << " mS" << "\n"; return 0; } for (int i = firstres; i < firstres + maxcount; i++) { Rcl::Doc doc; if (!query.getDoc(i, doc)) break; if (paths_only && doc.url.find("file://") != 0) { continue; } if (!extractfile.empty()) { if (path_exists(extractfile)) { std::cerr << "Output file must not exist.\n"; return 1; } TempFile unused; auto ret = FileInterner::idocToFile(unused, extractfile, rclconfig, doc, false); return ret ? 0 : 1; } if (op_flags & OPT_F) { output_fields(fields, doc, query, rcldb, op_flags & OPT_N, op_flags & (OPT_p|OPT_g), snipcnt, op_flags & OPT_g, hldata); continue; } if (op_flags & OPT_e) doc.url = url_encode(doc.url); if (op_flags & OPT_b) { cout << (paths_only ? doc.url.substr(7) : doc.url) << "\n"; } else { std::string titleorfn = doc.meta[Rcl::Doc::keytt]; if (titleorfn.empty()) titleorfn = doc.meta[Rcl::Doc::keyfn]; if (titleorfn.empty()) { std::string url; printableUrl(rclconfig->getDefCharset(), doc.url, url); titleorfn = path_getsimple(url); } char cpc[20]; sprintf(cpc, "%d", doc.pc); cout << doc.mimetype << "\t" << "[" << doc.url << "]" << "\t" << "[" << titleorfn << "]" << "\t" << doc.fbytes << "\tbytes" << "\t" << "\n"; if (op_flags & OPT_m) { for (const auto& ent : doc.meta) { cout << ent.first << " = " << ent.second << "\n"; } } if (op_flags & OPT_A) { bool asSnippets = (op_flags & (OPT_p|OPT_g)) != 0; bool showlines = (op_flags & OPT_g) != 0; std::string abstract = make_abstract(doc, query, asSnippets, snipcnt, showlines, hldata); std::string marker = asSnippets ? "SNIPPETS" : "ABSTRACT"; if (!abstract.empty()) { cout << marker << "\n" << abstract << "/" << marker << "\n"; } } } if (op_flags & OPT_d) { dump_contents(rclconfig, doc); } } return 0; } recoll-1.36.1/query/wasaparse.ypp0000644000175000017500000003026514426500174013701 00000000000000%{ #define YYDEBUG 1 #include "autoconfig.h" #include #include #include #include "searchdata.h" #include "wasaparserdriver.h" #include "wasaparse.hpp" using namespace std; //#define LOG_PARSER #ifdef LOG_PARSER #define LOGP(X) {cerr << X;} #else #define LOGP(X) #endif int yylex(yy::parser::semantic_type *, yy::parser::location_type *, WasaParserDriver *); void yyerror(char const *); static void qualify(Rcl::SearchDataClauseDist *, const string &); static void addSubQuery(WasaParserDriver *, Rcl::SearchData *sd, Rcl::SearchData *sq) { if (sd && sq) sd->addClause( new Rcl::SearchDataClauseSub(std::shared_ptr(sq))); } %} %skeleton "lalr1.cc" %defines %locations %error-verbose %parse-param {WasaParserDriver* d} %lex-param {WasaParserDriver* d} %union { std::string *str; Rcl::SearchDataClauseRange *rg; Rcl::SearchDataClauseSimple *cl; Rcl::SearchData *sd; } %destructor {delete $$;} %type qualquote %type fieldexpr %type range %type term %type query %type complexfieldname /* Non operator tokens need precedence because of the possibility of concatenation which needs to have lower prec than OR */ %left WORD %left QUOTED %left QUALIFIERS %left AND UCONCAT '(' '-' %left OR %token EQUALS CONTAINS SMALLEREQ SMALLER GREATEREQ GREATER RANGE %% topquery: query { // It's possible that we end up with no query (e.g.: because just a // date filter was set, no terms). Allocate an empty query so that we // have something to set the global criteria on (this will yield a // Xapian search like FILTER xxx if ($1 == nullptr) d->m_result = new Rcl::SearchData(Rcl::SCLT_AND, d->m_stemlang); else d->m_result = $1; } query: query query %prec UCONCAT { LOGP("q: query query\n"); Rcl::SearchData *sd = nullptr; if ($1 || $2) { sd = new Rcl::SearchData(Rcl::SCLT_AND, d->m_stemlang); addSubQuery(d, sd, $1); addSubQuery(d, sd, $2); } $$ = sd; } | query AND query { LOGP("q: query AND query\n"); Rcl::SearchData *sd = nullptr; if ($1 || $3) { sd = new Rcl::SearchData(Rcl::SCLT_AND, d->m_stemlang); addSubQuery(d, sd, $1); addSubQuery(d, sd, $3); } $$ = sd; } | query OR query { LOGP("query: query OR query\n"); Rcl::SearchData *top = nullptr; if ($1 || $3) { top = new Rcl::SearchData(Rcl::SCLT_OR, d->m_stemlang); addSubQuery(d, top, $1); addSubQuery(d, top, $3); } $$ = top; } | '(' query ')' { LOGP("q: ( query )\n"); $$ = $2; } | fieldexpr %prec UCONCAT { LOGP("q: fieldexpr\n"); Rcl::SearchData *sd = new Rcl::SearchData(Rcl::SCLT_AND, d->m_stemlang); if (d->addClause(sd, $1)) { $$ = sd; } else { delete sd; $$ = nullptr; } } ; fieldexpr: term { LOGP("fe: simple fieldexpr: " << $1->gettext() << endl); $$ = $1; } | complexfieldname EQUALS term { LOGP("fe: " << *$1 << " = " << $3->gettext() << endl); $3->setfield(*$1); $3->setrel(Rcl::SearchDataClause::REL_EQUALS); $$ = $3; delete $1; } | complexfieldname CONTAINS term { LOGP("fe: " << *$1 << " : " << $3->gettext() << endl); $3->setfield(*$1); $3->setrel(Rcl::SearchDataClause::REL_CONTAINS); $$ = $3; delete $1; } | complexfieldname CONTAINS range { LOGP("fe: " << *$1 << " : " << $3->gettext() << endl); $3->setfield(*$1); $3->setrel(Rcl::SearchDataClause::REL_CONTAINS); $$ = $3; delete $1; } | complexfieldname SMALLER term { LOGP("fe: " << *$1 << " < " << $3->gettext() << endl); $3->setfield(*$1); $3->setrel(Rcl::SearchDataClause::REL_LT); $$ = $3; delete $1; } | complexfieldname SMALLEREQ term { LOGP("fe: " << *$1 << " <= " << $3->gettext() << endl); $3->setfield(*$1); $3->setrel(Rcl::SearchDataClause::REL_LTE); $$ = $3; delete $1; } | complexfieldname GREATER term { LOGP("fe: " << *$1 << " > " << $3->gettext() << endl); $3->setfield(*$1); $3->setrel(Rcl::SearchDataClause::REL_GT); $$ = $3; delete $1; } | complexfieldname GREATEREQ term { LOGP("fe: " << *$1 << " >= " << $3->gettext() << endl); $3->setfield(*$1); $3->setrel(Rcl::SearchDataClause::REL_GTE); $$ = $3; delete $1; } | '-' fieldexpr { LOGP("fe: - fieldexpr[" << $2->gettext() << "]" << endl); $2->setexclude(true); $$ = $2; } ; /* Deal with field names like dc:title */ complexfieldname: WORD { LOGP("cfn: WORD" << endl); $$ = $1; } | complexfieldname CONTAINS WORD { LOGP("cfn: complexfieldname ':' WORD" << endl); $$ = new string(*$1 + string(":") + *$3); delete $1; delete $3; } range: WORD RANGE WORD { LOGP("Range: " << *$1 << string(" .. ") << *$3 << endl); $$ = new Rcl::SearchDataClauseRange(*$1, *$3); delete $1; delete $3; } | RANGE WORD { LOGP("Range: " << "" << string(" .. ") << *$2 << endl); $$ = new Rcl::SearchDataClauseRange("", *$2); delete $2; } | WORD RANGE { LOGP("Range: " << *$1 << string(" .. ") << "" << endl); $$ = new Rcl::SearchDataClauseRange(*$1, ""); delete $1; } ; term: WORD { LOGP("term[" << *$1 << "]" << endl); $$ = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND, *$1); delete $1; } | qualquote { $$ = $1; } qualquote: QUOTED { LOGP("QUOTED[" << *$1 << "]" << endl); $$ = new Rcl::SearchDataClauseDist(Rcl::SCLT_PHRASE, *$1, 0); delete $1; } | QUOTED QUALIFIERS { LOGP("QUOTED[" << *$1 << "] QUALIFIERS[" << *$2 << "]" << endl); Rcl::SearchDataClauseDist *cl = new Rcl::SearchDataClauseDist(Rcl::SCLT_PHRASE, *$1, 0); qualify(cl, *$2); $$ = cl; delete $1; delete $2; } %% #include // Look for int at index, skip and return new index found? value. static unsigned int qualGetInt(const string& q, unsigned int cur, int *pval) { unsigned int ncur = cur; if (cur < q.size() - 1) { char *endptr; int val = strtol(&q[cur + 1], &endptr, 10); if (endptr != &q[cur + 1]) { ncur += endptr - &q[cur + 1]; *pval = val; } } return ncur; } static void qualify(Rcl::SearchDataClauseDist *cl, const string& quals) { // cerr << "qualify(" << cl << ", " << quals << ")" << endl; for (unsigned int i = 0; i < quals.length(); i++) { //fprintf(stderr, "qual char %c\n", quals[i]); switch (quals[i]) { case 'b': cl->setWeight(10.0); break; case 'c': break; case 'C': cl->addModifier(Rcl::SearchDataClause::SDCM_CASESENS); break; case 'd': break; case 'D': cl->addModifier(Rcl::SearchDataClause::SDCM_DIACSENS); break; case 'e': cl->addModifier(Rcl::SearchDataClause::SDCM_CASESENS); cl->addModifier(Rcl::SearchDataClause::SDCM_DIACSENS); cl->addModifier(Rcl::SearchDataClause::SDCM_NOSTEMMING); break; case 'l': cl->addModifier(Rcl::SearchDataClause::SDCM_NOSTEMMING); break; case 'L': break; case 'o': { int slack = 10; i = qualGetInt(quals, i, &slack); cl->setslack(slack); //cerr << "set slack " << cl->getslack() << " done" << endl; } break; case 'p': cl->setTp(Rcl::SCLT_NEAR); if (cl->getslack() == 0) { cl->setslack(10); //cerr << "set slack " << cl->getslack() << " done" << endl; } break; case 's': cl->addModifier(Rcl::SearchDataClause::SDCM_NOSYNS); break; case 'S': break; case 'x': cl->addModifier(Rcl::SearchDataClause::SDCM_EXPANDPHRASE); break; case '.':case '0':case '1':case '2':case '3':case '4': case '5':case '6':case '7':case '8':case '9': { int n = 0; float factor = 1.0; if (sscanf(&(quals[i]), "%f %n", &factor, &n)) { if (factor != 1.0) { cl->setWeight(factor); } } if (n > 0) i += n - 1; } default: break; } } } // specialstartchars are special only at the beginning of a token // (e.g. doctor-who is a term, not 2 terms separated by '-') static const string specialstartchars("-"); // specialinchars are special everywhere except inside a quoted string static const string specialinchars(":=<>()"); // Called with the first dquote already read static int parseString(WasaParserDriver *d, yy::parser::semantic_type *yylval) { string* value = new string(); d->qualifiers().clear(); int c; while ((c = d->GETCHAR())) { switch (c) { case '\\': /* Escape: get next char */ c = d->GETCHAR(); if (c == 0) { value->push_back(c); goto out; } value->push_back(c); break; case '"': /* End of string. Look for qualifiers */ while ((c = d->GETCHAR()) && (isalnum(c) || c == '.')) d->qualifiers().push_back(c); d->UNGETCHAR(c); goto out; default: value->push_back(c); } } out: //cerr << "GOT QUOTED ["<qualifiers() << "]" << endl; yylval->str = value; return yy::parser::token::QUOTED; } int yylex(yy::parser::semantic_type *yylval, yy::parser::location_type *, WasaParserDriver *d) { if (!d->qualifiers().empty()) { yylval->str = new string(); yylval->str->swap(d->qualifiers()); return yy::parser::token::QUALIFIERS; } int c; /* Skip white space. */ while ((c = d->GETCHAR()) && isspace(c)) continue; if (c == 0) return 0; if (specialstartchars.find_first_of(c) != string::npos) { //cerr << "yylex: return " << c << endl; return c; } // field-term relations, and ranges switch (c) { case '=': return yy::parser::token::EQUALS; case ':': return yy::parser::token::CONTAINS; case '<': { int c1 = d->GETCHAR(); if (c1 == '=') { return yy::parser::token::SMALLEREQ; } else { d->UNGETCHAR(c1); return yy::parser::token::SMALLER; } } case '.': { int c1 = d->GETCHAR(); if (c1 == '.') { return yy::parser::token::RANGE; } else { d->UNGETCHAR(c1); break; } } case '>': { int c1 = d->GETCHAR(); if (c1 == '=') { return yy::parser::token::GREATEREQ; } else { d->UNGETCHAR(c1); return yy::parser::token::GREATER; } } case '(': case ')': return c; } if (c == '"') return parseString(d, yylval); d->UNGETCHAR(c); // Other chars start a term or field name or reserved word string* word = new string(); while ((c = d->GETCHAR())) { if (isspace(c)) { //cerr << "Word broken by whitespace" << endl; break; } else if (specialinchars.find_first_of(c) != string::npos) { //cerr << "Word broken by special char" << endl; d->UNGETCHAR(c); break; } else if (c == '.') { int c1 = d->GETCHAR(); if (c1 == '.') { d->UNGETCHAR(c1); d->UNGETCHAR(c); break; } else { d->UNGETCHAR(c1); word->push_back(c); } } else if (c == 0) { //cerr << "Word broken by EOF" << endl; break; } else { word->push_back(c); } } if (!word->compare("AND") || !word->compare("&&")) { delete word; return yy::parser::token::AND; } else if (!word->compare("OR") || !word->compare("||")) { delete word; return yy::parser::token::OR; } // cerr << "Got word [" << word << "]" << endl; yylval->str = word; return yy::parser::token::WORD; } recoll-1.36.1/query/location.hh0000644000175000017500000001727014410615043013306 00000000000000// A Bison parser, made by GNU Bison 3.8.2. // Locations for Bison parsers in C++ // Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . // As a special exception, you may create a larger work that contains // part or all of the Bison parser skeleton and distribute that work // under terms of your choice, so long as that work isn't itself a // parser generator using the skeleton or a modified version thereof // as a parser skeleton. Alternatively, if you modify or redistribute // the parser skeleton itself, you may (at your option) remove this // special exception, which will cause the skeleton and the resulting // Bison output files to be licensed under the GNU General Public // License without this special exception. // This special exception was added by the Free Software Foundation in // version 2.2 of Bison. /** ** \file location.hh ** Define the yy::location class. */ #ifndef YY_YY_LOCATION_HH_INCLUDED # define YY_YY_LOCATION_HH_INCLUDED # include # include # ifndef YY_NULLPTR # if defined __cplusplus # if 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # else # define YY_NULLPTR ((void*)0) # endif # endif namespace yy { #line 58 "location.hh" /// A point in a source file. class position { public: /// Type for file name. typedef const std::string filename_type; /// Type for line and column numbers. typedef int counter_type; /// Construct a position. explicit position (filename_type* f = YY_NULLPTR, counter_type l = 1, counter_type c = 1) : filename (f) , line (l) , column (c) {} /// Initialization. void initialize (filename_type* fn = YY_NULLPTR, counter_type l = 1, counter_type c = 1) { filename = fn; line = l; column = c; } /** \name Line and Column related manipulators ** \{ */ /// (line related) Advance to the COUNT next lines. void lines (counter_type count = 1) { if (count) { column = 1; line = add_ (line, count, 1); } } /// (column related) Advance to the COUNT next columns. void columns (counter_type count = 1) { column = add_ (column, count, 1); } /** \} */ /// File name to which this position refers. filename_type* filename; /// Current line number. counter_type line; /// Current column number. counter_type column; private: /// Compute max (min, lhs+rhs). static counter_type add_ (counter_type lhs, counter_type rhs, counter_type min) { return lhs + rhs < min ? min : lhs + rhs; } }; /// Add \a width columns, in place. inline position& operator+= (position& res, position::counter_type width) { res.columns (width); return res; } /// Add \a width columns. inline position operator+ (position res, position::counter_type width) { return res += width; } /// Subtract \a width columns, in place. inline position& operator-= (position& res, position::counter_type width) { return res += -width; } /// Subtract \a width columns. inline position operator- (position res, position::counter_type width) { return res -= width; } /** \brief Intercept output stream redirection. ** \param ostr the destination output stream ** \param pos a reference to the position to redirect */ template std::basic_ostream& operator<< (std::basic_ostream& ostr, const position& pos) { if (pos.filename) ostr << *pos.filename << ':'; return ostr << pos.line << '.' << pos.column; } /// Two points in a source file. class location { public: /// Type for file name. typedef position::filename_type filename_type; /// Type for line and column numbers. typedef position::counter_type counter_type; /// Construct a location from \a b to \a e. location (const position& b, const position& e) : begin (b) , end (e) {} /// Construct a 0-width location in \a p. explicit location (const position& p = position ()) : begin (p) , end (p) {} /// Construct a 0-width location in \a f, \a l, \a c. explicit location (filename_type* f, counter_type l = 1, counter_type c = 1) : begin (f, l, c) , end (f, l, c) {} /// Initialization. void initialize (filename_type* f = YY_NULLPTR, counter_type l = 1, counter_type c = 1) { begin.initialize (f, l, c); end = begin; } /** \name Line and Column related manipulators ** \{ */ public: /// Reset initial location to final location. void step () { begin = end; } /// Extend the current location to the COUNT next columns. void columns (counter_type count = 1) { end += count; } /// Extend the current location to the COUNT next lines. void lines (counter_type count = 1) { end.lines (count); } /** \} */ public: /// Beginning of the located region. position begin; /// End of the located region. position end; }; /// Join two locations, in place. inline location& operator+= (location& res, const location& end) { res.end = end.end; return res; } /// Join two locations. inline location operator+ (location res, const location& end) { return res += end; } /// Add \a width columns to the end position, in place. inline location& operator+= (location& res, location::counter_type width) { res.columns (width); return res; } /// Add \a width columns to the end position. inline location operator+ (location res, location::counter_type width) { return res += width; } /// Subtract \a width columns to the end position, in place. inline location& operator-= (location& res, location::counter_type width) { return res += -width; } /// Subtract \a width columns to the end position. inline location operator- (location res, location::counter_type width) { return res -= width; } /** \brief Intercept output stream redirection. ** \param ostr the destination output stream ** \param loc a reference to the location to redirect ** ** Avoid duplicate information. */ template std::basic_ostream& operator<< (std::basic_ostream& ostr, const location& loc) { location::counter_type end_col = 0 < loc.end.column ? loc.end.column - 1 : 0; ostr << loc.begin; if (loc.end.filename && (!loc.begin.filename || *loc.begin.filename != *loc.end.filename)) ostr << '-' << loc.end.filename << ':' << loc.end.line << '.' << end_col; else if (loc.begin.line < loc.end.line) ostr << '-' << loc.end.line << '.' << end_col; else if (loc.begin.column < end_col) ostr << '-' << end_col; return ostr; } } // yy #line 303 "location.hh" #endif // !YY_YY_LOCATION_HH_INCLUDED recoll-1.36.1/query/docseq.h0000644000175000017500000002230114427373216012606 00000000000000/* Copyright (C) 2004-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _DOCSEQ_H_INCLUDED_ #define _DOCSEQ_H_INCLUDED_ #include #include #include #include #include #include "rcldoc.h" #include "hldata.h" // Need this for the "Snippet" class def. #include "rclquery.h" // A result list entry. struct ResListEntry { Rcl::Doc doc; std::string subHeader; }; /** Sort specification. */ class DocSeqSortSpec { public: DocSeqSortSpec() : desc(false) {} bool isNotNull() const {return !field.empty();} void reset() {field.erase();} std::string field; bool desc; }; /** Filtering spec. This is only used to filter by doc category for now, hence the rather specialized interface */ class DocSeqFiltSpec { public: DocSeqFiltSpec() {} enum Crit {DSFS_MIMETYPE, DSFS_QLANG, DSFS_PASSALL}; void orCrit(Crit crit, const std::string& value) { crits.push_back(crit); values.push_back(value); } std::vector crits; std::vector values; void reset() {crits.clear(); values.clear();} bool isNotNull() const {return crits.size() != 0;} }; class PlainToRich; /** Interface for a list of documents coming from some source. The result list display data may come from different sources (ie: history or Db query), and be post-processed (DocSeqSorted). Additional functionality like filtering/sorting can either be obtained by stacking DocSequence objects (ie: sorting history), or by native capability (ex: docseqdb can sort and filter). The implementation might be nicer by using more sophisticated c++ with multiple inheritance of sort and filter virtual interfaces, but the current one will have to do for now. */ class DocSequence { public: DocSequence(const std::string &t) : m_title(t) {} virtual ~DocSequence() {} DocSequence(const DocSequence&) = delete; DocSequence& operator=(const DocSequence&) = delete; /** Get document at given rank. * * @param num document rank in sequence * @param doc return data * @param sh subheader to display before this result (ie: date change * inside history) * @return true if ok, false for error or end of data */ virtual bool getDoc(int num, Rcl::Doc &doc, std::string *sh = nullptr) = 0; /** Get next page of documents. This accumulates entries into the result * list parameter (doesn't reset it). */ virtual int getSeqSlice(int offs, int cnt, std::vector& result); /** Get abstract for document. This is special because it may take time. * The default is to return the input doc's abstract fields, but some * sequences can compute a better value (ie: docseqdb) */ virtual bool getAbstract(Rcl::Doc& doc, PlainToRich *, std::vector& abs) { abs.push_back(doc.meta[Rcl::Doc::keyabs]); return true; } virtual bool getAbstract(Rcl::Doc& doc, PlainToRich *, std::vector& abs, int, bool) { abs.push_back(Rcl::Snippet(0, doc.meta[Rcl::Doc::keyabs])); return true; } virtual int getFirstMatchPage(Rcl::Doc&, std::string&) { return -1; } virtual int getFirstMatchLine(const Rcl::Doc&, const std::string&) { return 1; } /** Get duplicates. */ virtual bool docDups(const Rcl::Doc&, std::vector&) { return false; } /** For an embedded document: get the immediately enclosing doc * (e.g., for an attachment, the message it is attached to. Only * makes sense is ipath is not empty. */ virtual bool getEnclosing(Rcl::Doc&, Rcl::Doc&); /** Get estimated total count in results */ virtual int getResCnt() = 0; /** Get title for result list */ virtual std::string title() { return m_title; } /** Can do snippets ? */ virtual bool snippetsCapable() { return false; } /** Get description for underlying query */ virtual std::string getDescription() = 0; /** Get search terms (for highlighting abstracts). Some sequences * may have no associated search terms. Implement this for them. */ virtual void getTerms(HighlightData& hld) { hld.clear(); } virtual std::list expand(Rcl::Doc &) { return std::list(); } virtual std::string getReason() { return m_reason; } /** Optional functionality. */ virtual bool canFilter() {return false;} virtual bool canSort() {return false;} virtual bool setFiltSpec(const DocSeqFiltSpec &) {return false;} virtual bool setSortSpec(const DocSeqSortSpec &) {return false;} virtual std::shared_ptr getSourceSeq() { return std::shared_ptr();} static void set_translations(const std::string& sort, const std::string& filt) { o_sort_trans = sort; o_filt_trans = filt; } protected: friend class DocSeqModifier; virtual std::shared_ptr getDb() = 0; static std::mutex o_dblock; static std::string o_sort_trans; static std::string o_filt_trans; std::string m_reason; private: std::string m_title; }; /** A modifier has a child sequence which does the real work and does * something with the results. Some operations are just delegated */ class DocSeqModifier : public DocSequence { public: DocSeqModifier(std::shared_ptr iseq) : DocSequence(""), m_seq(iseq) {} virtual ~DocSeqModifier() {} DocSeqModifier(const DocSeqModifier&) = delete; DocSeqModifier& operator=(const DocSeqModifier&) = delete; virtual bool getAbstract(Rcl::Doc& doc, PlainToRich *ptr, std::vector& abs) override { if (!m_seq) return false; return m_seq->getAbstract(doc, ptr, abs); } virtual bool getAbstract(Rcl::Doc& doc, PlainToRich *ptr, std::vector& abs, int maxlen, bool bypage) override { if (!m_seq) return false; return m_seq->getAbstract(doc, ptr, abs, maxlen, bypage); } /** Get duplicates. */ virtual bool docDups(const Rcl::Doc& doc, std::vector& dups) override { if (!m_seq) return false; return m_seq->docDups(doc, dups); } virtual bool snippetsCapable() override { if (!m_seq) return false; return m_seq->snippetsCapable(); } virtual std::string getDescription() override { if (!m_seq) return ""; return m_seq->getDescription(); } virtual void getTerms(HighlightData& hld) override { if (!m_seq) return; m_seq->getTerms(hld); } virtual bool getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc) override { if (!m_seq) return false; return m_seq->getEnclosing(doc, pdoc); } virtual std::string getReason() override { if (!m_seq) return std::string(); return m_seq->getReason(); } virtual std::string title() override { return m_seq->title(); } virtual std::shared_ptr getSourceSeq() override { return m_seq; } protected: virtual std::shared_ptr getDb() override { if (!m_seq) return nullptr; return m_seq->getDb(); } std::shared_ptr m_seq; }; class RclConfig; // A DocSource can juggle docseqs of different kinds to implement // sorting and filtering in ways depending on the base seqs capabilities class DocSource : public DocSeqModifier { public: DocSource(RclConfig *config, std::shared_ptr iseq) : DocSeqModifier(iseq), m_config(config) {} virtual bool canFilter() override {return true;} virtual bool canSort() override {return true;} virtual bool setFiltSpec(const DocSeqFiltSpec &) override; virtual bool setSortSpec(const DocSeqSortSpec &) override; virtual bool getDoc(int num, Rcl::Doc &doc, std::string *sh = nullptr) override { if (!m_seq) return false; return m_seq->getDoc(num, doc, sh); } virtual int getResCnt() override { if (!m_seq) return 0; return m_seq->getResCnt(); } virtual std::string title() override; private: bool buildStack(); void stripStack(); RclConfig *m_config; DocSeqFiltSpec m_fspec; DocSeqSortSpec m_sspec; }; #endif /* _DOCSEQ_H_ */ recoll-1.36.1/query/wasaparse.cpp0000644000175000017500000014334314426500174013655 00000000000000// A Bison parser, made by GNU Bison 3.8.2. // Skeleton implementation for Bison LALR(1) parsers in C++ // Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . // As a special exception, you may create a larger work that contains // part or all of the Bison parser skeleton and distribute that work // under terms of your choice, so long as that work isn't itself a // parser generator using the skeleton or a modified version thereof // as a parser skeleton. Alternatively, if you modify or redistribute // the parser skeleton itself, you may (at your option) remove this // special exception, which will cause the skeleton and the resulting // Bison output files to be licensed under the GNU General Public // License without this special exception. // This special exception was added by the Free Software Foundation in // version 2.2 of Bison. // DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, // especially those whose name start with YY_ or yy_. They are // private implementation details that can be changed or removed. // First part of user prologue. #line 1 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" #define YYDEBUG 1 #include "autoconfig.h" #include #include #include #include "searchdata.h" #include "wasaparserdriver.h" #include "wasaparse.hpp" using namespace std; //#define LOG_PARSER #ifdef LOG_PARSER #define LOGP(X) {cerr << X;} #else #define LOGP(X) #endif int yylex(yy::parser::semantic_type *, yy::parser::location_type *, WasaParserDriver *); void yyerror(char const *); static void qualify(Rcl::SearchDataClauseDist *, const string &); static void addSubQuery(WasaParserDriver *, Rcl::SearchData *sd, Rcl::SearchData *sq) { if (sd && sq) sd->addClause( new Rcl::SearchDataClauseSub(std::shared_ptr(sq))); } #line 78 "y.tab.c" #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include // FIXME: INFRINGES ON USER NAME SPACE. # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif // Whether we are compiled with exception support. #ifndef YY_EXCEPTIONS # if defined __GNUC__ && !defined __EXCEPTIONS # define YY_EXCEPTIONS 0 # else # define YY_EXCEPTIONS 1 # endif #endif #define YYRHSLOC(Rhs, K) ((Rhs)[K].location) /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ # ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).begin = YYRHSLOC (Rhs, 1).begin; \ (Current).end = YYRHSLOC (Rhs, N).end; \ } \ else \ { \ (Current).begin = (Current).end = YYRHSLOC (Rhs, 0).end; \ } \ while (false) # endif // Enable debugging if requested. #if YYDEBUG // A pseudo ostream that takes yydebug_ into account. # define YYCDEBUG if (yydebug_) (*yycdebug_) # define YY_SYMBOL_PRINT(Title, Symbol) \ do { \ if (yydebug_) \ { \ *yycdebug_ << Title << ' '; \ yy_print_ (*yycdebug_, Symbol); \ *yycdebug_ << '\n'; \ } \ } while (false) # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug_) \ yy_reduce_print_ (Rule); \ } while (false) # define YY_STACK_PRINT() \ do { \ if (yydebug_) \ yy_stack_print_ (); \ } while (false) #else // !YYDEBUG # define YYCDEBUG if (false) std::cerr # define YY_SYMBOL_PRINT(Title, Symbol) YY_USE (Symbol) # define YY_REDUCE_PRINT(Rule) static_cast (0) # define YY_STACK_PRINT() static_cast (0) #endif // !YYDEBUG #define yyerrok (yyerrstatus_ = 0) #define yyclearin (yyla.clear ()) #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus_) namespace yy { #line 175 "y.tab.c" /// Build a parser object. parser::parser (WasaParserDriver* d_yyarg) #if YYDEBUG : yydebug_ (false), yycdebug_ (&std::cerr), #else : #endif d (d_yyarg) {} parser::~parser () {} parser::syntax_error::~syntax_error () YY_NOEXCEPT YY_NOTHROW {} /*---------. | symbol. | `---------*/ // basic_symbol. template parser::basic_symbol::basic_symbol (const basic_symbol& that) : Base (that) , value (that.value) , location (that.location) {} /// Constructor for valueless symbols. template parser::basic_symbol::basic_symbol (typename Base::kind_type t, YY_MOVE_REF (location_type) l) : Base (t) , value () , location (l) {} template parser::basic_symbol::basic_symbol (typename Base::kind_type t, YY_RVREF (value_type) v, YY_RVREF (location_type) l) : Base (t) , value (YY_MOVE (v)) , location (YY_MOVE (l)) {} template parser::symbol_kind_type parser::basic_symbol::type_get () const YY_NOEXCEPT { return this->kind (); } template bool parser::basic_symbol::empty () const YY_NOEXCEPT { return this->kind () == symbol_kind::S_YYEMPTY; } template void parser::basic_symbol::move (basic_symbol& s) { super_type::move (s); value = YY_MOVE (s.value); location = YY_MOVE (s.location); } // by_kind. parser::by_kind::by_kind () YY_NOEXCEPT : kind_ (symbol_kind::S_YYEMPTY) {} #if 201103L <= YY_CPLUSPLUS parser::by_kind::by_kind (by_kind&& that) YY_NOEXCEPT : kind_ (that.kind_) { that.clear (); } #endif parser::by_kind::by_kind (const by_kind& that) YY_NOEXCEPT : kind_ (that.kind_) {} parser::by_kind::by_kind (token_kind_type t) YY_NOEXCEPT : kind_ (yytranslate_ (t)) {} void parser::by_kind::clear () YY_NOEXCEPT { kind_ = symbol_kind::S_YYEMPTY; } void parser::by_kind::move (by_kind& that) { kind_ = that.kind_; that.clear (); } parser::symbol_kind_type parser::by_kind::kind () const YY_NOEXCEPT { return kind_; } parser::symbol_kind_type parser::by_kind::type_get () const YY_NOEXCEPT { return this->kind (); } // by_state. parser::by_state::by_state () YY_NOEXCEPT : state (empty_state) {} parser::by_state::by_state (const by_state& that) YY_NOEXCEPT : state (that.state) {} void parser::by_state::clear () YY_NOEXCEPT { state = empty_state; } void parser::by_state::move (by_state& that) { state = that.state; that.clear (); } parser::by_state::by_state (state_type s) YY_NOEXCEPT : state (s) {} parser::symbol_kind_type parser::by_state::kind () const YY_NOEXCEPT { if (state == empty_state) return symbol_kind::S_YYEMPTY; else return YY_CAST (symbol_kind_type, yystos_[+state]); } parser::stack_symbol_type::stack_symbol_type () {} parser::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that) : super_type (YY_MOVE (that.state), YY_MOVE (that.value), YY_MOVE (that.location)) { #if 201103L <= YY_CPLUSPLUS // that is emptied. that.state = empty_state; #endif } parser::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that) : super_type (s, YY_MOVE (that.value), YY_MOVE (that.location)) { // that is emptied. that.kind_ = symbol_kind::S_YYEMPTY; } #if YY_CPLUSPLUS < 201103L parser::stack_symbol_type& parser::stack_symbol_type::operator= (const stack_symbol_type& that) { state = that.state; value = that.value; location = that.location; return *this; } parser::stack_symbol_type& parser::stack_symbol_type::operator= (stack_symbol_type& that) { state = that.state; value = that.value; location = that.location; // that is emptied. that.state = empty_state; return *this; } #endif template void parser::yy_destroy_ (const char* yymsg, basic_symbol& yysym) const { if (yymsg) YY_SYMBOL_PRINT (yymsg, yysym); // User destructor. switch (yysym.kind ()) { case symbol_kind::S_WORD: // WORD #line 52 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" {delete (yysym.value.str);} #line 387 "y.tab.c" break; case symbol_kind::S_QUOTED: // QUOTED #line 52 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" {delete (yysym.value.str);} #line 393 "y.tab.c" break; case symbol_kind::S_QUALIFIERS: // QUALIFIERS #line 52 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" {delete (yysym.value.str);} #line 399 "y.tab.c" break; case symbol_kind::S_complexfieldname: // complexfieldname #line 52 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" {delete (yysym.value.str);} #line 405 "y.tab.c" break; default: break; } } #if YYDEBUG template void parser::yy_print_ (std::ostream& yyo, const basic_symbol& yysym) const { std::ostream& yyoutput = yyo; YY_USE (yyoutput); if (yysym.empty ()) yyo << "empty symbol"; else { symbol_kind_type yykind = yysym.kind (); yyo << (yykind < YYNTOKENS ? "token" : "nterm") << ' ' << yysym.name () << " (" << yysym.location << ": "; YY_USE (yykind); yyo << ')'; } } #endif void parser::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym) { if (m) YY_SYMBOL_PRINT (m, sym); yystack_.push (YY_MOVE (sym)); } void parser::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym) { #if 201103L <= YY_CPLUSPLUS yypush_ (m, stack_symbol_type (s, std::move (sym))); #else stack_symbol_type ss (s, sym); yypush_ (m, ss); #endif } void parser::yypop_ (int n) YY_NOEXCEPT { yystack_.pop (n); } #if YYDEBUG std::ostream& parser::debug_stream () const { return *yycdebug_; } void parser::set_debug_stream (std::ostream& o) { yycdebug_ = &o; } parser::debug_level_type parser::debug_level () const { return yydebug_; } void parser::set_debug_level (debug_level_type l) { yydebug_ = l; } #endif // YYDEBUG parser::state_type parser::yy_lr_goto_state_ (state_type yystate, int yysym) { int yyr = yypgoto_[yysym - YYNTOKENS] + yystate; if (0 <= yyr && yyr <= yylast_ && yycheck_[yyr] == yystate) return yytable_[yyr]; else return yydefgoto_[yysym - YYNTOKENS]; } bool parser::yy_pact_value_is_default_ (int yyvalue) YY_NOEXCEPT { return yyvalue == yypact_ninf_; } bool parser::yy_table_value_is_error_ (int yyvalue) YY_NOEXCEPT { return yyvalue == yytable_ninf_; } int parser::operator() () { return parse (); } int parser::parse () { int yyn; /// Length of the RHS of the rule being reduced. int yylen = 0; // Error handling. int yynerrs_ = 0; int yyerrstatus_ = 0; /// The lookahead symbol. symbol_type yyla; /// The locations where the error started and ended. stack_symbol_type yyerror_range[3]; /// The return value of parse (). int yyresult; #if YY_EXCEPTIONS try #endif // YY_EXCEPTIONS { YYCDEBUG << "Starting parse\n"; /* Initialize the stack. The initial state will be set in yynewstate, since the latter expects the semantical and the location values to have been already stored, initialize these stacks with a primary value. */ yystack_.clear (); yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla)); /*-----------------------------------------------. | yynewstate -- push a new symbol on the stack. | `-----------------------------------------------*/ yynewstate: YYCDEBUG << "Entering state " << int (yystack_[0].state) << '\n'; YY_STACK_PRINT (); // Accept? if (yystack_[0].state == yyfinal_) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: // Try to take a decision without lookahead. yyn = yypact_[+yystack_[0].state]; if (yy_pact_value_is_default_ (yyn)) goto yydefault; // Read a lookahead token. if (yyla.empty ()) { YYCDEBUG << "Reading a token\n"; #if YY_EXCEPTIONS try #endif // YY_EXCEPTIONS { yyla.kind_ = yytranslate_ (yylex (&yyla.value, &yyla.location, d)); } #if YY_EXCEPTIONS catch (const syntax_error& yyexc) { YYCDEBUG << "Caught exception: " << yyexc.what() << '\n'; error (yyexc); goto yyerrlab1; } #endif // YY_EXCEPTIONS } YY_SYMBOL_PRINT ("Next token is", yyla); if (yyla.kind () == symbol_kind::S_YYerror) { // The scanner already issued an error message, process directly // to error recovery. But do not keep the error token as // lookahead, it is too special and may lead us to an endless // loop in error recovery. */ yyla.kind_ = symbol_kind::S_YYUNDEF; goto yyerrlab1; } /* If the proper action on seeing token YYLA.TYPE is to reduce or to detect an error, take that action. */ yyn += yyla.kind (); if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.kind ()) { goto yydefault; } // Reduce or error. yyn = yytable_[yyn]; if (yyn <= 0) { if (yy_table_value_is_error_ (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } // Count tokens shifted since error; after three, turn off error status. if (yyerrstatus_) --yyerrstatus_; // Shift the lookahead token. yypush_ ("Shifting", state_type (yyn), YY_MOVE (yyla)); goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact_[+yystack_[0].state]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- do a reduction. | `-----------------------------*/ yyreduce: yylen = yyr2_[yyn]; { stack_symbol_type yylhs; yylhs.state = yy_lr_goto_state_ (yystack_[yylen].state, yyr1_[yyn]); /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, use the top of the stack. Otherwise, the following line sets YYLHS.VALUE to garbage. This behavior is undocumented and Bison users should not rely upon it. */ if (yylen) yylhs.value = yystack_[yylen - 1].value; else yylhs.value = yystack_[0].value; // Default location. { stack_type::slice range (yystack_, yylen); YYLLOC_DEFAULT (yylhs.location, range, yylen); yyerror_range[1].location = yylhs.location; } // Perform the reduction. YY_REDUCE_PRINT (yyn); #if YY_EXCEPTIONS try #endif // YY_EXCEPTIONS { switch (yyn) { case 2: // topquery: query #line 74 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { // It's possible that we end up with no query (e.g.: because just a // date filter was set, no terms). Allocate an empty query so that we // have something to set the global criteria on (this will yield a // Xapian search like FILTER xxx if ((yystack_[0].value.sd) == nullptr) d->m_result = new Rcl::SearchData(Rcl::SCLT_AND, d->m_stemlang); else d->m_result = (yystack_[0].value.sd); } #line 685 "y.tab.c" break; case 3: // query: query query #line 87 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("q: query query\n"); Rcl::SearchData *sd = nullptr; if ((yystack_[1].value.sd) || (yystack_[0].value.sd)) { sd = new Rcl::SearchData(Rcl::SCLT_AND, d->m_stemlang); addSubQuery(d, sd, (yystack_[1].value.sd)); addSubQuery(d, sd, (yystack_[0].value.sd)); } (yylhs.value.sd) = sd; } #line 700 "y.tab.c" break; case 4: // query: query AND query #line 98 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("q: query AND query\n"); Rcl::SearchData *sd = nullptr; if ((yystack_[2].value.sd) || (yystack_[0].value.sd)) { sd = new Rcl::SearchData(Rcl::SCLT_AND, d->m_stemlang); addSubQuery(d, sd, (yystack_[2].value.sd)); addSubQuery(d, sd, (yystack_[0].value.sd)); } (yylhs.value.sd) = sd; } #line 715 "y.tab.c" break; case 5: // query: query OR query #line 109 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("query: query OR query\n"); Rcl::SearchData *top = nullptr; if ((yystack_[2].value.sd) || (yystack_[0].value.sd)) { top = new Rcl::SearchData(Rcl::SCLT_OR, d->m_stemlang); addSubQuery(d, top, (yystack_[2].value.sd)); addSubQuery(d, top, (yystack_[0].value.sd)); } (yylhs.value.sd) = top; } #line 730 "y.tab.c" break; case 6: // query: '(' query ')' #line 120 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("q: ( query )\n"); (yylhs.value.sd) = (yystack_[1].value.sd); } #line 739 "y.tab.c" break; case 7: // query: fieldexpr #line 126 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("q: fieldexpr\n"); Rcl::SearchData *sd = new Rcl::SearchData(Rcl::SCLT_AND, d->m_stemlang); if (d->addClause(sd, (yystack_[0].value.cl))) { (yylhs.value.sd) = sd; } else { delete sd; (yylhs.value.sd) = nullptr; } } #line 754 "y.tab.c" break; case 8: // fieldexpr: term #line 139 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: simple fieldexpr: " << (yystack_[0].value.cl)->gettext() << endl); (yylhs.value.cl) = (yystack_[0].value.cl); } #line 763 "y.tab.c" break; case 9: // fieldexpr: complexfieldname EQUALS term #line 144 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: " << *(yystack_[2].value.str) << " = " << (yystack_[0].value.cl)->gettext() << endl); (yystack_[0].value.cl)->setfield(*(yystack_[2].value.str)); (yystack_[0].value.cl)->setrel(Rcl::SearchDataClause::REL_EQUALS); (yylhs.value.cl) = (yystack_[0].value.cl); delete (yystack_[2].value.str); } #line 775 "y.tab.c" break; case 10: // fieldexpr: complexfieldname CONTAINS term #line 152 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: " << *(yystack_[2].value.str) << " : " << (yystack_[0].value.cl)->gettext() << endl); (yystack_[0].value.cl)->setfield(*(yystack_[2].value.str)); (yystack_[0].value.cl)->setrel(Rcl::SearchDataClause::REL_CONTAINS); (yylhs.value.cl) = (yystack_[0].value.cl); delete (yystack_[2].value.str); } #line 787 "y.tab.c" break; case 11: // fieldexpr: complexfieldname CONTAINS range #line 160 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: " << *(yystack_[2].value.str) << " : " << (yystack_[0].value.rg)->gettext() << endl); (yystack_[0].value.rg)->setfield(*(yystack_[2].value.str)); (yystack_[0].value.rg)->setrel(Rcl::SearchDataClause::REL_CONTAINS); (yylhs.value.cl) = (yystack_[0].value.rg); delete (yystack_[2].value.str); } #line 799 "y.tab.c" break; case 12: // fieldexpr: complexfieldname SMALLER term #line 168 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: " << *(yystack_[2].value.str) << " < " << (yystack_[0].value.cl)->gettext() << endl); (yystack_[0].value.cl)->setfield(*(yystack_[2].value.str)); (yystack_[0].value.cl)->setrel(Rcl::SearchDataClause::REL_LT); (yylhs.value.cl) = (yystack_[0].value.cl); delete (yystack_[2].value.str); } #line 811 "y.tab.c" break; case 13: // fieldexpr: complexfieldname SMALLEREQ term #line 176 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: " << *(yystack_[2].value.str) << " <= " << (yystack_[0].value.cl)->gettext() << endl); (yystack_[0].value.cl)->setfield(*(yystack_[2].value.str)); (yystack_[0].value.cl)->setrel(Rcl::SearchDataClause::REL_LTE); (yylhs.value.cl) = (yystack_[0].value.cl); delete (yystack_[2].value.str); } #line 823 "y.tab.c" break; case 14: // fieldexpr: complexfieldname GREATER term #line 184 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: " << *(yystack_[2].value.str) << " > " << (yystack_[0].value.cl)->gettext() << endl); (yystack_[0].value.cl)->setfield(*(yystack_[2].value.str)); (yystack_[0].value.cl)->setrel(Rcl::SearchDataClause::REL_GT); (yylhs.value.cl) = (yystack_[0].value.cl); delete (yystack_[2].value.str); } #line 835 "y.tab.c" break; case 15: // fieldexpr: complexfieldname GREATEREQ term #line 192 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: " << *(yystack_[2].value.str) << " >= " << (yystack_[0].value.cl)->gettext() << endl); (yystack_[0].value.cl)->setfield(*(yystack_[2].value.str)); (yystack_[0].value.cl)->setrel(Rcl::SearchDataClause::REL_GTE); (yylhs.value.cl) = (yystack_[0].value.cl); delete (yystack_[2].value.str); } #line 847 "y.tab.c" break; case 16: // fieldexpr: '-' fieldexpr #line 200 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("fe: - fieldexpr[" << (yystack_[0].value.cl)->gettext() << "]" << endl); (yystack_[0].value.cl)->setexclude(true); (yylhs.value.cl) = (yystack_[0].value.cl); } #line 857 "y.tab.c" break; case 17: // complexfieldname: WORD #line 210 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("cfn: WORD" << endl); (yylhs.value.str) = (yystack_[0].value.str); } #line 866 "y.tab.c" break; case 18: // complexfieldname: complexfieldname CONTAINS WORD #line 216 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("cfn: complexfieldname ':' WORD" << endl); (yylhs.value.str) = new string(*(yystack_[2].value.str) + string(":") + *(yystack_[0].value.str)); delete (yystack_[2].value.str); delete (yystack_[0].value.str); } #line 877 "y.tab.c" break; case 19: // range: WORD RANGE WORD #line 225 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("Range: " << *(yystack_[2].value.str) << string(" .. ") << *(yystack_[0].value.str) << endl); (yylhs.value.rg) = new Rcl::SearchDataClauseRange(*(yystack_[2].value.str), *(yystack_[0].value.str)); delete (yystack_[2].value.str); delete (yystack_[0].value.str); } #line 888 "y.tab.c" break; case 20: // range: RANGE WORD #line 233 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("Range: " << "" << string(" .. ") << *(yystack_[0].value.str) << endl); (yylhs.value.rg) = new Rcl::SearchDataClauseRange("", *(yystack_[0].value.str)); delete (yystack_[0].value.str); } #line 898 "y.tab.c" break; case 21: // range: WORD RANGE #line 240 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("Range: " << *(yystack_[1].value.str) << string(" .. ") << "" << endl); (yylhs.value.rg) = new Rcl::SearchDataClauseRange(*(yystack_[1].value.str), ""); delete (yystack_[1].value.str); } #line 908 "y.tab.c" break; case 22: // term: WORD #line 249 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("term[" << *(yystack_[0].value.str) << "]" << endl); (yylhs.value.cl) = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND, *(yystack_[0].value.str)); delete (yystack_[0].value.str); } #line 918 "y.tab.c" break; case 23: // term: qualquote #line 255 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { (yylhs.value.cl) = (yystack_[0].value.cl); } #line 926 "y.tab.c" break; case 24: // qualquote: QUOTED #line 261 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("QUOTED[" << *(yystack_[0].value.str) << "]" << endl); (yylhs.value.cl) = new Rcl::SearchDataClauseDist(Rcl::SCLT_PHRASE, *(yystack_[0].value.str), 0); delete (yystack_[0].value.str); } #line 936 "y.tab.c" break; case 25: // qualquote: QUOTED QUALIFIERS #line 267 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" { LOGP("QUOTED[" << *(yystack_[1].value.str) << "] QUALIFIERS[" << *(yystack_[0].value.str) << "]" << endl); Rcl::SearchDataClauseDist *cl = new Rcl::SearchDataClauseDist(Rcl::SCLT_PHRASE, *(yystack_[1].value.str), 0); qualify(cl, *(yystack_[0].value.str)); (yylhs.value.cl) = cl; delete (yystack_[1].value.str); delete (yystack_[0].value.str); } #line 950 "y.tab.c" break; #line 954 "y.tab.c" default: break; } } #if YY_EXCEPTIONS catch (const syntax_error& yyexc) { YYCDEBUG << "Caught exception: " << yyexc.what() << '\n'; error (yyexc); YYERROR; } #endif // YY_EXCEPTIONS YY_SYMBOL_PRINT ("-> $$ =", yylhs); yypop_ (yylen); yylen = 0; // Shift the result of the reduction. yypush_ (YY_NULLPTR, YY_MOVE (yylhs)); } goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: // If not already recovering from an error, report this error. if (!yyerrstatus_) { ++yynerrs_; context yyctx (*this, yyla); std::string msg = yysyntax_error_ (yyctx); error (yyla.location, YY_MOVE (msg)); } yyerror_range[1].location = yyla.location; if (yyerrstatus_ == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ // Return failure if at end of input. if (yyla.kind () == symbol_kind::S_YYEOF) YYABORT; else if (!yyla.empty ()) { yy_destroy_ ("Error: discarding", yyla); yyla.clear (); } } // Else will try to reuse lookahead token after shifting the error token. goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (false) YYERROR; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ yypop_ (yylen); yylen = 0; YY_STACK_PRINT (); goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus_ = 3; // Each real token shifted decrements this. // Pop stack until we find a state that shifts the error token. for (;;) { yyn = yypact_[+yystack_[0].state]; if (!yy_pact_value_is_default_ (yyn)) { yyn += symbol_kind::S_YYerror; if (0 <= yyn && yyn <= yylast_ && yycheck_[yyn] == symbol_kind::S_YYerror) { yyn = yytable_[yyn]; if (0 < yyn) break; } } // Pop the current state because it cannot handle the error token. if (yystack_.size () == 1) YYABORT; yyerror_range[1].location = yystack_[0].location; yy_destroy_ ("Error: popping", yystack_[0]); yypop_ (); YY_STACK_PRINT (); } { stack_symbol_type error_token; yyerror_range[2].location = yyla.location; YYLLOC_DEFAULT (error_token.location, yyerror_range, 2); // Shift the error token. error_token.state = state_type (yyn); yypush_ ("Shifting", YY_MOVE (error_token)); } goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; /*-----------------------------------------------------. | yyreturn -- parsing is finished, return the result. | `-----------------------------------------------------*/ yyreturn: if (!yyla.empty ()) yy_destroy_ ("Cleanup: discarding lookahead", yyla); /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ yypop_ (yylen); YY_STACK_PRINT (); while (1 < yystack_.size ()) { yy_destroy_ ("Cleanup: popping", yystack_[0]); yypop_ (); } return yyresult; } #if YY_EXCEPTIONS catch (...) { YYCDEBUG << "Exception caught: cleaning lookahead and stack\n"; // Do not try to display the values of the reclaimed symbols, // as their printers might throw an exception. if (!yyla.empty ()) yy_destroy_ (YY_NULLPTR, yyla); while (1 < yystack_.size ()) { yy_destroy_ (YY_NULLPTR, yystack_[0]); yypop_ (); } throw; } #endif // YY_EXCEPTIONS } void parser::error (const syntax_error& yyexc) { error (yyexc.location, yyexc.what ()); } /* Return YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. */ std::string parser::yytnamerr_ (const char *yystr) { if (*yystr == '"') { std::string yyr; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; else goto append; append: default: yyr += *yyp; break; case '"': return yyr; } do_not_strip_quotes: ; } return yystr; } std::string parser::symbol_name (symbol_kind_type yysymbol) { return yytnamerr_ (yytname_[yysymbol]); } // parser::context. parser::context::context (const parser& yyparser, const symbol_type& yyla) : yyparser_ (yyparser) , yyla_ (yyla) {} int parser::context::expected_tokens (symbol_kind_type yyarg[], int yyargn) const { // Actual number of expected tokens int yycount = 0; const int yyn = yypact_[+yyparser_.yystack_[0].state]; if (!yy_pact_value_is_default_ (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ const int yyxbegin = yyn < 0 ? -yyn : 0; // Stay within bounds of both yycheck and yytname. const int yychecklim = yylast_ - yyn + 1; const int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; for (int yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck_[yyx + yyn] == yyx && yyx != symbol_kind::S_YYerror && !yy_table_value_is_error_ (yytable_[yyx + yyn])) { if (!yyarg) ++yycount; else if (yycount == yyargn) return 0; else yyarg[yycount++] = YY_CAST (symbol_kind_type, yyx); } } if (yyarg && yycount == 0 && 0 < yyargn) yyarg[0] = symbol_kind::S_YYEMPTY; return yycount; } int parser::yy_syntax_error_arguments_ (const context& yyctx, symbol_kind_type yyarg[], int yyargn) const { /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yyla) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yyla. (However, yyla is currently not documented for users.) - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (!yyctx.lookahead ().empty ()) { if (yyarg) yyarg[0] = yyctx.token (); int yyn = yyctx.expected_tokens (yyarg ? yyarg + 1 : yyarg, yyargn - 1); return yyn + 1; } return 0; } // Generate an error message. std::string parser::yysyntax_error_ (const context& yyctx) const { // Its maximum. enum { YYARGS_MAX = 5 }; // Arguments of yyformat. symbol_kind_type yyarg[YYARGS_MAX]; int yycount = yy_syntax_error_arguments_ (yyctx, yyarg, YYARGS_MAX); char const* yyformat = YY_NULLPTR; switch (yycount) { #define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: // Avoid compiler warnings. YYCASE_ (0, YY_("syntax error")); YYCASE_ (1, YY_("syntax error, unexpected %s")); YYCASE_ (2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_ (3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_ (4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_ (5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); #undef YYCASE_ } std::string yyres; // Argument number. std::ptrdiff_t yyi = 0; for (char const* yyp = yyformat; *yyp; ++yyp) if (yyp[0] == '%' && yyp[1] == 's' && yyi < yycount) { yyres += symbol_name (yyarg[yyi++]); ++yyp; } else yyres += *yyp; return yyres; } const signed char parser::yypact_ninf_ = -3; const signed char parser::yytable_ninf_ = -19; const signed char parser::yypact_[] = { 31, 32, 3, 31, 33, 6, 14, -3, 38, -3, -3, -3, 1, -3, -3, 31, 31, 4, -2, 9, -2, -2, -2, -2, -3, 4, -3, -3, -3, 16, 18, -3, -3, -3, -3, -3, -3, 22, -3, -3 }; const signed char parser::yydefact_[] = { 0, 22, 24, 0, 0, 0, 2, 7, 0, 8, 23, 25, 0, 16, 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 6, 4, 5, 22, 9, 22, 0, 11, 10, 13, 12, 15, 14, 21, 20, 19 }; const signed char parser::yypgoto_[] = { -3, -3, 0, 34, -3, -3, 37, -3 }; const signed char parser::yydefgoto_[] = { 0, 5, 17, 7, 8, 31, 9, 10 }; const signed char parser::yytable_[] = { 6, 27, 2, 12, 1, 2, 14, 15, 11, 3, 4, 16, 29, 2, 16, 25, 26, 1, 2, 24, 15, 38, 3, 4, 16, 39, 30, -18, -18, -18, -18, -18, -18, 37, 1, 2, 1, 2, 13, 3, 4, 0, 4, -17, -17, -17, -17, -17, -17, 18, 19, 20, 21, 22, 23, 28, 32, 33, 34, 35, 36 }; const signed char parser::yycheck_[] = { 0, 3, 4, 3, 3, 4, 0, 6, 5, 8, 9, 10, 3, 4, 10, 15, 16, 3, 4, 18, 6, 3, 8, 9, 10, 3, 17, 11, 12, 13, 14, 15, 16, 17, 3, 4, 3, 4, 4, 8, 9, -1, 9, 11, 12, 13, 14, 15, 16, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23 }; const signed char parser::yystos_[] = { 0, 3, 4, 8, 9, 20, 21, 22, 23, 25, 26, 5, 21, 22, 0, 6, 10, 21, 11, 12, 13, 14, 15, 16, 18, 21, 21, 3, 25, 3, 17, 24, 25, 25, 25, 25, 25, 17, 3, 3 }; const signed char parser::yyr1_[] = { 0, 19, 20, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 24, 24, 24, 25, 25, 26, 26 }; const signed char parser::yyr2_[] = { 0, 2, 1, 2, 3, 3, 3, 1, 1, 3, 3, 3, 3, 3, 3, 3, 2, 1, 3, 3, 2, 2, 1, 1, 1, 2 }; #if YYDEBUG || 1 // YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. // First, the terminals, then, starting at \a YYNTOKENS, nonterminals. const char* const parser::yytname_[] = { "\"end of file\"", "error", "\"invalid token\"", "WORD", "QUOTED", "QUALIFIERS", "AND", "UCONCAT", "'('", "'-'", "OR", "EQUALS", "CONTAINS", "SMALLEREQ", "SMALLER", "GREATEREQ", "GREATER", "RANGE", "')'", "$accept", "topquery", "query", "fieldexpr", "complexfieldname", "range", "term", "qualquote", YY_NULLPTR }; #endif #if YYDEBUG const short parser::yyrline_[] = { 0, 73, 73, 86, 97, 108, 119, 125, 138, 143, 151, 159, 167, 175, 183, 191, 199, 209, 215, 224, 232, 239, 248, 254, 260, 266 }; void parser::yy_stack_print_ () const { *yycdebug_ << "Stack now"; for (stack_type::const_iterator i = yystack_.begin (), i_end = yystack_.end (); i != i_end; ++i) *yycdebug_ << ' ' << int (i->state); *yycdebug_ << '\n'; } void parser::yy_reduce_print_ (int yyrule) const { int yylno = yyrline_[yyrule]; int yynrhs = yyr2_[yyrule]; // Print the symbols being reduced, and their result. *yycdebug_ << "Reducing stack by rule " << yyrule - 1 << " (line " << yylno << "):\n"; // The symbols being reduced. for (int yyi = 0; yyi < yynrhs; yyi++) YY_SYMBOL_PRINT (" $" << yyi + 1 << " =", yystack_[(yynrhs) - (yyi + 1)]); } #endif // YYDEBUG parser::symbol_kind_type parser::yytranslate_ (int t) YY_NOEXCEPT { // YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to // TOKEN-NUM as returned by yylex. static const signed char translate_table[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 18, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17 }; // Last valid token kind. const int code_max = 270; if (t <= 0) return symbol_kind::S_YYEOF; else if (t <= code_max) return static_cast (translate_table[t]); else return symbol_kind::S_YYUNDEF; } } // yy #line 1491 "y.tab.c" #line 278 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" #include // Look for int at index, skip and return new index found? value. static unsigned int qualGetInt(const string& q, unsigned int cur, int *pval) { unsigned int ncur = cur; if (cur < q.size() - 1) { char *endptr; int val = strtol(&q[cur + 1], &endptr, 10); if (endptr != &q[cur + 1]) { ncur += endptr - &q[cur + 1]; *pval = val; } } return ncur; } static void qualify(Rcl::SearchDataClauseDist *cl, const string& quals) { // cerr << "qualify(" << cl << ", " << quals << ")" << endl; for (unsigned int i = 0; i < quals.length(); i++) { //fprintf(stderr, "qual char %c\n", quals[i]); switch (quals[i]) { case 'b': cl->setWeight(10.0); break; case 'c': break; case 'C': cl->addModifier(Rcl::SearchDataClause::SDCM_CASESENS); break; case 'd': break; case 'D': cl->addModifier(Rcl::SearchDataClause::SDCM_DIACSENS); break; case 'e': cl->addModifier(Rcl::SearchDataClause::SDCM_CASESENS); cl->addModifier(Rcl::SearchDataClause::SDCM_DIACSENS); cl->addModifier(Rcl::SearchDataClause::SDCM_NOSTEMMING); break; case 'l': cl->addModifier(Rcl::SearchDataClause::SDCM_NOSTEMMING); break; case 'L': break; case 'o': { int slack = 10; i = qualGetInt(quals, i, &slack); cl->setslack(slack); //cerr << "set slack " << cl->getslack() << " done" << endl; } break; case 'p': cl->setTp(Rcl::SCLT_NEAR); if (cl->getslack() == 0) { cl->setslack(10); //cerr << "set slack " << cl->getslack() << " done" << endl; } break; case 's': cl->addModifier(Rcl::SearchDataClause::SDCM_NOSYNS); break; case 'S': break; case 'x': cl->addModifier(Rcl::SearchDataClause::SDCM_EXPANDPHRASE); break; case '.':case '0':case '1':case '2':case '3':case '4': case '5':case '6':case '7':case '8':case '9': { int n = 0; float factor = 1.0; if (sscanf(&(quals[i]), "%f %n", &factor, &n)) { if (factor != 1.0) { cl->setWeight(factor); } } if (n > 0) i += n - 1; } default: break; } } } // specialstartchars are special only at the beginning of a token // (e.g. doctor-who is a term, not 2 terms separated by '-') static const string specialstartchars("-"); // specialinchars are special everywhere except inside a quoted string static const string specialinchars(":=<>()"); // Called with the first dquote already read static int parseString(WasaParserDriver *d, yy::parser::semantic_type *yylval) { string* value = new string(); d->qualifiers().clear(); int c; while ((c = d->GETCHAR())) { switch (c) { case '\\': /* Escape: get next char */ c = d->GETCHAR(); if (c == 0) { value->push_back(c); goto out; } value->push_back(c); break; case '"': /* End of string. Look for qualifiers */ while ((c = d->GETCHAR()) && (isalnum(c) || c == '.')) d->qualifiers().push_back(c); d->UNGETCHAR(c); goto out; default: value->push_back(c); } } out: //cerr << "GOT QUOTED ["<qualifiers() << "]" << endl; yylval->str = value; return yy::parser::token::QUOTED; } int yylex(yy::parser::semantic_type *yylval, yy::parser::location_type *, WasaParserDriver *d) { if (!d->qualifiers().empty()) { yylval->str = new string(); yylval->str->swap(d->qualifiers()); return yy::parser::token::QUALIFIERS; } int c; /* Skip white space. */ while ((c = d->GETCHAR()) && isspace(c)) continue; if (c == 0) return 0; if (specialstartchars.find_first_of(c) != string::npos) { //cerr << "yylex: return " << c << endl; return c; } // field-term relations, and ranges switch (c) { case '=': return yy::parser::token::EQUALS; case ':': return yy::parser::token::CONTAINS; case '<': { int c1 = d->GETCHAR(); if (c1 == '=') { return yy::parser::token::SMALLEREQ; } else { d->UNGETCHAR(c1); return yy::parser::token::SMALLER; } } case '.': { int c1 = d->GETCHAR(); if (c1 == '.') { return yy::parser::token::RANGE; } else { d->UNGETCHAR(c1); break; } } case '>': { int c1 = d->GETCHAR(); if (c1 == '=') { return yy::parser::token::GREATEREQ; } else { d->UNGETCHAR(c1); return yy::parser::token::GREATER; } } case '(': case ')': return c; } if (c == '"') return parseString(d, yylval); d->UNGETCHAR(c); // Other chars start a term or field name or reserved word string* word = new string(); while ((c = d->GETCHAR())) { if (isspace(c)) { //cerr << "Word broken by whitespace" << endl; break; } else if (specialinchars.find_first_of(c) != string::npos) { //cerr << "Word broken by special char" << endl; d->UNGETCHAR(c); break; } else if (c == '.') { int c1 = d->GETCHAR(); if (c1 == '.') { d->UNGETCHAR(c1); d->UNGETCHAR(c); break; } else { d->UNGETCHAR(c1); word->push_back(c); } } else if (c == 0) { //cerr << "Word broken by EOF" << endl; break; } else { word->push_back(c); } } if (!word->compare("AND") || !word->compare("&&")) { delete word; return yy::parser::token::AND; } else if (!word->compare("OR") || !word->compare("||")) { delete word; return yy::parser::token::OR; } // cerr << "Got word [" << word << "]" << endl; yylval->str = word; return yy::parser::token::WORD; } recoll-1.36.1/query/wasatorcl.h0000644000175000017500000000222614410615043013320 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _WASATORCL_H_INCLUDED_ #define _WASATORCL_H_INCLUDED_ #include #include namespace Rcl { class SearchData; } class RclConfig; extern std::shared_ptrwasaStringToRcl( const RclConfig *, const std::string& stemlang, const std::string& query, std::string &reason, const std::string& autosuffs = ""); #endif /* _WASATORCL_H_INCLUDED_ */ recoll-1.36.1/query/docseqdb.h0000644000175000017500000000651414426500470013115 00000000000000/* Copyright (C) 2004-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _DOCSEQDB_H_INCLUDED_ #define _DOCSEQDB_H_INCLUDED_ #include #include "docseq.h" #include "searchdata.h" #include "rclquery.h" class PlainToRich; /** A DocSequence from a Db query */ class DocSequenceDb : public DocSequence { public: DocSequenceDb(std::shared_ptr db, std::shared_ptr q, const std::string &t, std::shared_ptr sdata); virtual ~DocSequenceDb() {} DocSequenceDb(const DocSequenceDb&) = delete; DocSequenceDb& operator=(const DocSequenceDb&) = delete; virtual bool getDoc(int num, Rcl::Doc &doc, std::string * = nullptr) override; virtual int getResCnt() override; virtual void getTerms(HighlightData& hld) override; // Called to fill-up the snippets window. Ignoers // buildabstract/replaceabstract and syntabslen virtual bool getAbstract(Rcl::Doc &doc, PlainToRich *ptr, std::vector&, int maxlen, bool sortbypage) override; virtual bool getAbstract(Rcl::Doc &doc, PlainToRich *ptr, std::vector&) override; virtual int getFirstMatchPage(Rcl::Doc&, std::string& term) override; virtual int getFirstMatchLine(const Rcl::Doc&, const std::string& term) override; virtual bool docDups(const Rcl::Doc& doc, std::vector& dups) override; virtual std::string getDescription() override; virtual std::list expand(Rcl::Doc &doc) override; virtual bool canFilter() override {return true;} virtual bool setFiltSpec(const DocSeqFiltSpec &filtspec) override; virtual bool canSort() override {return true;} virtual bool setSortSpec(const DocSeqSortSpec &sortspec) override; virtual void setAbstractParams(bool qba, bool qra) { m_queryBuildAbstract = qba; m_queryReplaceAbstract = qra; } virtual bool snippetsCapable() override { return true; } virtual std::string title() override; protected: virtual std::shared_ptr getDb() override { return m_db; } private: std::shared_ptr m_db; std::shared_ptr m_q; std::shared_ptr m_sdata; std::shared_ptr m_fsdata; // Filtered int m_rescnt{-1}; bool m_queryBuildAbstract{true}; bool m_queryReplaceAbstract{false}; bool m_isFiltered{false}; bool m_isSorted{false}; bool m_needSetQuery{false}; // search data changed, need to reapply before fetch bool m_lastSQStatus{true}; bool setQuery(); }; #endif /* _DOCSEQDB_H_INCLUDED_ */ recoll-1.36.1/query/filtseq.cpp0000644000175000017500000001006314427373216013334 00000000000000/* Copyright (C) 2005-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "log.h" #include "filtseq.h" #include "rclconfig.h" static bool filter(const DocSeqFiltSpec& fs, const Rcl::Doc *x) { LOGDEB2(" Filter: ncrits " << fs.crits.size() << "\n"); // Compare using each criterion in term. We're doing an or: 1st ok ends for (unsigned int i = 0; i < fs.crits.size(); i++) { switch (fs.crits[i]) { case DocSeqFiltSpec::DSFS_MIMETYPE: LOGDEB2(" filter: MIMETYPE: me [" << fs.values[i] << "] doc [" << x->mimetype << "]\n"); if (x->mimetype == fs.values[i]) return true; break; case DocSeqFiltSpec::DSFS_QLANG: { LOGDEB(" filter: QLANG [" << fs.values[i] << "]!!\n"); } break; case DocSeqFiltSpec::DSFS_PASSALL: return true; } } // Did all comparisons return false; } DocSeqFiltered::DocSeqFiltered( RclConfig *conf, std::shared_ptr iseq, DocSeqFiltSpec &filtspec) : DocSeqModifier(iseq), m_config(conf) { setFiltSpec(filtspec); } bool DocSeqFiltered::setFiltSpec(const DocSeqFiltSpec &filtspec) { LOGDEB0("DocSeqFiltered::setFiltSpec\n"); for (unsigned int i = 0; i < filtspec.crits.size(); i++) { switch (filtspec.crits[i]) { case DocSeqFiltSpec::DSFS_MIMETYPE: m_spec.orCrit(filtspec.crits[i], filtspec.values[i]); break; case DocSeqFiltSpec::DSFS_QLANG: { // There are very few lang constructs that we can interpret. The // default config uses rclcat:value only. That will be all for now... std::string val = filtspec.values[i]; if (val.find("rclcat:") == 0) { std::string catg = val.substr(7); std::vector tps; m_config->getMimeCatTypes(catg, tps); for (const auto& mime : tps) { LOGDEB2("Adding mime: [" << mime << "]\n"); m_spec.orCrit(DocSeqFiltSpec::DSFS_MIMETYPE, mime); } } } break; default: break; } } // If m_spec ends up empty, pass everything, better than filtering all. if (m_spec.crits.empty()) { m_spec.orCrit(DocSeqFiltSpec::DSFS_PASSALL, ""); } m_dbindices.clear(); return true; } bool DocSeqFiltered::getDoc(int idx, Rcl::Doc &doc, std::string *) { LOGDEB2("DocSeqFiltered::getDoc() fetching " << idx << "\n"); if (idx >= (int)m_dbindices.size()) { // Have to fetch docs and filter until we get enough or // fail m_dbindices.reserve(idx+1); // First backend seq doc we fetch is the one after last stored int backend_idx = m_dbindices.size() > 0 ? m_dbindices.back() + 1 : 0; // Loop until we get enough docs Rcl::Doc tdoc; while (idx >= (int)m_dbindices.size()) { if (!m_seq->getDoc(backend_idx, tdoc)) return false; if (filter(m_spec, &tdoc)) { m_dbindices.push_back(backend_idx); } backend_idx++; } doc = tdoc; } else { // The corresponding backend indice is already known if (!m_seq->getDoc(m_dbindices[idx], doc)) return false; } return true; } recoll-1.36.1/query/sortseq.h0000644000175000017500000000341614427373216013036 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SORTSEQ_H_INCLUDED_ #define _SORTSEQ_H_INCLUDED_ #include "autoconfig.h" #include #include #include #include "docseq.h" /** * A sorted sequence is created from the first N documents of another one, * and sorts them according to the given criteria. */ class DocSeqSorted : public DocSeqModifier { public: DocSeqSorted(std::shared_ptr iseq, DocSeqSortSpec &sortspec) : DocSeqModifier(iseq) { setSortSpec(sortspec); } virtual ~DocSeqSorted() {} DocSeqSorted(const DocSeqSorted&) = delete; DocSeqSorted& operator=(const DocSeqSorted&) = delete; virtual bool canSort() {return true;} virtual bool setSortSpec(const DocSeqSortSpec &sortspec); virtual bool getDoc(int num, Rcl::Doc &doc, std::string *sh = nullptr); virtual int getResCnt() {return int(m_docsp.size());} private: DocSeqSortSpec m_spec; std::vector m_docs; std::vector m_docsp; }; #endif /* _SORTSEQ_H_INCLUDED_ */ recoll-1.36.1/query/qresultstore.cpp0000644000175000017500000001350014410615043014425 00000000000000/* Copyright (C) 2017-2020 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "qresultstore.h" #include #include #include #include #include #include "rcldoc.h" #include "rclquery.h" namespace Rcl { class QResultStore::Internal { public: bool testentry(const std::pair& entry) { return !entry.second.empty() && (isinc ? fieldspec.find(entry.first) != fieldspec.end() : fieldspec.find(entry.first) == fieldspec.end()); } std::map keyidx; // Notes: offsets[0] is always 0, not really useful, simpler this // way. Also could use simple C array instead of c++ vector... struct docoffs { ~docoffs() { free(base); } char *base{nullptr}; std::vector offsets; }; std::vector docs; std::set fieldspec; bool isinc{false}; }; QResultStore::QResultStore() { m = new Internal; } QResultStore::~QResultStore() { delete m; } // For reference : Fields normally excluded by uprcl: // {"author", "ipath", "rcludi", "relevancyrating", "sig", "abstract", "caption", // "filename", "origcharset", "sig"}; bool QResultStore::storeQuery(Rcl::Query& query, std::set fldspec, bool isinc) { m->fieldspec = fldspec; m->isinc = isinc; ///////////// // Enumerate all existing keys and assign array indexes for // them. Count documents while we are at it. m->keyidx = {{"url",0}, {"mimetype", 1}, {"fmtime", 2}, {"dmtime", 3}, {"fbytes", 4}, {"dbytes", 5} }; int count = 0; for (;;count++) { Rcl::Doc doc; if (!query.getDoc(count, doc, false)) { break; } for (const auto& entry : doc.meta) { if (m->testentry(entry)) { auto it = m->keyidx.find(entry.first); if (it == m->keyidx.end()) { int idx = m->keyidx.size(); m->keyidx.insert({entry.first, idx}); }; } } } /////// // Populate the main array with doc-equivalent structures. m->docs.resize(count); for (int i = 0; i < count; i++) { Rcl::Doc doc; if (!query.getDoc(i, doc, false)) { break; } auto& vdoc = m->docs[i]; vdoc.offsets.resize(m->keyidx.size()); int nbytes = doc.url.size() + 1 + doc.mimetype.size() + 1 + doc.fmtime.size() + 1 + doc.dmtime.size() + 1 + doc.fbytes.size() + 1 + doc.dbytes.size() + 1; for (const auto& entry : doc.meta) { if (m->testentry(entry)) { if (m->keyidx.find(entry.first) == m->keyidx.end()) { continue; } nbytes += entry.second.size() + 1; } } char *cp = (char*)malloc(nbytes); if (nullptr == cp) { abort(); } #define STRINGCPCOPY(CHARP, S) do { \ memcpy(CHARP, S.c_str(), S.size()+1); \ CHARP += S.size()+1; \ } while (false); vdoc.base = cp; vdoc.offsets[0] = cp - vdoc.base; STRINGCPCOPY(cp, doc.url); vdoc.offsets[1] = cp - vdoc.base; STRINGCPCOPY(cp, doc.mimetype); vdoc.offsets[2] = cp - vdoc.base; STRINGCPCOPY(cp, doc.fmtime); vdoc.offsets[3] = cp - vdoc.base; STRINGCPCOPY(cp, doc.dmtime); vdoc.offsets[4] = cp - vdoc.base; STRINGCPCOPY(cp, doc.fbytes); vdoc.offsets[5] = cp - vdoc.base; STRINGCPCOPY(cp, doc.dbytes); for (const auto& entry : doc.meta) { if (m->testentry(entry)) { auto it = m->keyidx.find(entry.first); if (it == m->keyidx.end()) { std::cerr << "Unknown key: " << entry.first << "\n"; } if (it->second <= 5) { // Already done ! Storing another address would be // wasteful and crash when freeing... continue; } vdoc.offsets[it->second] = cp - vdoc.base; STRINGCPCOPY(cp, entry.second); } } // Point all empty entries to the final null byte for (unsigned int i = 1; i < vdoc.offsets.size(); i++) { if (vdoc.offsets[i] == 0) { vdoc.offsets[i] = cp - 1 - vdoc.base; } } } return true; } int QResultStore::getCount() { return int(m->docs.size()); } const char *QResultStore::fieldValue(int docindex, const std::string& fldname) { if (docindex < 0 || docindex >= int(m->docs.size())) { return nullptr; } auto& vdoc = m->docs[docindex]; auto it = m->keyidx.find(fldname); if (it == m->keyidx.end() || it->second < 0 || it->second >= int(vdoc.offsets.size())) { return nullptr; } return vdoc.base + vdoc.offsets[it->second]; } } // namespace Rcl recoll-1.36.1/query/xadump.cpp0000644000175000017500000002337714410615043013164 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include "pathut.h" #ifndef NO_NAMESPACES using namespace std; #endif /* NO_NAMESPACES */ #include "utf8iter.h" #include "xapian.h" static string thisprog; static string usage = " -d \n" "-e \n" " -i docid -D : get document data for docid\n" " -i docid -X : delete document docid\n" " -i docid -T : term list for doc docid\n" " -i docid -r : reconstructed text for docid\n" " -t term -E : term existence test\n" " -t term -F : retrieve term frequency data for given term\n" " -t term -P : retrieve postings for term\n" " -T : list all terms\n" " -f : precede each term in the list with its occurrence counts\n" " -n : raw data (no [])\n" " -l : don't list prefixed terms\n" " -x : separate each output char with a space\n" " -s : special mode to dump recoll stem db\n" " -q term [term ...] : perform AND query\n" " \n\n" ; static void Usage(void) { cerr << thisprog << ": usage:\n" << usage; exit(1); } static int op_flags; #define OPT_D 0x1 #define OPT_E 0x2 #define OPT_F 0x4 #define OPT_P 0x8 #define OPT_T 0x10 #define OPT_X 0x20 #define OPT_d 0x80 #define OPT_e 0x100 #define OPT_f 0x200 #define OPT_i 0x400 #define OPT_n 0x800 #define OPT_q 0x1000 #define OPT_t 0x4000 #define OPT_x 0x8000 #define OPT_l 0x10000 #define OPT_r 0x20000 // Compute an exploded version of string, inserting a space between each char. // (no character combining possible) static string detailstring(const string& in) { if (!(op_flags & OPT_x)) return in; string out; Utf8Iter it(in); for (; !it.eof(); it++) { it.appendchartostring(out); out += ' '; } // Strip last space if (!out.empty()) out.resize(out.size()-1); return out; } Xapian::Database *db; static void cleanup() { delete db; } static void sigcleanup(int sig) { fprintf(stderr, "sigcleanup\n"); cleanup(); exit(1); } bool o_index_stripchars; inline bool has_prefix(const string& trm) { if (o_index_stripchars) { return trm.size() && 'A' <= trm[0] && trm[0] <= 'Z'; } else { return trm.size() > 0 && trm[0] == ':'; } } void wholedoc(Xapian::Database* db, int docid) { vector buf; Xapian::TermIterator term; for (term = db->termlist_begin(docid); term != db->termlist_end(docid); term++) { Xapian::PositionIterator pos; for (pos = db->positionlist_begin(docid, *term); pos != db->positionlist_end(docid, *term); pos++) { if (buf.size() < *pos) buf.resize(2*((*pos)+1)); buf[(*pos)] = detailstring(*term); } } for (vector::iterator it = buf.begin(); it != buf.end(); it++) { if (!it->empty()) cout << *it << " "; } } int main(int argc, char **argv) { string dbdir = path_cat(path_home(), ".recoll/xapiandb"); string outencoding = "ISO8859-1"; int docid = 1; string aterm; thisprog = argv[0]; argc--; argv++; while (argc > 0 && **argv == '-') { (*argv)++; if (!(**argv)) /* Cas du "adb - core" */ Usage(); while (**argv) switch (*(*argv)++) { case 'D': op_flags |= OPT_D; break; case 'd': op_flags |= OPT_d; if (argc < 2) Usage(); dbdir = *(++argv); argc--; goto b1; case 'E': op_flags |= OPT_E; break; case 'e': op_flags |= OPT_d; if (argc < 2) Usage(); outencoding = *(++argv); argc--; goto b1; case 'F': op_flags |= OPT_F; break; case 'f': op_flags |= OPT_f; break; case 'i': op_flags |= OPT_i; if (argc < 2) Usage(); if (sscanf(*(++argv), "%d", &docid) != 1) Usage(); argc--; goto b1; case 'l': op_flags |= OPT_l; break; case 'n': op_flags |= OPT_n; break; case 'P': op_flags |= OPT_P; break; case 'q': op_flags |= OPT_q; break; case 'r': case 'b': op_flags |= OPT_r; break; case 'T': op_flags |= OPT_T; break; case 't': op_flags |= OPT_t; if (argc < 2) Usage(); aterm = *(++argv); argc--; goto b1; case 'X': op_flags |= OPT_X; break; case 'x': op_flags |= OPT_x; break; default: Usage(); break; } b1: argc--; argv++; } vector qterms; if (op_flags & OPT_q) { fprintf(stderr, "q argc %d\n", argc); if (argc < 1) Usage(); while (argc > 0) { qterms.push_back(*argv++); argc--; } } if (argc != 0) Usage(); atexit(cleanup); if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sigcleanup); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, sigcleanup); if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) signal(SIGQUIT, sigcleanup); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sigcleanup); try { db = new Xapian::Database(dbdir); cout << "DB: ndocs " << db->get_doccount() << " lastdocid " << db->get_lastdocid() << " avglength " << db->get_avlength() << endl; // If we have terms with a leading ':' it's a new style, // unstripped index { Xapian::TermIterator term = db->allterms_begin(":"); if (term == db->allterms_end()) o_index_stripchars = true; else o_index_stripchars = false; cout<<"DB: terms are "<<(o_index_stripchars?"stripped":"raw")<termlist_begin(docid); term != db->termlist_end(docid);term++) { const string& s = *term; if ((op_flags&OPT_l) && has_prefix(s)) continue; cout << op << detailstring(s) << cl << endl; } } else { for (term = db->allterms_begin(); term != db->allterms_end();term++) { const string& s = *term; if ((op_flags&OPT_l) && has_prefix(s)) continue; if (op_flags & OPT_f) cout << db->get_collection_freq(*term) << " " << term.get_termfreq() << " "; cout << op << detailstring(s) << cl << endl; } } } else if (op_flags & OPT_D) { Xapian::Document doc = db->get_document(docid); string data = doc.get_data(); cout << data << endl; } else if (op_flags & OPT_r) { wholedoc(db, docid); } else if (op_flags & OPT_X) { Xapian::Document doc = db->get_document(docid); string data = doc.get_data(); cout << data << endl; cout << "Really delete xapian document ?" << endl; string rep; cin >> rep; if (!rep.empty() && (rep[0] == 'y' || rep[0] == 'Y')) { Xapian::WritableDatabase wdb(dbdir, Xapian::DB_OPEN); cout << "Deleting" << endl; wdb.delete_document(docid); } } else if (op_flags & OPT_P) { Xapian::PostingIterator doc; for (doc = db->postlist_begin(aterm); doc != db->postlist_end(aterm); doc++) { cout << *doc << "(" << doc.get_wdf() << ") : " ; Xapian::PositionIterator pos; for (pos = doc.positionlist_begin(); pos != doc.positionlist_end(); pos++) { cout << *pos << " " ; } cout << endl; } } else if (op_flags & OPT_F) { cout << "FreqFor " << aterm << " : " << db->get_termfreq(aterm) << endl; } else if (op_flags & OPT_E) { cout << "Exists [" << aterm << "] : " << db->term_exists(aterm) << endl; } else if (op_flags & OPT_q) { Xapian::Enquire enquire(*db); Xapian::Query query(Xapian::Query::OP_AND, qterms.begin(), qterms.end()); cout << "Performing query `" << query.get_description() << "'" << endl; enquire.set_query(query); Xapian::MSet matches = enquire.get_mset(0, 10); cout << "Estimated results: " << matches.get_matches_lower_bound() << endl; Xapian::MSetIterator i; for (i = matches.begin(); i != matches.end(); ++i) { cout << "Document ID " << *i << "\t"; cout << i.get_percent() << "% "; Xapian::Document doc = i.get_document(); cout << "[" << doc.get_data() << "]" << endl; } } } catch (const Xapian::Error &e) { cout << "Exception: " << e.get_msg() << endl; } catch (const string &s) { cout << "Exception: " << s << endl; } catch (const char *s) { cout << "Exception: " << s << endl; } catch (...) { cout << "Caught unknown exception" << endl; } exit(0); } recoll-1.36.1/query/recollqmain.cpp0000644000175000017500000000201414410615043014155 00000000000000/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Takes a query and run it, no gui, results to stdout #include "autoconfig.h" #include #include "rclconfig.h" #include "recollq.h" static RclConfig *rclconfig; int main(int argc, char **argv) { return(recollq(&rclconfig, argc, argv)); } recoll-1.36.1/query/filtseq.h0000644000175000017500000000363314426500174013000 00000000000000/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _FILTSEQ_H_INCLUDED_ #define _FILTSEQ_H_INCLUDED_ #include "autoconfig.h" #include #include #include #include "docseq.h" class RclConfig; /** * A filtered sequence is created from another one by selecting entries * according to the given criteria. * Note that this class can only filter on mime type (or rclcatg) at the moment, * and is only used for history. Normal query filtering is performed by adding a * clause to the Xapian query. */ class DocSeqFiltered : public DocSeqModifier { public: DocSeqFiltered(RclConfig *conf, std::shared_ptr iseq, DocSeqFiltSpec &filtspec); virtual ~DocSeqFiltered() {} DocSeqFiltered(const DocSeqFiltered&) = delete; DocSeqFiltered& operator=(const DocSeqFiltered&) = delete; virtual bool canFilter() {return true;} virtual bool setFiltSpec(const DocSeqFiltSpec &filtspec); virtual bool getDoc(int num, Rcl::Doc &doc, std::string *sh = nullptr); virtual int getResCnt() {return m_seq->getResCnt();} private: RclConfig *m_config; DocSeqFiltSpec m_spec; std::vector m_dbindices; }; #endif /* _FILTSEQ_H_INCLUDED_ */ recoll-1.36.1/query/reslistpager.cpp0000644000175000017500000004504614477551316014406 00000000000000/* Copyright (C) 2007-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include #include #include #include #include #include #include #include "cstr.h" #include "reslistpager.h" #include "log.h" #include "rclconfig.h" #include "smallut.h" #include "rclutil.h" #include "plaintorich.h" #include "mimehandler.h" #include "transcode.h" #include "pathut.h" #include "execmd.h" using std::ostringstream; using std::list; using std::map; using std::string; using std::vector; // Default highlighter. No need for locking, this is query-only. static const string cstr_hlfontcolor(""); static const string cstr_hlendfont(""); class PlainToRichHtReslist : public PlainToRich { public: virtual string startMatch(unsigned int) { return cstr_hlfontcolor; } virtual string endMatch() { return cstr_hlendfont; } }; static PlainToRichHtReslist g_hiliter; ResListPager::ResListPager(RclConfig *cnf, int pagesize, bool alwaysSnippets) : m_pagesize(pagesize), m_alwaysSnippets(alwaysSnippets), m_newpagesize(pagesize), m_resultsInCurrentPage(0), m_winfirst(-1), m_hasNext(true), m_hiliter(&g_hiliter) { cnf->getConfParam("thumbnailercmd", &m_thumbnailercmd); } void ResListPager::resultPageNext() { if (!m_docSource) { LOGDEB("ResListPager::resultPageNext: null source\n"); return; } int resCnt = m_docSource->getResCnt(); LOGDEB("ResListPager::resultPageNext: rescnt " << resCnt << ", winfirst " << m_winfirst << "\n"); if (m_winfirst < 0) { m_winfirst = 0; } else { m_winfirst += int(m_respage.size()); } // Get the next page of results. Note that we look ahead by one to // determine if there is actually a next page vector npage; int pagelen = m_docSource->getSeqSlice(m_winfirst, m_pagesize + 1, npage); // If page was truncated, there is no next m_hasNext = (pagelen == m_pagesize + 1); // Get rid of the possible excess result if (pagelen == m_pagesize + 1) { npage.resize(m_pagesize); pagelen--; } if (pagelen <= 0) { // No results ? This can only happen on the first page or if the // actual result list size is a multiple of the page pref (else // there would have been no Next on the last page) if (m_winfirst > 0) { // Have already results. Let them show, just disable the // Next button. We'd need to remove the Next link from the page // too. // Restore the m_winfirst value, let the current result vector alone m_winfirst -= int(m_respage.size()); } else { // No results at all (on first page) m_winfirst = -1; } return; } m_resultsInCurrentPage = pagelen; m_respage = npage; } static string maybeEscapeHtml(const string& fld) { if (fld.compare(0, cstr_fldhtm.size(), cstr_fldhtm)) return escapeHtml(fld); else return fld.substr(cstr_fldhtm.size()); } void ResListPager::resultPageFor(int docnum) { if (!m_docSource) { LOGDEB("ResListPager::resultPageFor: null source\n"); return; } int resCnt = m_docSource->getResCnt(); LOGDEB("ResListPager::resultPageFor(" << docnum << "): rescnt " << resCnt << ", winfirst " << m_winfirst << "\n"); m_winfirst = (docnum / m_pagesize) * m_pagesize; // Get the next page of results. vector npage; int pagelen = m_docSource->getSeqSlice(m_winfirst, m_pagesize, npage); // If page was truncated, there is no next m_hasNext = (pagelen == m_pagesize); if (pagelen <= 0) { m_winfirst = -1; return; } m_respage = npage; } static const SimpleRegexp pagenumre("(^ *\\[[pP]\\.* [0-9]+])", 0); void ResListPager::displayDoc(RclConfig *config, int i, Rcl::Doc& doc, const HighlightData& hdata, const string& sh) { ostringstream chunk; // Determine icon to display if any string iconurl = iconUrl(config, doc); // Printable url: either utf-8 if transcoding succeeds, or url-encoded string url; printableUrl(config->getDefCharset(), doc.url, url); // Same as url, but with file:// possibly stripped. output by %u instead of %U. string urlOrLocal; urlOrLocal = fileurltolocalpath(url); if (urlOrLocal.empty()) urlOrLocal = url; // Make title out of file name if none yet string titleOrFilename; string utf8fn; doc.getmeta(Rcl::Doc::keytt, &titleOrFilename); doc.getmeta(Rcl::Doc::keyfn, &utf8fn); if (utf8fn.empty()) { utf8fn = path_getsimple(url); } if (titleOrFilename.empty()) { titleOrFilename = utf8fn; } // Url for the parent directory. We strip the file:// part for local paths string parenturl = url_parentfolder(url); { string localpath = fileurltolocalpath(parenturl); if (!localpath.empty()) parenturl = localpath; } // Result number char numbuf[20]; int docnumforlinks = m_winfirst + 1 + i; sprintf(numbuf, "%d", docnumforlinks); // Document date: either doc or file modification times string datebuf; if (!doc.dmtime.empty() || !doc.fmtime.empty()) { time_t mtime = doc.dmtime.empty() ? atoll(doc.fmtime.c_str()) : atoll(doc.dmtime.c_str()); struct tm *tm = localtime(&mtime); datebuf = utf8datestring(dateFormat(), tm); } // Size information. We print both doc and file if they differ a lot int64_t fsize = -1, dsize = -1; if (!doc.dbytes.empty()) dsize = static_cast(atoll(doc.dbytes.c_str())); if (!doc.fbytes.empty()) fsize = static_cast(atoll(doc.fbytes.c_str())); string sizebuf; if (dsize > 0) { sizebuf = displayableBytes(dsize); if (fsize > 10 * dsize && fsize - dsize > 1000) sizebuf += string(" / ") + displayableBytes(fsize); } else if (fsize >= 0) { sizebuf = displayableBytes(fsize); } string richabst; bool needabstract = parFormat().find("%A") != string::npos; if (needabstract && m_docSource) { vector snippets; m_hiliter->set_inputhtml(false); m_docSource->getAbstract(doc, m_hiliter, snippets); for (const auto& snippet : snippets) { if (!snippet.empty()) { richabst += snippet; richabst += absSep(); } } } // Links; Uses utilities from mimehandler.h ostringstream linksbuf; if (canIntern(&doc, config)) { linksbuf << "" << trans("Preview") << "  "; } if (canOpen(&doc, config, useAll())) { linksbuf << "" << trans("Open") << ""; } ostringstream snipsbuf; if (m_alwaysSnippets || doc.haspages) { snipsbuf << "" << trans("Snippets") << "  "; linksbuf << "  " << snipsbuf.str(); } string collapscnt; if (doc.getmeta(Rcl::Doc::keycc, &collapscnt) && !collapscnt.empty()) { ostringstream collpsbuf; int clc = atoi(collapscnt.c_str()) + 1; collpsbuf << "" << trans("Dups") << "(" << clc << ")" << "  "; linksbuf << "  " << collpsbuf.str(); } // Build the result list paragraph: // Subheader: this is used by history if (!sh.empty()) chunk << "

" << sh << "

\n

"; else chunk << "

"; char xdocidbuf[100]; sprintf(xdocidbuf, "%lu", doc.xdocid); // Configurable stuff map subs; subs["A"] = !richabst.empty() ? richabst : ""; subs["D"] = datebuf; subs["E"] = snipsbuf.str(); subs["I"] = iconurl; subs["i"] = doc.ipath; subs["K"] = !doc.meta[Rcl::Doc::keykw].empty() ? string("[") + maybeEscapeHtml(doc.meta[Rcl::Doc::keykw]) + "]" : ""; subs["L"] = linksbuf.str(); subs["N"] = numbuf; subs["M"] = doc.mimetype; subs["P"] = parenturl; subs["R"] = doc.meta[Rcl::Doc::keyrr]; subs["S"] = sizebuf; subs["T"] = maybeEscapeHtml(titleOrFilename); subs["t"] = maybeEscapeHtml(doc.meta[Rcl::Doc::keytt]); subs["U"] = url; subs["u"] = urlOrLocal; subs["x"] = xdocidbuf; // Let %(xx) access all metadata. HTML-neuter everything: for (const auto& entry : doc.meta) { if (!entry.first.empty()) subs[entry.first] = maybeEscapeHtml(entry.second); } string formatted; pcSubst(parFormat(), formatted, subs); chunk << formatted << "

\n"; LOGDEB2("Chunk: [" << chunk.rdbuf()->str() << "]\n"); append(chunk.rdbuf()->str(), i, doc); } bool ResListPager::getDoc(int num, Rcl::Doc& doc) { if (m_winfirst < 0 || m_respage.size() == 0) return false; if (num < m_winfirst || num >= m_winfirst + int(m_respage.size())) return false; doc = m_respage[num-m_winfirst].doc; return true; } void ResListPager::displayPage(RclConfig *config) { LOGDEB("ResListPager::displayPage. linkPrefix: " << linkPrefix() << "\n"); if (!m_docSource) { LOGDEB("ResListPager::displayPage: null source\n"); return; } if (m_winfirst < 0 && !pageEmpty()) { LOGDEB("ResListPager::displayPage: sequence error: winfirst < 0\n"); return; } ostringstream chunk; // Display list header // We could use a but the textedit doesnt display // it prominently // Note: have to append text in chunks that make sense // html-wise. If we break things up too much, the editor // gets confused. Hence the use of the 'chunk' text // accumulator // Also note that there can be results beyond the estimated resCnt. chunk << "<html><head>\n" << "<meta http-equiv=\"content-type\"" << " content=\"text/html; charset=utf-8\">\n" << headerContent() << "</head><body " << bodyAttrs() << ">\n" << pageTop() << "<p><span style=\"font-size:110%;\"><b>" << m_docSource->title() << "</b></span>   "; if (pageEmpty()) { chunk << trans("<p><b>No results found</b><br>"); string reason = m_docSource->getReason(); if (!reason.empty()) { chunk << "<blockquote>" << escapeHtml(reason) << "</blockquote></p>"; } else { HighlightData hldata; m_docSource->getTerms(hldata); vector<string> uterms(hldata.uterms.begin(), hldata.uterms.end()); if (!uterms.empty()) { map<string, vector<string> > spellings; suggest(uterms, spellings); if (!spellings.empty()) { if (o_index_stripchars) { chunk << trans("<p><i>Alternate spellings (accents suppressed): </i>") << "<br /><blockquote>"; } else { chunk << trans("<p><i>Alternate spellings: </i>") << "<br /><blockquote>"; } for (const auto& entry: spellings) { chunk << "<b>" << entry.first << "</b> : "; for (const auto& spelling : entry.second) { chunk << spelling << " "; } chunk << "<br />"; } chunk << "</blockquote></p>"; } } } } else { HighlightData hldata; m_docSource->getTerms(hldata); if (!hldata.spellexpands.empty()) { string msg; if (hldata.spellexpands.size() == 1) { msg = trans("This spelling guess was added to the search:"); } else { msg = trans("These spelling guesses were added to the search:"); } chunk << "<br><i>" << msg << "</i> " << stringsToString(hldata.spellexpands) << "<br/>\n"; } unsigned int resCnt = m_docSource->getResCnt(); if (m_winfirst + m_respage.size() < resCnt) { chunk << trans("Documents") << " <b>" << m_winfirst + 1 << "-" << m_winfirst + m_respage.size() << "</b> " << trans("out of at least") << " " << resCnt << " " << trans("for") << " " ; } else { chunk << trans("Documents") << " <b>" << m_winfirst + 1 << "-" << m_winfirst + m_respage.size() << "</b> " << trans("for") << " "; } } chunk << detailsLink(); if (hasPrev() || hasNext()) { chunk << "  "; if (hasPrev()) { chunk << "<a href=\"" << linkPrefix() + prevUrl() + "\"><b>" << trans("Previous") << "</b></a>   "; } if (hasNext()) { chunk << "<a href=\"" << linkPrefix() + nextUrl() + "\"><b>" << trans("Next") << "</b></a>"; } } chunk << "</p>\n"; append(chunk.rdbuf()->str()); chunk.rdbuf()->str(""); if (pageEmpty()) return; HighlightData hdata; m_docSource->getTerms(hdata); // Emit data for result entry paragraph. Do it in chunks that make sense // html-wise, else our client may get confused for (int i = 0; i < (int)m_respage.size(); i++) { Rcl::Doc& doc(m_respage[i].doc); string& sh(m_respage[i].subHeader); displayDoc(config, i, doc, hdata, sh); } // Footer chunk << "<p align=\"center\">"; if (hasPrev() || hasNext()) { if (hasPrev()) { chunk << "<a href=\"" + linkPrefix() + prevUrl() + "\"><b>" << trans("Previous") << "</b></a>   "; } if (hasNext()) { chunk << "<a href=\"" << linkPrefix() + nextUrl() + "\"><b>" << trans("Next") << "</b></a>"; } } chunk << "</p>\n"; chunk << "</body></html>\n"; append(chunk.rdbuf()->str()); flush(); } void ResListPager::displaySingleDoc( RclConfig *config, int idx, Rcl::Doc& doc, const HighlightData& hdata) { ostringstream chunk; // Header // Note: have to append text in chunks that make sense // html-wise. If we break things up too much, the editor // gets confused. string bdtag("<body "); bdtag += bodyAttrs(); rtrimstring(bdtag, " "); bdtag += ">"; chunk << "<html><head>\n" << "<meta http-equiv=\"content-type\"" << " content=\"text/html; charset=utf-8\">\n" << headerContent() << "</head>\n" << bdtag << "\n"; append(chunk.rdbuf()->str()); // Document displayDoc(config, idx, doc, hdata, string()); // Footer append("</body></html>\n"); flush(); } // Default implementations for things that should be implemented by // specializations string ResListPager::nextUrl() { return "n-1"; } string ResListPager::prevUrl() { return "p-1"; } string ResListPager::iconUrl(RclConfig *config, Rcl::Doc& doc) { // If this is a top level doc, check for a thumbnail image if (doc.ipath.empty()) { vector<string> paths; Rcl::docsToPaths({doc}, paths); if (!paths.empty()) { string path; string url = cstr_fileu + paths[0]; LOGDEB2("ResList::iconUrl: source path [" << paths[0] << "]\n"); if (thumbPathForUrl(url, 128, path)) { LOGDEB2("ResList::iconUrl: icon path [" << path << "]\n"); return cstr_fileu + path; } else { LOGDEB2("ResList::iconUrl: no icon: path [" << path << "]\n"); if (!m_thumbnailercmd.empty()) { std::string thumbpath; thumbPathForUrl(url, 128, thumbpath); ExecCmd cmd; std::vector<std::string> cmdvec{m_thumbnailercmd}; cmdvec.push_back(url); cmdvec.push_back(doc.mimetype); cmdvec.push_back("128"); cmdvec.push_back(thumbpath); if (cmd.doexec(cmdvec) == 0) { if (thumbPathForUrl(url, 128, path)) { LOGDEB2("ResList::iconUrl: icon path [" << path << "]\n"); return cstr_fileu + path; } } } } } else { LOGDEB("ResList::iconUrl: docsToPaths failed\n"); } } // No thumbnail, look for the MIME type icon. string apptag; doc.getmeta(Rcl::Doc::keyapptg, &apptag); return path_pathtofileurl(config->getMimeIconPath(doc.mimetype, apptag)); } bool ResListPager::append(const string& data) { fprintf(stderr, "%s", data.c_str()); return true; } string ResListPager::trans(const string& in) { return in; } string ResListPager::detailsLink() { string chunk = string("<a href=\"") + linkPrefix() + "H-1\">"; chunk += trans("(show query)") + "</a>"; return chunk; } const string &ResListPager::parFormat() { static const string cstr_format("<img src=\"%I\" align=\"left\">" "%R %S %L   <b>%T</b><br>" "%M %D   <i>%U</i><br>" "%A %K"); return cstr_format; } const string &ResListPager::dateFormat() { static const string cstr_format(" %Y-%m-%d %H:%M:%S %z"); return cstr_format; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/qresultstore.h������������������������������������������������������������������0000644�0001750�0001750�00000004227�14410615043�014100� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2017-2020 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _QRESULTSTORE_H_INCLUDED_ #define _QRESULTSTORE_H_INCLUDED_ /** * Implement an efficient way to store the whole or part of a query result set. * This would naturally be done as a vector<Rcl::Doc>, but the natural * way leads to a huge space waste (8-10x), which may be a problem in * some cases. This is mostly used by the uprcl Media Server. */ #include <string> #include <set> namespace Rcl { class Query; class QResultStore { public: QResultStore(); ~QResultStore(); QResultStore(const QResultStore&) = delete; QResultStore& operator=(const QResultStore&) = delete; /** * Fetch and store the results of the input query. * * @param q the executed query object to use for fetching results. * @param fldspec list of fields to be excluded or included. * @param isinc if true, the field list defines the fields to be stored, * else, those to be excluded. */ bool storeQuery(Rcl::Query& q, std::set<std::string> fldspec = {}, bool isinc = false); /** Retrieve count of stored results */ int getCount(); /** * Retrieve field value. * * @param docindex index in query results. * @param fldname field name. */ const char *fieldValue(int docindex, const std::string& fldname); class Internal; private: Internal *m{nullptr}; }; } #endif /* _QRESULTSTORE_H_INCLUDED_ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/wasaparse.hpp�������������������������������������������������������������������0000644�0001750�0001750�00000060055�14410615043�013653� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// A Bison parser, made by GNU Bison 3.8.2. // Skeleton interface for Bison LALR(1) parsers in C++ // Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. // As a special exception, you may create a larger work that contains // part or all of the Bison parser skeleton and distribute that work // under terms of your choice, so long as that work isn't itself a // parser generator using the skeleton or a modified version thereof // as a parser skeleton. Alternatively, if you modify or redistribute // the parser skeleton itself, you may (at your option) remove this // special exception, which will cause the skeleton and the resulting // Bison output files to be licensed under the GNU General Public // License without this special exception. // This special exception was added by the Free Software Foundation in // version 2.2 of Bison. /** ** \file y.tab.h ** Define the yy::parser class. */ // C++ LALR(1) parser skeleton written by Akim Demaille. // DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, // especially those whose name start with YY_ or yy_. They are // private implementation details that can be changed or removed. #ifndef YY_YY_Y_TAB_H_INCLUDED # define YY_YY_Y_TAB_H_INCLUDED # include <cstdlib> // std::abort # include <iostream> # include <stdexcept> # include <string> # include <vector> #if defined __cplusplus # define YY_CPLUSPLUS __cplusplus #else # define YY_CPLUSPLUS 199711L #endif // Support move semantics when possible. #if 201103L <= YY_CPLUSPLUS # define YY_MOVE std::move # define YY_MOVE_OR_COPY move # define YY_MOVE_REF(Type) Type&& # define YY_RVREF(Type) Type&& # define YY_COPY(Type) Type #else # define YY_MOVE # define YY_MOVE_OR_COPY copy # define YY_MOVE_REF(Type) Type& # define YY_RVREF(Type) const Type& # define YY_COPY(Type) const Type& #endif // Support noexcept when possible. #if 201103L <= YY_CPLUSPLUS # define YY_NOEXCEPT noexcept # define YY_NOTHROW #else # define YY_NOEXCEPT # define YY_NOTHROW throw () #endif // Support constexpr when possible. #if 201703 <= YY_CPLUSPLUS # define YY_CONSTEXPR constexpr #else # define YY_CONSTEXPR #endif # include "location.hh" #ifndef YY_ATTRIBUTE_PURE # if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) # define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) # else # define YY_ATTRIBUTE_PURE # endif #endif #ifndef YY_ATTRIBUTE_UNUSED # if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) # define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) # else # define YY_ATTRIBUTE_UNUSED # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YY_USE(E) ((void) (E)) #else # define YY_USE(E) /* empty */ #endif /* Suppress an incorrect diagnostic about yylval being uninitialized. */ #if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ # if __GNUC__ * 100 + __GNUC_MINOR__ < 407 # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") # else # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # endif # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ # define YY_IGNORE_USELESS_CAST_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") # define YY_IGNORE_USELESS_CAST_END \ _Pragma ("GCC diagnostic pop") #endif #ifndef YY_IGNORE_USELESS_CAST_BEGIN # define YY_IGNORE_USELESS_CAST_BEGIN # define YY_IGNORE_USELESS_CAST_END #endif # ifndef YY_CAST # ifdef __cplusplus # define YY_CAST(Type, Val) static_cast<Type> (Val) # define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) # else # define YY_CAST(Type, Val) ((Type) (Val)) # define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) # endif # endif # ifndef YY_NULLPTR # if defined __cplusplus # if 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # else # define YY_NULLPTR ((void*)0) # endif # endif /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif namespace yy { #line 182 "y.tab.h" /// A Bison parser. class parser { public: #ifdef YYSTYPE # ifdef __GNUC__ # pragma GCC message "bison: do not #define YYSTYPE in C++, use %define api.value.type" # endif typedef YYSTYPE value_type; #else /// Symbol semantic values. union value_type { #line 46 "/home/dockes/projets/fulltext/recoll/src/query/wasaparse.ypp" std::string *str; Rcl::SearchDataClauseRange *rg; Rcl::SearchDataClauseSimple *cl; Rcl::SearchData *sd; #line 207 "y.tab.h" }; #endif /// Backward compatibility (Bison 3.8). typedef value_type semantic_type; /// Symbol locations. typedef location location_type; /// Syntax errors thrown from user actions. struct syntax_error : std::runtime_error { syntax_error (const location_type& l, const std::string& m) : std::runtime_error (m) , location (l) {} syntax_error (const syntax_error& s) : std::runtime_error (s.what ()) , location (s.location) {} ~syntax_error () YY_NOEXCEPT YY_NOTHROW; location_type location; }; /// Token kinds. struct token { enum token_kind_type { YYEMPTY = -2, YYEOF = 0, // "end of file" YYerror = 256, // error YYUNDEF = 257, // "invalid token" WORD = 258, // WORD QUOTED = 259, // QUOTED QUALIFIERS = 260, // QUALIFIERS AND = 261, // AND UCONCAT = 262, // UCONCAT OR = 263, // OR EQUALS = 264, // EQUALS CONTAINS = 265, // CONTAINS SMALLEREQ = 266, // SMALLEREQ SMALLER = 267, // SMALLER GREATEREQ = 268, // GREATEREQ GREATER = 269, // GREATER RANGE = 270 // RANGE }; /// Backward compatibility alias (Bison 3.6). typedef token_kind_type yytokentype; }; /// Token kind, as returned by yylex. typedef token::token_kind_type token_kind_type; /// Backward compatibility alias (Bison 3.6). typedef token_kind_type token_type; /// Symbol kinds. struct symbol_kind { enum symbol_kind_type { YYNTOKENS = 19, ///< Number of tokens. S_YYEMPTY = -2, S_YYEOF = 0, // "end of file" S_YYerror = 1, // error S_YYUNDEF = 2, // "invalid token" S_WORD = 3, // WORD S_QUOTED = 4, // QUOTED S_QUALIFIERS = 5, // QUALIFIERS S_AND = 6, // AND S_UCONCAT = 7, // UCONCAT S_8_ = 8, // '(' S_9_ = 9, // '-' S_OR = 10, // OR S_EQUALS = 11, // EQUALS S_CONTAINS = 12, // CONTAINS S_SMALLEREQ = 13, // SMALLEREQ S_SMALLER = 14, // SMALLER S_GREATEREQ = 15, // GREATEREQ S_GREATER = 16, // GREATER S_RANGE = 17, // RANGE S_18_ = 18, // ')' S_YYACCEPT = 19, // $accept S_topquery = 20, // topquery S_query = 21, // query S_fieldexpr = 22, // fieldexpr S_complexfieldname = 23, // complexfieldname S_range = 24, // range S_term = 25, // term S_qualquote = 26 // qualquote }; }; /// (Internal) symbol kind. typedef symbol_kind::symbol_kind_type symbol_kind_type; /// The number of tokens. static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS; /// A complete symbol. /// /// Expects its Base type to provide access to the symbol kind /// via kind (). /// /// Provide access to semantic value and location. template <typename Base> struct basic_symbol : Base { /// Alias to Base. typedef Base super_type; /// Default constructor. basic_symbol () YY_NOEXCEPT : value () , location () {} #if 201103L <= YY_CPLUSPLUS /// Move constructor. basic_symbol (basic_symbol&& that) : Base (std::move (that)) , value (std::move (that.value)) , location (std::move (that.location)) {} #endif /// Copy constructor. basic_symbol (const basic_symbol& that); /// Constructor for valueless symbols. basic_symbol (typename Base::kind_type t, YY_MOVE_REF (location_type) l); /// Constructor for symbols with semantic value. basic_symbol (typename Base::kind_type t, YY_RVREF (value_type) v, YY_RVREF (location_type) l); /// Destroy the symbol. ~basic_symbol () { clear (); } /// Destroy contents, and record that is empty. void clear () YY_NOEXCEPT { Base::clear (); } /// The user-facing name of this symbol. std::string name () const YY_NOEXCEPT { return parser::symbol_name (this->kind ()); } /// Backward compatibility (Bison 3.6). symbol_kind_type type_get () const YY_NOEXCEPT; /// Whether empty. bool empty () const YY_NOEXCEPT; /// Destructive move, \a s is emptied into this. void move (basic_symbol& s); /// The semantic value. value_type value; /// The location. location_type location; private: #if YY_CPLUSPLUS < 201103L /// Assignment operator. basic_symbol& operator= (const basic_symbol& that); #endif }; /// Type access provider for token (enum) based symbols. struct by_kind { /// The symbol kind as needed by the constructor. typedef token_kind_type kind_type; /// Default constructor. by_kind () YY_NOEXCEPT; #if 201103L <= YY_CPLUSPLUS /// Move constructor. by_kind (by_kind&& that) YY_NOEXCEPT; #endif /// Copy constructor. by_kind (const by_kind& that) YY_NOEXCEPT; /// Constructor from (external) token numbers. by_kind (kind_type t) YY_NOEXCEPT; /// Record that this symbol is empty. void clear () YY_NOEXCEPT; /// Steal the symbol kind from \a that. void move (by_kind& that); /// The (internal) type number (corresponding to \a type). /// \a empty when empty. symbol_kind_type kind () const YY_NOEXCEPT; /// Backward compatibility (Bison 3.6). symbol_kind_type type_get () const YY_NOEXCEPT; /// The symbol kind. /// \a S_YYEMPTY when empty. symbol_kind_type kind_; }; /// Backward compatibility for a private implementation detail (Bison 3.6). typedef by_kind by_type; /// "External" symbols: returned by the scanner. struct symbol_type : basic_symbol<by_kind> {}; /// Build a parser object. parser (WasaParserDriver* d_yyarg); virtual ~parser (); #if 201103L <= YY_CPLUSPLUS /// Non copyable. parser (const parser&) = delete; /// Non copyable. parser& operator= (const parser&) = delete; #endif /// Parse. An alias for parse (). /// \returns 0 iff parsing succeeded. int operator() (); /// Parse. /// \returns 0 iff parsing succeeded. virtual int parse (); #if YYDEBUG /// The current debugging stream. std::ostream& debug_stream () const YY_ATTRIBUTE_PURE; /// Set the current debugging stream. void set_debug_stream (std::ostream &); /// Type for debugging levels. typedef int debug_level_type; /// The current debugging level. debug_level_type debug_level () const YY_ATTRIBUTE_PURE; /// Set the current debugging level. void set_debug_level (debug_level_type l); #endif /// Report a syntax error. /// \param loc where the syntax error is found. /// \param msg a description of the syntax error. virtual void error (const location_type& loc, const std::string& msg); /// Report a syntax error. void error (const syntax_error& err); /// The user-facing name of the symbol whose (internal) number is /// YYSYMBOL. No bounds checking. static std::string symbol_name (symbol_kind_type yysymbol); class context { public: context (const parser& yyparser, const symbol_type& yyla); const symbol_type& lookahead () const YY_NOEXCEPT { return yyla_; } symbol_kind_type token () const YY_NOEXCEPT { return yyla_.kind (); } const location_type& location () const YY_NOEXCEPT { return yyla_.location; } /// Put in YYARG at most YYARGN of the expected tokens, and return the /// number of tokens stored in YYARG. If YYARG is null, return the /// number of expected tokens (guaranteed to be less than YYNTOKENS). int expected_tokens (symbol_kind_type yyarg[], int yyargn) const; private: const parser& yyparser_; const symbol_type& yyla_; }; private: #if YY_CPLUSPLUS < 201103L /// Non copyable. parser (const parser&); /// Non copyable. parser& operator= (const parser&); #endif /// Stored state numbers (used for stacks). typedef signed char state_type; /// The arguments of the error message. int yy_syntax_error_arguments_ (const context& yyctx, symbol_kind_type yyarg[], int yyargn) const; /// Generate an error message. /// \param yyctx the context in which the error occurred. virtual std::string yysyntax_error_ (const context& yyctx) const; /// Compute post-reduction state. /// \param yystate the current state /// \param yysym the nonterminal to push on the stack static state_type yy_lr_goto_state_ (state_type yystate, int yysym); /// Whether the given \c yypact_ value indicates a defaulted state. /// \param yyvalue the value to check static bool yy_pact_value_is_default_ (int yyvalue) YY_NOEXCEPT; /// Whether the given \c yytable_ value indicates a syntax error. /// \param yyvalue the value to check static bool yy_table_value_is_error_ (int yyvalue) YY_NOEXCEPT; static const signed char yypact_ninf_; static const signed char yytable_ninf_; /// Convert a scanner token kind \a t to a symbol kind. /// In theory \a t should be a token_kind_type, but character literals /// are valid, yet not members of the token_kind_type enum. static symbol_kind_type yytranslate_ (int t) YY_NOEXCEPT; /// Convert the symbol name \a n to a form suitable for a diagnostic. static std::string yytnamerr_ (const char *yystr); /// For a symbol, its name in clear. static const char* const yytname_[]; // Tables. // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing // STATE-NUM. static const signed char yypact_[]; // YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. // Performed when YYTABLE does not specify something else to do. Zero // means the default is an error. static const signed char yydefact_[]; // YYPGOTO[NTERM-NUM]. static const signed char yypgoto_[]; // YYDEFGOTO[NTERM-NUM]. static const signed char yydefgoto_[]; // YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If // positive, shift that token. If negative, reduce the rule whose // number is the opposite. If YYTABLE_NINF, syntax error. static const signed char yytable_[]; static const signed char yycheck_[]; // YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of // state STATE-NUM. static const signed char yystos_[]; // YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. static const signed char yyr1_[]; // YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. static const signed char yyr2_[]; #if YYDEBUG // YYRLINE[YYN] -- Source line where rule number YYN was defined. static const short yyrline_[]; /// Report on the debug stream that the rule \a r is going to be reduced. virtual void yy_reduce_print_ (int r) const; /// Print the state stack on the debug stream. virtual void yy_stack_print_ () const; /// Debugging level. int yydebug_; /// Debug stream. std::ostream* yycdebug_; /// \brief Display a symbol kind, value and location. /// \param yyo The output stream. /// \param yysym The symbol. template <typename Base> void yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const; #endif /// \brief Reclaim the memory associated to a symbol. /// \param yymsg Why this token is reclaimed. /// If null, print nothing. /// \param yysym The symbol. template <typename Base> void yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const; private: /// Type access provider for state based symbols. struct by_state { /// Default constructor. by_state () YY_NOEXCEPT; /// The symbol kind as needed by the constructor. typedef state_type kind_type; /// Constructor. by_state (kind_type s) YY_NOEXCEPT; /// Copy constructor. by_state (const by_state& that) YY_NOEXCEPT; /// Record that this symbol is empty. void clear () YY_NOEXCEPT; /// Steal the symbol kind from \a that. void move (by_state& that); /// The symbol kind (corresponding to \a state). /// \a symbol_kind::S_YYEMPTY when empty. symbol_kind_type kind () const YY_NOEXCEPT; /// The state number used to denote an empty symbol. /// We use the initial state, as it does not have a value. enum { empty_state = 0 }; /// The state. /// \a empty when empty. state_type state; }; /// "Internal" symbol: element of the stack. struct stack_symbol_type : basic_symbol<by_state> { /// Superclass. typedef basic_symbol<by_state> super_type; /// Construct an empty symbol. stack_symbol_type (); /// Move or copy construction. stack_symbol_type (YY_RVREF (stack_symbol_type) that); /// Steal the contents from \a sym to build this. stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym); #if YY_CPLUSPLUS < 201103L /// Assignment, needed by push_back by some old implementations. /// Moves the contents of that. stack_symbol_type& operator= (stack_symbol_type& that); /// Assignment, needed by push_back by other implementations. /// Needed by some other old implementations. stack_symbol_type& operator= (const stack_symbol_type& that); #endif }; /// A stack with random access from its top. template <typename T, typename S = std::vector<T> > class stack { public: // Hide our reversed order. typedef typename S::iterator iterator; typedef typename S::const_iterator const_iterator; typedef typename S::size_type size_type; typedef typename std::ptrdiff_t index_type; stack (size_type n = 200) YY_NOEXCEPT : seq_ (n) {} #if 201103L <= YY_CPLUSPLUS /// Non copyable. stack (const stack&) = delete; /// Non copyable. stack& operator= (const stack&) = delete; #endif /// Random access. /// /// Index 0 returns the topmost element. const T& operator[] (index_type i) const { return seq_[size_type (size () - 1 - i)]; } /// Random access. /// /// Index 0 returns the topmost element. T& operator[] (index_type i) { return seq_[size_type (size () - 1 - i)]; } /// Steal the contents of \a t. /// /// Close to move-semantics. void push (YY_MOVE_REF (T) t) { seq_.push_back (T ()); operator[] (0).move (t); } /// Pop elements from the stack. void pop (std::ptrdiff_t n = 1) YY_NOEXCEPT { for (; 0 < n; --n) seq_.pop_back (); } /// Pop all elements from the stack. void clear () YY_NOEXCEPT { seq_.clear (); } /// Number of elements on the stack. index_type size () const YY_NOEXCEPT { return index_type (seq_.size ()); } /// Iterator on top of the stack (going downwards). const_iterator begin () const YY_NOEXCEPT { return seq_.begin (); } /// Bottom of the stack. const_iterator end () const YY_NOEXCEPT { return seq_.end (); } /// Present a slice of the top of a stack. class slice { public: slice (const stack& stack, index_type range) YY_NOEXCEPT : stack_ (stack) , range_ (range) {} const T& operator[] (index_type i) const { return stack_[range_ - i]; } private: const stack& stack_; index_type range_; }; private: #if YY_CPLUSPLUS < 201103L /// Non copyable. stack (const stack&); /// Non copyable. stack& operator= (const stack&); #endif /// The wrapped container. S seq_; }; /// Stack type. typedef stack<stack_symbol_type> stack_type; /// The stack. stack_type yystack_; /// Push a new state on the stack. /// \param m a debug message to display /// if null, no trace is output. /// \param sym the symbol /// \warning the contents of \a s.value is stolen. void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym); /// Push a new look ahead token on the state on the stack. /// \param m a debug message to display /// if null, no trace is output. /// \param s the state /// \param sym the symbol (for its value and location). /// \warning the contents of \a sym.value is stolen. void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym); /// Pop \a n symbols from the stack. void yypop_ (int n = 1) YY_NOEXCEPT; /// Constants. enum { yylast_ = 60, ///< Last index in yytable_. yynnts_ = 8, ///< Number of nonterminal symbols. yyfinal_ = 14 ///< Termination state number. }; // User arguments. WasaParserDriver* d; }; } // yy #line 826 "y.tab.h" #endif // !YY_YY_Y_TAB_H_INCLUDED �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/recollq.h�����������������������������������������������������������������������0000644�0001750�0001750�00000002040�14410615043�012754� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _recollq_h_included_ #define _recollq_h_included_ /// Execute query, print results to stdout. This is just an api to the /// recollq command line program. class RclConfig; extern int recollq(RclConfig **cfp, int argc, char **argv); #endif /* _recollq_h_included_ */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/dynconf.h�����������������������������������������������������������������������0000644�0001750�0001750�00000013655�14410615043�012771� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2004-2017 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _DYNCONF_H_INCLUDED_ #define _DYNCONF_H_INCLUDED_ /** * Dynamic configuration storage * * This used to be called "history" because of the initial usage. * Used to store some parameters which would fit neither in recoll.conf, * basically because they change a lot, nor in the QT preferences file, mostly * because they are specific to a configuration directory. * Examples: * - History of documents selected for preview * - Active and inactive external databases (depend on the * configuration directory) * - ... * * The storage is performed in a ConfSimple file, with subkeys and * encodings which depend on the data stored. Under each section, the keys * are sequential numeric, so this basically manages a set of lists. * * The code ensures that a a given value (as defined by the * DynConfEntry::equal() method) is only stored once. If this is * undesirable, equal() should always return false. */ #include <string> #include <list> #include <vector> #include "conftree.h" #include "base64.h" /** Interface for a stored object. */ class DynConfEntry { public: DynConfEntry() {} virtual ~DynConfEntry() {} DynConfEntry(const DynConfEntry&) = default; DynConfEntry& operator=(const DynConfEntry&) = default; /** Decode object-as-string coming out from storage */ virtual bool decode(const std::string &value) = 0; /** Encode object state into state for storing */ virtual bool encode(std::string& value) = 0; /** Compare objects */ virtual bool equal(const DynConfEntry &other) = 0; }; /** Stored object specialization for generic string storage */ class RclSListEntry : public DynConfEntry { public: RclSListEntry() {} virtual ~RclSListEntry() {} RclSListEntry(const RclSListEntry&) = default; RclSListEntry& operator=(const RclSListEntry&) = default; RclSListEntry(const std::string& v) : value(v) { } virtual bool decode(const std::string &enc) override { base64_decode(enc, value); return true; } virtual bool encode(std::string& enc) override { base64_encode(value, enc); return true; } virtual bool equal(const DynConfEntry& other) override { const RclSListEntry& e = dynamic_cast<const RclSListEntry&>(other); return e.value == value; } std::string value; }; /** The dynamic configuration class */ class RclDynConf { public: RclDynConf(const std::string &fn); bool ro() { return m_data.getStatus() == ConfSimple::STATUS_RO; } bool rw() { return m_data.getStatus() == ConfSimple::STATUS_RW; } bool ok() { return m_data.getStatus() != ConfSimple::STATUS_ERROR; } std::string getFilename() { return m_data.getFilename(); } // Generic methods bool eraseAll(const std::string& sk); /** Insert new entry for section sk * @param sk section this is for * @param n new entry * @param s a scratch entry used for decoding and comparisons, * avoiding templating the routine for the actual entry type. */ bool insertNew(const std::string& sk, DynConfEntry &n, DynConfEntry &s, int maxlen = -1); // General method to extract entries. Maybe there would be a way to // express the fact that Type should derive from DynConfEntry, not // too sure how. We are just certain (further down) that it does // have a decode() method. It's up to the user that they call // insertNew() and getEntries() for the same type... template <template <class, class> class Container, class Type> Container<Type, std::allocator<Type>> getEntries(const std::string& sk); // Specialized methods for simple strings bool enterString(const std::string sk, const std::string value, int maxlen = -1); template <template <class, class> class Container> Container<std::string, std::allocator<std::string>> getStringEntries(const std::string& sk); private: ConfSimple m_data; }; template <template <class, class> class Container, class Type> Container<Type, std::allocator<Type>> RclDynConf::getEntries(const std::string& sk) { Container<Type, std::allocator<Type>> out; Type entry; std::vector<std::string> names = m_data.getNames(sk); for (const auto& name : names) { std::string value; if (m_data.get(name, value, sk)) { if (!entry.decode(value)) continue; out.push_back(entry); } } return out; } template <template <class, class> class Container> Container<std::string, std::allocator<std::string>> RclDynConf::getStringEntries(const std::string& sk) { std::vector<RclSListEntry> el = getEntries<std::vector, RclSListEntry>(sk); Container<std::string, std::allocator<std::string>> sl; for (const auto& entry : el) { sl.push_back(entry.value); } return sl; } // Defined subkeys. Values in dynconf.cpp // History extern const std::string docHistSubKey; // All external indexes extern const std::string allEdbsSk; // Active external indexes extern const std::string actEdbsSk; // Advanced search history extern const std::string advSearchHistSk; #endif /* _DYNCONF_H_INCLUDED_ */ �����������������������������������������������������������������������������������recoll-1.36.1/query/docseqdocs.h��������������������������������������������������������������������0000644�0001750�0001750�00000004014�14427373216�013460� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2004-2013 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _DOCSEQDOCS_H_INCLUDED_ #define _DOCSEQDOCS_H_INCLUDED_ #include <memory> #include "docseq.h" #include "rcldoc.h" namespace Rcl { class Db; } /** A DocSequence that's just built from a bunch of docs */ class DocSequenceDocs : public DocSequence { public: DocSequenceDocs(std::shared_ptr<Rcl::Db> d, const std::vector<Rcl::Doc> docs, const std::string &t) : DocSequence(t), m_db(d), m_docs(docs) {} virtual ~DocSequenceDocs() {} DocSequenceDocs(const DocSequenceDocs&) = delete; DocSequenceDocs& operator=(const DocSequenceDocs&) = delete; virtual bool getDoc(int num, Rcl::Doc &doc, std::string *sh = 0) { if (sh) *sh = std::string(); if (num < 0 || num >= int(m_docs.size())) return false; doc = m_docs[num]; return true; } virtual int getResCnt() { return m_docs.size(); } virtual std::string getDescription() { return m_description; } void setDescription(const std::string& desc) { m_description = desc; } protected: virtual std::shared_ptr<Rcl::Db> getDb() { return m_db; } private: std::shared_ptr<Rcl::Db> m_db; std::string m_description; std::vector<Rcl::Doc> m_docs; }; #endif /* _DOCSEQ_H_INCLUDED_ */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/reslistpager.h������������������������������������������������������������������0000644�0001750�0001750�00000012373�14427373216�014044� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _reslistpager_h_included_ #define _reslistpager_h_included_ #include <vector> #include <memory> #include "docseq.h" #include "hldata.h" class RclConfig; class PlainToRich; /** * Manage a paged HTML result list. */ class ResListPager { public: ResListPager(RclConfig *cnf, int pagesize=10, bool alwaysSnippets = false); virtual ~ResListPager() {} ResListPager(const ResListPager&) = delete; ResListPager& operator=(const ResListPager&) = delete; void setHighLighter(PlainToRich *ptr) { m_hiliter = ptr; } void setDocSource(std::shared_ptr<DocSequence> src, int winfirst = -1) { m_pagesize = m_newpagesize; m_winfirst = winfirst; m_hasNext = true; m_docSource = src; m_respage.clear(); } void setPageSize(int ps) { m_newpagesize = ps; } int pageNumber() { if (m_winfirst < 0 || m_pagesize <= 0) return -1; return m_winfirst / m_pagesize; } int pageFirstDocNum() { return m_winfirst; } int pageLastDocNum() { if (m_winfirst < 0 || m_respage.size() == 0) return -1; return m_winfirst + int(m_respage.size()) - 1; } virtual int pageSize() const {return m_pagesize;} void pageNext(); bool hasNext() {return m_hasNext;} bool hasPrev() {return m_winfirst > 0;} bool atBot() {return m_winfirst <= 0;} void resultPageFirst() { m_winfirst = -1; m_pagesize = m_newpagesize; resultPageNext(); } void resultPageBack() { if (m_winfirst <= 0) return; m_winfirst -= m_resultsInCurrentPage + m_pagesize; resultPageNext(); } void resultPageNext(); void resultPageFor(int docnum); /* Display page of results */ void displayPage(RclConfig *); /* Display page with single document */ void displaySingleDoc(RclConfig *config, int idx, Rcl::Doc& doc, const HighlightData& hdata); /* Generate HTML for single document inside page */ void displayDoc(RclConfig *, int idx, Rcl::Doc& doc, const HighlightData& hdata, const std::string& sh = ""); bool pageEmpty() {return m_respage.size() == 0;} std::string queryDescription() { return m_docSource ? m_docSource->getDescription() : ""; } bool getDoc(int num, Rcl::Doc &doc); // Things that need to be reimplemented in the subclass: virtual bool append(const std::string& data); virtual bool append(const std::string& data, int, const Rcl::Doc&) { return append(data); } /* Implementing this allows accumulating the text and setting the HTML at once */ virtual bool flush() {return true;} // Translation function. This is reimplemented in the qt reslist // object For this to work, the strings must be duplicated inside // reslist.cpp (see the QT_TR_NOOP in there). Very very unwieldy. // To repeat: any change to a string used with trans() inside // reslistpager.cpp must be reflected in the string table inside // reslist.cpp for translation to work. virtual std::string trans(const std::string& in); virtual std::string detailsLink(); virtual const std::string &parFormat(); virtual const std::string &dateFormat(); virtual std::string nextUrl(); virtual std::string prevUrl(); virtual std::string pageTop() {return std::string();} virtual std::string headerContent() {return std::string();} virtual std::string iconUrl(RclConfig *, Rcl::Doc& doc); virtual void suggest(const std::vector<std::string>, std::map<std::string, std::vector<std::string> >& sugg){ sugg.clear(); } virtual std::string absSep() {return "…";} virtual std::string linkPrefix() {return "";} virtual std::string bodyAttrs() {return std::string();} // This is used for specifying if we should use the application/x-all entry when looking for a // viewer virtual bool useAll() {return false;} private: int m_pagesize; bool m_alwaysSnippets; int m_newpagesize; int m_resultsInCurrentPage; // First docnum (from docseq) in current page int m_winfirst; bool m_hasNext; PlainToRich *m_hiliter; std::shared_ptr<DocSequence> m_docSource; std::vector<ResListEntry> m_respage; std::vector<std::string> m_thumbnailercmd; }; #endif /* _reslistpager_h_included_ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/stack.hh������������������������������������������������������������������������0000644�0001750�0001750�00000000500�14410615043�012567� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// A Bison parser, made by GNU Bison 3.8.2. // Starting with Bison 3.2, this file is useless: the structure it // used to define is now defined with the parser itself. // // To get rid of this file: // 1. add '%require "3.2"' (or newer) to your grammar file // 2. remove references to this file from your build system. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/sortseq.cpp���������������������������������������������������������������������0000644�0001750�0001750�00000004535�14410615043�013361� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2005-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <algorithm> #include "sortseq.h" #include "log.h" using std::string; class CompareDocs { DocSeqSortSpec ss; public: CompareDocs(const DocSeqSortSpec &sortspec) : ss(sortspec) {} // It's not too clear in the std::sort doc what this should do. This // behaves as operator< int operator()(const Rcl::Doc *x, const Rcl::Doc *y) { LOGDEB1("Comparing .. \n" ); const auto xit = x->meta.find(ss.field); const auto yit = y->meta.find(ss.field); if (xit == x->meta.end() || yit == y->meta.end()) return 0; return ss.desc ? yit->second < xit->second : xit->second < yit->second; } }; bool DocSeqSorted::setSortSpec(const DocSeqSortSpec &sortspec) { LOGDEB("DocSeqSorted::setSortSpec\n" ); m_spec = sortspec; int count = m_seq->getResCnt(); LOGDEB("DocSeqSorted:: count " << (count) << "\n" ); m_docs.resize(count); int i; for (i = 0; i < count; i++) { if (!m_seq->getDoc(i, m_docs[i])) { LOGERR("DocSeqSorted: getDoc failed for doc " << i << "\n"); count = i; break; } } m_docs.resize(count); m_docsp.resize(count); for (i = 0; i < count; i++) m_docsp[i] = &m_docs[i]; CompareDocs cmp(sortspec); sort(m_docsp.begin(), m_docsp.end(), cmp); return true; } bool DocSeqSorted::getDoc(int num, Rcl::Doc &doc, string *) { LOGDEB("DocSeqSorted::getDoc(" << num << ")\n"); if (num < 0 || num >= int(m_docsp.size())) return false; doc = *m_docsp[num]; return true; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/docseq.cpp����������������������������������������������������������������������0000644�0001750�0001750�00000007246�14427373216�013154� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2005-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "docseq.h" #include "filtseq.h" #include "sortseq.h" #include "log.h" #include "internfile.h" #include "rcldb.h" using std::string; using std::vector; std::mutex DocSequence::o_dblock; string DocSequence::o_sort_trans; string DocSequence::o_filt_trans; int DocSequence::getSeqSlice(int offs, int cnt, vector<ResListEntry>& result) { int ret = 0; for (int num = offs; num < offs + cnt; num++, ret++) { result.push_back(ResListEntry()); if (!getDoc(num, result.back().doc, &result.back().subHeader)) { result.pop_back(); return ret; } } return ret; } bool DocSequence::getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc) { std::shared_ptr<Rcl::Db> db = getDb(); if (!db) { LOGERR("DocSequence::getEnclosing: no db\n" ); return false; } std::unique_lock<std::mutex> locker(o_dblock); string udi; if (!FileInterner::getEnclosingUDI(doc, udi)) return false; bool dbret = db->getDoc(udi, doc, pdoc); return dbret && pdoc.pc != -1; } // Remove stacked modifying sources (sort, filter) until we get to a real one void DocSource::stripStack() { if (!m_seq) return; while (m_seq->getSourceSeq()) { m_seq = m_seq->getSourceSeq(); } } bool DocSource::buildStack() { LOGDEB2("DocSource::buildStack()\n" ); stripStack(); if (!m_seq) return false; // Filtering must be done before sorting, (which may // truncates the original list) if (m_seq->canFilter()) { if (!m_seq->setFiltSpec(m_fspec)) { LOGERR("DocSource::buildStack: setfiltspec failed\n" ); } } else { if (m_fspec.isNotNull()) { m_seq = std::shared_ptr<DocSequence>(new DocSeqFiltered(m_config, m_seq, m_fspec)); } } if (m_seq->canSort()) { if (!m_seq->setSortSpec(m_sspec)) { LOGERR("DocSource::buildStack: setsortspec failed\n" ); } } else { if (m_sspec.isNotNull()) { m_seq = std::shared_ptr<DocSequence>(new DocSeqSorted(m_seq, m_sspec)); } } return true; } string DocSource::title() { if (!m_seq) return string(); string qual; if (m_fspec.isNotNull() && !m_sspec.isNotNull()) qual = string(" (") + o_filt_trans + string(")"); else if (!m_fspec.isNotNull() && m_sspec.isNotNull()) qual = string(" (") + o_sort_trans + string(")"); else if (m_fspec.isNotNull() && m_sspec.isNotNull()) qual = string(" (") + o_sort_trans + string(",") + o_filt_trans + string(")"); return m_seq->title() + qual; } bool DocSource::setFiltSpec(const DocSeqFiltSpec &f) { LOGDEB2("DocSource::setFiltSpec\n" ); m_fspec = f; buildStack(); return true; } bool DocSource::setSortSpec(const DocSeqSortSpec &s) { LOGDEB2("DocSource::setSortSpec\n" ); m_sspec = s; buildStack(); return true; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/query/dynconf.cpp���������������������������������������������������������������������0000644�0001750�0001750�00000013651�14410615043�013320� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2005 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TEST_HISTORY #include "safeunistd.h" #include "dynconf.h" #include "base64.h" #include "smallut.h" #include "log.h" using namespace std; // Well known keys for history and external indexes. const string docHistSubKey = "docs"; const string allEdbsSk = "allExtDbs"; const string actEdbsSk = "actExtDbs"; const string advSearchHistSk = "advSearchHist"; RclDynConf::RclDynConf(const std::string &fn) : m_data(fn.c_str()) { if (m_data.getStatus() != ConfSimple::STATUS_RW) { // Maybe the config dir is readonly, in which case we try to // open readonly, but we must also handle the case where the // history file does not exist if (access(fn.c_str(), 0) != 0) { m_data = ConfSimple(string(), 1); } else { m_data = ConfSimple(fn.c_str(), 1); } } } bool RclDynConf::insertNew(const string &sk, DynConfEntry &n, DynConfEntry &s, int maxlen) { if (!rw()) { LOGDEB("RclDynConf::insertNew: not writable\n"); return false; } // Is this doc already in list ? If it is we remove the old entry vector<string> names = m_data.getNames(sk); vector<string>::const_iterator it; bool changed = false; for (it = names.begin(); it != names.end(); it++) { string oval; if (!m_data.get(*it, oval, sk)) { LOGDEB("No data for " << *it << "\n"); continue; } s.decode(oval); if (s.equal(n)) { LOGDEB("Erasing old entry\n"); m_data.erase(*it, sk); changed = true; } } // Maybe reget things if (changed) names = m_data.getNames(sk); // Need to prune ? if (maxlen > 0 && names.size() >= (unsigned int)maxlen) { // Need to erase entries until we're back to size. Note that // we don't ever reset numbers. Problems will arise when // history is 4 billion entries old it = names.begin(); for (unsigned int i = 0; i < names.size() - maxlen + 1; i++, it++) { m_data.erase(*it, sk); } } // Increment highest number unsigned int hi = names.empty() ? 0 : (unsigned int)atoi(names.back().c_str()); hi++; char nname[20]; sprintf(nname, "%010u", hi); string value; n.encode(value); LOGDEB1("Encoded value [" << value << "] (" << value.size() << ")\n"); if (!m_data.set(string(nname), value, sk)) { LOGERR("RclDynConf::insertNew: set failed\n"); return false; } return true; } bool RclDynConf::eraseAll(const string &sk) { if (!rw()) { LOGDEB("RclDynConf::eraseAll: not writable\n"); return false; } for (const auto& nm : m_data.getNames(sk)) { m_data.erase(nm, sk); } return true; } // Specialization for plain strings /////////////////////////////////// bool RclDynConf::enterString(const string sk, const string value, int maxlen) { if (!rw()) { LOGDEB("RclDynConf::enterString: not writable\n"); return false; } RclSListEntry ne(value); RclSListEntry scratch; return insertNew(sk, ne, scratch, maxlen); } #else #include <string> #include <iostream> #include "history.h" #include "log.h" #ifndef NO_NAMESPACES using namespace std; #endif static string thisprog; static string usage = "trhist [opts] <filename>\n" " [-s <subkey>]: specify subkey (default: RclDynConf::docHistSubKey)\n" " [-e] : erase all\n" " [-a <string>] enter string (needs -s, no good for history entries\n" "\n" ; static void Usage(void) { cerr << thisprog << ": usage:\n" << usage; exit(1); } static int op_flags; #define OPT_e 0x2 #define OPT_s 0x4 #define OPT_a 0x8 int main(int argc, char **argv) { string sk = "docs"; string value; thisprog = argv[0]; argc--; argv++; while (argc > 0 && **argv == '-') { (*argv)++; if (!(**argv)) /* Cas du "adb - core" */ Usage(); while (**argv) switch (*(*argv)++) { case 'a': op_flags |= OPT_a; if (argc < 2) Usage(); value = *(++argv); argc--; goto b1; case 's': op_flags |= OPT_s; if (argc < 2) Usage(); sk = *(++argv); argc--; goto b1; case 'e': op_flags |= OPT_e; break; default: Usage(); break; } b1: argc--; argv++; } if (argc != 1) Usage(); string filename = *argv++;argc--; RclDynConf hist(filename, 5); DebugLog::getdbl()->setloglevel(DEBDEB1); DebugLog::setfilename("stderr"); if (op_flags & OPT_e) { hist.eraseAll(sk); } else if (op_flags & OPT_a) { if (!(op_flags & OPT_s)) Usage(); hist.enterString(sk, value); } else { for (int i = 0; i < 10; i++) { char docname[200]; sprintf(docname, "A very long document document name" "is very long indeed and this is the end of " "it here and exactly here:\n%d", i); hist.enterDoc(string(docname), "ipathx"); } list<RclDHistoryEntry> hlist = hist.getDocHistory(); for (list<RclDHistoryEntry>::const_iterator it = hlist.begin(); it != hlist.end(); it++) { printf("[%ld] [%s] [%s]\n", it->unixtime, it->fn.c_str(), it->ipath.c_str()); } } } #endif ���������������������������������������������������������������������������������������recoll-1.36.1/query/position.hh���������������������������������������������������������������������0000644�0001750�0001750�00000000621�14410615043�013332� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// A Bison parser, made by GNU Bison 3.8.2. // Starting with Bison 3.2, this file is useless: the structure it // used to define is now defined in "location.hh". // // To get rid of this file: // 1. add '%require "3.2"' (or newer) to your grammar file // 2. remove references to this file from your build system // 3. if you used to include it, include "location.hh" instead. #include "location.hh" ���������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/��������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�14521161751�011175� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/subtreelist.h�������������������������������������������������������������������0000644�0001750�0001750�00000002442�14427373216�013643� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SUBTREELIST_H_INCLUDED_ #define _SUBTREELIST_H_INCLUDED_ #include <vector> #include <string> class RclConfig; // This queries the database with a pure directory-filter query, to // retrieve all the entries below the specified path. This is used by // the real time indexer to purge entries when a top directory is // renamed. This is really convoluted, I'd like a better way. extern bool subtreelist( RclConfig *config, const std::string& top, std::vector<std::string>& paths); #endif /* _SUBTREELIST_H_INCLUDED_ */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/webqueue.cpp��������������������������������������������������������������������0000644�0001750�0001750�00000042076�14427373216�013462� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2005-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "webqueue.h" #include <string.h> #include <errno.h> #include "safeunistd.h" #include <vector> #include <fstream> #include "cstr.h" #include "pathut.h" #include "rclutil.h" #include "log.h" #include "fstreewalk.h" #include "webstore.h" #include "circache.h" #include "smallut.h" #include "fileudi.h" #include "internfile.h" #include "wipedir.h" #include "indexer.h" #include "readfile.h" #include "conftree.h" #include "transcode.h" #include "cancelcheck.h" using namespace std; #define DOTFILEPREFIX "_" // The browser plugin creates a file named .xxx (where xxx is the name // for the main file in the queue), to hold external metadata (http or // created by the plugin). This class reads the .xxx, dotfile, and turns // it into an Rcl::Doc holder class WebQueueDotFile { public: WebQueueDotFile(RclConfig *conf, const string& fn) : m_conf(conf), m_fn(fn) {} // Read input line, strip it of eol and return as c++ string bool readLine(ifstream& input, string& line) { static const int LL = 2048; char cline[LL]; cline[0] = 0; input.getline(cline, LL-1); if (!input.good()) { if (input.bad()) { LOGERR("WebQueueDotFileRead: input.bad()\n"); } return false; } int ll = strlen(cline); while (ll > 0 && (cline[ll-1] == '\n' || cline[ll-1] == '\r')) { cline[ll-1] = 0; ll--; } line.assign(cline, ll); LOGDEB2("WebQueueDotFile:readLine: [" << line << "]\n"); return true; } // Process a Web queue dot file and set interesting stuff in the doc bool toDoc(Rcl::Doc& doc) { string line; ifstream input; input.open(m_fn.c_str(), ios::in); if (!input.good()) { LOGERR("WebQueueDotFile: open failed for [" << m_fn << "]\n"); return false; } // Read the 3 first lines: // - url // - hit type: we only know about Bookmark and WebHistory for now // - content-type. if (!readLine(input, line)) return false; doc.url = line; if (!readLine(input, line)) return false; doc.meta[Rcl::Doc::keybght] = line; if (!readLine(input, line)) return false; doc.mimetype = line; // We set the bookmarks mtype as html (the text is empty anyway), so that the HTML viewer // will be called on 'Open' bool isbookmark = false; if (!stringlowercmp("bookmark", doc.meta[Rcl::Doc::keybght])) { isbookmark = true; doc.mimetype = "text/html"; } string confstr; string ss(" "); // Read the rest: fields and keywords. We do a little massaging of the input lines, then use // a ConfSimple to parse, and finally insert the key/value pairs into the doc meta[] array for (;;) { if (!readLine(input, line)) { // Eof hopefully break; } if (line.find("t:") != 0) continue; line = line.substr(2); confstr += line + "\n"; } ConfSimple fields(confstr, 1); vector<string> names = fields.getNames(cstr_null); for (const auto& name : names) { string value; fields.get(name, value, cstr_null); if (!value.compare("undefined") || !value.compare("null")) continue; string *valuep = &value; string cvalue; if (isbookmark) { // It appears that bookmarks are stored in the users' // locale charset (not too sure). No idea what to do // for other types, would have to check the plugin. string charset = m_conf->getDefCharset(true); transcode(value, cvalue, charset, "UTF-8"); valuep = &cvalue; } string caname = m_conf->fieldCanon(name); doc.meta[caname].append(ss + *valuep); } // Finally build the confsimple that we will save to the // cache, from the doc fields. This could also be done in // parallel with the doc.meta build above, but simpler this // way. We need it because not all interesting doc fields are // in the meta array (ie: mimetype, url), and we want // something homogenous and easy to save. for (const auto& entry : doc.meta) { m_fields.set(entry.first, entry.second, cstr_null); } m_fields.set(cstr_url, doc.url, cstr_null); m_fields.set(cstr_bgc_mimetype, doc.mimetype, cstr_null); return true; } RclConfig *m_conf; ConfSimple m_fields; string m_fn; }; // Initialize. Compute paths and create a temporary directory that will be // used by internfile() WebQueueIndexer::WebQueueIndexer(RclConfig *cnf, Rcl::Db *db) : m_config(cnf), m_db(db) { m_queuedir = m_config->getWebQueueDir(); path_catslash(m_queuedir); m_cache = new WebStore(cnf); string keepinterval; m_config->getConfParam("webcachekeepinterval", keepinterval); if (keepinterval == "day") { m_keepinterval = WQKI_DAY; } else if (keepinterval == "week") { m_keepinterval = WQKI_WEEK; } else if (keepinterval == "month") { m_keepinterval = WQKI_MONTH; } else if (keepinterval == "year") { m_keepinterval = WQKI_YEAR; } else if (!keepinterval.empty()) { LOGERR("WebQueueIndexer: bad value for keepinterval: " << keepinterval << "\n"); } } WebQueueIndexer::~WebQueueIndexer() { LOGDEB("WebQueueIndexer::~\n"); deleteZ(m_cache); } // Index document stored in the cache. bool WebQueueIndexer::indexFromCache(const string& udi) { if (!m_db) return false; CancelCheck::instance().checkCancel(); Rcl::Doc dotdoc; string data; string hittype; if (!m_cache || !m_cache->getFromCache(udi, dotdoc, data, &hittype)) { LOGERR("WebQueueIndexer::indexFromCache: cache failed\n"); return false; } if (hittype.empty()) { LOGERR("WebQueueIndexer::index: cc entry has no hit type\n"); return false; } if (!stringlowercmp("bookmark", hittype)) { // Just index the dotdoc dotdoc.meta[Rcl::Doc::keybcknd] = "BGL"; return m_db->addOrUpdate(udi, cstr_null, dotdoc); } else { Rcl::Doc doc; FileInterner interner(data, m_config, FileInterner::FIF_doUseInputMimetype, dotdoc.mimetype); FileInterner::Status fis; try { fis = interner.internfile(doc); } catch (CancelExcept) { LOGERR("WebQueueIndexer: interrupted\n"); return false; } if (fis != FileInterner::FIDone) { LOGERR("WebQueueIndexer: bad status from internfile\n"); return false; } doc.mimetype = dotdoc.mimetype; doc.fmtime = dotdoc.fmtime; doc.url = dotdoc.url; doc.pcbytes = dotdoc.pcbytes; doc.sig.clear(); doc.meta[Rcl::Doc::keybcknd] = "BGL"; return m_db->addOrUpdate(udi, cstr_null, doc); } } void WebQueueIndexer::updstatus(const string& udi) { statusUpdater()->update(DbIxStatus::DBIXS_FILES, udi, DbIxStatusUpdater::IncrDocsDone); } bool WebQueueIndexer::index() { if (!m_db) return false; LOGDEB("WebQueueIndexer::index: [" << m_queuedir << "]\n"); m_config->setKeyDir(m_queuedir); if (!path_makepath(m_queuedir, 0700)) { LOGSYSERR("WebQueueIndexer", "create queuedir", m_queuedir); return false; } if (!m_cache || !m_cache->cc()) { LOGERR("WebQueueIndexer: cache initialization failed\n"); return false; } CirCache *cc = m_cache->cc(); // First check/index files found in the cache. If the index was reset, // this actually does work, else it sets the existence flags (avoid // purging). We don't do this when called from indexFiles if (!m_nocacheindex) { bool eof{false}; if (!cc->rewind(eof)) { // rewind can return false/eof if the cache is empty, normal case. if (!eof) return false; } int nentries = 0; if (!eof) do { string udi; if (!cc->getCurrentUdi(udi)) { if (!eof) { LOGERR("WebQueueIndexer:: cache file damaged\n"); } break; } if (udi.empty()) continue; if (m_db->needUpdate(udi, cstr_null)) { try { // indexFromCache does a CirCache::get(). We could // arrange to use a getCurrent() instead, would be more // efficient indexFromCache(udi); updstatus(udi); } catch (CancelExcept) { LOGERR("WebQueueIndexer: interrupted\n"); return false; } } nentries++; } while (cc->next(eof)); } // Finally index the queue FsTreeWalker walker(FsTreeWalker::FtwNoRecurse); walker.addSkippedName(DOTFILEPREFIX "*"); FsTreeWalker::Status status = walker.walk(m_queuedir, *this); LOGDEB("WebQueueIndexer::processqueue: done: status " << status << "\n"); return true; } // Index a list of files (sent by the real time monitor) bool WebQueueIndexer::indexFiles(list<string>& files) { LOGDEB("WebQueueIndexer::indexFiles\n"); if (!m_db) { LOGERR("WebQueueIndexer::indexfiles no db??\n"); return false; } for (auto it = files.begin(); it != files.end();) { if (it->empty()) {//?? it++; continue; } string father = path_getfather(*it); if (father.compare(m_queuedir)) { LOGDEB("WebQueueIndexer::indexfiles: skipping ["<<*it << "] (nq)\n"); it++; continue; } // Pb: we are often called with the dot file, before the // normal file exists, and sometimes never called for the // normal file afterwards (ie for bookmarks where the normal // file is empty). So we perform a normal queue run at the end // of the function to catch older stuff. Still this is not // perfect, sometimes some files will not be indexed before // the next run. string fn = path_getsimple(*it); if (fn.empty() || fn.at(0) == '.') { it++; continue; } struct PathStat st; if (path_fileprops(*it, &st) != 0) { LOGERR("WebQueueIndexer::indexfiles: cant stat [" << *it << "]\n"); it++; continue; } if (st.pst_type != PathStat::PST_REGULAR) { LOGDEB("WebQueueIndexer::indexfiles: skipping [" << *it << "] (nr)\n"); it++; continue; } processone(*it, FsTreeWalker::FtwRegular, st); it = files.erase(it); } m_nocacheindex = true; index(); // Note: no need to reset nocacheindex, we're in the monitor now return true; } static std::string date_string(const char *fmt) { time_t now = time(nullptr); struct tm tmb; localtime_r(&now, &tmb); char buf[200]; strftime(buf, sizeof(buf)-1, fmt, &tmb); return buf; } static std::string yearday() { return date_string("%Y%j"); } static std::string yearweek() { return date_string("%Y%V"); } static std::string yearmonth() { return date_string("%Y%m"); } static std::string yearyear() { return date_string("%Y"); } FsTreeWalker::Status WebQueueIndexer::processone( const string &path, FsTreeWalker::CbFlag flg, const struct PathStat& stp) { if (!m_db) //?? return FsTreeWalker::FtwError; bool dounlink = false; string ascdate; if (flg != FsTreeWalker::FtwRegular) return FsTreeWalker::FtwOk; string dotpath = path_cat(path_getfather(path), string(DOTFILEPREFIX) + path_getsimple(path)); LOGDEB("WebQueueIndexer::processone: [" << path << "]\n"); WebQueueDotFile dotfile(m_config, dotpath); Rcl::Doc dotdoc; string udi, udipath; if (!dotfile.toDoc(dotdoc)) { LOGERR("WebQueueIndexer::processone: could not convert dotfile " << dotpath << "\n"); goto out; } //dotdoc.dump(1); // Have to use the hit type for the udi, because the same url can // exist as a bookmark or a page. Also add a date with the // specified granularity so that multiple versions can be in the // index. udipath = path_cat(dotdoc.meta[Rcl::Doc::keybght], url_gpath(dotdoc.url)); // !! is an arbitrary separator rather unlikely to be found in urls. switch (m_keepinterval) { case WQKI_DAY: udipath = udipath + "!!" + yearday(); break; case WQKI_WEEK: udipath = udipath + "!!" + yearweek(); break; case WQKI_MONTH: udipath= udipath + "!!" + yearmonth(); break; case WQKI_YEAR: udipath = udipath + "!!" + yearyear(); break; default: break; } LOGDEB("WebQueueIndexer::processone: UDI: " << udipath << "\n"); make_udi(udipath, cstr_null, udi); LOGDEB("WebQueueIndexer::processone: udi [" << udi << "]\n"); ascdate = lltodecstr(stp.pst_mtime); if (!stringlowercmp("bookmark", dotdoc.meta[Rcl::Doc::keybght])) { // For bookmarks, we just index the doc that was built from the metadata. if (dotdoc.fmtime.empty()) dotdoc.fmtime = ascdate; dotdoc.pcbytes = lltodecstr(stp.pst_size); // Document signature for up to date checks: none. dotdoc.sig.clear(); dotdoc.meta[Rcl::Doc::keybcknd] = "BGL"; if (!m_db->addOrUpdate(udi, cstr_null, dotdoc)) return FsTreeWalker::FtwError; } else { Rcl::Doc doc; // Store the dotdoc fields in the future doc. In case someone wants // to use fields generated by the browser plugin like inurl doc.meta = dotdoc.meta; FileInterner interner(path, stp, m_config, FileInterner::FIF_doUseInputMimetype, &dotdoc.mimetype); FileInterner::Status fis; try { fis = interner.internfile(doc); } catch (CancelExcept) { LOGERR("WebQueueIndexer: interrupted\n"); goto out; } if (fis != FileInterner::FIDone && fis != FileInterner::FIAgain) { LOGERR("WebQueueIndexer: bad status from internfile\n"); // TOBEDONE: internfile can return FIAgain here if it is // paging a big text file, we should loop. Means we're // only indexing the first page for text/plain files // bigger than the page size (dlft: 1MB) for now. goto out; } if (doc.fmtime.empty()) doc.fmtime = ascdate; dotdoc.fmtime = doc.fmtime; dotdoc.pcbytes = doc.pcbytes = lltodecstr(stp.pst_size); // Document signature for up to date checks: none. doc.sig.clear(); doc.url = dotdoc.url; doc.meta[Rcl::Doc::keybcknd] = "BGL"; if (!m_db->addOrUpdate(udi, cstr_null, doc)) return FsTreeWalker::FtwError; } // Copy to cache { // doc fields not in meta, needing saving to the cache dotfile.m_fields.set("fmtime", dotdoc.fmtime, cstr_null); // fbytes is used for historical reasons, should be pcbytes, but makes // no sense to change. dotfile.m_fields.set(cstr_fbytes, dotdoc.pcbytes, cstr_null); dotfile.m_fields.set("udi", udi, cstr_null); string fdata; file_to_string(path, fdata); if (!m_cache || !m_cache->cc()) { LOGERR("WebQueueIndexer: cache initialization failed\n"); goto out; } if (!m_cache->cc()->put(udi, &dotfile.m_fields, fdata, 0)) { LOGERR("WebQueueIndexer::prc1: cache_put failed; "<<m_cache->cc()->getReason() << "\n"); goto out; } } updstatus(udi); dounlink = true; out: if (dounlink) { if (!path_unlink(path)) { LOGSYSERR("WebQueueIndexer::processone", "unlink", path); } if (!path_unlink(dotpath)) { LOGSYSERR("WebQueueIndexer::processone", "unlink", dotpath); } } return FsTreeWalker::FtwOk; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/rclmon.h������������������������������������������������������������������������0000644�0001750�0001750�00000006736�14427373216�012602� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2006 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _RCLMON_H_INCLUDED_ #define _RCLMON_H_INCLUDED_ #include "autoconfig.h" #ifdef RCL_MONITOR /** * Definitions for the real-time monitoring recoll. * We're interested in file modifications, deletions and renaming. * We use two threads, one to receive events from the source, the * other to perform adequate processing. * * The two threads communicate through an event buffer which is * actually a hash map indexed by file path for easy coalescing of * multiple events to the same file. */ #include <time.h> #include <string> #include <mutex> #include "rclconfig.h" /** * Monitoring event: something changed in the filesystem */ class RclMonEvent { public: enum EvType {RCLEVT_NONE= 0, RCLEVT_MODIFY=1, RCLEVT_DELETE=2, RCLEVT_DIRCREATE=3, RCLEVT_ISDIR=0x10}; std::string m_path; // Type and flags int m_etyp; ///// For fast changing files: minimum time interval before reindex // Minimum interval (from config) int m_itvsecs; // Don't process this entry before: time_t m_minclock; // Changed since put in purgatory after reindex bool m_needidx; RclMonEvent() : m_etyp(RCLEVT_NONE), m_itvsecs(0), m_minclock(0), m_needidx(false) {} EvType evtype() {return EvType(m_etyp & 0xf);} int evflags() {return m_etyp & 0xf0;} }; enum RclMonitorOption {RCLMON_NONE=0, RCLMON_NOFORK=1, RCLMON_NOX11=2, RCLMON_NOCONFCHECK=4}; /** * Monitoring event queue. This is the shared object between the main thread * (which does the actual indexing work), and the monitoring thread which * receives events from FAM / inotify / etc. */ class RclEQData; class RclMonEventQueue { public: RclMonEventQueue(); ~RclMonEventQueue(); RclMonEventQueue(const RclMonEventQueue&) = delete; RclMonEventQueue& operator=(const RclMonEventQueue&) = delete; /** Wait for event or timeout. Returns with the queue locked */ std::unique_lock<std::mutex> wait(int secs = -1, bool *timedout = nullptr); /** Add event. */ bool pushEvent(const RclMonEvent &ev); /** To all threads: end processing */ void setTerminate(); bool ok(); bool empty(); RclMonEvent pop(); void setopts(int opts); // Convenience function for initially communicating config to mon thr void setConfig(RclConfig *conf); RclConfig *getConfig(); private: RclEQData *m_data; }; /** Start monitoring on the topdirs described in conf */ extern bool startMonitor(RclConfig *conf, int flags); /** Main routine for the event receiving thread */ extern void *rclMonRcvRun(void *); // Specific debug macro for monitor synchronization events #define MONDEB LOGDEB2 #endif // RCL_MONITOR #endif /* _RCLMON_H_INCLUDED_ */ ����������������������������������recoll-1.36.1/index/fetcher.cpp���������������������������������������������������������������������0000644�0001750�0001750�00000003323�14427373216�013250� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <memory> #include "log.h" #include "rclconfig.h" #include "fetcher.h" #include "fsfetcher.h" #include "webqueuefetcher.h" #include "exefetcher.h" using std::string; std::unique_ptr<DocFetcher> docFetcherMake(RclConfig *config, const Rcl::Doc& idoc) { if (idoc.url.empty()) { LOGERR("docFetcherMakeg:: no url in doc!\n" ); return std::unique_ptr<DocFetcher>(); } string backend; idoc.getmeta(Rcl::Doc::keybcknd, &backend); if (backend.empty() || !backend.compare("FS")) { return std::unique_ptr<DocFetcher>(new FSDocFetcher); #ifndef DISABLE_WEB_INDEXER } else if (!backend.compare("BGL")) { return std::unique_ptr<DocFetcher>(new WQDocFetcher); #endif } else { std::unique_ptr<DocFetcher> f(exeDocFetcherMake(config, backend)); if (!f) { LOGERR("DocFetcherFactory: unknown backend [" << backend << "]\n"); } return f; } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/indexer.cpp���������������������������������������������������������������������0000644�0001750�0001750�00000036005�14477552737�013305� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <stdio.h> #include <errno.h> #include <algorithm> #include "cstr.h" #include "log.h" #include "recollindex.h" #include "indexer.h" #include "fsindexer.h" #ifndef DISABLE_WEB_INDEXER #include "webqueue.h" #endif #include "mimehandler.h" #include "pathut.h" #include "idxstatus.h" #include "execmd.h" #include "conftree.h" #ifdef RCL_USE_ASPELL #include "rclaspell.h" #endif using std::list; using std::string; using std::vector; // This would more logically live in recollindex.cpp, but then librecoll would // have an undefined symbol ConfSimple idxreasons; void addIdxReason(string who, string reason) { reason = neutchars(reason, "\r\n"); if (!idxreasons.set(who, reason)) { std::cerr << "addIdxReason: confsimple set failed\n"; } } #ifndef DISABLE_WEB_INDEXER bool runWebFilesMoverScript(RclConfig *config) { static string downloadsdir; if (downloadsdir.empty()) { if (!config->getConfParam("webdownloadsdir", downloadsdir)) { downloadsdir = "~/Downloads"; } downloadsdir = path_tildexpand(downloadsdir); } /* Arrange to not actually run the script if the directory did not change */ static time_t dirmtime; time_t ndirmtime = 0; struct PathStat st; if (path_fileprops(downloadsdir.c_str(), &st) == 0) { ndirmtime = st.pst_mtime; } // If stat fails, presumably Downloads does not exist or is not accessible, dirmtime and // mdirmtime stay at 0, and we never execute the script, which is the right thing. if (dirmtime != ndirmtime) { // The script is going to change the directory, so updating dirmtime before it runs means // that we are going to execute it one time too many (it will run without doing anything), // but we can't set the mtime to after the run in case files are created during the run. dirmtime = ndirmtime; vector<string> cmdvec; config->pythonCmd("recoll-we-move-files.py", cmdvec); ExecCmd cmd; cmd.putenv("RECOLL_CONFDIR", config->getConfDir()); int status = cmd.doexec(cmdvec); return status == 0; } return true; } #endif ConfIndexer::ConfIndexer(RclConfig *cnf) : m_config(cnf), m_db(cnf) { m_config->getConfParam("processwebqueue", &m_doweb); } ConfIndexer::~ConfIndexer() { deleteZ(m_fsindexer); #ifndef DISABLE_WEB_INDEXER deleteZ(m_webindexer); #endif } // Determine if this is likely the first time that the user runs // indexing. We don't look at the xapiandb as this may have been // explicitly removed for valid reasons, but at the indexing status // file, which should be unexistant-or-empty only before any indexing // has ever run bool ConfIndexer::runFirstIndexing() { // Indexing status file existing and not empty ? if (path_filesize(m_config->getIdxStatusFile()) > 0) { LOGDEB0("ConfIndexer::runFirstIndexing: no: status file not empty\n"); return false; } // And only do this if the user has kept the default topdirs (~). vector<string> tdl = m_config->getTopdirs(); if (tdl.size() != 1 || tdl[0].compare(path_canon(path_tildexpand("~")))) { LOGDEB0("ConfIndexer::runFirstIndexing: no: not home only\n"); return false; } return true; } bool ConfIndexer::firstFsIndexingSequence() { LOGDEB("ConfIndexer::firstFsIndexingSequence\n"); deleteZ(m_fsindexer); m_fsindexer = new FsIndexer(m_config, &m_db); if (!m_fsindexer) { return false; } int flushmb = m_db.getFlushMb(); m_db.setFlushMb(2); m_fsindexer->index(IxFQuickShallow); m_db.doFlush(); m_db.setFlushMb(flushmb); return true; } bool ConfIndexer::index(bool resetbefore, ixType typestorun, int flags) { Rcl::Db::OpenMode mode = resetbefore ? Rcl::Db::DbTrunc : Rcl::Db::DbUpd; if (!m_db.open(mode)) { LOGERR("ConfIndexer: error opening database " << m_config->getDbDir() << " : " << m_db.getReason() << "\n"); addIdxReason("indexer", m_db.getReason()); return false; } if (flags & IxFInPlaceReset) { m_db.setInPlaceReset(); } std::string logloc; if (Logger::getTheLog()->logisstderr()) { logloc = "program error output."; } else { logloc = std::string(" log in ") + Logger::getTheLog()->getlogfilename() + "."; } m_config->setKeyDir(cstr_null); if (typestorun & IxTFs) { m_db.preparePurge("FS"); if (runFirstIndexing()) { firstFsIndexingSequence(); } deleteZ(m_fsindexer); m_fsindexer = new FsIndexer(m_config, &m_db); if (!m_fsindexer || !m_fsindexer->index(flags)) { if (stopindexing) { addIdxReason("indexer", "Indexing was interrupted."); } else { addIdxReason("indexer", "Index creation failed. See" + logloc); } m_db.close(); return false; } if (flags & IxFDoPurge) { // Status test absolutely necessary: don't want to run purge after an // incomplete/interrupted indexing pass ! if (!statusUpdater()->update(DbIxStatus::DBIXS_PURGE, string())) { m_db.close(); addIdxReason("indexer", "Index purge failed. See" + logloc); return false; } m_db.purge(); } } #ifndef DISABLE_WEB_INDEXER if (m_doweb && (typestorun & IxTWebQueue)) { runWebFilesMoverScript(m_config); deleteZ(m_webindexer); m_db.preparePurge("BGL"); m_webindexer = new WebQueueIndexer(m_config, &m_db); if (!m_webindexer || !m_webindexer->index()) { m_db.close(); addIdxReason("indexer", "Web index creation failed. See" + logloc); return false; } if (flags & IxFDoPurge) { // Status test absolutely necessary: don't want to run purge after an // incomplete/interrupted indexing pass ! if (!statusUpdater()->update(DbIxStatus::DBIXS_PURGE, string())) { m_db.close(); addIdxReason("indexer", "Index purge failed. See" + logloc); return false; } m_db.purge(); } } #endif // It makes no sense to check for cancel, we'll have to close anyway, do it to show status statusUpdater()->update(DbIxStatus::DBIXS_CLOSING, string()); if (!m_db.close()) { LOGERR("ConfIndexer::index: error closing database in " << m_config->getDbDir() << "\n"); addIdxReason("indexer", "Index close/flush failed. See" +logloc); return false; } // Check for external indexers and run them if needed. string bconfname = path_cat(m_config->getConfDir(), "backends"); LOGDEB("ConfIndexer: using config in " << bconfname << "\n"); ConfSimple bconf(bconfname.c_str(), true); if (bconf.ok()) { auto bckids = bconf.getSubKeys(); for (const auto& bckid : bckids) { string sindex; if (!bconf.get("index", sindex, bckid) || sindex.empty()) { LOGDEB0("ConfIndexer: no 'index' entry for [" << bckid << "]\n"); continue; } sindex = path_tildexpand(sindex); vector<string> vindex; stringToStrings(sindex, vindex); // We look up the command as we do for filters vindex[0] = m_config->findFilter(vindex[0]); if (!path_isabsolute(vindex[0])) { LOGERR("ConfIndexer: "<<vindex[0] <<" not found in exec path or filters folder.\n"); continue; } if (!statusUpdater()->update(DbIxStatus::DBIXS_FILES, bckid)) { return false; } LOGINF("ConfIndexer: starting indexing for " << bckid << "\n"); ExecCmd ecmd(ExecCmd::EXF_NOSETPG); ecmd.putenv(std::string("RECOLL_CONFDIR=") + m_config->getConfDir()); auto status = ecmd.doexec(vindex); if (status) { LOGERR("ConfIndexer: " << bckid << ": " << stringsToString(vindex) << " failed with status " << std::hex << status << std::dec << "\n"); } else { LOGINF("ConfIndexer: indexing for " << bckid << " done.\n"); } } } if (!statusUpdater()->update(DbIxStatus::DBIXS_STEMDB, string())) return false; bool ret = true; if (!createStemmingDatabases()) { ret = false; } if (!statusUpdater()->update(DbIxStatus::DBIXS_CLOSING, string())) return false; // Don't fail indexing because of an aspell issue: we ignore the status. // Messages were written to the reasons output (void)createAspellDict(); clearMimeHandlerCache(); statusUpdater()->update(DbIxStatus::DBIXS_DONE, string()); return ret; } bool ConfIndexer::indexFiles(list<string>& ifiles, int flags) { list<string> myfiles; string origcwd = m_config->getOrigCwd(); for (const auto& entry : ifiles) { myfiles.push_back(path_canon(entry, &origcwd)); } myfiles.sort(); if (!m_db.open(Rcl::Db::DbUpd)) { LOGERR("ConfIndexer: indexFiles error opening database " << m_config->getDbDir() << "\n"); return false; } if (flags & IxFInPlaceReset) { m_db.setInPlaceReset(); } m_config->setKeyDir(cstr_null); bool ret = false; if (!m_fsindexer) m_fsindexer = new FsIndexer(m_config, &m_db); if (m_fsindexer) ret = m_fsindexer->indexFiles(myfiles, flags); LOGDEB2("ConfIndexer::indexFiles: fsindexer returned " << ret << ", " << myfiles.size() << " files remainining\n"); #ifndef DISABLE_WEB_INDEXER if (m_doweb && !myfiles.empty() && !(flags & IxFNoWeb)) { if (!m_webindexer) m_webindexer = new WebQueueIndexer(m_config, &m_db); if (m_webindexer) { ret = ret && m_webindexer->indexFiles(myfiles); } else { ret = false; } } #endif if (flags & IxFDoPurge) { m_db.purge(); } // The close would be done in our destructor, but we want status here if (!m_db.close()) { LOGERR("ConfIndexer::index: error closing database in " << m_config->getDbDir() << "\n"); return false; } ifiles = myfiles; clearMimeHandlerCache(); return ret; } bool ConfIndexer::purgeFiles(list<string> &files, int flags) { list<string> myfiles; string origcwd = m_config->getOrigCwd(); for (const auto& entry : files) { myfiles.push_back(path_canon(entry, &origcwd)); } myfiles.sort(); if (!m_db.open(Rcl::Db::DbUpd)) { LOGERR("ConfIndexer: purgeFiles error opening database " << m_config->getDbDir() << "\n"); return false; } bool ret = false; m_config->setKeyDir(cstr_null); if (!m_fsindexer) m_fsindexer = new FsIndexer(m_config, &m_db); if (m_fsindexer) ret = m_fsindexer->purgeFiles(myfiles); #ifndef DISABLE_WEB_INDEXER if (m_doweb && !myfiles.empty() && !(flags & IxFNoWeb)) { if (!m_webindexer) m_webindexer = new WebQueueIndexer(m_config, &m_db); if (m_webindexer) { ret = ret && m_webindexer->purgeFiles(myfiles); } else { ret = false; } } #endif // The close would be done in our destructor, but we want status here if (!m_db.close()) { LOGERR("ConfIndexer::purgefiles: error closing database in " << m_config->getDbDir() << "\n"); return false; } return ret; } // Create stemming databases. We also remove those which are not // configured. bool ConfIndexer::createStemmingDatabases() { string slangs; bool ret = true; if (m_config->getConfParam("indexstemminglanguages", slangs)) { if (!m_db.open(Rcl::Db::DbUpd)) { LOGERR("ConfIndexer::createStemmingDb: could not open db\n"); addIdxReason("stemming", "could not open db"); return false; } vector<string> langs; stringToStrings(slangs, langs); // Get the list of existing stem dbs from the database (some may have // been manually created, we just keep those from the config vector<string> dblangs = m_db.getStemLangs(); vector<string>::const_iterator it; for (it = dblangs.begin(); it != dblangs.end(); it++) { if (find(langs.begin(), langs.end(), *it) == langs.end()) m_db.deleteStemDb(*it); } ret = ret && m_db.createStemDbs(langs); if (!ret) { addIdxReason("stemming", "stem db creation failed"); } } m_db.close(); return ret; } bool ConfIndexer::createStemDb(const string &lang) { if (!m_db.open(Rcl::Db::DbUpd)) return false; vector<string> langs; stringToStrings(lang, langs); return m_db.createStemDbs(langs); } // The language for the aspell dictionary is handled internally by the aspell // module, either from a configuration variable or the NLS environment. bool ConfIndexer::createAspellDict() { LOGDEB2("ConfIndexer::createAspellDict()\n"); #ifdef RCL_USE_ASPELL // For the benefit of the real-time indexer, we only initialize // noaspell from the configuration once. It can then be set to // true if dictionary generation fails, which avoids retrying // it forever. static int noaspell = -12345; if (noaspell == -12345) { noaspell = false; m_config->getConfParam("noaspell", &noaspell); } if (noaspell) return true; if (!m_db.open(Rcl::Db::DbRO)) { LOGERR("ConfIndexer::createAspellDict: could not open db\n"); return false; } Aspell aspell(m_config); string reason; if (!aspell.init(reason)) { LOGERR("ConfIndexer::createAspellDict: aspell init failed: " << reason << "\n"); noaspell = true; return false; } LOGDEB("ConfIndexer::createAspellDict: creating dictionary\n"); if (!aspell.buildDict(m_db, reason)) { LOGERR("ConfIndexer::createAspellDict: aspell buildDict failed: " << reason << "\n"); addIdxReason("aspell", reason); noaspell = true; return false; } #endif return true; } vector<string> ConfIndexer::getStemmerNames() { return Rcl::Db::getStemmerNames(); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/recollindex.h�������������������������������������������������������������������0000644�0001750�0001750�00000003621�14410615043�013573� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2009 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _recollindex_h_included_ #define _recollindex_h_included_ #include <list> #include <string> /** Helper methods in recollindex.cpp for initial checks/setup to index * a list of files (either from the monitor or the command line) */ class RclConfig; extern bool indexfiles(RclConfig *config, std::list<std::string> &filenames); extern bool purgefiles(RclConfig *config, std::list<std::string> &filenames); extern bool createAuxDbs(RclConfig *config); /** * Helper method for executing the recoll-we (new WebExtensions plugin) helper * script. This moves files from the browser download directory (only * place where the browser accepts to create them), to the web queue * dir). This keeps the c++ code compatible with old and new addon. * The script is executed before a batch pass, or from time to time in * the monitor, if web processing is enabled. */ extern bool runWebFilesMoverScript(RclConfig *); extern int stopindexing; // Try to explain what went wrong... extern void addIdxReason(std::string who, std::string reason); class ReExec; extern ReExec *o_reexec; extern std::string thisprog; #endif /* _recollindex_h_included_ */ ���������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/checkretryfailed.h��������������������������������������������������������������0000644�0001750�0001750�00000002376�14410615043�014601� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef _CHECKRETRYFAILED_H_INCLUDED_ #define _CHECKRETRYFAILED_H_INCLUDED_ /* Copyright (C) 2015 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** Check if retrying failed files may be needed. We execute a shell-script for this. The default one checks if any of the common bin directories changed. @param conf the config @param record if true, record the state instead of testing @return true if retrying should be performed */ class RclConfig; bool checkRetryFailed(RclConfig *conf, bool record); #endif /* _CHECKRETRYFAILED_H_INCLUDED_ */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/idxdiags.cpp��������������������������������������������������������������������0000644�0001750�0001750�00000004603�14410615043�013413� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2021 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <stdio.h> #include <mutex> #include "idxdiags.h" static std::mutex diagmutex; class IdxDiags::Internal { public: ~Internal() { if (fp) { fclose(fp); } } FILE *fp{nullptr}; }; IdxDiags::IdxDiags() { m = new Internal; } IdxDiags::~IdxDiags() { delete m; } bool IdxDiags::flush() { std::unique_lock<std::mutex> lock(diagmutex); if (m && m->fp) { return fflush(m->fp) ? false : true; } return true; } static IdxDiags *theInstance; IdxDiags& IdxDiags::theDiags() { if (nullptr == theInstance) { theInstance = new IdxDiags; } return *theInstance; } bool IdxDiags::init(const std::string& outpath) { m->fp = fopen(outpath.c_str(), "w"); if (nullptr == m->fp) { return false; } return true; } bool IdxDiags::record(DiagKind diag, const std::string& path, const std::string& detail) { if (nullptr == m || nullptr == m->fp || (path.empty() && detail.empty())) { return true; } const char *skind = "Unknown"; switch (diag) { case Ok: skind = "Ok";break; case Skipped: skind = "Skipped";break; case NoContentSuffix: skind = "NoContentSuffix";break; case MissingHelper: skind = "MissingHelper";break; case Error: skind = "Error";break; case NoHandler: skind = "NoHandler";break; case ExcludedMime: skind = "ExcludedMime";break; case NotIncludedMime: skind = "NotIncludedMime";break; } std::unique_lock<std::mutex> lock(diagmutex); fprintf(m->fp, "%s %s | %s\n", skind, path.c_str(), detail.c_str()); return true; } �����������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/checkindexed.h������������������������������������������������������������������0000644�0001750�0001750�00000002342�14410615043�013700� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2021 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _CHECKINDEXED_H_INCLUDED_ #define _CHECKINDEXED_H_INCLUDED_ #include <vector> #include <string> class RclConfig; // Diagnostic routine. Reads paths from stdin (one per line) if filepaths is empty. // For each path, check that the file is indexed, print back its path // with an ERROR or ABSENT prefix if it's not extern bool checkindexed(RclConfig *conf, const std::vector<std::string>& filepaths); #endif /* _CHECKINDEXED_H_INCLUDED_ */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/fetcher.h�����������������������������������������������������������������������0000644�0001750�0001750�00000007001�14410615043�012677� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2012-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _FETCHER_H_INCLUDED_ #define _FETCHER_H_INCLUDED_ #include <string> #include <memory> #include "rcldoc.h" #include "pathut.h" class RclConfig; /** * Generic interface to retrieve the data for a document designated by * its index data (udi/ipath/url). This is used to retrieve the data * for previewing. The actual implementation is specific to the kind * of backend (file system, web cache, others?...), and the * implementation may of course share code with the indexing-time * functions from the specific backend. * * This normally gives access the raw document container (either as a * file or as a memory block). The Internfile code will then further * process it to get to the actual document, especially if * de-embedding is involved. * * The DATADIRECT document kind, which holds final extracted data, is only * returned when using an external indexer (only the python demo sample at * this point), in which case the whole extraction is performed by the * external code. */ class DocFetcher { public: /** A RawDoc is the data for a document-holding entity either as a memory block, or pointed to by a file name */ struct RawDoc { enum RawDocKind {RDK_FILENAME, RDK_DATA, RDK_DATADIRECT}; RawDocKind kind; std::string data; // Doc data or file name struct PathStat st; // Only used if RDK_FILENAME }; /** * Return the data for the requested document, either as a * file-system file or as a memory object (maybe stream too in the * future?) * @param cnf the global config * @param idoc the data gathered from the index for this doc (udi/ipath) * @param out we may return either a file name or the document data. */ virtual bool fetch(RclConfig* cnf, const Rcl::Doc& idoc, RawDoc& out) = 0; /** * Return the signature for the requested document. This is used for * up-to-date tests performed when not indexing (e.g.: verifying that a * document is not stale before previewing it). * @param cnf the global config * @param idoc the data gathered from the index for this doc (udi/ipath) * @param sig output. */ virtual bool makesig(RclConfig* cnf, const Rcl::Doc& idoc, std::string& sig) = 0; enum Reason{FetchOk, FetchNotExist, FetchNoPerm, FetchOther}; virtual Reason testAccess(RclConfig*, const Rcl::Doc&) { return FetchOther; } DocFetcher() {} virtual ~DocFetcher() {} DocFetcher(const DocFetcher&) = delete; DocFetcher& operator=(const DocFetcher&) = delete; }; /** Return an appropriate fetcher object given the backend string identifier inside idoc*/ std::unique_ptr<DocFetcher> docFetcherMake(RclConfig *config, const Rcl::Doc& idoc); #endif /* _FETCHER_H_INCLUDED_ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/webqueue.h����������������������������������������������������������������������0000644�0001750�0001750�00000005744�14427373216�013130� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2009 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _webqueue_h_included_ #define _webqueue_h_included_ #include <list> /** * Process the WEB indexing queue. * * This was originally written to reuse the Beagle Firefox plug-in (which * copied visited pages and bookmarks to the queue), long dead and replaced by a * recoll-specific plugin. */ #include "fstreewalk.h" #include "rcldoc.h" class CirCache; class RclConfig; class WebStore; namespace Rcl { class Db; } class WebQueueIndexer : public FsTreeWalkerCB { public: WebQueueIndexer(RclConfig *cnf, Rcl::Db *db); ~WebQueueIndexer(); WebQueueIndexer(const WebQueueIndexer&) = delete; WebQueueIndexer& operator=(const WebQueueIndexer&) = delete; /** This is called by the top indexer in recollindex. * Does the walking and the talking */ bool index(); /** Called when we fstreewalk the queue dir */ FsTreeWalker::Status processone(const std::string &, FsTreeWalker::CbFlag, const struct PathStat&) override; /** Index a list of files. No db cleaning or stemdb updating. * Used by the real time monitor */ bool indexFiles(std::list<std::string>& files); /** Purge a list of files. No way to do this currently and dont want * to do anything as this is mostly called by the monitor when *I* delete * files inside the queue dir */ bool purgeFiles(std::list<std::string>&) {return true;} /** Called when indexing data from the cache, and from internfile for * search result preview */ bool getFromCache(const std::string& udi, Rcl::Doc &doc, std::string& data, std::string *hittype = nullptr); private: RclConfig *m_config{nullptr}; Rcl::Db *m_db{nullptr}; WebStore *m_cache{nullptr}; std::string m_queuedir; // Don't process the cache. Set by indexFiles(). bool m_nocacheindex{false}; // Config: page erase interval. We normally keep only one // instance. This can be set to "day", "week", "month", "year" to // keep more. enum KeepInterval {WQKI_NONE, WQKI_DAY, WQKI_WEEK, WQKI_MONTH, WQKI_YEAR}; KeepInterval m_keepinterval{WQKI_NONE}; bool indexFromCache(const std::string& udi); void updstatus(const std::string& udi); }; #endif /* _webqueue_h_included_ */ ����������������������������recoll-1.36.1/index/rclmon.sh�����������������������������������������������������������������������0000755�0001750�0001750�00000003577�14410615043�012755� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # Copyright (C) 2006 J.F.Dockes ####################################################### # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ###################################################### ################### # Start/stop a recollindex program running as permanent real time indexer. # The running program writes its pid in $RECOLL_CONFDIR/index.pid # The portability of this script has not been fully tested. # fatal() { echo $* exit 1 } usage() { fatal "Usage: rclmon.sh <start|stop>" } test $# -eq 1 || usage export LANG=C RECOLL_CONFDIR=${RECOLL_CONFDIR:-$HOME/.recoll} #echo RECOLL_CONFDIR = ${RECOLL_CONFDIR} pidfile="${RECOLL_CONFDIR}/index.pid" opid=0 if test -f $pidfile ; then read opid junk < $pidfile fi if test $opid -gt 0; then out=`kill -0 ${opid} 2>&1` if test $? -ne 0 ; then if test `expr "$out" : '.*such *process.*'` -ne 0 ; then opid=0 else fatal cant test existence of running process fi fi fi #echo "Existing pid $opid" case $1 in start) if test "$opid" -ne 0 ; then fatal "Already running process: $opid" fi recollindex -m ;; stop) if test "$opid" -eq 0 ; then fatal "No process running" fi kill $opid ;; *) usage esac ���������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/fsindexer.cpp�������������������������������������������������������������������0000644�0001750�0001750�00000075065�14427373216�013633� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2009-2020 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "fsindexer.h" #include <stdio.h> #include <errno.h> #include <cstring> #include <iostream> #include <list> #include <map> #include <algorithm> #include "cstr.h" #include "pathut.h" #include "rclutil.h" #include "conftree.h" #include "rclconfig.h" #include "fstreewalk.h" #include "rcldb.h" #include "readfile.h" #include "indexer.h" #include "transcode.h" #include "log.h" #include "internfile.h" #include "smallut.h" #include "chrono.h" #include "wipedir.h" #include "fileudi.h" #include "cancelcheck.h" #include "rclinit.h" #include "extrameta.h" #include "utf8fn.h" #include "idxdiags.h" #include "fsfetcher.h" #if defined(HAVE_POSIX_FADVISE) #include <unistd.h> #include <fcntl.h> #endif using namespace std; #ifdef IDX_THREADS class DbUpdTask { public: // Take some care to avoid sharing string data (if string impl is cow) DbUpdTask(const string& u, const string& p, const Rcl::Doc& d) : udi(u.begin(), u.end()), parent_udi(p.begin(), p.end()) { d.copyto(&doc); } string udi; string parent_udi; Rcl::Doc doc; }; extern void *FsIndexerDbUpdWorker(void*); class InternfileTask { public: // Take some care to avoid sharing string data (if string impl is cow) InternfileTask(const std::string &f, const struct PathStat& i_stp, map<string,string> lfields) : fn(f.begin(), f.end()), statbuf(i_stp) { map_ss_cp_noshr(lfields, &localfields); } string fn; struct PathStat statbuf; map<string,string> localfields; }; extern void *FsIndexerInternfileWorker(void*); #endif // IDX_THREADS // Thread safe variation of the "missing helpers" storage. Only the // addMissing method needs protection, the rest are called from the // main thread either before or after the exciting part class FSIFIMissingStore : public FIMissingStore { #ifdef IDX_THREADS std::mutex m_mutex; #endif public: virtual void addMissing(const string& prog, const string& mt) { #ifdef IDX_THREADS std::unique_lock<std::mutex> locker(m_mutex); #endif FIMissingStore::addMissing(prog, mt); } }; FsIndexer::FsIndexer(RclConfig *cnf, Rcl::Db *db) : m_config(cnf), m_db(db), m_missing(new FSIFIMissingStore), m_detectxattronly(false), m_noretryfailed(false) #ifdef IDX_THREADS , m_iwqueue("Internfile", cnf->getThrConf(RclConfig::ThrIntern).first), m_dwqueue("Split", cnf->getThrConf(RclConfig::ThrSplit).first) #endif // IDX_THREADS { LOGDEB1("FsIndexer::FsIndexer\n"); m_havelocalfields = m_config->hasNameAnywhere("localfields"); m_config->getConfParam("detectxattronly", &m_detectxattronly); #ifdef IDX_THREADS m_stableconfig = new RclConfig(*m_config); m_haveInternQ = m_haveSplitQ = false; int internqlen = cnf->getThrConf(RclConfig::ThrIntern).first; int internthreads = cnf->getThrConf(RclConfig::ThrIntern).second; if (internqlen >= 0) { if (!m_iwqueue.start(internthreads, FsIndexerInternfileWorker, this)) { LOGERR("FsIndexer::FsIndexer: intern worker start failed\n"); return; } m_haveInternQ = true; } int splitqlen = cnf->getThrConf(RclConfig::ThrSplit).first; int splitthreads = cnf->getThrConf(RclConfig::ThrSplit).second; if (splitqlen >= 0) { if (!m_dwqueue.start(splitthreads, FsIndexerDbUpdWorker, this)) { LOGERR("FsIndexer::FsIndexer: split worker start failed\n"); return; } m_haveSplitQ = true; } LOGDEB("FsIndexer: threads: haveIQ " << m_haveInternQ << " iql " << internqlen << " iqts " << internthreads << " haveSQ " << m_haveSplitQ << " sql " << splitqlen << " sqts " << splitthreads << "\n"); #endif // IDX_THREADS } FsIndexer::~FsIndexer() { LOGDEB1("FsIndexer::~FsIndexer()\n"); #ifdef IDX_THREADS void *status; if (m_haveInternQ) { status = m_iwqueue.setTerminateAndWait(); LOGDEB0("FsIndexer: internfile wrkr status: "<< status << " (1->ok)\n"); } if (m_haveSplitQ) { status = m_dwqueue.setTerminateAndWait(); LOGDEB0("FsIndexer: dbupd worker status: " << status << " (1->ok)\n"); } delete m_stableconfig; #endif // IDX_THREADS delete m_missing; } bool FsIndexer::init() { if (m_tdl.empty()) { m_tdl = m_config->getTopdirs(); if (m_tdl.empty()) { LOGERR("FsIndexers: no topdirs list defined\n"); return false; } } return true; } // Recursively index each directory in the topdirs: bool FsIndexer::index(int flags) { bool quickshallow = (flags & ConfIndexer::IxFQuickShallow) != 0; m_noretryfailed = (flags & ConfIndexer::IxFNoRetryFailed) != 0; m_cleancache = (flags & ConfIndexer::IxFCleanCache) != 0; Chrono chron; if (!init()) return false; statusUpdater()->setDbTotDocs(m_db->docCnt()); m_walker.setSkippedPaths(m_config->getSkippedPaths()); if (quickshallow) { m_walker.setOpts(m_walker.getOpts() | FsTreeWalker::FtwSkipDotFiles); m_walker.setMaxDepth(2); } bool walkok(true); for (const auto& topdir : m_tdl) { LOGDEB("FsIndexer::index: Indexing " << topdir << " into " << getDbDir() << "\n"); // If a topdirs member appears to be not here or not mounted // (empty), avoid deleting all the related index content by // marking the current docs as existing. if (path_empty(topdir)) { m_db->udiTreeMarkExisting(topdir); continue; } // Set the current directory in config so that subsequent // getConfParams() will get local values m_config->setKeyDir(topdir); // Adjust the "follow symlinks" option bool follow{false}; int opts = m_walker.getOpts(); if (m_config->getConfParam("followLinks", &follow) && follow) { opts |= FsTreeWalker::FtwFollow; } else { opts &= ~FsTreeWalker::FtwFollow; } m_walker.setOpts(opts); int abslen; if (m_config->getConfParam("idxabsmlen", &abslen)) m_db->setAbstractParams(abslen, -1, -1); // Walk the directory tree if (m_walker.walk(topdir, *this) != FsTreeWalker::FtwOk) { LOGERR("FsIndexer::index: error while indexing " << topdir << ": " << m_walker.getReason() << "\n"); // DO NOT return: we need to flush the queues before the Db can be closed ! walkok = false; break; } } shutdownQueues(walkok); if (m_missing) { string missing; m_missing->getMissingDescription(missing); if (!missing.empty()) { LOGINFO("FsIndexer::index missing helper program(s):\n" << missing << "\n"); } m_config->storeMissingHelperDesc(missing); } LOGINFO("fsindexer: status: " << walkok << " index time: " << chron.millis() << " mS\n"); return walkok; } void FsIndexer::shutdownQueues(bool ok) { #ifdef IDX_THREADS if (!ok) { // Error or more probably interrupt. Discard everything for fast shutdown if (m_haveInternQ) { m_iwqueue.closeShop(); } if (m_haveSplitQ) { m_dwqueue.closeShop(); } m_db->closeQueue(); } if (m_haveInternQ) { m_iwqueue.waitIdle(); } if (m_haveSplitQ) { m_dwqueue.waitIdle(); } m_db->waitUpdIdle(); #endif // IDX_THREADS } static bool matchesSkipped( const vector<string>& tdl, FsTreeWalker& walker, const string& path) { // Check path against topdirs and skippedPaths. We go up the // ancestors until we find either a topdirs or a skippedPaths // match. If topdirs is found first-> ok to index (it's possible // and useful to configure a topdir under a skippedPath in the // config). This matches what happens during the normal fs tree // walk. string canonpath = path_canon(path); string mpath = canonpath; string topdir; for (;;) { // Used to test not root here, but root may be in topdirs ! for (const auto& tdlent : tdl) { // the topdirs members are already canonized. LOGDEB1("matchesSkipped: comparing ancestor [" << mpath << "] to topdir [" << tdlent << "]\n"); if (mpath == tdlent) { topdir = tdlent; goto goodpath; } } if (walker.inSkippedPaths(mpath, false)) { LOGDEB("FsIndexer::indexFiles: skipping [" << path << "] (skpp)\n"); return true; } if (path_isroot(mpath)) { break; } // Compute father string::size_type len = mpath.length(); mpath = path_getfather(mpath); // getfather normally returns a path ending with /, canonic // paths don't (except for '/' itself). if (!path_isroot(mpath) && mpath[mpath.size()-1] == '/') mpath.erase(mpath.size()-1); // should not be necessary, but lets be prudent. If the // path did not shorten, something is seriously amiss // (could be an assert actually) if (mpath.length() >= len) { LOGERR("FsIndexer::indexFile: internal Error: path [" << mpath << "] did not shorten\n"); return true; } } // We get there if neither topdirs nor skippedPaths tests matched LOGDEB("FsIndexer::indexFiles: skipping [" << path << "] (ntd)\n"); return true; goodpath: // Then check all path components up to the topdir against skippedNames mpath = canonpath; while (mpath.length() >= topdir.length() && mpath.length() > 1) { string fn = path_getsimple(mpath); if (walker.inSkippedNames(fn)) { LOGDEB("FsIndexer::indexFiles: skipping [" << path << "] (skpn)\n"); return true; } string::size_type len = mpath.length(); mpath = path_getfather(mpath); // getfather normally returns a path ending with /, getsimple // would then return '' if (!mpath.empty() && mpath[mpath.size()-1] == '/') mpath.erase(mpath.size()-1); // should not be necessary, but lets be prudent. If the // path did not shorten, something is seriously amiss // (could be an assert actually) if (mpath.length() >= len) return true; } return false; } /** * Index individual files, out of a full tree run. No database purging */ bool FsIndexer::indexFiles(list<string>& files, int flags) { LOGDEB("FsIndexer::indexFiles\n"); m_noretryfailed = (flags & ConfIndexer::IxFNoRetryFailed) != 0; m_cleancache = (flags & ConfIndexer::IxFCleanCache) != 0; bool ret = false; if (!init()) return false; int abslen; if (m_config->getConfParam("idxabsmlen", &abslen)) m_db->setAbstractParams(abslen, -1, -1); m_purgeCandidates.setRecord(true); // We use an FsTreeWalker just for handling the skipped path/name lists FsTreeWalker walker; walker.setSkippedPaths(m_config->getSkippedPaths()); for (auto it = files.begin(); it != files.end(); ) { LOGDEB2("FsIndexer::indexFiles: [" << *it << "]\n"); m_config->setKeyDir(path_getfather(*it)); if (m_havelocalfields) localfieldsfromconf(); bool follow{false}; m_config->getConfParam("followLinks", &follow); walker.setOnlyNames(m_config->getOnlyNames()); walker.setSkippedNames(m_config->getSkippedNames()); // Check path against indexed areas and skipped names/paths if (!(flags & ConfIndexer::IxFIgnoreSkip) && matchesSkipped(m_tdl, walker, *it)) { it++; continue; } struct PathStat stb; int ststat = path_fileprops(*it, &stb, follow); if (ststat != 0) { LOGERR("FsIndexer::indexFiles: (l)stat " << *it << ": " << strerror(errno) << "\n"); it++; continue; } if (!(flags & ConfIndexer::IxFIgnoreSkip) && (stb.pst_type == PathStat::PST_REGULAR || stb.pst_type == PathStat::PST_SYMLINK)) { if (!walker.inOnlyNames(path_getsimple(*it))) { it++; continue; } } if (processone(*it, FsTreeWalker::FtwRegular, stb) != FsTreeWalker::FtwOk) { LOGERR("FsIndexer::indexFiles: processone failed\n"); goto out; } it = files.erase(it); } ret = true; out: shutdownQueues(ret); // Purge possible orphan documents if (ret == true) { LOGDEB("Indexfiles: purging orphans\n"); for (const auto& udi : m_purgeCandidates.getCandidates()) { LOGDEB("Indexfiles: purging orphans for " << udi << "\n"); m_db->purgeOrphans(udi); } #ifdef IDX_THREADS m_db->waitUpdIdle(); #endif // IDX_THREADS } LOGDEB("FsIndexer::indexFiles: done\n"); return ret; } /** Purge docs for given files out of the database */ bool FsIndexer::purgeFiles(list<string>& files) { LOGDEB("FsIndexer::purgeFiles\n"); bool ret = false; if (!init()) return false; for (list<string>::iterator it = files.begin(); it != files.end(); ) { string udi; make_udi(*it, cstr_null, udi); // rcldb::purgefile returns true if the udi was either not // found or deleted, false only in case of actual error bool existed; if (!m_db->purgeFile(udi, &existed)) { LOGERR("FsIndexer::purgeFiles: Database error\n"); goto out; } // If we actually deleted something, take it off the list if (existed) { it = files.erase(it); } else { it++; } } ret = true; out: shutdownQueues(ret); LOGDEB("FsIndexer::purgeFiles: done\n"); return ret; } // Local fields can be set for fs subtrees in the configuration file void FsIndexer::localfieldsfromconf() { LOGDEB1("FsIndexer::localfieldsfromconf\n"); string sfields; m_config->getConfParam("localfields", sfields); if (!sfields.compare(m_slocalfields)) return; m_slocalfields = sfields; m_localfields.clear(); if (sfields.empty()) return; string value; ConfSimple attrs; m_config->valueSplitAttributes(sfields, value, attrs); vector<string> nmlst = attrs.getNames(cstr_null); for (const auto& anm : nmlst) { string nm = m_config->fieldCanon(anm); attrs.get(anm, m_localfields[nm]); LOGDEB2("FsIndexer::localfieldsfromconf: [" << nm << "]->[" << m_localfields[nm] << "]\n"); } } void FsIndexer::setlocalfields(const map<string, string>& fields, Rcl::Doc& doc) { for (const auto& field : fields) { // Being chosen by the user, localfields override values from // the filter. The key is already canonic (see // localfieldsfromconf()) doc.meta[field.first] = field.second; } } #ifdef IDX_THREADS // Called updworker as seen from here, but the first step (and only in // most meaningful configurations) is doing the word-splitting, which // is why the task is referred as "Split" in the grand scheme of // things. An other stage usually deals with the actual index update. void *FsIndexerDbUpdWorker(void * fsp) { recoll_threadinit(); FsIndexer *fip = (FsIndexer*)fsp; WorkQueue<DbUpdTask*> *tqp = &fip->m_dwqueue; DbUpdTask *tsk; for (;;) { size_t qsz; if (!tqp->take(&tsk, &qsz)) { tqp->workerExit(); return (void*)1; } LOGDEB0("FsIndexerDbUpdWorker: task ql " << qsz << "\n"); if (!fip->m_db->addOrUpdate(tsk->udi, tsk->parent_udi, tsk->doc)) { LOGERR("FsIndexerDbUpdWorker: addOrUpdate failed\n"); tqp->workerExit(); return (void*)0; } delete tsk; } } void *FsIndexerInternfileWorker(void * fsp) { recoll_threadinit(); FsIndexer *fip = (FsIndexer*)fsp; WorkQueue<InternfileTask*> *tqp = &fip->m_iwqueue; RclConfig myconf(*(fip->m_stableconfig)); InternfileTask *tsk{nullptr}; for (;;) { if (!tqp->take(&tsk)) { tqp->workerExit(); return (void*)1; } LOGDEB0("FsIndexerInternfileWorker: task fn " << tsk->fn << "\n"); if (fip->processonefile( &myconf, tsk->fn, tsk->statbuf, tsk->localfields) != FsTreeWalker::FtwOk) { LOGERR("FsIndexerInternfileWorker: processone failed\n"); tqp->workerExit(); return (void*)0; } LOGDEB1("FsIndexerInternfileWorker: done fn " << tsk->fn << "\n"); delete tsk; } } #endif // IDX_THREADS /// This method gets called for every file and directory found by the /// tree walker. /// /// It checks with the db if the file has changed and needs to be /// reindexed. If so, it calls internfile() which will identify the /// file type and call an appropriate handler to convert the document into /// internal format, which we then add to the database. /// /// Accent and majuscule handling are performed by the db module when doing /// the actual indexing work. The Rcl::Doc created by internfile() /// mostly contains pretty raw utf8 data. FsTreeWalker::Status FsIndexer::processone( const std::string &fn, FsTreeWalker::CbFlag flg, const struct PathStat& stp) { if (!statusUpdater()->update(DbIxStatus::DBIXS_FILES, fn)) { return FsTreeWalker::FtwStop; } // If we're changing directories, possibly adjust parameters (set // the current directory in configuration object) if (flg == FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwDirReturn) { m_config->setKeyDir(fn); // Set up filter/skipped patterns for this subtree. m_walker.setOnlyNames(m_config->getOnlyNames()); m_walker.setSkippedNames(m_config->getSkippedNames()); // Adjust local fields from config for this subtree if (m_havelocalfields) localfieldsfromconf(); if (flg == FsTreeWalker::FtwDirReturn) return FsTreeWalker::FtwOk; } if (flg == FsTreeWalker::FtwSkipped) { IdxDiags::theDiags().record(IdxDiags::Skipped, fn); return FsTreeWalker::FtwOk; } #ifdef IDX_THREADS if (m_haveInternQ) { InternfileTask *tp = new InternfileTask(fn, stp, m_localfields); if (m_iwqueue.put(tp)) { return FsTreeWalker::FtwOk; } else { return FsTreeWalker::FtwError; } } #endif return processonefile(m_config, fn, stp, m_localfields); } // Start db update, either by queueing or by direct call bool FsIndexer::launchAddOrUpdate(const string& udi, const string& parent_udi, Rcl::Doc& doc) { #ifdef IDX_THREADS if (m_haveSplitQ) { DbUpdTask *tp = new DbUpdTask(udi, parent_udi, doc); if (!m_dwqueue.put(tp)) { LOGERR("processonefile: wqueue.put failed\n"); return false; } else { return true; } } #endif return m_db->addOrUpdate(udi, parent_udi, doc); } FsTreeWalker::Status FsIndexer::processonefile( RclConfig *config, const std::string &fn, const struct PathStat& stp, const map<string, string>& localfields) { //////////////////// // Check db up to date ? Doing this before file type // identification means that, if usesystemfilecommand is switched // from on to off it may happen that some files which are now // without mime type will not be purged from the db, resulting // in possible 'cannot intern file' messages at query time... // This is needed if we are in a separate thread than processone() // (mostly always when multithreading). Needed esp. for // excludedmimetypes, etc. config->setKeyDir(path_getfather(fn)); // File signature and up to date check. The sig is based on // m/ctime and size and the possibly new value is checked against // the stored one. string sig; fsmakesig(stp, sig); string udi; make_udi(fn, cstr_null, udi); unsigned int existingDoc; string oldsig; bool needupdate; if (m_noretryfailed) { needupdate = m_db->needUpdate(udi, sig, &existingDoc, &oldsig); } else { needupdate = m_db->needUpdate(udi, sig, &existingDoc, nullptr); } // If ctime (which we use for the sig) differs from mtime, then at most // the extended attributes were changed, no need to index content. // This unfortunately leaves open the case where the data was // modified, then the extended attributes, in which case we will // miss the data update. We would have to store both the mtime and // the ctime to avoid this bool xattronly = m_detectxattronly && !m_db->inFullReset() && existingDoc && needupdate && (stp.pst_mtime < stp.pst_ctime); LOGDEB("processone: needupdate " << needupdate << " noretry " << m_noretryfailed << " existing " << existingDoc << " oldsig [" << oldsig << "]\n"); // If noretryfailed is set, check for a file which previously // failed to index, and avoid re-processing it if (needupdate && m_noretryfailed && existingDoc && !oldsig.empty() && oldsig.back() == '+') { // Check that the sigs are the same except for the '+'. If the file // actually changed, we always retry (maybe it was fixed) string nold = oldsig.substr(0, oldsig.size()-1); if (!nold.compare(sig)) { LOGDEB("processone: not retrying previously failed file\n"); m_db->setExistingFlags(udi, existingDoc); needupdate = false; } } if (!needupdate) { LOGDEB0("processone: up to date: " << fn << "\n"); if (!statusUpdater()->update( DbIxStatus::DBIXS_FILES, fn, DbIxStatusUpdater::IncrFilesDone)) { return FsTreeWalker::FtwStop; } return FsTreeWalker::FtwOk; } LOGDEB0("processone: processing: [" << displayableBytes(stp.pst_size) << "] " << fn << "\n"); // Note that we used to do the full path here, but I ended up // believing that it made more sense to use only the file name string utf8fn = compute_utf8fn(config, fn, true); // parent_udi is initially the same as udi, it will be used if there // are subdocs. string parent_udi = udi; Rcl::Doc doc; std::string ascdate; lltodecstr(stp.pst_mtime, ascdate); #ifdef EXT4_BIRTH_TIME std::string brdate; if (stp.pst_btime) { // Note that btime==0 is a valid date. At the moment though we reserve it for "no value" // TBD if this is acceptable or not lltodecstr(stp.pst_btime, brdate); } #endif bool hadNullIpath = false; string mimetype; if (!xattronly) { FileInterner interner(fn, stp, config, FileInterner::FIF_none); if (!interner.ok()) { // no indexing whatsoever in this case. This typically means that // indexallfilenames is not set return FsTreeWalker::FtwOk; } mimetype = interner.getMimetype(); interner.setMissingStore(m_missing); FileInterner::Status fis = FileInterner::FIAgain; bool hadNonNullIpath = false; while (fis == FileInterner::FIAgain) { doc.erase(); try { fis = interner.internfile(doc); } catch (CancelExcept) { LOGERR("fsIndexer::processone: interrupted\n"); return FsTreeWalker::FtwStop; } // We index at least the file name even if there was an error. // We'll change the signature to ensure that the indexing will // be retried every time. // If there is an error and the base doc was already seen, // we're done if (fis == FileInterner::FIError && hadNullIpath) { return FsTreeWalker::FtwOk; } // Internal access path for multi-document files. If empty, this is // for the main file. if (doc.ipath.empty()) { hadNullIpath = true; if (hadNonNullIpath) { // Note that only the filters can reliably compute // this. What we do is dependant of the doc order (if // we see the top doc first, we won't set the flag) doc.haschildren = true; } } else { hadNonNullIpath = true; } make_udi(fn, doc.ipath, udi); // Set file name, mod time and url if not done by // filter. We used to set the top-level container file // name for all subdocs without a proper file name, but // this did not make sense (resulted in multiple not // useful hits on the subdocs when searching for the // file name). if (doc.fmtime.empty()) doc.fmtime = ascdate; #ifdef EXT4_BIRTH_TIME if (!brdate.empty() && !doc.hasmetavalue(Rcl::Doc::keybrt) ) { doc.addmeta(Rcl::Doc::keybrt, brdate); } #endif if (doc.url.empty()) doc.url = path_pathtofileurl(fn); if (doc.ipath.empty() && !doc.hasmetavalue(Rcl::Doc::keyfn)) { doc.addmeta(Rcl::Doc::keyfn, utf8fn); } // Set container file name for all docs, top or subdoc doc.meta[Rcl::Doc::keyctfn] = utf8fn; doc.pcbytes = lltodecstr(stp.pst_size); // Document signature for up to date checks. All subdocs inherit the // file's. doc.sig = sig; // If there was an error, ensure indexing will be // retried. This is for the once missing, later installed // filter case. It can make indexing much slower (if there are // myriads of such files, the ext script is executed for them // and fails every time) if (fis == FileInterner::FIError) { IdxDiags::theDiags().record(IdxDiags::Error, fn, doc.ipath); doc.sig += cstr_plus; } // Possibly add fields from local config if (m_havelocalfields) setlocalfields(localfields, doc); // Add document to database. If there is an ipath, add it // as a child of the file document. if (!launchAddOrUpdate(udi, doc.ipath.empty() ? cstr_null : parent_udi, doc)) { return FsTreeWalker::FtwError; } // Tell what we are doing and check for interrupt request int incr = DbIxStatusUpdater::IncrDocsDone; std::string sfn(fn); if (!doc.ipath.empty()) { sfn += "|" + doc.ipath; } else { if (fis == FileInterner::FIError) { incr |= DbIxStatusUpdater::IncrFileErrors; } incr |= DbIxStatusUpdater::IncrFilesDone; } if (!statusUpdater()->update(DbIxStatus::DBIXS_FILES, sfn, incr)) { return FsTreeWalker::FtwStop; } } if (fis == FileInterner::FIError) { // In case of error, avoid purging any existing // subdoc. For example on windows, this will avoid erasing // all the emails from a .ost because it is currently // locked by Outlook. LOGDEB("processonefile: internfile error, marking " "subdocs as existing\n"); m_db->udiTreeMarkExisting(parent_udi); } else { // If this doc existed and it's a container, recording for // possible subdoc purge (this will be used only if we don't do a // db-wide purge, e.g. if we're called from indexfiles()). LOGDEB2("processOnefile: existingDoc " << existingDoc << " hadNonNullIpath " << hadNonNullIpath << "\n"); if (existingDoc && hadNonNullIpath) { m_purgeCandidates.record(parent_udi); } } #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_DONTNEED) // See framagit issue 26. This is off by default and controlled by a command line switch. if (m_cleancache) { int fd = open(fn.c_str(), O_RDONLY); if (fd >= 0) { if (posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED)) { LOGSYSERR("processonefile", "posix_fadvise", fn); } close(fd); } } #endif } // If we had no instance with a null ipath, we create an empty // document to stand for the file itself, to be used mainly for up // to date checks. Typically this happens for an mbox file. // // If xattronly is set, ONLY the extattr metadata is valid and will be used // by the following step. if (xattronly || hadNullIpath == false) { LOGDEB("Creating empty doc for file or pure xattr update\n"); Rcl::Doc fileDoc; if (xattronly) { map<string, string> xfields; reapXAttrs(config, fn, xfields); docFieldsFromXattrs(config, xfields, fileDoc); fileDoc.onlyxattr = true; } else { fileDoc.fmtime = ascdate; #ifdef EXT4_BIRTH_TIME if (!brdate.empty()) fileDoc.addmeta(Rcl::Doc::keybrt, brdate); #endif fileDoc.meta[Rcl::Doc::keyfn] = fileDoc.meta[Rcl::Doc::keyctfn] = utf8fn; fileDoc.haschildren = true; fileDoc.mimetype = mimetype; fileDoc.url = path_pathtofileurl(fn); if (m_havelocalfields) setlocalfields(localfields, fileDoc); fileDoc.pcbytes = lltodecstr(stp.pst_size); } fileDoc.sig = sig; if (!launchAddOrUpdate(parent_udi, cstr_null, fileDoc)) { return FsTreeWalker::FtwError; } } return FsTreeWalker::FtwOk; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/checkretryfailed.cpp������������������������������������������������������������0000644�0001750�0001750�00000004733�14410615043�015133� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2014 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "checkretryfailed.h" #include "safesysstat.h" #include <string> #include <vector> #include "rclconfig.h" #include "execmd.h" #include "log.h" #include "pathut.h" #include "recollindex.h" using namespace std; bool checkRetryFailed(RclConfig *conf, bool record) { #ifdef _WIN32 PRETEND_USE(record); // Under Windows we only retry if the recollindex program is newer // than the index struct PathStat st; string path(thisprog); if (path_suffix(path).empty()) { path = path + ".exe"; } if (path_fileprops(path, &st) != 0) { LOGERR("checkRetryFailed: can't stat the program file: " << thisprog << endl); return false; } time_t exetime = st.pst_mtime; if (path_fileprops(conf->getDbDir(), &st) != 0) { // Maybe it just does not exist. LOGDEB("checkRetryFailed: can't stat the index directory: " << conf->getDbDir() << endl); return false; } time_t dbtime = st.pst_mtime; return exetime > dbtime; #else string cmd; if (!conf->getConfParam("checkneedretryindexscript", cmd)) { LOGDEB("checkRetryFailed: 'checkneedretryindexscript' " "not set in config\n"); // We could toss a dice ? Say no retry in this case. return false; } // Look in the filters directory (ies). If not found execpath will // be the same as cmd, and we'll let execvp do its thing. string execpath = conf->findFilter(cmd); vector<string> args; if (record) { args.push_back("1"); } ExecCmd ecmd; int status = ecmd.doexec(execpath, args); if (status == 0) { return true; } return false; #endif } �������������������������������������recoll-1.36.1/index/recollindex.service�������������������������������������������������������������0000644�0001750�0001750�00000000444�14410615043�015004� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Contributed by Frank Dana, licensed as Recoll itself [Unit] Description=Recoll real-time document indexing After=gnome-session-manager.target [Service] Type=simple ExecStart=/usr/bin/recollindex -m -D -x -w 30 -c %h/.recoll/ Restart=on-failure [Install] WantedBy=graphical-session.target ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/fsfetcher.cpp�������������������������������������������������������������������0000644�0001750�0001750�00000005467�14427373216�013614� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2012-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <errno.h> #include "log.h" #include "cstr.h" #include "fetcher.h" #include "fsfetcher.h" #include "fsindexer.h" #include "pathut.h" using std::string; static DocFetcher::Reason urltopath(RclConfig* cnf, const Rcl::Doc& idoc, string& fn, struct PathStat& st) { // The url has to be like file:// fn = fileurltolocalpath(idoc.url); if (fn.empty()) { LOGERR("FSDocFetcher::fetch/sig: non fs url: [" << idoc.url << "]\n"); return DocFetcher::FetchOther; } cnf->setKeyDir(path_getfather(fn)); bool follow = false; cnf->getConfParam("followLinks", &follow); if (path_fileprops(fn, &st, follow) < 0) { LOGERR("FSDocFetcher::fetch: stat errno " << errno << " for [" << fn << "]\n"); return DocFetcher::FetchNotExist; } return DocFetcher::FetchOk; } bool FSDocFetcher::fetch(RclConfig* cnf, const Rcl::Doc& idoc, RawDoc& out) { string fn; if (urltopath(cnf, idoc, fn, out.st) != DocFetcher::FetchOk) return false; out.kind = RawDoc::RDK_FILENAME; out.data = fn; return true; } void fsmakesig(const struct PathStat& stp, string& out) { out = lltodecstr(stp.pst_size) + lltodecstr(o_uptodate_test_use_mtime ? stp.pst_mtime : stp.pst_ctime); } bool FSDocFetcher::makesig(RclConfig* cnf, const Rcl::Doc& idoc, string& sig) { string fn; struct PathStat st; if (urltopath(cnf, idoc, fn, st) != DocFetcher::FetchOk) return false; fsmakesig(st, sig); return true; } DocFetcher::Reason FSDocFetcher::testAccess(RclConfig* cnf, const Rcl::Doc& idoc) { string fn; struct PathStat st; DocFetcher::Reason reason = urltopath(cnf, idoc, fn, st); if (reason != DocFetcher::FetchOk) { return reason; } if (!path_readable(fn)) { return DocFetcher::FetchNoPerm; } // We have no way to know if the file is fully readable without // trying (local Windows locks), which would take too much time. return DocFetcher::FetchOther; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/checkindexed.cpp����������������������������������������������������������������0000644�0001750�0001750�00000005100�14410615043�014226� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2021 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "checkindexed.h" #include <stdio.h> #include <iostream> #include "rclconfig.h" #include "fileudi.h" #include "rcldb.h" #include "rcldoc.h" #include "smallut.h" class PathYielder { public: PathYielder(const std::vector<std::string>& paths) : m_paths(paths) { if (m_paths.size()) { m_index = 0; } } std::string getPath() { if (m_index >= 0) { if (m_index < int(m_paths.size())) { return m_paths[m_index++]; } } else { char line[1024]; if (fgets(line, 1023, stdin)) { std::string sl(line); trimstring(sl, "\n\r"); return sl; } } return std::string(); } int m_index{-1}; const std::vector<std::string>& m_paths; }; bool checkindexed(RclConfig *conf, const std::vector<std::string>& filepaths) { PathYielder paths(filepaths); Rcl::Db db(conf); if (!db.open(Rcl::Db::DbRO)) { std::cerr << "Could not open index for reading\n"; return false; } for (;;) { auto path = paths.getPath(); if (path.empty()) { break; } std::string udi; make_udi(path, std::string(), udi); Rcl::Doc doc; if (!db.getDoc(udi, "", doc)) { std::cerr << "Unexpected error from getdoc\n"; return false; } // See comments in getdoc if (doc.pc == -1) { std::cout << "ABSENT " << path << std::endl; } else { std::string sig; if (!doc.getmeta(Rcl::Doc::keysig, &sig) || sig.back() == '+') { std::cout << "ERROR " << path << std::endl; } } } return true; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/webqueuefetcher.h���������������������������������������������������������������0000644�0001750�0001750�00000002462�14410615043�014450� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _WEBQUEUEFETCHER_H_INCLUDED_ #define _WEBQUEUEFETCHER_H_INCLUDED_ #include "fetcher.h" /** * The WEB queue cache fetcher: */ class WQDocFetcher : public DocFetcher { public: virtual bool fetch(RclConfig* cnf, const Rcl::Doc& idoc, RawDoc& out); virtual bool makesig(RclConfig* cnf, const Rcl::Doc& idoc, std::string& sig); WQDocFetcher() {} virtual ~WQDocFetcher() {} WQDocFetcher(const WQDocFetcher&) = delete; WQDocFetcher& operator=(const WQDocFetcher&) = delete; }; #endif /* _WEBQUEUEFETCHER_H_INCLUDED_ */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/rclmonrcv.cpp�������������������������������������������������������������������0000644�0001750�0001750�00000122327�14444307651�013642� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "autoconfig.h" #ifdef RCL_MONITOR /* Copyright (C) 2006-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* The code for the Win32 version of the monitor was largely copied from efsw: * https://github.com/SpartanJ/efsw * LICENSE for the original WIN32 code: * Copyright (c) 2020 Martn Lucas Golini * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com) * http://code.google.com/p/simplefilewatcher/ also MIT licensed. */ #include "autoconfig.h" #include <string> #include <vector> #include <map> #include <errno.h> #include <cstdio> #include <cstring> #include "safeunistd.h" #include "log.h" #include "rclmon.h" #include "rclinit.h" #include "fstreewalk.h" #include "pathut.h" using std::string; using std::vector; using std::map; /** * Recoll real time monitor event receiver. This file has code to interface * to FAM, inotify, etc. and place events on the event queue. */ /** Virtual interface for the actual filesystem monitoring module. */ class RclMonitor { public: RclMonitor() {} virtual ~RclMonitor() {} virtual bool addWatch(const string& path, bool isDir, bool follow = false) = 0; virtual bool getEvent(RclMonEvent& ev, int msecs = -1) = 0; virtual bool ok() const = 0; // Does this monitor generate 'exist' events at startup? virtual bool generatesExist() const { return false; } virtual bool isRecursive() const { return false; } // Save significant errno after monitor calls int saved_errno{0}; }; // Monitor factory. We only have one compiled-in kind at a time, no // need for a 'kind' parameter static RclMonitor *makeMonitor(); /** * Create directory watches during the initial file system tree walk. * * This class is a callback for the file system tree walker * class. The callback method alternatively creates the directory * watches and flushes the event queue (to avoid a possible overflow * while we create the watches) */ class WalkCB : public FsTreeWalkerCB { public: WalkCB(RclConfig *conf, RclMonitor *mon, RclMonEventQueue *queue, FsTreeWalker& walker) : m_config(conf), m_mon(mon), m_queue(queue), m_walker(walker) {} virtual ~WalkCB() {} virtual FsTreeWalker::Status processone( const string &fn, FsTreeWalker::CbFlag flg, const struct PathStat&) override { MONDEB("walkerCB: processone " << fn << " m_mon " << m_mon << " m_mon->ok " << (m_mon ? m_mon->ok() : false) << "\n"); // We set the watch follow links flag for the topdir only. bool initfollow = m_initfollow; m_initfollow = false; if (flg == FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwDirReturn) { m_config->setKeyDir(fn); // Set up skipped patterns for this subtree. m_walker.setSkippedNames(m_config->getSkippedNames()); } if (flg == FsTreeWalker::FtwDirEnter) { // Create watch when entering directory, but first empty // whatever events we may already have on queue while (m_queue->ok() && m_mon->ok()) { RclMonEvent ev; if (m_mon->getEvent(ev, 0)) { if (ev.m_etyp != RclMonEvent::RCLEVT_NONE) m_queue->pushEvent(ev); } else { MONDEB("walkerCB: no event pending\n"); break; } } if (!m_mon || !m_mon->ok()) return FsTreeWalker::FtwError; // We do nothing special if addWatch fails for a reasonable reason if (!m_mon->isRecursive() && !m_mon->addWatch(fn, true, initfollow)) { if (m_mon->saved_errno != EACCES && m_mon->saved_errno != ENOENT) { LOGINF("walkerCB: addWatch failed\n"); return FsTreeWalker::FtwError; } } } else if (!m_mon->generatesExist() && flg == FsTreeWalker::FtwRegular) { // Have to synthetize events for regular files existence // at startup because the monitor does not do it // Note 2011-09-29: no sure this is actually needed. We just ran // an incremental indexing pass (before starting the // monitor). Why go over the files once more ? The only // reason I can see would be to catch modifications that // happen between the incremental and the start of // monitoring ? There should be another way: maybe start // monitoring without actually handling events (just // queue), then run incremental then start handling // events ? ** But we also have to do it on a directory // move! So keep it ** We could probably skip it on the initial run though. RclMonEvent ev; ev.m_path = fn; ev.m_etyp = RclMonEvent::RCLEVT_MODIFY; m_queue->pushEvent(ev); } return FsTreeWalker::FtwOk; } private: RclConfig *m_config; RclMonitor *m_mon; RclMonEventQueue *m_queue; FsTreeWalker& m_walker; bool m_initfollow{true}; }; static bool rclMonAddTopWatches( FsTreeWalker& walker, RclConfig& lconfig, RclMonitor *mon, RclMonEventQueue *queue) { // Get top directories from config. Special monitor sublist if // set, else full list. vector<string> tdl = lconfig.getTopdirs(true); if (tdl.empty()) { LOGERR("rclMonRcvRun:: top directory list (topdirs param.) not found " "in configuration or topdirs list parse error"); queue->setTerminate(); return false; } // Walk the directory trees to add watches for (const auto& topdir : tdl) { lconfig.setKeyDir(topdir); // Adjust the follow symlinks options bool follow{false}; if (lconfig.getConfParam("followLinks", &follow) && follow) { walker.setOpts(FsTreeWalker::FtwFollow); } else { walker.setOpts(FsTreeWalker::FtwOptNone); } if (path_isdir(topdir, true)) { LOGDEB("rclMonRcvRun: walking " << topdir <<" monrecurs "<< mon->isRecursive() << "\n"); // If the fs watcher is recursive, we add the watches for the topdirs here, and walk the // tree just for generating initial events. if (mon->isRecursive() && !mon->addWatch(topdir, true, true)) { if (mon->saved_errno != EACCES && mon->saved_errno != ENOENT) { LOGERR("rclMonAddTopWatches: addWatch failed for [" << topdir << "]\n"); return false; } } // Note: need to rebuild the walkcb each time to reset the initial followlinks (always // true for the topdir) WalkCB walkcb(&lconfig, mon, queue, walker); if (walker.walk(topdir, walkcb) != FsTreeWalker::FtwOk) { LOGERR("rclMonRcvRun: tree walk failed\n"); return false; } if (walker.getErrCnt() > 0) { LOGINFO("rclMonRcvRun: fs walker errors: " << walker.getReason() << "\n"); } } else { // We have to special-case regular files which are part of the topdirs list because the // tree walker only adds watches for directories MONDEB("rclMonRcvRun: adding watch for non dir topdir " << topdir << "\n"); if (!mon->addWatch(topdir, false, true)) { LOGSYSERR("rclMonRcvRun", "addWatch", topdir); } } } bool doweb{false}; lconfig.getConfParam("processwebqueue", &doweb); if (doweb) { string webqueuedir = lconfig.getWebQueueDir(); if (!mon->addWatch(webqueuedir, true)) { LOGERR("rclMonRcvRun: addwatch (webqueuedir) failed\n"); if (mon->saved_errno != EACCES && mon->saved_errno != ENOENT) return false; } } return true; } static bool rclMonAddSubWatches( const std::string& path, FsTreeWalker& walker, RclConfig& lconfig, RclMonitor *mon, RclMonEventQueue *queue) { WalkCB walkcb(&lconfig, mon, queue, walker); if (walker.walk(path, walkcb) != FsTreeWalker::FtwOk) { LOGERR("rclMonRcvRun: walking new dir " << path << " : " << walker.getReason() << "\n"); return false; } if (walker.getErrCnt() > 0) { LOGINFO("rclMonRcvRun: fs walker errors: " << walker.getReason() << "\n"); } return true; } // Don't push events for skipped files. This would get filtered on the processing side // anyway, but causes unnecessary wakeups and messages. Do not test skippedPaths here, // this would be incorrect (because a topdir can be under a skippedPath and this was // handled while adding the watches). Also we let the other side process onlyNames. static bool rclMonShouldSkip(const std::string& path, RclConfig& lconfig, FsTreeWalker& walker) { lconfig.setKeyDir(path_getfather(path)); walker.setSkippedNames(lconfig.getSkippedNames()); if (walker.inSkippedNames(path_getsimple(path))) return true; return false; } // Main thread routine: create watches, then forever wait for and queue events void *rclMonRcvRun(void *q) { RclMonEventQueue *queue = (RclMonEventQueue *)q; LOGDEB("rclMonRcvRun: running\n"); recoll_threadinit(); // Make a local copy of the configuration as it doesn't like // concurrent accesses. It's ok to copy it here as the other // thread will not work before we have sent events. RclConfig lconfig(*queue->getConfig()); // Create the fam/whatever interface object RclMonitor *mon; if ((mon = makeMonitor()) == nullptr) { LOGERR("rclMonRcvRun: makeMonitor failed\n"); queue->setTerminate(); return nullptr; } FsTreeWalker walker; walker.setSkippedPaths(lconfig.getDaemSkippedPaths()); if (!rclMonAddTopWatches(walker, lconfig, mon, queue)) { LOGERR("rclMonRcvRun: addtopwatches failed\n"); goto terminate; } // Forever wait for monitoring events and add them to queue: MONDEB("rclMonRcvRun: waiting for events. q->ok(): " << queue->ok() << "\n"); while (queue->ok() && mon->ok()) { RclMonEvent ev; // Note: I could find no way to get the select call to return when a signal is delivered to // the process (it goes to the main thread, from which I tried to close or write to the // select fd, with no effect). So set a timeout so that an intr will be detected if (mon->getEvent(ev, 2000)) { if (rclMonShouldSkip(ev.m_path, lconfig, walker)) continue; if (ev.m_etyp == RclMonEvent::RCLEVT_DIRCREATE) { // Recursive addwatch: there may already be stuff inside this directory. E.g.: files // were quickly created, or this is actually the target of a directory move. This is // necessary for inotify, but it seems that fam/gamin is doing the job for us so // that we are generating double events here (no big deal as prc will sort/merge). LOGDEB("rclMonRcvRun: walking new dir " << ev.m_path << "\n"); if (!rclMonAddSubWatches(ev.m_path, walker, lconfig, mon, queue)) { goto terminate; } } if (ev.m_etyp != RclMonEvent::RCLEVT_NONE) queue->pushEvent(ev); } } terminate: queue->setTerminate(); LOGINFO("rclMonRcvRun: monrcv thread routine returning\n"); return nullptr; } // Utility routine used by both the fam/gamin and inotify versions to get // rid of the id-path translation for a moved dir bool eraseWatchSubTree(map<int, string>& idtopath, const string& top) { bool found = false; MONDEB("Clearing map for [" << top << "]\n"); map<int,string>::iterator it = idtopath.begin(); while (it != idtopath.end()) { if (it->second.find(top) == 0) { found = true; it = idtopath.erase(it); } else { it++; } } return found; } // We dont compile both the inotify and the fam interface and inotify // has preference #ifndef RCL_USE_INOTIFY #ifdef RCL_USE_FAM ////////////////////////////////////////////////////////////////////////// /** Fam/gamin -based monitor class */ #include <fam.h> #include <sys/select.h> #include <setjmp.h> #include <signal.h> /** FAM based monitor class. We have to keep a record of FAM watch request numbers to directory names as the event only contain the request number and file name, not the full path */ class RclFAM : public RclMonitor { public: RclFAM(); virtual ~RclFAM(); virtual bool addWatch(const string& path, bool isdir, bool follow) override; virtual bool getEvent(RclMonEvent& ev, int msecs = -1) override; bool ok() override const {return m_ok;} virtual bool generatesExist() override const {return true;} private: bool m_ok; FAMConnection m_conn; void close() { FAMClose(&m_conn); m_ok = false; } map<int,string> m_idtopath; const char *event_name(int code); }; // Translate event code to string (debug) const char *RclFAM::event_name(int code) { static const char *famevent[] = { "", "FAMChanged", "FAMDeleted", "FAMStartExecuting", "FAMStopExecuting", "FAMCreated", "FAMMoved", "FAMAcknowledge", "FAMExists", "FAMEndExist" }; static char unknown_event[30]; if (code < FAMChanged || code > FAMEndExist) { sprintf(unknown_event, "unknown (%d)", code); return unknown_event; } return famevent[code]; } RclFAM::RclFAM() : m_ok(false) { if (FAMOpen2(&m_conn, "Recoll")) { LOGERR("RclFAM::RclFAM: FAMOpen2 failed, errno " << errno << "\n"); return; } m_ok = true; } RclFAM::~RclFAM() { if (ok()) FAMClose(&m_conn); } static jmp_buf jbuf; static void onalrm(int sig) { longjmp(jbuf, 1); } bool RclFAM::addWatch(const string& path, bool isdir, bool) { if (!ok()) return false; bool ret = false; MONDEB("RclFAM::addWatch: adding " << path << "\n"); // It happens that the following call block forever. // We'd like to be able to at least terminate on a signal here, but // gamin forever retries its write call on EINTR, so it's not even useful // to unblock signals. SIGALRM is not used by the main thread, so at least // ensure that we exit after gamin gets stuck. if (setjmp(jbuf)) { LOGERR("RclFAM::addWatch: timeout talking to FAM\n"); return false; } signal(SIGALRM, onalrm); alarm(20); FAMRequest req; if (isdir) { if (FAMMonitorDirectory(&m_conn, path.c_str(), &req, 0) != 0) { LOGERR("RclFAM::addWatch: FAMMonitorDirectory failed\n"); goto out; } } else { if (FAMMonitorFile(&m_conn, path.c_str(), &req, 0) != 0) { LOGERR("RclFAM::addWatch: FAMMonitorFile failed\n"); goto out; } } m_idtopath[req.reqnum] = path; ret = true; out: alarm(0); return ret; } // Note: return false only for queue empty or error // Return EVT_NONE for bad event to keep queue processing going bool RclFAM::getEvent(RclMonEvent& ev, int msecs) { if (!ok()) return false; MONDEB("RclFAM::getEvent:\n"); fd_set readfds; int fam_fd = FAMCONNECTION_GETFD(&m_conn); FD_ZERO(&readfds); FD_SET(fam_fd, &readfds); MONDEB("RclFAM::getEvent: select. fam_fd is " << fam_fd << "\n"); // Fam / gamin is sometimes a bit slow to send events. Always add // a little timeout, because if we fail to retrieve enough events, // we risk deadlocking in addwatch() if (msecs == 0) msecs = 2; struct timeval timeout; if (msecs >= 0) { timeout.tv_sec = msecs / 1000; timeout.tv_usec = (msecs % 1000) * 1000; } int ret; if ((ret=select(fam_fd+1, &readfds, 0, 0, msecs >= 0 ? &timeout : 0)) < 0) { LOGERR("RclFAM::getEvent: select failed, errno " << errno << "\n"); close(); return false; } else if (ret == 0) { // timeout MONDEB("RclFAM::getEvent: select timeout\n"); return false; } MONDEB("RclFAM::getEvent: select returned " << ret << "\n"); if (!FD_ISSET(fam_fd, &readfds)) return false; // ?? 2011/03/15 gamin v0.1.10. There is initially a single null // byte on the connection so the first select always succeeds. If // we then call FAMNextEvent we stall. Using FAMPending works // around the issue, but we did not need this in the past and this // is most weird. if (FAMPending(&m_conn) <= 0) { MONDEB("RclFAM::getEvent: FAMPending says no events\n"); return false; } MONDEB("RclFAM::getEvent: call FAMNextEvent\n"); FAMEvent fe; if (FAMNextEvent(&m_conn, &fe) < 0) { LOGERR("RclFAM::getEvent: FAMNextEvent: errno " << errno << "\n"); close(); return false; } MONDEB("RclFAM::getEvent: FAMNextEvent returned\n"); map<int,string>::const_iterator it; if ((!path_isabsolute(fe.filename)) && (it = m_idtopath.find(fe.fr.reqnum)) != m_idtopath.end()) { ev.m_path = path_cat(it->second, fe.filename); } else { ev.m_path = fe.filename; } MONDEB("RclFAM::getEvent: " << event_name(fe.code) < " " << ev.m_path << "\n"); switch (fe.code) { case FAMCreated: if (path_isdir(ev.m_path)) { ev.m_etyp = RclMonEvent::RCLEVT_DIRCREATE; break; } /* FALLTHROUGH */ case FAMChanged: case FAMExists: // Let the other side sort out the status of this file vs the db ev.m_etyp = RclMonEvent::RCLEVT_MODIFY; break; case FAMMoved: case FAMDeleted: ev.m_etyp = RclMonEvent::RCLEVT_DELETE; // We would like to signal a directory here to enable cleaning // the subtree (on a dir move), but can't test the actual file // which is gone, and fam doesn't tell us if it's a dir or reg. // Let's rely on the fact that a directory should be watched if (eraseWatchSubTree(m_idtopath, ev.m_path)) ev.m_etyp |= RclMonEvent::RCLEVT_ISDIR; break; case FAMStartExecuting: case FAMStopExecuting: case FAMAcknowledge: case FAMEndExist: default: // Have to return something, this is different from an empty queue, // esp if we are trying to empty it... if (fe.code != FAMEndExist) LOGDEB("RclFAM::getEvent: got other event " << fe.code << "!\n"); ev.m_etyp = RclMonEvent::RCLEVT_NONE; break; } return true; } #endif // RCL_USE_FAM #endif // ! INOTIFY #ifdef RCL_USE_INOTIFY ////////////////////////////////////////////////////////////////////////// /** Inotify-based monitor class */ #include <sys/inotify.h> #include <sys/select.h> class RclIntf : public RclMonitor { public: RclIntf() : m_ok(false), m_fd(-1), m_evp(nullptr), m_ep(nullptr) { if ((m_fd = inotify_init()) < 0) { LOGERR("RclIntf:: inotify_init failed, errno " << errno << "\n"); return; } m_ok = true; } virtual ~RclIntf() { close(); } virtual bool addWatch(const string& path, bool isdir, bool follow) override; virtual bool getEvent(RclMonEvent& ev, int msecs = -1) override; bool ok() const override {return m_ok;} private: bool m_ok; int m_fd; map<int,string> m_idtopath; // Watch descriptor to name #define EVBUFSIZE (32*1024) char m_evbuf[EVBUFSIZE]; // Event buffer char *m_evp; // Pointer to next event or 0 char *m_ep; // Pointer to end of events const char *event_name(int code); void close() { if (m_fd >= 0) { ::close(m_fd); m_fd = -1; } m_ok = false; } }; const char *RclIntf::event_name(int code) { code &= ~(IN_ISDIR|IN_ONESHOT); switch (code) { case IN_ACCESS: return "IN_ACCESS"; case IN_MODIFY: return "IN_MODIFY"; case IN_ATTRIB: return "IN_ATTRIB"; case IN_CLOSE_WRITE: return "IN_CLOSE_WRITE"; case IN_CLOSE_NOWRITE: return "IN_CLOSE_NOWRITE"; case IN_CLOSE: return "IN_CLOSE"; case IN_OPEN: return "IN_OPEN"; case IN_MOVED_FROM: return "IN_MOVED_FROM"; case IN_MOVED_TO: return "IN_MOVED_TO"; case IN_MOVE: return "IN_MOVE"; case IN_CREATE: return "IN_CREATE"; case IN_DELETE: return "IN_DELETE"; case IN_DELETE_SELF: return "IN_DELETE_SELF"; case IN_MOVE_SELF: return "IN_MOVE_SELF"; case IN_UNMOUNT: return "IN_UNMOUNT"; case IN_Q_OVERFLOW: return "IN_Q_OVERFLOW"; case IN_IGNORED: return "IN_IGNORED"; default: { static char msg[50]; sprintf(msg, "Unknown event 0x%x", code); return msg; } }; } bool RclIntf::addWatch(const string& path, bool, bool follow) { if (!ok()) { return false; } MONDEB("RclIntf::addWatch: adding " << path << " follow " << follow << "\n"); // CLOSE_WRITE is covered through MODIFY. CREATE is needed for mkdirs uint32_t mask = IN_MODIFY | IN_CREATE | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE // IN_ATTRIB used to be not needed to receive extattr // modification events, which was a bit weird because only ctime is // set, and now it is... | IN_ATTRIB #ifdef IN_DONT_FOLLOW | (follow ? 0 : IN_DONT_FOLLOW) #endif #ifdef IN_EXCL_UNLINK | IN_EXCL_UNLINK #endif ; int wd; if ((wd = inotify_add_watch(m_fd, path.c_str(), mask)) < 0) { saved_errno = errno; LOGSYSERR("RclIntf::addWatch", "inotify_add_watch", path); if (errno == ENOSPC) { LOGERR("RclIntf::addWatch: ENOSPC error may mean that you should " "increase the inotify kernel constants. See inotify(7)\n"); } return false; } m_idtopath[wd] = path; return true; } // Note: return false only for queue empty or error // Return EVT_NONE for bad event to keep queue processing going bool RclIntf::getEvent(RclMonEvent& ev, int msecs) { if (!ok()) return false; ev.m_etyp = RclMonEvent::RCLEVT_NONE; MONDEB("RclIntf::getEvent:\n"); if (nullptr == m_evp) { fd_set readfds; FD_ZERO(&readfds); FD_SET(m_fd, &readfds); struct timeval timeout; if (msecs >= 0) { timeout.tv_sec = msecs / 1000; timeout.tv_usec = (msecs % 1000) * 1000; } int ret; MONDEB("RclIntf::getEvent: select\n"); if ((ret = select(m_fd + 1, &readfds, nullptr, nullptr, msecs >= 0 ? &timeout : nullptr)) < 0) { LOGSYSERR("RclIntf::getEvent", "select", ""); close(); return false; } else if (ret == 0) { MONDEB("RclIntf::getEvent: select timeout\n"); // timeout return false; } MONDEB("RclIntf::getEvent: select returned\n"); if (!FD_ISSET(m_fd, &readfds)) return false; int rret; if ((rret=read(m_fd, m_evbuf, sizeof(m_evbuf))) <= 0) { LOGSYSERR("RclIntf::getEvent", "read", sizeof(m_evbuf)); close(); return false; } m_evp = m_evbuf; m_ep = m_evbuf + rret; } struct inotify_event *evp = (struct inotify_event *)m_evp; m_evp += sizeof(struct inotify_event); if (evp->len > 0) m_evp += evp->len; if (m_evp >= m_ep) m_evp = m_ep = nullptr; map<int,string>::const_iterator it; if ((it = m_idtopath.find(evp->wd)) == m_idtopath.end()) { LOGERR("RclIntf::getEvent: unknown wd " << evp->wd << "\n"); return true; } ev.m_path = it->second; if (evp->len > 0) { ev.m_path = path_cat(ev.m_path, evp->name); } MONDEB("RclIntf::getEvent: " << event_name(evp->mask) << " " << ev.m_path << "\n"); if ((evp->mask & IN_MOVED_FROM) && (evp->mask & IN_ISDIR)) { // We get this when a directory is renamed. Erase the subtree // entries in the map. The subsequent MOVED_TO will recreate // them. This is probably not needed because the watches // actually still exist in the kernel, so that the wds // returned by future addwatches will be the old ones, and the // map will be updated in place. But still, this feels safer eraseWatchSubTree(m_idtopath, ev.m_path); } // IN_ATTRIB used to be not needed, but now it is if (evp->mask & (IN_MODIFY|IN_ATTRIB)) { ev.m_etyp = RclMonEvent::RCLEVT_MODIFY; } else if (evp->mask & (IN_DELETE | IN_MOVED_FROM)) { ev.m_etyp = RclMonEvent::RCLEVT_DELETE; if (evp->mask & IN_ISDIR) ev.m_etyp |= RclMonEvent::RCLEVT_ISDIR; } else if (evp->mask & (IN_CREATE | IN_MOVED_TO)) { if (evp->mask & IN_ISDIR) { ev.m_etyp = RclMonEvent::RCLEVT_DIRCREATE; } else { // We used to return null event because we would get a // modify event later, but it seems not to be the case any // more (10-2011). So generate MODIFY event ev.m_etyp = RclMonEvent::RCLEVT_MODIFY; } } else if (evp->mask & (IN_IGNORED)) { if (!m_idtopath.erase(evp->wd)) { LOGDEB0("Got IGNORE event for unknown watch\n"); } else { eraseWatchSubTree(m_idtopath, ev.m_path); } } else { LOGDEB("RclIntf::getEvent: unhandled event " << event_name(evp->mask) << " " << evp->mask << " " << ev.m_path << "\n"); return true; } return true; } #endif // RCL_USE_INOTIFY #ifdef _WIN32 /* * WIN32 VERSION NOTES: * * - When using non-recursive watches (one per dir), it appeared that * watching a subdirectory of a given directory prevented renaming * the top directory, Windows says: can't rename because open or a * file in it is open. This is mostly why we use recursive watches * on the topdirs only. */ #include <string> #include <vector> #include <thread> #include <queue> #include "safewindows.h" typedef long WatchID; class WatcherWin32; class RclFSWatchWin32; enum class Action {Add = 1, Delete = 2, Modify = 3, Move = 4}; // Virtual interface for the monitor callback. Note: this for compatibility with the efsw code, as // rclmon uses a pull, not push interface. The callback pushes the events to a local queue from // which they are then pulled by the upper level code. class FileWatchListener { public: virtual ~FileWatchListener() {} virtual void handleFileAction(WatchID watchid, const std::string& dir, const std::string& fn, Action action, bool isdir, std::string oldfn = "" ) = 0; }; // Internal watch data. This piggy-back our actual data pointer to the MS overlapped pointer. This // is a bit of a hack, and we could probably use event Ids instead. struct WatcherStructWin32 { OVERLAPPED Overlapped; WatcherWin32 *Watch; }; // Actual data structure for one directory watch class WatcherWin32 { public: WatchID ID; FileWatchListener *Listener{nullptr}; bool Recursive; std::string DirName; std::string OldFileName; HANDLE DirHandle{nullptr}; // do NOT make this bigger than 64K because it will fail if the folder being watched is on the // network! (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx) BYTE Buffer[8 * 1024]; DWORD NotifyFilter{0}; bool StopNow{false}; RclFSWatchWin32 *Watch{nullptr}; }; // The efsw top level file system watcher: manages all the directory watches. class RclFSWatchWin32 { public: RclFSWatchWin32(); virtual ~RclFSWatchWin32(); // Add a directory watch // On error returns -1 WatchID addWatch(const std::string& directory, FileWatchListener *watcher, bool recursive); // 2nd stage of action processing (after the static handler which just reads the data) void handleAction(WatcherWin32 *watch, const std::string& fn, unsigned long action); bool ok() const { return mInitOK; } // Fetch events, with msecs timeout if there are no more void run(DWORD msecs); private: HANDLE mIOCP; // Using a vector because we don't remove watches. Change to list if needed. std::vector<WatcherStructWin32*> mWatches; bool mInitOK{false}; WatchID mLastWatchID{0}; std::mutex mWatchesLock; bool pathInWatches(const std::string& path); /// Remove all directory watches. void removeAllWatches(); }; // Adapter for the rclmon interface class RclMonitorWin32 : public RclMonitor, public FileWatchListener { public: virtual ~RclMonitorWin32() {} virtual bool addWatch(const string& path, bool /*isDir*/, bool /*follow*/) override { MONDEB("RclMonitorWin32::addWatch: " << path << "\n"); return m_fswatcher.addWatch(path, this, true) != -1; } virtual bool getEvent(RclMonEvent& ev, int msecs = -1) override { PRETEND_USE(msecs); if (!m_events.empty()) { ev = m_events.front(); m_events.pop(); return true; } m_fswatcher.run(msecs); if (!m_events.empty()) { ev = m_events.front(); m_events.pop(); return true; } return false; } virtual bool ok() const override { return m_fswatcher.ok(); } // Does this monitor generate 'exist' events at startup? virtual bool generatesExist() const override { return false; } // Can the caller avoid setting watches on subdirs ? virtual bool isRecursive() const override { return true; } virtual void handleFileAction(WatchID watchid, const std::string& dir, const std::string& fn, Action action, bool isdir, std::string oldfn = "") { MONDEB("RclMonitorWin32::handleFileAction: dir [" << dir << "] fn [" << fn << "] act " << int(action) << " isdir " << isdir << " oldfn [" << oldfn << "]\n"); RclMonEvent event; switch (action) { case Action::Move: case Action::Add: event.m_etyp = isdir ? RclMonEvent::RCLEVT_DIRCREATE : RclMonEvent::RCLEVT_MODIFY; break; case Action::Delete: event.m_etyp = RclMonEvent::RCLEVT_DELETE; if (isdir) { event.m_etyp |= RclMonEvent::RCLEVT_ISDIR; } break; case Action::Modify: event.m_etyp = RclMonEvent::RCLEVT_MODIFY; break; } event.m_path = path_cat(dir, fn); m_events.push(event); } // Save significant errno after monitor calls int saved_errno{0}; private: std::queue<RclMonEvent> m_events; RclFSWatchWin32 m_fswatcher; }; /// Stops monitoring a directory. void DestroyWatch(WatcherStructWin32 *pWatch) { if (pWatch) { WatcherWin32 *ww32 = pWatch->Watch; ww32->StopNow = true; CancelIoEx(ww32->DirHandle, &pWatch->Overlapped); CloseHandle(ww32->DirHandle); delete ww32; // Shouldn't we call heapfree on the parameter here ?? } } /// Refreshes the directory monitoring. bool RefreshWatch(WatcherStructWin32 *pWatch) { WatcherWin32 *ww32 = pWatch->Watch; return ReadDirectoryChangesW( ww32->DirHandle, ww32->Buffer, sizeof(ww32->Buffer), ww32->Recursive, ww32->NotifyFilter, NULL, &pWatch->Overlapped, NULL ) != 0; } /// Starts monitoring a directory. WatcherStructWin32 *CreateWatch(LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, HANDLE iocp) { WatcherStructWin32 *wsw32; size_t ptrsize = sizeof(*wsw32); wsw32 =static_cast<WatcherStructWin32*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize)); WatcherWin32 *ww32 = new WatcherWin32(); wsw32->Watch = ww32; ww32->DirHandle = CreateFileW( szDirectory, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL ); if (ww32->DirHandle != INVALID_HANDLE_VALUE && CreateIoCompletionPort(ww32->DirHandle, iocp, 0, 1)) { ww32->NotifyFilter = NotifyFilter; ww32->Recursive = recursive; if (RefreshWatch(wsw32)) { return wsw32; } } CloseHandle(ww32->DirHandle); delete ww32; HeapFree(GetProcessHeap(), 0, wsw32); return NULL; } RclFSWatchWin32::RclFSWatchWin32() : mLastWatchID(0) { mIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); if (mIOCP && mIOCP != INVALID_HANDLE_VALUE) mInitOK = true; } RclFSWatchWin32::~RclFSWatchWin32() { mInitOK = false; if (mIOCP && mIOCP != INVALID_HANDLE_VALUE) { PostQueuedCompletionStatus(mIOCP, 0, reinterpret_cast<ULONG_PTR>(this), NULL); } removeAllWatches(); CloseHandle(mIOCP); } WatchID RclFSWatchWin32::addWatch(const std::string& _dir,FileWatchListener *watcher,bool recursive) { LOGDEB("RclFSWatchWin32::addWatch: " << _dir << " recursive " << recursive << "\n"); std::string dir(_dir); path_slashize(dir); if (!path_isdir(dir)) { LOGDEB("RclFSWatchWin32::addWatch: not a directory: " << dir << "\n"); return 0; } if (!path_readable(dir)) { LOGINF("RclFSWatchWin32::addWatch: not readable: " << dir << "\n"); return 0; } path_catslash(dir); auto wdir = utf8towchar(dir); std::unique_lock<std::mutex> lock(mWatchesLock); if (pathInWatches(dir)) { MONDEB("RclFSWatchWin32::addWatch: already in watches: " << dir << "\n"); return 0; } WatchID watchid = ++mLastWatchID; WatcherStructWin32 *watch = CreateWatch( wdir.get(), recursive, FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE, mIOCP ); if (nullptr == watch) { LOGINF("RclFSWatchWin32::addWatch: CreateWatch failed\n"); return -1; } // Add the handle to the handles vector watch->Watch->ID = watchid; watch->Watch->Watch = this; watch->Watch->Listener = watcher; watch->Watch->DirName = dir; mWatches.push_back(watch); return watchid; } void RclFSWatchWin32::removeAllWatches() { std::unique_lock<std::mutex> lock(mWatchesLock); for( auto& watchp : mWatches) { DestroyWatch(watchp); } mWatches.clear(); } // Unpacks events and passes them to the event processor void CALLBACK WatchCallback(DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) { if (dwNumberOfBytesTransfered == 0 || NULL == lpOverlapped) { return; } WatcherStructWin32 *wsw32 = (WatcherStructWin32*)lpOverlapped; WatcherWin32 *ww32 = wsw32->Watch; PFILE_NOTIFY_INFORMATION pNotify; size_t offset = 0; do { pNotify = (PFILE_NOTIFY_INFORMATION) &ww32->Buffer[offset]; offset += pNotify->NextEntryOffset; std::string sfn; wchartoutf8(pNotify->FileName, sfn, pNotify->FileNameLength / sizeof(WCHAR)); ww32->Watch->handleAction(ww32, sfn, pNotify->Action); } while (pNotify->NextEntryOffset != 0); if (!ww32->StopNow) { RefreshWatch(wsw32); } } void RclFSWatchWin32::run(DWORD msecs) { if (!mWatches.empty()) { DWORD numOfBytes = 0; OVERLAPPED* ov = NULL; ULONG_PTR compKey = 0; BOOL res = FALSE; DWORD ms = msecs == -1 ? INFINITE : msecs; while ((res = GetQueuedCompletionStatus(mIOCP, &numOfBytes, &compKey, &ov, ms))) { if (compKey != 0 && compKey == reinterpret_cast<ULONG_PTR>(this)) { // Called from ~RclFSWatchWin32. Must exit. MONDEB("RclFSWatchWin32::run: queuedcompletion said need exit\n"); return; } else { std::unique_lock<std::mutex> lock(mWatchesLock); WatchCallback(numOfBytes, ov); } } } else { // No watches yet. MONDEB("RclFSWatchWin32::run: no watches yet\n"); DWORD ms = msecs == -1 ? 1000 : msecs; std::this_thread::sleep_for(std::chrono::milliseconds(ms)); } } void RclFSWatchWin32::handleAction(WatcherWin32 *watch, const std::string& _fn, unsigned long action) { std::string fn(_fn); Action fwAction; path_slashize(fn); MONDEB("handleAction: fn [" << fn << "] action " << action << "\n"); // In case fn is not a simple name but a relative path (probably // possible/common if recursive is set ?), sort out the directory // path and simple file name. std::string newpath = path_cat(watch->DirName, fn); bool isdir = path_isdir(newpath); std::string simplefn = path_getsimple(newpath); std::string folderPath = path_getfather(newpath); switch (action) { case FILE_ACTION_RENAMED_OLD_NAME: watch->OldFileName = fn; /* FALLTHROUGH */ case FILE_ACTION_REMOVED: fwAction = Action::Delete; // The system does not tell us if this was a directory, but we // need the info. Check if it was in the watches. // TBD: for a delete, we should delete all watches on the subtree ! path_catslash(newpath); for (auto& watchp : mWatches) { if (watchp->Watch->DirName == newpath) { isdir = true; break; } } break; case FILE_ACTION_ADDED: fwAction = Action::Add; break; case FILE_ACTION_MODIFIED: fwAction = Action::Modify; break; case FILE_ACTION_RENAMED_NEW_NAME: { fwAction = Action::Move; // If this is a directory, possibly update the watches. TBD: this seems wrong because we // should process the whole subtree ? Also probably not needed at all because we are // recursive and only set watches on the top directories. if (isdir) { // Update the new directory path std::string oldpath = path_cat(watch->DirName, watch->OldFileName); path_catslash(oldpath); for (auto& watchp : mWatches) { if (watchp->Watch->DirName == oldpath) { watchp->Watch->DirName = newpath; break; } } } std::string oldFolderPath = watch->DirName + watch->OldFileName.substr(0, watch->OldFileName.find_last_of("/\\")); if (folderPath == oldFolderPath) { watch->Listener->handleFileAction(watch->ID, folderPath, simplefn, fwAction, isdir, path_getsimple(watch->OldFileName)); } else { // Calling the client with non-simple paths?? watch->Listener->handleFileAction(watch->ID, watch->DirName, fn, fwAction, isdir, watch->OldFileName); } return; } default: return; }; watch->Listener->handleFileAction(watch->ID, folderPath, simplefn, fwAction, isdir); } bool RclFSWatchWin32::pathInWatches(const std::string& path) { for (const auto& wsw32 : mWatches) { if (wsw32->Watch->DirName == path ) { return true; } } return false; } #endif // _WIN32 /////////////////////////////////////////////////////////////////////// // The monitor 'factory' static RclMonitor *makeMonitor() { #ifdef _WIN32 return new RclMonitorWin32; #else # ifdef RCL_USE_INOTIFY return new RclIntf; # elif defined(RCL_USE_FAM) return new RclFAM; # endif #endif LOGINFO("RclMonitor: neither Inotify nor Fam was compiled as file system " "change notification interface\n"); return nullptr; } #endif // RCL_MONITOR ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/fsfetcher.h���������������������������������������������������������������������0000644�0001750�0001750�00000003073�14427373216�013250� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _FSFETCHER_H_INCLUDED_ #define _FSFETCHER_H_INCLUDED_ #include "fetcher.h" #include "pathut.h" /** * The file-system fetcher: */ class FSDocFetcher : public DocFetcher { public: /** FSDocFetcher::fetch always returns a file name */ virtual bool fetch(RclConfig* cnf, const Rcl::Doc& idoc, RawDoc& out); /** Calls stat to retrieve file signature data */ virtual bool makesig(RclConfig* cnf,const Rcl::Doc& idoc, std::string& sig); virtual DocFetcher::Reason testAccess(RclConfig* cnf, const Rcl::Doc& idoc); FSDocFetcher() {} virtual ~FSDocFetcher() {} FSDocFetcher(const FSDocFetcher&) = delete; FSDocFetcher& operator=(const FSDocFetcher&) = delete; }; extern void fsmakesig(const struct PathStat& stp, std::string& out); #endif /* _FSFETCHER_H_INCLUDED_ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/idxdiags.h����������������������������������������������������������������������0000644�0001750�0001750�00000003263�14410615043�013061� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2021 J.F.Dockes * * License: GPL 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _IDXDIAGS_H_INCLUDED_ #define _IDXDIAGS_H_INCLUDED_ #include <string> class IdxDiags { public: enum DiagKind {Ok, Skipped, NoContentSuffix, MissingHelper, Error, NoHandler, ExcludedMime, NotIncludedMime}; // Retrieve a reference to the single instance. static IdxDiags& theDiags(); // Initialize, setting the output file path. outpath will be truncated. // No locking: this must be called from the main thread, before going multithread. // If init is never called, further calls to record() or flush() will be noops. bool init(const std::string& outpath); // Record a reason for a document not to be indexed. bool record(DiagKind diag, const std::string& path, const std::string& detail = std::string()); bool flush(); class Internal; private: Internal *m; IdxDiags(); ~IdxDiags(); }; #endif /* _IDXDIAGS_H_INCLUDED_ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/rclmonprc.cpp�������������������������������������������������������������������0000644�0001750�0001750�00000050561�14427373216�013635� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "autoconfig.h" #ifdef RCL_MONITOR /* Copyright (C) 2006-2022 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** * Recoll real time monitor processing. This file has the code to retrieve * event from the event queue and do the database-side processing. Also the * initialization function. */ #include <errno.h> #include <fnmatch.h> #include "safeunistd.h" #include <cstring> #include <cstdio> #include <cstdlib> #include <list> #include <vector> #include <thread> #include <mutex> #include <condition_variable> #include <chrono> #include "log.h" #include "rclmon.h" #include "log.h" #include "smallut.h" #include "execmd.h" #include "recollindex.h" #include "pathut.h" #ifndef _WIN32 #include "x11mon.h" #endif #include "subtreelist.h" #include "idxstatus.h" using std::list; using std::vector; using std::map; using std::string; typedef unsigned long mttcast; // Seconds between auxiliary db (stem, spell) updates: static const int dfltauxinterval = 60 *60; static int auxinterval = dfltauxinterval; // Seconds between indexing queue processing: for merging events to // fast changing files and saving some of the indexing overhead. static const int dfltixinterval = 30; static int ixinterval = dfltixinterval; static RclMonEventQueue rclEQ; // // Delayed events: this is a special feature for fast changing files. // A list of pattern/delays can be specified in the configuration so // that they don't get re-indexed before some timeout is elapsed. Such // events are kept on a separate queue (m_dqueue) with an auxiliary // list in time-to-reindex order, while the normal events are on // m_iqueue. // Queue management performance: on a typical recoll system there will // be only a few entries on the event queues and no significant time // will be needed to manage them. Even on a busy system, the time used // would most probably be negligible compared to the actual processing // of the indexing events. So this is just for reference. Let I be the // number of immediate events and D the number of delayed ones, N // stands for either. // // Periodic timeout polling: the recollindex process periodically (2S) // wakes up to check for exit requests. At this time it also checks // the queues for new entries (should not happen because the producer // would normally wake up the consumer threads), or ready entries // among the delayed ones. At this time it calls the "empty()" // routine. This has constant time behaviour (checks for stl container // emptiness and the top entry of the delays list). // // Adding a new event (pushEvent()): this performs a search for an // existing event with the same path (O(log(N)), then an insert on the // appropriate queue (O(log(N))) and an insert on the times list (O(D)). // // Popping an event: this is constant time as it just looks at the // tops of the normal and delayed queues. // Indexing event container: a map indexed by file path for fast // insertion of duplicate events to the same file typedef map<string, RclMonEvent> queue_type; // Entries for delayed events are duplicated (as iterators) on an // auxiliary, sorted by time-to-reindex list. We could get rid of // this, the price would be that the RclEQ.empty() call would have to // walk the whole queue instead of only looking at the first delays // entry. typedef list<queue_type::iterator> delays_type; // DelayPat stores a path wildcard pattern and a minimum time between // reindexes, it is read from the recoll configuration struct DelayPat { string pattern; int seconds; DelayPat() : seconds(0) {} }; /** Private part of RclEQ: things that we don't wish to exist in the interface * include file. */ class RclEQData { public: int m_opts{0}; // Queue for normal files (unlimited reindex) queue_type m_iqueue; // Queue for delayed reindex files queue_type m_dqueue; // The delays list stores pointers (iterators) to elements on // m_dqueue. The list is kept in time-to-index order. Elements of // m_dqueue which are also in m_delays can only be deleted while // walking m_delays, so we are certain that the m_dqueue iterators // stored in m_delays remain valid. delays_type m_delays; // Configured intervals for path patterns, read from the configuration. vector<DelayPat> m_delaypats; RclConfig *m_config{nullptr}; bool m_ok{true}; std::mutex m_mutex; std::condition_variable m_cond; void readDelayPats(int dfltsecs); DelayPat searchDelayPats(const string& path) { for (const auto& dpat: m_delaypats) { if (fnmatch(dpat.pattern.c_str(), path.c_str(), 0) == 0) { return dpat; } } return DelayPat(); } void delayInsert(const queue_type::iterator &qit); }; void RclEQData::readDelayPats(int dfltsecs) { if (nullptr == m_config) return; string patstring; if (!m_config->getConfParam("mondelaypatterns", patstring) || patstring.empty()) return; vector<string> dplist; if (!stringToStrings(patstring, dplist)) { LOGERR("rclEQData: bad pattern list: [" << patstring << "]\n"); return; } for (const auto& entry : dplist) { string::size_type pos = entry.find_last_of(":"); DelayPat dp; dp.pattern = entry.substr(0, pos); if (pos != string::npos && pos != entry.size() - 1) { dp.seconds = atoi(entry.substr(pos+1).c_str()); } else { dp.seconds = dfltsecs; } m_delaypats.push_back(dp); LOGDEB2("rclmon::readDelayPats: add [" << dp.pattern << "] " << dp.seconds << "\n"); } } // Insert event (as queue iterator) into delays list, in time order, // We DO NOT take care of duplicate qits. erase should be called first // when necessary. void RclEQData::delayInsert(const queue_type::iterator &qit) { MONDEB("RclEQData::delayInsert: minclock " << qit->second.m_minclock << "\n"); for (delays_type::iterator dit = m_delays.begin(); dit != m_delays.end(); dit++) { queue_type::iterator qit1 = *dit; if ((*qit1).second.m_minclock > qit->second.m_minclock) { m_delays.insert(dit, qit); return; } } m_delays.push_back(qit); } RclMonEventQueue::RclMonEventQueue() { m_data = new RclEQData; } RclMonEventQueue::~RclMonEventQueue() { delete m_data; } void RclMonEventQueue::setopts(int opts) { if (m_data) m_data->m_opts = opts; } /** Wait until there is something to process on the queue, or timeout. * returns a queue lock */ std::unique_lock<std::mutex> RclMonEventQueue::wait(int seconds, bool *top) { std::unique_lock<std::mutex> lock(m_data->m_mutex); MONDEB("RclMonEventQueue::wait, seconds: " << seconds << "\n"); if (!empty()) { MONDEB("RclMonEventQueue:: immediate return\n"); return lock; } if (seconds > 0) { if (top) *top = false; if (m_data->m_cond.wait_for(lock, std::chrono::seconds(seconds)) == std::cv_status::timeout) { *top = true; MONDEB("RclMonEventQueue:: timeout\n"); return lock; } } else { m_data->m_cond.wait(lock); } MONDEB("RclMonEventQueue:: non-timeout return\n"); return lock; } void RclMonEventQueue::setConfig(RclConfig *cnf) { m_data->m_config = cnf; // Don't use ixinterval here, could be 0 ! Base the default // delayed reindex delay on the default ixinterval delay m_data->readDelayPats(10 * dfltixinterval); } RclConfig *RclMonEventQueue::getConfig() { return m_data->m_config; } bool RclMonEventQueue::ok() { if (nullptr == m_data) { LOGINFO("RclMonEventQueue: not ok: bad state\n" ); return false; } if (stopindexing) { LOGINFO("RclMonEventQueue: not ok: stop request\n" ); return false; } if (!m_data->m_ok) { LOGINFO("RclMonEventQueue: not ok: queue terminated\n" ); return false; } return true; } void RclMonEventQueue::setTerminate() { MONDEB("RclMonEventQueue:: setTerminate\n"); std::unique_lock<std::mutex> lock(m_data->m_mutex); m_data->m_ok = false; m_data->m_cond.notify_all(); } // Must be called with the queue locked bool RclMonEventQueue::empty() { if (nullptr == m_data) { MONDEB("RclMonEventQueue::empty(): true (m_data==0)\n"); return true; } if (!m_data->m_iqueue.empty()) { MONDEB("RclMonEventQueue::empty(): false (m_iqueue not empty)\n"); return true; } if (m_data->m_dqueue.empty()) { MONDEB("RclMonEventQueue::empty(): true (m_Xqueue both empty)\n"); return true; } // Only dqueue has events. Have to check the delays (only the // first, earliest one): queue_type::iterator qit = *(m_data->m_delays.begin()); if (qit->second.m_minclock > time(nullptr)) { MONDEB("RclMonEventQueue::empty(): true (no delay ready " << qit->second.m_minclock << ")\n"); return true; } MONDEB("RclMonEventQueue::empty(): returning false (delay expired)\n"); return false; } // Retrieve indexing event for processing. Returns empty event if // nothing interesting is found // Must be called with the queue locked RclMonEvent RclMonEventQueue::pop() { time_t now = time(nullptr); MONDEB("RclMonEventQueue::pop(), now " << now << "\n"); // Look at the delayed events, get rid of the expired/unactive // ones, possibly return an expired/needidx one. while (!m_data->m_delays.empty()) { delays_type::iterator dit = m_data->m_delays.begin(); queue_type::iterator qit = *dit; MONDEB("RclMonEventQueue::pop(): in delays: evt minclock " << qit->second.m_minclock << "\n"); if (qit->second.m_minclock <= now) { if (qit->second.m_needidx) { RclMonEvent ev = qit->second; qit->second.m_minclock = time(nullptr) + qit->second.m_itvsecs; qit->second.m_needidx = false; m_data->m_delays.erase(dit); m_data->delayInsert(qit); return ev; } else { // Delay elapsed without new update, get rid of event. m_data->m_dqueue.erase(qit); m_data->m_delays.erase(dit); } } else { // This and following events are for later processing, we // are done with the delayed event list. break; } } // Look for non-delayed event if (!m_data->m_iqueue.empty()) { queue_type::iterator qit = m_data->m_iqueue.begin(); RclMonEvent ev = qit->second; m_data->m_iqueue.erase(qit); return ev; } return RclMonEvent(); } // Add new event (update or delete) to the processing queue. // It seems that a newer event is always correct to override any // older. TBVerified ? // Some conf-designated files, supposedly updated at a high rate get // special processing to limit their reindexing rate. bool RclMonEventQueue::pushEvent(const RclMonEvent &ev) { MONDEB("RclMonEventQueue::pushEvent for " << ev.m_path << "\n"); std::unique_lock<std::mutex> lock(m_data->m_mutex); DelayPat pat = m_data->searchDelayPats(ev.m_path); if (pat.seconds != 0) { // Using delayed reindex queue. Need to take care of minclock and also // insert into the in-minclock-order list queue_type::iterator qit = m_data->m_dqueue.find(ev.m_path); if (qit == m_data->m_dqueue.end()) { // Not there yet, insert new qit = m_data->m_dqueue.insert(queue_type::value_type(ev.m_path, ev)).first; // Set the time to next index to "now" as it has not been // indexed recently (otherwise it would still be in the // queue), and add the iterator to the delay queue. qit->second.m_minclock = time(nullptr); qit->second.m_needidx = true; qit->second.m_itvsecs = pat.seconds; m_data->delayInsert(qit); } else { // Already in queue. Possibly update type but save minclock // (so no need to touch m_delays). Flag as needing indexing time_t saved_clock = qit->second.m_minclock; qit->second = ev; qit->second.m_minclock = saved_clock; qit->second.m_needidx = true; } } else { // Immediate event: just insert it, erasing any previously // existing entry m_data->m_iqueue[ev.m_path] = ev; } m_data->m_cond.notify_all(); return true; } static bool checkfileanddelete(const string& fname) { bool ret; ret = path_exists(fname); unlink(fname.c_str()); return ret; } // It's possible to override the normal indexing delay by creating a // file in the config directory (which we then remove). And yes there // is definitely a race condition (we can suppress the delay and file // before the target doc is queued), and we can't be sure that the // delay suppression will be used for the doc the user intended it // for. But this is used for non-critical function and the race // condition should happen reasonably seldom. // We check for the request file in all possible user config dirs // (usually, there is only the main one) static bool expeditedIndexingRequested(RclConfig *conf) { static vector<string> rqfiles; if (rqfiles.empty()) { rqfiles.push_back(path_cat(conf->getConfDir(), "rclmonixnow")); const char *cp; if ((cp = getenv("RECOLL_CONFTOP"))) { rqfiles.push_back(path_cat(cp, "rclmonixnow")); } if ((cp = getenv("RECOLL_CONFMID"))) { rqfiles.push_back(path_cat(cp, "rclmonixnow")); } } bool found = false; for (vector<string>::const_iterator it = rqfiles.begin(); it != rqfiles.end(); it++) { found = found || checkfileanddelete(*it); } return found; } bool startMonitor(RclConfig *conf, int opts) { if (!conf->getConfParam("monauxinterval", &auxinterval)) auxinterval = dfltauxinterval; if (!conf->getConfParam("monixinterval", &ixinterval)) ixinterval = dfltixinterval; bool doweb{false}; conf->getConfParam("processwebqueue", &doweb); rclEQ.setConfig(conf); rclEQ.setopts(opts); std::thread treceive(rclMonRcvRun, &rclEQ); treceive.detach(); LOGDEB("start_monitoring: entering main loop\n" ); bool timedout; time_t lastauxtime = time(nullptr); time_t lastixtime = lastauxtime; time_t lastmovetime = 0; bool didsomething = false; list<string> modified; list<string> deleted; while (true) { time_t now = time(nullptr); #ifndef DISABLE_WEB_INDEXER if (doweb && (now - lastmovetime > ixinterval)) { lastmovetime = now; runWebFilesMoverScript(conf); } #endif // DISABLE_WEB_INDEXER { // Wait for event or timeout. // Set a relatively short timeout for better monitoring of // exit requests. std::unique_lock<std::mutex> lock = rclEQ.wait(2, &timedout); // x11IsAlive() can't be called from ok() because both // threads call it and Xlib is not multithreaded. #ifndef _WIN32 bool x11dead = !(opts & RCLMON_NOX11) && !x11IsAlive(); if (x11dead) LOGDEB("RclMonprc: x11 is dead\n"); #else bool x11dead = false; #endif if (!rclEQ.ok() || x11dead) { break; } // Process event queue for (;;) { // Retrieve event RclMonEvent ev = rclEQ.pop(); if (ev.m_path.empty()) break; switch (ev.evtype()) { case RclMonEvent::RCLEVT_MODIFY: case RclMonEvent::RCLEVT_DIRCREATE: LOGDEB0("Monitor: Modify/Check on " << ev.m_path << "\n"); modified.push_back(ev.m_path); break; case RclMonEvent::RCLEVT_DELETE: LOGDEB0("Monitor: Delete on " << ev.m_path << "\n"); // If this is for a directory (which the caller should tell us because he // knows), we should purge the db of all the subtree entries, because on a // directory rename, inotify will only generate one event for the renamed top, // not the subentries. The entries from the new subtree are updated when the // monitor walks it on the DIRCREATE event. deleted.push_back(ev.m_path); #ifndef _WIN32 // We don't know the type of deleted entries on // win32. So do the subtree things always. if (ev.evflags() & RclMonEvent::RCLEVT_ISDIR) #endif { vector<string> paths; if (subtreelist(conf, ev.m_path, paths)) { deleted.insert(deleted.end(), paths.begin(), paths.end()); } } break; default: LOGDEB("Monitor: got Other on [" << ev.m_path << "]\n"); } } } now = time(nullptr); // Process. We don't do this every time but let the lists accumulate // a little, this saves processing. Start at once if list is big. if (expeditedIndexingRequested(conf) || (now - lastixtime > ixinterval) || (deleted.size() + modified.size() > 20)) { lastixtime = now; // Used to do the modified list first, but it does seem // smarter to make room first... if (!deleted.empty()) { deleted.sort(); deleted.unique(); if (!purgefiles(conf, deleted)) break; deleted.clear(); didsomething = true; } if (!modified.empty()) { modified.sort(); modified.unique(); if (!indexfiles(conf, modified)) break; modified.clear(); didsomething = true; } } // Recreate the auxiliary dbs every hour at most. now = time(nullptr); if (didsomething && now - lastauxtime > auxinterval) { lastauxtime = now; didsomething = false; if (!createAuxDbs(conf)) { // We used to bail out on error here. Not anymore, // because this is most of the time due to a failure // of aspell dictionary generation, which is not // critical. } } // Check for a config change if (!(opts & RCLMON_NOCONFCHECK) && o_reexec && conf->sourceChanged()) { LOGDEB("Rclmonprc: config changed, reexecuting myself\n"); // We never want to have a -n option after a config // change. -n was added by the reexec after the initial // pass even if it was not given on the command line o_reexec->removeArg("-n"); o_reexec->reexec(); } if (!statusUpdater()->update(DbIxStatus::DBIXS_MONITOR, "")) { LOGDEB("Monitor returning because stop request (stop file)\n"); break; } } LOGDEB("Rclmonprc: calling queue setTerminate\n"); rclEQ.setTerminate(); // We used to wait for the receiver thread here before returning, // but this is not useful and may waste time / risk problems // during our limited time window for exiting. To be reviewed if // we ever need several monitor invocations in the same process // (can't foresee any reason why we'd want to do this). LOGDEB("Monitor: returning\n"); return true; } #endif // RCL_MONITOR �����������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/idxstatus.h���������������������������������������������������������������������0000644�0001750�0001750�00000006562�14410615043�013322� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2017-2018 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _IDXSTATUS_H_INCLUDED_ #define _IDXSTATUS_H_INCLUDED_ #include <string> // Current status of an indexing operation. This is updated in // $RECOLL_CONFDIR/idxstatus.txt class DbIxStatus { public: enum Phase {DBIXS_NONE, DBIXS_FILES, DBIXS_FLUSH, DBIXS_PURGE, DBIXS_STEMDB, DBIXS_CLOSING, DBIXS_MONITOR, DBIXS_DONE}; Phase phase; std::string fn; // Last file processed int docsdone; // Documents actually updated int filesdone; // Files tested (updated or not) int fileerrors; // Failed files (e.g.: missing input handler). int dbtotdocs; // Doc count in index at start // Total files in index.This is actually difficult to compute from // the index so it's preserved from last indexing int totfiles; // Is this indexer a monitoring one? This is a permanent value // telling if option -m was set, not about what we are currently // doing bool hasmonitor{false}; void reset() { phase = DBIXS_FILES; fn.erase(); docsdone = filesdone = fileerrors = dbtotdocs = totfiles = 0; } DbIxStatus() {reset();} bool operator==(const DbIxStatus& other) { return phase == other.phase && fn == other.fn && docsdone == other.docsdone && filesdone == other.filesdone && fileerrors == other.fileerrors && dbtotdocs == other.dbtotdocs && totfiles == other.totfiles; } bool operator!=(const DbIxStatus& other) { return !operator==(other); } }; class RclConfig; extern void readIdxStatus(RclConfig *config, DbIxStatus &status); /** Callback to say what we're doing. If the update func returns false, we * stop as soon as possible without corrupting state */ class DbIxStatusUpdater { public: DbIxStatusUpdater(const RclConfig *config, bool nox11monitor); virtual ~DbIxStatusUpdater(){} DbIxStatusUpdater(const DbIxStatusUpdater&) = delete; DbIxStatusUpdater& operator=(const DbIxStatusUpdater&) = delete; enum Incr {IncrNone, IncrDocsDone = 0x1, IncrFilesDone = 0x2, IncrFileErrors = 0x4}; // Change phase/fn and update virtual bool update(DbIxStatus::Phase phase, const std::string& fn, int incr = IncrNone); void setMonitor(bool onoff); void setDbTotDocs(int totdocs); class Internal; private: Internal *m; }; // We use the updater as a singleton everywhere. It is instanciated in // idxstatus.cpp. Must be called once with non-null config at first. extern DbIxStatusUpdater *statusUpdater(RclConfig *config=nullptr, bool nox11monitor=false); #endif /* _IDXSTATUS_H_INCLUDED_ */ ����������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/recollindex.cpp�����������������������������������������������������������������0000644�0001750�0001750�00000076523�14466635222�014155� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2004-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <stdio.h> #include <signal.h> #include <errno.h> #include <fnmatch.h> #ifndef _WIN32 #include <sys/time.h> #include <sys/resource.h> #else #include <direct.h> #endif #include "safefcntl.h" #include "safeunistd.h" #include <getopt.h> #include <iostream> #include <list> #include <string> #include <cstdlib> using namespace std; #include "log.h" #include "rclinit.h" #include "indexer.h" #include "smallut.h" #include "chrono.h" #include "pathut.h" #include "rclutil.h" #include "rclmon.h" #include "x11mon.h" #include "cancelcheck.h" #include "checkindexed.h" #include "rcldb.h" #include "readfile.h" #ifndef DISABLE_WEB_INDEXER #include "webqueue.h" #endif #include "recollindex.h" #include "fsindexer.h" #ifndef _WIN32 #include "rclionice.h" #endif #include "execmd.h" #include "checkretryfailed.h" #include "circache.h" #include "idxdiags.h" #include "conftree.h" // Command line options static int op_flags; #define OPT_C 0x1 #define OPT_c 0x2 #define OPT_d 0x4 #define OPT_D 0x8 #define OPT_E 0x10 #define OPT_e 0x20 #define OPT_f 0x40 #define OPT_h 0x80 #define OPT_i 0x200 #define OPT_K 0x400 #define OPT_k 0x800 #define OPT_l 0x1000 #define OPT_m 0x2000 #define OPT_n 0x4000 #define OPT_P 0x8000 #define OPT_p 0x10000 #define OPT_R 0x20000 #define OPT_r 0x40000 #define OPT_S 0x80000 #define OPT_s 0x100000 #define OPT_w 0x200000 #define OPT_x 0x400000 #define OPT_Z 0x800000 #define OPT_z 0x1000000 #define OPTVAL_WEBCACHE_COMPACT 1000 #define OPTVAL_WEBCACHE_BURST 1001 #define OPTVAL_DIAGS_NOTINDEXED 1002 #define OPTVAL_DIAGS_DIAGSFILE 1003 #define OPTVAL_NOPURGE 1004 static struct option long_options[] = { {"webcache-compact", 0, nullptr, OPTVAL_WEBCACHE_COMPACT}, {"webcache-burst", required_argument, nullptr, OPTVAL_WEBCACHE_BURST}, {"notindexed", 0, nullptr, OPTVAL_DIAGS_NOTINDEXED}, {"diagsfile", required_argument, nullptr, OPTVAL_DIAGS_DIAGSFILE}, {"nopurge", 0, nullptr, OPTVAL_NOPURGE}, {nullptr, 0, nullptr, 0} }; ReExec *o_reexec; // Globals for atexit cleanup static ConfIndexer *confindexer; // This is set as an atexit routine, static void cleanup() { deleteZ(confindexer); IdxDiags::theDiags().flush(); recoll_exitready(); } // This holds the state of topdirs (exist+nonempty) on indexing // startup. If it changes after a resume from sleep we interrupt the // indexing (the assumption being that a volume has been mounted or // unmounted while we slept). This is not foolproof as the user can // always pull out a removable volume while we work. It just avoids a // harmful purge in a common case. static vector<string> o_topdirs; static vector<bool> o_topdirs_emptiness; bool topdirs_state(vector<bool> tdlstate) { tdlstate.clear(); for (const auto& dir : o_topdirs) { tdlstate.push_back(path_empty(dir)); } return true; } static void sigcleanup(int sig) { if (sig == RCLSIG_RESUME) { vector<bool> emptiness; topdirs_state(emptiness); if (emptiness != o_topdirs_emptiness) { string msg = "Recollindex: resume: topdirs state changed while " "we were sleeping\n"; cerr << msg; LOGDEB(msg); CancelCheck::instance().setCancel(); stopindexing = 1; } } else { cerr << "Recollindex: got signal " << sig << ", registering stop request\n"; LOGDEB("Got signal " << sig << ", registering stop request\n"); CancelCheck::instance().setCancel(); stopindexing = 1; } } static void makeIndexerOrExit(RclConfig *config) { if (!confindexer) { confindexer = new ConfIndexer(config); } if (!confindexer) { std::cerr << "Cannot create indexer" << "\n"; exit(1); } } // Adjust IO priority (if available), and also Linux Out-Of-Memory killer badness (idem) void rclIxIonice(const RclConfig *config) { PRETEND_USE(config); #ifndef _WIN32 string clss, classdata; if (!config->getConfParam("monioniceclass", clss) || clss.empty()) clss = "3"; // Classdata may be empty (must be for idle class) config->getConfParam("monioniceclassdata", classdata); rclionice(clss, classdata); std::string choompath; if (ExecCmd::which("choom", choompath) && !choompath.empty()) { std::string oomadj = "300"; config->getConfParam("oomadj", oomadj); std::string spid = lltodecstr(getpid()); ExecCmd cmd; std::string msg; cmd.doexec(choompath, {"-n", oomadj, "-p", spid}, nullptr, &msg); LOGDEB("rclIxIonice: oomadj output: " << msg); } #endif } static void setMyPriority(const RclConfig *config) { PRETEND_USE(config); #ifndef _WIN32 int prio{19}; std::string sprio; config->getConfParam("idxniceprio", sprio); if (!sprio.empty()) { prio = atoi(sprio.c_str()); } if (setpriority(PRIO_PROCESS, 0, prio) != 0) { LOGINFO("recollindex: can't setpriority(), errno " << errno << "\n"); } // Try to ionice. This does not work on all platforms rclIxIonice(config); #endif } class MakeListWalkerCB : public FsTreeWalkerCB { public: MakeListWalkerCB(list<string>& files, const vector<string>& selpats) : m_files(files), m_pats(selpats) {} virtual FsTreeWalker::Status processone( const string& fn, FsTreeWalker::CbFlag flg, const struct PathStat&) override { if (flg== FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwRegular){ if (m_pats.empty()) { m_files.push_back(fn); } else { for (const auto& pat : m_pats) { if (fnmatch(pat.c_str(), fn.c_str(), 0) == 0) { m_files.push_back(fn); break; } } } } return FsTreeWalker::FtwOk; } list<string>& m_files; const vector<string>& m_pats; }; // Build a list of things to index, then call purgefiles and/or indexfiles. // This is basically the same as find xxx | recollindex -i [-e] without the find (so, simpler but // less powerful) bool recursive_index(RclConfig *config, const string& top, const vector<string>& selpats) { list<string> files; MakeListWalkerCB cb(files, selpats); FsTreeWalker walker; walker.walk(top, cb); bool ret = false; if (op_flags & OPT_e) { if (!(ret = purgefiles(config, files))) { return ret; } } if (!(op_flags & OPT_e) || ((op_flags & OPT_e) && (op_flags & OPT_i))) { ret = indexfiles(config, files); } return ret; } // Index a list of files. We just call the top indexer method, which // will sort out what belongs to the indexed trees and call the // appropriate indexers. // // This is called either from the command line or from the monitor. In // this case we're called repeatedly in the same process, and the // confindexer is only created once by makeIndexerOrExit (but the db closed and // flushed every time) bool indexfiles(RclConfig *config, list<string> &filenames) { if (filenames.empty()) return true; makeIndexerOrExit(config); // The default is to retry failed files int indexerFlags = ConfIndexer::IxFNone; if (op_flags & OPT_K) indexerFlags |= ConfIndexer::IxFNoRetryFailed; if (op_flags & OPT_f) indexerFlags |= ConfIndexer::IxFIgnoreSkip; if (op_flags & OPT_P) { indexerFlags |= ConfIndexer::IxFDoPurge; } if (op_flags & OPT_Z) { indexerFlags |= ConfIndexer::IxFInPlaceReset; } return confindexer->indexFiles(filenames, indexerFlags); } // Delete a list of files. Same comments about call contexts as indexfiles. bool purgefiles(RclConfig *config, list<string> &filenames) { if (filenames.empty()) return true; makeIndexerOrExit(config); return confindexer->purgeFiles(filenames, ConfIndexer::IxFNone); } // Create stemming and spelling databases bool createAuxDbs(RclConfig *config) { makeIndexerOrExit(config); if (!confindexer->createStemmingDatabases()) return false; if (!confindexer->createAspellDict()) return false; return true; } // Create additional stem database static bool createstemdb(RclConfig *config, const string &lang) { makeIndexerOrExit(config); return confindexer->createStemDb(lang); } // Check that topdir entries are valid (successful tilde exp + abs // path) or fail. // In addition, topdirs, skippedPaths, daemSkippedPaths entries should // match existing files or directories. Warn if they don't static bool checktopdirs(RclConfig *config, vector<string>& nonexist) { if (!config->getConfParam("topdirs", &o_topdirs)) { cerr << "No 'topdirs' parameter in configuration\n"; LOGERR("recollindex:No 'topdirs' parameter in configuration\n"); return false; } // If a restricted list for real-time monitoring exists check that // all entries are descendants from a topdir vector<string> mondirs; if (config->getConfParam("monitordirs", &mondirs)) { for (const auto& sub : mondirs) { bool found{false}; for (const auto& top : o_topdirs) { if (path_isdesc(top, sub)) { found = true; break; } } if (!found) { string s("Real time monitoring directory entry " + sub + " is not part of the topdirs tree\n"); cerr << s; LOGERR(s); return false; } } } bool onegood{false}; for (auto& dir : o_topdirs) { dir = path_tildexpand(dir); if (!dir.size() || !path_isabsolute(dir)) { if (dir[0] == '~') { cerr << "Tilde expansion failed: " << dir << endl; LOGERR("recollindex: tilde expansion failed: " << dir << "\n"); } else { cerr << "Not an absolute path: " << dir << endl; LOGERR("recollindex: not an absolute path: " << dir << "\n"); } return false; } if (!path_exists(dir)) { nonexist.push_back(dir); } else { onegood = true; } } topdirs_state(o_topdirs_emptiness); // We'd like to check skippedPaths too, but these are wildcard // exprs, so reasonably can't return onegood; } string thisprog; static const char usage [] = "\n" "recollindex [-h] \n" " Print help\n" "recollindex [-z|-Z] [-k] [-P]\n" " Index everything according to configuration file\n" " -z : reset database before starting indexing\n" " -Z : in place reset: consider all documents as changed. Can also\n" " be combined with -i or -r but not -m\n" " -k : retry files on which we previously failed\n" " -P : useful if the index is configured for not purging by default: do purge\n" " --diagsfile <outputpath> : list skipped or otherwise not indexed documents to <outputpath>\n" " <outputpath> will be truncated\n" #ifdef RCL_MONITOR "recollindex -m [-w <secs>] -x [-D] [-C]\n" " Perform real time indexing. Don't become a daemon if -D is set.\n" " -w sets number of seconds to wait before starting.\n" " -C disables monitoring config for changes/reexecuting.\n" " -n disables initial incremental indexing (!and purge!).\n" #ifndef DISABLE_X11MON " -x disables exit on end of x11 session\n" #endif /* DISABLE_X11MON */ #endif /* RCL_MONITOR */ "recollindex -e [<filepath [path ...]>]\n" " Purge data for individual files. No stem database updates.\n" " Reads paths on stdin if none is given as argument.\n" "recollindex -i [-f] [-Z] [-P] [<filepath [path ...]>]\n" " Index individual files. No database purge or stem database updates\n" " Will read paths on stdin if none is given as argument\n" " -f : ignore skippedPaths and skippedNames while doing this\n" " -Z : force reindex of each file\n" " -P : force running a purge pass (very special use, don't do this if not sure)\n" "recollindex -r [-K] [-f] [-Z] [-p pattern] <top> \n" " Recursive partial reindex. \n" " -p : filter file names, multiple instances are allowed, e.g.: \n" " -p *.odt -p *.pdf\n" " -K : skip previously failed files (they are retried by default)\n" "recollindex -l\n" " List available stemming languages\n" "recollindex -s <lang>\n" " Build stem database for additional language <lang>\n" "recollindex -E\n" " Check configuration file for topdirs and other paths existence\n" "recollindex --webcache-compact : recover wasted space from the Web cache\n" "recollindex --webcache-burst <targetdir> : extract entries from the Web cache to the target\n" "recollindex --notindexed [filepath [filepath ...]] : check if the file arguments are indexed\n" " will read file paths from stdin if there are no arguments\n" #ifdef FUTURE_IMPROVEMENT "recollindex -W\n" " Process the Web queue\n" #endif #ifdef RCL_USE_ASPELL "recollindex -S\n" " Build aspell spelling dictionary.>\n" #endif "Common options:\n" " -c <configdir> : specify config directory, overriding $RECOLL_CONFDIR\n" #if defined(HAVE_POSIX_FADVISE) " -d : call fadvise() with the POSIX_FADV_DONTNEED flag on indexed files\n" " (avoids trashing the page cache)\n"; #endif ; static void Usage() { FILE *fp = (op_flags & OPT_h) ? stdout : stderr; fprintf(fp, "%s: Usage: %s", path_getsimple(thisprog).c_str(), usage); fprintf(fp, "Recoll version: %s\n", Rcl::version_string().c_str()); exit((op_flags & OPT_h)==0); } static RclConfig *config; static void lockorexit(Pidfile *pidfile, RclConfig *config) { PRETEND_USE(config); pid_t pid; if ((pid = pidfile->open()) != 0) { if (pid > 0) { cerr << "Can't become exclusive indexer: " << pidfile->getreason() << ". Return (other pid?): " << pid << endl; // Have a look at the status file. If the other process is // a monitor we can tell it to start an incremental pass // by touching the configuration file DbIxStatus status; readIdxStatus(config, status); if (status.hasmonitor) { string path = path_cat(config->getConfDir(), "recoll.conf"); if (!path_utimes(path, nullptr)) { cerr << "Could not notify indexer" << endl; } else { cerr << "Monitoring indexer process was notified of indexing request" << endl; } } } else { cerr << "Can't become exclusive indexer: " << pidfile->getreason() << endl; } exit(1); } if (pidfile->write_pid() != 0) { cerr << "Can't become exclusive indexer: " << pidfile->getreason() << endl; exit(1); } } static string reasonsfile; extern ConfSimple idxreasons; static void flushIdxReasons() { if (reasonsfile.empty()) return; if (reasonsfile == "stdout") { idxreasons.write(cout); } else if (reasonsfile == "stderr") { idxreasons.write(std::cerr); } else { ofstream out; try { out.open(reasonsfile, ofstream::out|ofstream::trunc); idxreasons.write(out); } catch (...) { std::cerr << "Could not write reasons file " << reasonsfile << endl; idxreasons.write(std::cerr); } } } // With more recent versions of mingw, we could use -municode to // enable wmain. Another workaround is to use main, then call // GetCommandLineW and CommandLineToArgvW, to then call wmain(). If // ever we need to build with mingw again. #if defined(_WIN32) && defined(_MSC_VER) #define USE_WMAIN 1 #endif #if USE_WMAIN #define WARGTOSTRING(w) wchartoutf8(w) static vector<const char*> argstovector(int argc, wchar_t **argv, vector<string>& storage) #else #define WARGTOSTRING(w) (w) static vector<const char*> argstovector(int argc, char **argv, vector<string>& storage) #endif { vector<const char *> args(argc+1); storage.resize(argc); thisprog = path_absolute(WARGTOSTRING(argv[0])); for (int i = 0; i < argc; i++) { storage[i] = WARGTOSTRING(argv[i]); args[i] = storage[i].c_str(); } args[argc] = nullptr; return args; } // Working directory before we change: it's simpler to change early // but some options need the original for computing absolute paths. static std::string orig_cwd; // A bit of history: it's difficult to pass non-ASCII parameters // (e.g. path names) on the command line under Windows without using // Unicode. It was first thought possible to use a temporary file to // hold the args, and make sure that the path for this would be ASCII, // based on using shortpath(). Unfortunately, this does not work in // all cases, so the second change was to use wmain(). The // args-in-file was removed quite a long time after. #if USE_WMAIN int wmain(int argc, wchar_t *argv[]) #else int main(int argc, char *argv[]) #endif { // Only actually useful on Windows: convert wargs to utf-8 chars vector<string> astore; vector<const char*> args = argstovector(argc, argv, astore); // The reexec struct is used by the daemon to shed memory after // the initial indexing pass and to restart when the configuration // changes o_reexec = new ReExec(astore); vector<string> selpatterns; int sleepsecs{60}; string a_config; int ret; bool webcache_compact{false}; bool webcache_burst{false}; bool diags_notindexed{false}; bool opt_nopurge{false}; std::string burstdir; std::string diagsfile; while ((ret = getopt_long(argc, (char *const*)&args[0], "c:CDdEefhikKlmnPp:rR:sSw:xZz", long_options, NULL)) != -1) { switch (ret) { case 'c': op_flags |= OPT_c; a_config = optarg; break; #ifdef RCL_MONITOR case 'C': op_flags |= OPT_C; break; case 'D': op_flags |= OPT_D; break; #endif #if defined(HAVE_POSIX_FADVISE) case 'd': op_flags |= OPT_d; break; #endif case 'E': op_flags |= OPT_E; break; case 'e': op_flags |= OPT_e; break; case 'f': op_flags |= OPT_f; break; case 'h': op_flags |= OPT_h; break; case 'i': op_flags |= OPT_i; break; case 'k': op_flags |= OPT_k; break; case 'K': op_flags |= OPT_K; break; case 'l': op_flags |= OPT_l; break; case 'm': op_flags |= OPT_m; break; case 'n': op_flags |= OPT_n; break; case 'P': op_flags |= OPT_P; break; case 'p': op_flags |= OPT_p; selpatterns.push_back(optarg); break; case 'r': op_flags |= OPT_r; break; case 'R': op_flags |= OPT_R; reasonsfile = optarg; break; case 's': op_flags |= OPT_s; break; #ifdef RCL_USE_ASPELL case 'S': op_flags |= OPT_S; break; #endif case 'w': op_flags |= OPT_w; if ((sscanf(optarg, "%d", &sleepsecs)) != 1) Usage(); break; case 'x': op_flags |= OPT_x; break; case 'Z': op_flags |= OPT_Z; break; case 'z': op_flags |= OPT_z; break; case OPTVAL_WEBCACHE_COMPACT: webcache_compact = true; break; case OPTVAL_WEBCACHE_BURST: burstdir = optarg; webcache_burst = true;break; case OPTVAL_DIAGS_NOTINDEXED: diags_notindexed = true;break; case OPTVAL_DIAGS_DIAGSFILE: diagsfile = optarg;break; case OPTVAL_NOPURGE: opt_nopurge = true;break; default: Usage(); break; } } int aremain = argc - optind; if (op_flags & OPT_h) Usage(); #ifndef RCL_MONITOR if (op_flags & (OPT_m | OPT_w|OPT_x)) { std::cerr << "-m not available: real-time monitoring was not configured in this build\n"; exit(1); } #endif if ((op_flags & OPT_z) && (op_flags & (OPT_i|OPT_e|OPT_r))) Usage(); if ((op_flags & OPT_Z) && (op_flags & (OPT_m))) Usage(); if ((op_flags & OPT_E) && (op_flags & ~(OPT_E|OPT_c))) { Usage(); } string reason; int flags = RCLINIT_IDX; if ((op_flags & OPT_m) && !(op_flags & OPT_D)) { flags |= RCLINIT_DAEMON; } config = recollinit(flags, cleanup, sigcleanup, reason, &a_config); if (nullptr == config || !config->ok()) { addIdxReason("init", reason); flushIdxReasons(); std::cerr << "Configuration problem: " << reason << "\n"; exit(1); } // Auxiliary, non-index-related things. Avoids having a separate binary. if (webcache_compact || webcache_burst || diags_notindexed) { std::string ccdir = config->getWebcacheDir(); std::string reason; if (webcache_compact) { if (!CirCache::compact(ccdir, &reason)) { std::cerr << "Web cache compact failed: " << reason << "\n"; exit(1); } } else if (webcache_burst) { if (!CirCache::burst(ccdir, burstdir, &reason)) { std::cerr << "Web cache burst failed: " << reason << "\n"; exit(1); } } else if (diags_notindexed) { std::vector<std::string> filepaths; while (aremain--) { filepaths.push_back(args[optind++]); } if (!checkindexed(config, filepaths)) { exit(1); } } exit(0); } o_reexec->atexit(cleanup); vector<string> nonexist; if (!checktopdirs(config, nonexist)) { std::cerr << "topdirs not set or only contains invalid paths.\n"; addIdxReason("init", "topdirs not set or only contains invalid paths."); flushIdxReasons(); exit(1); } if (nonexist.size()) { ostream& out = (op_flags & OPT_E) ? std::cout : std::cerr; if (!(op_flags & OPT_E)) { std::cerr << "Warning: invalid paths in topdirs, skippedPaths or daemSkippedPaths:\n"; } for (const auto& entry : nonexist) { out << entry << endl; } } if ((op_flags & OPT_E)) { exit(0); } if (op_flags & OPT_l) { if (aremain != 0) Usage(); vector<string> stemmers = ConfIndexer::getStemmerNames(); for (const auto& stemmer : stemmers) { std::cout << stemmer << "\n"; } exit(0); } orig_cwd = path_cwd(); string rundir; config->getConfParam("idxrundir", rundir); if (!rundir.empty()) { if (!rundir.compare("tmp")) { rundir = tmplocation(); } LOGINFO("recollindex: changing current directory to [" << rundir <<"]\n"); if (!path_chdir(rundir)) { LOGSYSERR("main", "chdir", rundir); } } if (!diagsfile.empty()) { if (!IdxDiags::theDiags().init(diagsfile)) { std::cerr << "Could not initialize diags file " << diagsfile << "\n"; LOGERR("recollindex: Could not initialize diags file " << diagsfile << "\n"); } } bool rezero((op_flags & OPT_z) != 0); // The default is not to retry previously failed files by default. // If -k is set, we do. // If the checker script says so, we do too, except if -K is set. int indexerFlags = ConfIndexer::IxFNoRetryFailed | ConfIndexer::IxFDoPurge; if (op_flags & OPT_k) { indexerFlags &= ~ConfIndexer::IxFNoRetryFailed; } else { if (op_flags & OPT_K) { indexerFlags |= ConfIndexer::IxFNoRetryFailed; } else { if (checkRetryFailed(config, false)) { indexerFlags &= ~ConfIndexer::IxFNoRetryFailed; } else { indexerFlags |= ConfIndexer::IxFNoRetryFailed; } } } if (indexerFlags & ConfIndexer::IxFNoRetryFailed) { LOGDEB("recollindex: files in error will not be retried\n"); } else { LOGDEB("recollindex: files in error will be retried\n"); } if (op_flags & OPT_Z) { indexerFlags |= ConfIndexer::IxFInPlaceReset; } bool noautopurge{false}; config->getConfParam("idxnoautopurge", &noautopurge); // If the index is configured for not purging by default and the purge force option is not set, // or if the "no purge" command line option is set, unset the purge flag (set by default above). if ((noautopurge && !(op_flags & OPT_P)) || opt_nopurge) { indexerFlags &= ~ConfIndexer::IxFDoPurge; } LOGDEB("recollindex: the purge flag is " << ((indexerFlags & ConfIndexer::IxFDoPurge)?"SET":"NOT SET") << "\n"); #if defined(HAVE_POSIX_FADVISE) if (op_flags & OPT_d) { indexerFlags |= ConfIndexer::IxFCleanCache; } #endif Pidfile pidfile(config->getPidfile()); lockorexit(&pidfile, config); // Log something at LOGINFO to reset the trace file. Else at level // 3 it's not even truncated if all docs are up to date. { time_t tt = time(nullptr); LOGINFO("recollindex: starting up. Cmdline: [" << stringsToString(astore) << "] now: " << ctime(&tt)); } setMyPriority(config); // Init status updater if (nullptr == statusUpdater(config, op_flags & OPT_x)) { std::cerr << "Could not initialize status updater\n"; LOGERR("Could not initialize status updater\n"); exit(1); } statusUpdater()->update(DbIxStatus::DBIXS_NONE, ""); if (op_flags & OPT_r) { if (aremain != 1) Usage(); string top = args[optind++]; aremain--; top = path_canon(top, &orig_cwd); bool status = recursive_index(config, top, selpatterns); if (confindexer && !confindexer->getReason().empty()) { addIdxReason("indexer", confindexer->getReason()); cerr << confindexer->getReason() << endl; } flushIdxReasons(); exit(status ? 0 : 1); } else if (op_flags & (OPT_i|OPT_e)) { list<string> filenames; if (aremain == 0) { // Read from stdin char line[1024]; while (fgets(line, 1023, stdin)) { string sl(line); trimstring(sl, "\n\r"); filenames.push_back(sl); } } else { while (aremain--) { filenames.push_back(args[optind++]); } } // Note that -e and -i may be both set. In this case we first erase, // then index. This is a slightly different from -Z -i because we // warranty that all subdocs are purged. bool status = true; if (op_flags & OPT_e) { status = purgefiles(config, filenames); } if (status && (op_flags & OPT_i)) { status = indexfiles(config, filenames); } if (confindexer && !confindexer->getReason().empty()) { addIdxReason("indexer", confindexer->getReason()); cerr << confindexer->getReason() << endl; } flushIdxReasons(); exit(status ? 0 : 1); } else if (op_flags & OPT_s) { if (aremain != 1) Usage(); string lang = args[optind++]; aremain--; exit(!createstemdb(config, lang)); #ifdef RCL_USE_ASPELL } else if (op_flags & OPT_S) { makeIndexerOrExit(config); exit(!confindexer->createAspellDict()); #endif // ASPELL #ifdef RCL_MONITOR } else if (op_flags & OPT_m) { if (aremain != 0) Usage(); statusUpdater()->setMonitor(true); if (!(op_flags&OPT_D)) { #ifndef _WIN32 LOGDEB("recollindex: daemonizing\n"); if (daemon(0,0) != 0) { addIdxReason("monitor", "daemon() failed"); cerr << "daemon() failed, errno " << errno << endl; LOGERR("daemon() failed, errno " << errno << "\n"); flushIdxReasons(); exit(1); } #endif } // Need to rewrite pid, it changed pidfile.write_pid(); // Not too sure if I have to redo the nice thing after daemon(), // can't hurt anyway (easier than testing on all platforms...) setMyPriority(config); if (sleepsecs > 0) { LOGDEB("recollindex: sleeping " << sleepsecs << "\n"); for (int i = 0; i < sleepsecs; i++) { sleep(1); #ifndef _WIN32 // Check that x11 did not go away while we were sleeping. if (!(op_flags & OPT_x) && !x11IsAlive()) { LOGDEB("X11 session went away during initial sleep period\n"); exit(0); } #endif } } if (!(op_flags & OPT_n)) { makeIndexerOrExit(config); LOGDEB("Recollindex: initial indexing pass before monitoring\n"); if (!confindexer->index(rezero, ConfIndexer::IxTAll, indexerFlags) || stopindexing) { LOGERR("recollindex, initial indexing pass failed, " "not going into monitor mode\n"); flushIdxReasons(); exit(1); } else { // Record success of indexing pass with failed files retries. if (!(indexerFlags & ConfIndexer::IxFNoRetryFailed)) { checkRetryFailed(config, true); } } deleteZ(confindexer); o_reexec->insertArgs(vector<string>(1, "-n")); LOGINFO("recollindex: reexecuting with -n after initial full pass\n"); // Note that -n will be inside the reexec when we come // back, but the monitor will explicitly strip it before // starting a config change exec to ensure that we do a // purging pass in this latter case (full restart). o_reexec->reexec(); } statusUpdater()->update(DbIxStatus::DBIXS_MONITOR, ""); int opts = RCLMON_NONE; if (op_flags & OPT_D) opts |= RCLMON_NOFORK; if (op_flags & OPT_C) opts |= RCLMON_NOCONFCHECK; if (op_flags & OPT_x) opts |= RCLMON_NOX11; bool monret = startMonitor(config, opts); MONDEB(("Monitor returned %d, exiting\n", monret)); exit(monret == false); #endif // MONITOR } makeIndexerOrExit(config); bool status = confindexer->index(rezero, ConfIndexer::IxTAll, indexerFlags); // Record success of indexing pass with failed files retries. if (status && !(indexerFlags & ConfIndexer::IxFNoRetryFailed)) { checkRetryFailed(config, true); } if (!status) cerr << "Indexing failed" << endl; if (!confindexer->getReason().empty()) { addIdxReason("indexer", confindexer->getReason()); cerr << confindexer->getReason() << endl; } statusUpdater()->update(DbIxStatus::DBIXS_DONE, ""); flushIdxReasons(); { time_t tt = time(nullptr); LOGINFO("recollindex: exiting: " << ctime(&tt)); } return !status; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/subtreelist.cpp�����������������������������������������������������������������0000644�0001750�0001750�00000004044�14427373216�014176� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2007 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "subtreelist.h" #include <memory> #include <string> #include <vector> #include "cstr.h" #include "rcldb.h" #include "searchdata.h" #include "rclquery.h" #include "log.h" #include "rcldoc.h" #include "rclconfig.h" bool subtreelist(RclConfig *config, const std::string& _top, std::vector<std::string>& paths) { std::string top(_top); #ifdef _WIN32 // Need to convert c:path to /c/path because this is how paths are indexed top = path_slashdrive(top); #endif LOGDEB("subtreelist: top: [" << top << "]\n"); Rcl::Db rcldb(config); if (!rcldb.open(Rcl::Db::DbRO)) { LOGERR("subtreelist: can't open index in [" << config->getDbDir() << "]: " << rcldb.getReason() << "\n"); return false; } Rcl::SearchData *sd = new Rcl::SearchData(Rcl::SCLT_OR, cstr_null); std::shared_ptr<Rcl::SearchData> rq(sd); sd->addClause(new Rcl::SearchDataClausePath(top, false)); Rcl::Query query(&rcldb); query.setQuery(rq); int cnt = query.getResCnt(); for (int i = 0; i < cnt; i++) { Rcl::Doc doc; if (!query.getDoc(i, doc)) break; std::string path = fileurltolocalpath(doc.url); if (!path.empty()) paths.push_back(path); } return true; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/recollindex@.service������������������������������������������������������������0000644�0001750�0001750�00000000555�14410615043�015107� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Instantiate with a username as argument # e.g. `systemctl enable recollindex@myuser.service` [Unit] Description=Recollindex indexing for %i After=network-online.target RequiresMountsFor=/home/%i [Service] Type=simple Restart=on-failure RestartSec=30 ExecStart=/usr/bin/recollindex -m -D -x -w 10 -c /home/%i/.recoll User=%i [Install] WantedBy=multi-user.target ���������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/indexer.h�����������������������������������������������������������������������0000644�0001750�0001750�00000007270�14477552754�012753� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2004-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _INDEXER_H_INCLUDED_ #define _INDEXER_H_INCLUDED_ #include <string> #include <list> #include <vector> #include "rclconfig.h" #include "rcldb.h" #include "rcldoc.h" #include "idxstatus.h" class FsIndexer; class WebQueueIndexer; /** * The top level batch indexing object. Processes the configuration, * then invokes file system walking or other to populate/update the * database(s). */ class ConfIndexer { public: enum runStatus {IndexerOk, IndexerError}; ConfIndexer(RclConfig *cnf); virtual ~ConfIndexer(); ConfIndexer(const ConfIndexer&) = delete; ConfIndexer& operator=(const ConfIndexer&) = delete; // Indexer types. Maybe we'll have something more dynamic one day enum ixType {IxTNone, IxTFs=1, IxTWebQueue=2, IxTAll = IxTFs | IxTWebQueue}; // Misc indexing flags enum IxFlag {IxFNone = 0, IxFIgnoreSkip = 1, // Ignore skipped lists IxFNoWeb = 2, // Do not process the web queue. // First pass: just do the top files so that the user can // try searching asap. IxFQuickShallow = 4, // Do not retry files which previously failed ('+' sigs) IxFNoRetryFailed = 8, // Perform the purge pass (normally on). IxFDoPurge = 16, // Evict each indexed file from the page cache. IxFCleanCache = 32, // In place reset: pretend all documents need updating: allows updating all while // keeping a usable index. IxFInPlaceReset = 64, }; /** Run indexers */ bool index(bool resetbefore, ixType typestorun, int f = IxFNone); const std::string &getReason() {return m_reason;} /** Stemming reset to config: create needed, delete unconfigured */ bool createStemmingDatabases(); /** Create stem database for given language */ bool createStemDb(const std::string &lang); /** Create misspelling expansion dictionary if aspell i/f is available */ bool createAspellDict(); /** List possible stemmer names */ static std::vector<std::string> getStemmerNames(); /** Index a list of files. No db cleaning or stemdb updating */ bool indexFiles(std::list<std::string> &files, int f = IxFNone); /** Purge a list of files. */ bool purgeFiles(std::list<std::string> &files, int f = IxFNone); private: RclConfig *m_config; Rcl::Db m_db; FsIndexer *m_fsindexer{nullptr}; bool m_doweb{false}; WebQueueIndexer *m_webindexer{nullptr}; std::string m_reason; // The first time we index, we do things a bit differently to // avoid user frustration (make at least some results available // fast by using several passes, the first ones to index common // interesting locations). bool runFirstIndexing(); bool firstFsIndexingSequence(); }; #endif /* _INDEXER_H_INCLUDED_ */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/fsindexer.h���������������������������������������������������������������������0000644�0001750�0001750�00000012465�14427373216�013273� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2009-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _fsindexer_h_included_ #define _fsindexer_h_included_ #include <list> #include <mutex> #include "indexer.h" #include "fstreewalk.h" #ifdef IDX_THREADS #include "workqueue.h" #endif // IDX_THREADS class FIMissingStore; class DbUpdTask; class InternfileTask; namespace Rcl { class Doc; } /** Index selected parts of the file system Tree indexing: we inherits FsTreeWalkerCB so that, the processone() method is called by the file-system tree walk code for each file and directory. We keep all state needed while indexing, and finally call the methods to purge the db of stale entries and create the stemming databases. Single file(s) indexing: there are also calls to index or purge lists of files. No database purging or stem db updating in this case. */ class FsIndexer : public FsTreeWalkerCB { public: /** Constructor does nothing but store parameters * * @param cnf Configuration data */ FsIndexer(RclConfig *cnf, Rcl::Db *db); virtual ~FsIndexer(); FsIndexer(const FsIndexer&) = delete; FsIndexer& operator=(const FsIndexer&) = delete; /** * Top level file system tree index method for updating a given database. * * We open the database, * then call a file system walk for each top-level directory. */ bool index(int flags); /** Index a list of files. No db cleaning or stemdb updating */ bool indexFiles(std::list<std::string> &files, int f = ConfIndexer::IxFNone); /** Purge a list of files. */ bool purgeFiles(std::list<std::string> &files); /** Tree walker callback method */ FsTreeWalker::Status processone(const std::string &fn, FsTreeWalker::CbFlag, const struct PathStat& st) override; private: class PurgeCandidateRecorder { public: PurgeCandidateRecorder() : dorecord(false) {} void setRecord(bool onoff) { dorecord = onoff; } void record(const std::string& udi) { // This test does not need to be protected: the value is set at // init and never changed. if (!dorecord) return; #ifdef IDX_THREADS std::unique_lock<std::mutex> locker(mutex); #endif udis.push_back(udi); } const std::vector<std::string>& getCandidates() { return udis; } private: #ifdef IDX_THREADS std::mutex mutex; #endif bool dorecord; std::vector<std::string> udis; }; bool launchAddOrUpdate(const std::string& udi, const std::string& parent_udi, Rcl::Doc& doc); FsTreeWalker m_walker; RclConfig *m_config; Rcl::Db *m_db; std::string m_reason; // Top/start directories list std::vector<std::string> m_tdl; // Store for missing filters and associated mime types FIMissingStore *m_missing; // Recorder for files that may need subdoc purging. PurgeCandidateRecorder m_purgeCandidates; // The configuration can set attribute fields to be inherited by // all files in a file system area. Ie: set "rclaptg = thunderbird" // inside ~/.thunderbird. The boolean is set at init to avoid // further wasteful processing if no local fields are set. // This should probably moved to internfile so that the // localfields get exactly the same processing as those generated by the // filters (as was done for metadatacmds fields) bool m_havelocalfields; std::string m_slocalfields; std::map<std::string, std::string> m_localfields; // Activate detection of xattr-only document updates. Experimental, so // needs a config option bool m_detectxattronly; // No retry of previously failed files bool m_noretryfailed; // use FADV_DONTNEED if available bool m_cleancache{false}; #ifdef IDX_THREADS friend void *FsIndexerDbUpdWorker(void*); friend void *FsIndexerInternfileWorker(void*); WorkQueue<InternfileTask*> m_iwqueue; WorkQueue<DbUpdTask*> m_dwqueue; bool m_haveInternQ; bool m_haveSplitQ; RclConfig *m_stableconfig; #endif // IDX_THREADS bool init(); void localfieldsfromconf(); void setlocalfields(const std::map<std::string, std::string>& flds, Rcl::Doc& doc); std::string getDbDir() {return m_config->getDbDir();} FsTreeWalker::Status processonefile(RclConfig *config, const std::string &fn, const struct PathStat &, const std::map<std::string, std::string>& localfields); void shutdownQueues(bool); }; #endif /* _fsindexer_h_included_ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/mimetype.cpp��������������������������������������������������������������������0000644�0001750�0001750�00000016655�14427373216�013475� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2004-2023 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <ctype.h> #include <string> #include <list> #ifdef ENABLE_LIBMAGIC #include <magic.h> #endif #include "mimetype.h" #include "log.h" #include "execmd.h" #include "rclconfig.h" #include "smallut.h" #include "idfile.h" #include "pxattr.h" #include "rclutil.h" using namespace std; /// Identification of file from contents. This is called for files with /// unrecognized extensions. /// /// The system 'file' utility does not always work for us. For example /// it will mistake mail folders for simple text files if there is no /// 'Received' header, which would be the case, for example in a /// 'Sent' folder. Also "file -i" does not exist on all systems, and /// is quite costly to execute. /// So we first call the internal file identifier, which currently /// only knows about mail, but in which we can add the more /// current/interesting file types. /// As a last resort we execute 'file' or its configured replacement /// (except if forbidden by config) static string mimetypefromdata(RclConfig *cfg, const string &fn, bool usfc) { LOGDEB1("mimetypefromdata: fn [" << fn << "]\n"); PRETEND_USE(cfg); // First try the internal identifying routine string mime = idFile(fn.c_str()); if (!mime.empty()) { return mime; } if (!usfc) { LOGDEB1("mimetypefromdata: usfc unset: not using libmagic or MIME guess command\n"); return string(); } // Last resort: use "file -i", or its configured replacement, or libmagic (the latter on // Windows mostly at the moment) #ifdef ENABLE_LIBMAGIC // Caching the open mgtoken would slightly improve performance but we'd need locking because // libmagic is not thread-safe auto mgtoken = magic_open(MAGIC_MIME_TYPE); if (mgtoken) { #ifdef _WIN32 string magicfile = path_cat(path_pkgdatadir(), "magic.mgc"); int ret = magic_load(mgtoken, magicfile.c_str()); #else int ret = magic_load(mgtoken, nullptr); #endif if (ret == 0) { mime = magic_file(mgtoken, fn.c_str()); magic_close(mgtoken); } } #else /* Not using libmagic, use command -> */ // 'file' fallback if the configured command (default: xdg-mime) is not found static const vector<string> tradfilecmd = {{FILE_PROG}, #ifdef __APPLE__ // On the Mac, /usr/bin/file wants a -I to print the MIME type, but we may be using // a MacPorts or Homebrew version, needing -i. Fortunately both accept --mime-type {"--mime-type"} #else {"-i"} #endif }; vector<string> cmd; string scommand; if (cfg->getConfParam("systemfilecommand", scommand)) { LOGDEB2("mimetype: syscmd from config: " << scommand << "\n"); stringToStrings(scommand, cmd); string exe; if (cmd.empty()) { cmd = tradfilecmd; } else if (!ExecCmd::which(cmd[0], exe)) { cmd = tradfilecmd; } else { cmd[0] = exe; } cmd.push_back(fn); } else { LOGDEB("mimetype:systemfilecommand not found, using " << stringsToString(tradfilecmd) << "\n"); cmd = tradfilecmd; } string result; LOGDEB2("mimetype: executing: [" << stringsToString(cmd) << "]\n"); if (!ExecCmd::backtick(cmd, result)) { LOGERR("mimetypefromdata: exec " << stringsToString(cmd) << " failed\n"); return string(); } trimstring(result, " \t\n\r"); LOGDEB2("mimetype: systemfilecommand output [" << result << "]\n"); // The normal output from "file -i" looks like the following: // thefilename.xxx: text/plain; charset=us-ascii // Sometimes the semi-colon is missing like in: // mimetype.cpp: text/x-c charset=us-ascii // And sometimes we only get the mime type. This apparently happens // when 'file' believes that the file name is binary // xdg-mime only outputs the MIME type. // If there is no colon and there is a slash, this is hopefully the MIME type if (result.find(":") == string::npos && result.find("/") != string::npos) { return result; } // Else the result should begin with the file name. Get rid of it: if (result.find(fn) != 0) { // Garbage "file" output. Maybe the result of a charset conversion attempt? LOGERR("mimetype: can't interpret output from [" << stringsToString(cmd) << "] : [" << result << "]\n"); return string(); } result = result.substr(fn.size()); // Now should look like ": text/plain; charset=us-ascii". mime = growmimearoundslash(mime); #endif // Not libmagic return mime; } // Guess mime type, first from suffix, then from file data. We also have a list of suffixes that we // don't touch at all. string mimetype(const string &fn, RclConfig *cfg, bool usfc, const struct PathStat& stp) { // Use stat data if available to check for non regular files if (stp.pst_type != PathStat::PST_INVALID) { if (stp.pst_type == PathStat::PST_DIR) return "inode/directory"; if (stp.pst_type == PathStat::PST_SYMLINK) return "inode/symlink"; if (stp.pst_type != PathStat::PST_REGULAR) return "inode/x-fsspecial"; // Empty files are just this: avoid further errors with actual filters. if (stp.pst_size == 0) return "inode/x-empty"; } string mtype; if (cfg && cfg->inStopSuffixes(fn)) { LOGDEB("mimetype: fn [" << fn << "] in stopsuffixes\n"); return mtype; } // Extended attribute has priority on everything, as per: // http://freedesktop.org/wiki/CommonExtendedAttributes if (pxattr::get(fn, "mime_type", &mtype)) { LOGDEB0("Mimetype: 'mime_type' xattr : [" << mtype << "]\n"); if (mtype.empty()) { LOGDEB0("Mimetype: getxattr() returned empty mime type !\n"); } else { return mtype; } } if (nullptr == cfg) { LOGERR("Mimetype: null config ??\n"); return mtype; } // Compute file name suffix and search the mimetype map string::size_type dot = fn.find_first_of("."); while (dot != string::npos) { string suff = stringtolower(fn.substr(dot)); mtype = cfg->getMimeTypeFromSuffix(suff); if (!mtype.empty() || dot >= fn.size() - 1) break; dot = fn.find_first_of(".", dot + 1); } // If the type was not determined from the suffix, examine file data. Can only do this if we // have an actual file (as opposed to a pure name). if (mtype.empty() && stp.pst_type != PathStat::PST_INVALID) mtype = mimetypefromdata(cfg, fn, usfc); return mtype; } �����������������������������������������������������������������������������������recoll-1.36.1/index/exefetcher.cpp������������������������������������������������������������������0000644�0001750�0001750�00000010351�14470343414�013744� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2016 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <string> #include <vector> #include <iostream> #include "exefetcher.h" #include "log.h" #include "pathut.h" #include "rclconfig.h" #include "execmd.h" #include "rcldoc.h" #include "conftree.h" using namespace std; class EXEDocFetcher::Internal { public: string bckid; vector<string> sfetch; vector<string> smkid; bool docmd(RclConfig *config, const vector<string>& cmd, const Rcl::Doc& idoc, string& out) { ExecCmd ecmd; // We're always called for preview (or Open) ecmd.putenv("RECOLL_FILTER_FORPREVIEW=yes"); ecmd.putenv(std::string("RECOLL_CONFDIR=") + config->getConfDir()); string udi; idoc.getmeta(Rcl::Doc::keyudi, &udi); vector<string> args(cmd); args.push_back(udi); args.push_back(idoc.url); args.push_back(idoc.ipath); int status = ecmd.doexec(args, 0, &out); if (status == 0) { LOGDEB0("EXEDocFetcher::Internal: got [" << out << "]\n"); return true; } else { LOGERR("EXEDOcFetcher::fetch: " << bckid << ": " << stringsToString(cmd) << " failed for " << udi << " " << idoc.url << " " << idoc.ipath << "\n"); return false; } } }; EXEDocFetcher::EXEDocFetcher(const EXEDocFetcher::Internal& _m) { m = new Internal(_m); LOGDEB("EXEDocFetcher::EXEDocFetcher: fetch is " << stringsToString(m->sfetch) << "\n"); } bool EXEDocFetcher::fetch(RclConfig *cnf, const Rcl::Doc& idoc, RawDoc& out) { out.kind = RawDoc::RDK_DATADIRECT; return m->docmd(cnf, m->sfetch, idoc, out.data); } bool EXEDocFetcher::makesig(RclConfig *cnf, const Rcl::Doc& idoc, string& sig) { return m->docmd(cnf, m->smkid, idoc, sig); } // Lookup bckid in the config and create an appropriate fetcher. std::unique_ptr<EXEDocFetcher> exeDocFetcherMake(RclConfig *config, const string& bckid) { // The config we only read once, not gonna change. static ConfSimple *bconf; if (!bconf) { string bconfname = path_cat(config->getConfDir(), "backends"); LOGDEB("exeDocFetcherMake: using config in " << bconfname << "\n"); bconf = new ConfSimple(bconfname.c_str(), true); if (!bconf->ok()) { delete bconf; bconf = 0; LOGDEB("exeDocFetcherMake: bad/no config: " << bconfname << "\n"); return 0; } } EXEDocFetcher::Internal m; m.bckid = bckid; string sfetch; if (!bconf->get("fetch", sfetch, bckid) || sfetch.empty()) { LOGERR("exeDocFetcherMake: no 'fetch' for [" << bckid << "]\n"); return 0; } sfetch = path_tildexpand(sfetch); stringToStrings(sfetch, m.sfetch); // We look up the command as we do for filters for now m.sfetch[0] = config->findFilter(m.sfetch[0]); if (!path_isabsolute(m.sfetch[0])) { LOGERR("exeDocFetcherMake: " << m.sfetch[0] << " not found in exec path or filters dir\n"); return 0; } string smkid; if (!bconf->get("makesig", smkid, bckid) || smkid.empty()) { LOGDEB("exeDocFetcherMake: no 'makesig' for [" << bckid << "]\n"); return 0; } smkid = path_tildexpand(smkid); stringToStrings(smkid, m.smkid); m.smkid[0] = config->findFilter(m.smkid[0]); if (!path_isabsolute(m.smkid[0])) { LOGERR("exeDocFetcherMake: " << m.smkid[0] << " not found in exec path or filters dir\n"); return 0; } return std::unique_ptr<EXEDocFetcher>(new EXEDocFetcher(m)); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/exefetcher.h��������������������������������������������������������������������0000644�0001750�0001750�00000004451�14410615043�013407� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _EXEFETCHER_H_INCLUDED_ #define _EXEFETCHER_H_INCLUDED_ #include <memory> #include "fetcher.h" class RclConfig; /** * A fetcher which works by executing external programs, defined in a * configuration file. * At this point this is only used with the sample python mbox indexer, * to show how recoll can work with completely external data extraction code. * * Configuration: The external indexer sets the 'rclbes' recoll field * (backend definition, can be FS or BGL -web- in standard recoll) to * a unique value (e.g. MBOX for the python sample). A 'backends' file * in the configuration directory then links the 'rclbes' value with * commands to execute for fetching the data, which recoll uses at * query time for previewing and opening the document. */ class EXEDocFetcher : public DocFetcher { public: class Internal; EXEDocFetcher(const Internal&); virtual ~EXEDocFetcher() {} EXEDocFetcher(const EXEDocFetcher&) = delete; EXEDocFetcher& operator=(const EXEDocFetcher&) = delete; virtual bool fetch(RclConfig* cnf, const Rcl::Doc& idoc, RawDoc& out); /** Calls stat to retrieve file signature data */ virtual bool makesig(RclConfig* cnf, const Rcl::Doc& idoc,std::string& sig); friend std::unique_ptr<EXEDocFetcher> exeDocFetcherMake(RclConfig *, const std::string&); private: Internal *m; }; // Lookup bckid in the config and create an appropriate fetcher. std::unique_ptr<EXEDocFetcher> exeDocFetcherMake(RclConfig *config, const std::string& bckid); #endif /* _EXEFETCHER_H_INCLUDED_ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/idxstatus.cpp�������������������������������������������������������������������0000644�0001750�0001750�00000013707�14427373216�013667� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2017-2021 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <mutex> #include "chrono.h" #include "conftree.h" #include "idxstatus.h" #include "log.h" #include "rclconfig.h" #include "x11mon.h" using std::string; // Global stop request flag. This is checked in a number of place in the // indexing routines. int stopindexing; void readIdxStatus(RclConfig *config, DbIxStatus &status) { ConfSimple cs(config->getIdxStatusFile().c_str(), 1); status.phase = DbIxStatus::Phase(cs.getInt("phase", 0)); cs.get("fn", status.fn); status.docsdone = (int)cs.getInt("docsdone", 0); status.filesdone = (int)cs.getInt("filesdone", 0); status.fileerrors = (int)cs.getInt("fileerrors", 0); status.dbtotdocs = (int)cs.getInt("dbtotdocs", 0); status.totfiles = (int)cs.getInt("totfiles", 0); status.hasmonitor = cs.getBool("hasmonitor", false); } // Receive status updates from the ongoing indexing operation // Also check for an interrupt request and return the info to caller which // should subsequently orderly terminate what it is doing. class DbIxStatusUpdater::Internal { public: #ifdef IDX_THREADS std::mutex m_mutex; #endif Internal(const RclConfig *config, bool nox11mon) : m_file(config->getIdxStatusFile().c_str()), m_stopfilename(config->getIdxStopFile()), nox11monitor(nox11mon) { // The total number of files included in the index is actually // difficult to compute from the index itself. For display // purposes, we save it in the status file from indexing to // indexing (mostly...) string stf; if (m_file.get("totfiles", stf)) { status.totfiles = atoi(stf.c_str()); } } virtual bool update() { if (status.dbtotdocs < status.docsdone) status.dbtotdocs = status.docsdone; // Update the status file. Avoid doing it too often. Always do // it at the end (status DONE) if (status.phase == DbIxStatus::DBIXS_DONE || status.phase != m_prevphase || m_chron.millis() > 300) { if (status.totfiles < status.filesdone || status.phase == DbIxStatus::DBIXS_DONE) { status.totfiles = status.filesdone; } m_prevphase = status.phase; m_chron.restart(); if (status != prevstatus) { m_file.holdWrites(true); m_file.set("phase", int(status.phase)); m_file.set("docsdone", status.docsdone); m_file.set("filesdone", status.filesdone); m_file.set("fileerrors", status.fileerrors); m_file.set("dbtotdocs", status.dbtotdocs); m_file.set("totfiles", status.totfiles); m_file.set("fn", status.fn); m_file.set("hasmonitor", status.hasmonitor); m_file.holdWrites(false); prevstatus = status; } } if (path_exists(m_stopfilename)) { LOGINF("recollindex: asking indexer to stop because " << m_stopfilename << " exists\n"); path_unlink(m_stopfilename); stopindexing = true; } if (stopindexing) { return false; } #ifndef DISABLE_X11MON // If we are in the monitor, we also need to check X11 status // during the initial indexing pass (else the user could log // out and the indexing would go on, not good (ie: if the user // logs in again, the new recollindex will fail). if (status.hasmonitor && !nox11monitor && !x11IsAlive()) { LOGDEB("X11 session went away during initial indexing pass\n"); stopindexing = true; return false; } #endif return true; } DbIxStatus status; DbIxStatus prevstatus; ConfSimple m_file; string m_stopfilename; Chrono m_chron; bool nox11monitor{false}; DbIxStatus::Phase m_prevphase{DbIxStatus::DBIXS_NONE}; }; DbIxStatusUpdater::DbIxStatusUpdater(const RclConfig *config, bool nox11monitor) { m = new Internal(config, nox11monitor); } void DbIxStatusUpdater::setMonitor(bool onoff) { m->status.hasmonitor = onoff; } void DbIxStatusUpdater::setDbTotDocs(int totdocs) { #ifdef IDX_THREADS std::unique_lock<std::mutex> lock(m->m_mutex); #endif m->status.dbtotdocs = totdocs; } bool DbIxStatusUpdater::update(DbIxStatus::Phase phase, const string& fn, int incr) { #ifdef IDX_THREADS std::unique_lock<std::mutex> lock(m->m_mutex); #endif // We don't change a FLUSH status except if the new status is NONE // (recollindex init or rcldb after commit(). Else, the flush status maybe // overwritten by a "file updated" status and not be displayed if (phase == DbIxStatus::DBIXS_NONE || m->status.phase != DbIxStatus::DBIXS_FLUSH) m->status.phase = phase; m->status.fn = fn; if (incr & IncrDocsDone) m->status.docsdone++; if (incr & IncrFilesDone) m->status.filesdone++; if (incr & IncrFileErrors) m->status.fileerrors++; return m->update(); } static DbIxStatusUpdater *updater; DbIxStatusUpdater *statusUpdater(RclConfig *config, bool nox11mon) { if (updater) { return updater; } return (updater = new DbIxStatusUpdater(config, nox11mon)); } ���������������������������������������������������������recoll-1.36.1/index/webqueuefetcher.cpp�������������������������������������������������������������0000644�0001750�0001750�00000004253�14410615043�015003� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2012 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include "webqueuefetcher.h" #include <mutex> #include "rcldoc.h" #include "fetcher.h" #include "log.h" #include "webstore.h" using std::string; // We use a single WebStore object to access the data. We protect it // against multiple thread access. static std::mutex o_beagler_mutex; bool WQDocFetcher::fetch(RclConfig* cnf, const Rcl::Doc& idoc, RawDoc& out) { string udi; if (!idoc.getmeta(Rcl::Doc::keyudi, &udi) || udi.empty()) { LOGERR("WQDocFetcher:: no udi in idoc\n" ); return false; } Rcl::Doc dotdoc; { std::unique_lock<std::mutex> locker(o_beagler_mutex); // Retrieve from our webcache (beagle data). The beagler // object is created at the first call of this routine and // deleted when the program exits. static WebStore o_beagler(cnf); if (!o_beagler.getFromCache(udi, dotdoc, out.data)) { LOGINFO("WQDocFetcher::fetch: failed for [" << udi << "]\n"); return false; } } if (dotdoc.mimetype.compare(idoc.mimetype)) { LOGINFO("WQDocFetcher:: udi [" << udi << "], mimetp mismatch: in: [" << idoc.mimetype << "], bgl [" << dotdoc.mimetype << "]\n"); } out.kind = RawDoc::RDK_DATA; return true; } bool WQDocFetcher::makesig(RclConfig*, const Rcl::Doc&, string& sig) { // Web queue sigs are empty sig.clear(); return true; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/index/mimetype.h����������������������������������������������������������������������0000644�0001750�0001750�00000002654�14427373216�013134� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _MIMETYPE_H_INCLUDED_ #define _MIMETYPE_H_INCLUDED_ #include <string> #include "pathut.h" class RclConfig; /** * Try to determine a mime type for file. * * If stp is valid, this may imply more than matching the suffix, * the name must be usable to actually access file data. * @param filename file/path name to use * @param stp if not null use st_mode bits for directories etc. * @param cfg recoll config * @param usfc Use system's 'file' command as last resort (or not) */ std::string mimetype(const std::string &filename, RclConfig *cfg, bool usfc, const struct PathStat& stp = PathStat()); #endif /* _MIMETYPE_H_INCLUDED_ */ ������������������������������������������������������������������������������������recoll-1.36.1/ChangeLog�����������������������������������������������������������������������������0000644�0001750�0001750�00001214113�14410615043�011556� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������2010-02-02 15:33 +0100 Jean-Francois Dockes <jfd@recoll.org> (d11da0283f03 [tip]) * src/common/textsplit.cpp, src/common/textsplit.h, src/query/plaintorich.cpp, src/query/recollq.cpp, src/query/wasatorcl.cpp, src/rcldb/rcldb.cpp, src/rcldb/searchdata.cpp, src/rcldb/stoplist.cpp, src/rcldb/stoplist.h: cosmetics: use derived class for actual splitter instead of callback 2010-02-02 10:24 +0100 Jean-Francois Dockes <jfd@recoll.org> (a8caf709bcd3) * src/qt4gui/rclmain.ui, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp: QT GUI: define accelerators for res list page movements 2010-02-02 08:20 +0100 Jean-Francois Dockes <jfd@recoll.org> (ec31e285a553) * src/qtgui/reslist.cpp, src/query/reslistpager.h: Qt GUI: ensure that new page size is taken into account ASAP (no need for restarting app) 2010-02-01 17:51 +0100 Jean-Francois Dockes <jfd@recoll.org> (db953bb94c7f) * src/qt4gui/rclmain.ui, src/qtgui/preview_w.cpp, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: QT GUI: add fullscreen mode 2010-02-01 10:31 +0100 Jean-Francois Dockes <jfd@recoll.org> (1eda55ae3be9) * src/mk/manifest.txt: new file. * src/excludefile, src/makesrcdist.sh, src/mk/manifest.txt: Making a source dist: check the new list against old reference + other checks 2010-01-31 19:53 +0100 Israel G. Lugo <Israel G. Lugo> (74d4e25d43c2) * src/recollinstall.in: Install recollq and its manpage when in cmdline mode. Don't install the recoll.1 manpage when in cmdline mode. 2010-01-31 19:47 +0100 Jean-Francois Dockes <jfd@recoll.org> (c88b0ef40512) * src/common/autoconfig.h.in: use 3-arg version of ac_define as the 1-arg one is being obsoleted 2010-01-31 19:45 +0100 Jean-Francois Dockes <jfd@recoll.org> (1960435ccb68) * src/configure.ac: Dispense with the x11-monitoring when neither fam nor inotify are configured 2010-01-31 19:35 +0100 Jean-Francois Dockes <jfd@recoll.org> (08e6abfc5fdf) * src/configure.ac: use 3-arg version of ac_define as the 1-arg one is being obsoleted 2010-01-31 19:34 +0100 Jean-Francois Dockes <jfd@recoll.org> (c0add9dd8ad4) * website/download.html: none 2010-01-30 17:47 +0100 Jean-Francois Dockes <jfd@recoll.org> (5ed138ff2230) * src/qtgui/spell_w.cpp, src/qtgui/spell_w.h: QT GUI: fix small problems in newly native qt4 term expander 2010-01-30 17:31 +0100 Jean-Francois Dockes <jfd@recoll.org> (6ecf959a8e01) * src/qt4gui/spell.ui: new file. * src/qt4gui/recollmain.ui: deleted file. * .hgignore, src/qt4gui/recollmain.ui, src/qt4gui/spell.ui, src/qt4gui/uifrom3, src/qtgui/spell_w.cpp, src/qtgui/spell_w.h: QT GUI: converted the qt4 term expander dialog to native qt4 2010-01-30 14:09 +0100 Jean-Francois Dockes <jfd@recoll.org> (df8a91aaff88) * src/qt4gui/rclmain.ui: new file. * .hgignore, src/qt4gui/rclmain.ui, src/qt4gui/uifrom3, src/qtgui/confgui/confguiindex.h, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: Qt GUI: made the Qt4 main window native Qt4 (no more use of Q3MainWindow) 2010-01-30 08:23 +0100 Jean-Francois Dockes <jfd@recoll.org> (ed18703563b7) * .hgignore, src/ChangeLog, website/BUGS.html, website/devel.html, website/doc.html, website/download.html, website/index.html.en: none 2010-01-30 08:23 +0100 Jean-Francois Dockes <jfd@recoll.org> (22044a3b2e2c) * src/qtgui/rclmain_w.h: Qt 4.6.1 Uic bug: change qt version test from == to <= as bug still here in 4.6.2 2010-01-30 08:21 +0100 Jean-Francois Dockes <jfd@recoll.org> (b1cb8c664953) * src/common/autoconfig.h.in, src/configure, src/configure.ac, src/index/recollindex.cpp, src/utils/x11mon.cpp: Renamed WITHOUT_X11 to DISABLE_X11MON for clarification 2010-01-30 08:18 +0100 Israel G. Lugo <Israel G. Lugo> (be03b72e1258) * src/configure.ac: Rename option without-gui to disable-qtgui. New option disable- x11mon. Separate control of creation of the gui and X11 session monitoring. 2010-01-29 19:00 +0100 Jean-Francois Dockes <jfd@recoll.org> (d95c21312a15) * src/kde/kioslave/recoll/CMakeLists.txt: KIO slave: fixed CMakeList to configure Recoll with --enable-pic 2010-01-29 17:22 +0100 Jean-Francois Dockes <jfd@recoll.org> (266941720a99) * src/python/README.txt: new file. * src/configure, src/configure.ac, src/doc/user/usermanual.sgml, src/lib/Makefile, src/lib/mkMake, src/mk/commondefs, src/mk/localdefs.in, src/php/00README.txt, src/python/README.txt, src/python/recoll/setup.py: Implemented configure --enable-pic flag to build the main lib with position-independent objects. This avoids having to edit localdefs by hand to build the new php extension, and voids the need for the Python module to recompile Recoll source files. 2010-01-29 15:47 +0100 Jean-Francois Dockes <jfd@recoll.org> (69c42078b8d3) * src/php/00README.txt: new file. * src/php/00README.txt, src/php/recoll/make.sh, src/php/recoll/recoll.cpp: PHP extension by Wenqiang Song : make ready for external use. - added minimal doc - fixed build script to work around php/libtool issue - have the module default to Query Language (instead of AND) 2010-01-28 18:22 +0100 Jean-Francois Dockes <jfd@recoll.org> (45e7ec5e16c5) * .hgignore, website/usermanual/README-dir.txt: new file. * packaging/debian/changelog, packaging/debian/compat, packaging/debian/control, packaging/debian/copyright, packaging/debian/docs, packaging/debian/menu, packaging/debian/rules, packaging/debian/watch: deleted file. * .hgignore, packaging/debian/changelog, packaging/debian/compat, packaging/debian/control, packaging/debian/copyright, packaging/debian/docs, packaging/debian/menu, packaging/debian/rules, packaging/debian/watch, src/makesrcdist.sh, website/usermanual/README-dir.txt: svn->mercurial modifications 2010-01-28 16:13 +0000 convert-repo <convert-repo> (e85c82d42126) * .hgtags: new file. * .hgtags: update tags 2010-01-26 13:23 +0000 dockes <dockes> (c0cb63a2702a) * last before trial switch to mercurial. really. Yeah 2010-01-26 13:22 +0000 dockes <dockes> (c40e044c63dd) * tests/chm/chm.sh, tests/chm/chm.txt, tests/ics/ics.sh, tests/ics/ics.txt, tests/zip/mcKee.zip, tests/zip/zip.sh, tests/zip/zip.txt, website/download-1.12.html: new file. * tests/chm/chm.sh, tests/chm/chm.txt, tests/ics/ics.sh, tests/ics/ics.txt, tests/zip/mcKee.zip, tests/zip/zip.sh, tests/zip/zip.txt, website/download-1.12.html: last before trial switch to mercurial. really 2010-01-26 13:21 +0000 dockes <dockes> (7918f7073757) * website/BUGS.html, website/CHANGES.html, website/download.html, website/index.html.en, website/index.html.fr: last before trial switch to mercurial 2010-01-26 07:06 +0000 dockes <dockes> (0b5ec08c2ba2) * src/INSTALL, src/README: 2010-01-26 07:06 +0000 dockes <dockes> (f6a420527382) * src/VERSION: 1.13.02 2010-01-26 06:50 +0000 dockes <dockes> (b223f221578a [RECOLL_1_13_02]) * src/doc/user/usermanual.sgml: clarified --prefix et al 2010-01-25 20:43 +0000 dockes <dockes> (7d69ae778654) * src/qt4gui/ui_rclmain.h-4.5: new file. * src/qt4gui/ui_rclmain.h-4.5, src/qtgui/rclmain_w.h: use older ui include file under qt 4.6.1, the one its uic generates is broken 2010-01-25 11:08 +0000 dockes <dockes> (e2e5a1dd802d) * src/php/recoll/recollq.h: deleted file. * src/php/recoll/recollq.h: not used? 2010-01-25 11:06 +0000 dockes <dockes> (1683475297c1) * src/php/recoll/config.m4, src/php/recoll/make.sh, src/php/recoll/php_recoll.h, src/php/recoll/recoll.cpp, src/php/recoll/recollq.h, src/php/sample/shell.php: new file. * src/php/recoll/config.m4, src/php/recoll/make.sh, src/php/recoll/php_recoll.h, src/php/recoll/recoll.cpp, src/php/recoll/recollq.h, src/php/sample/shell.php: initial import from W. Song 2010-01-20 07:42 +0000 dockes <dockes> (4df8ebfbb72d) * packaging/rpm/recoll.spec, packaging/rpm/recollfedora.spec, packaging/rpm/recollfedora10.spec, packaging/rpm/recollmdk.spec: change mail address 2010-01-10 10:18 +0000 dockes <dockes> (1c62f24a5ca4) * packaging/rpm/recollfedora.spec: updated for fc12: depend on xapian-core, use qt4 2010-01-07 15:20 +0000 dockes <dockes> (01eb4176400c) * src/query/recollq.cpp: add option to print abstracts 2010-01-07 08:42 +0000 dockes <dockes> (a41bbccff862) * src/VERSION: 1.13.01 2010-01-07 08:41 +0000 dockes <dockes> (dde7b27846ef) * src/Makefile.in: distclean removes rclexecm.pyc 2010-01-07 08:34 +0000 dockes <dockes> (324bea9902a4) * src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/ssearch_w.cpp, src/qtgui/ssearch_w.h: moved initial db open and possible message boxes from main.cpp to rclmain_w.cpp first post-init job to avoid random crashes apparently related to the dialogs being created before app.exec(). Exact cause not certain, but crashes gone... 2010-01-07 08:29 +0000 dockes <dockes> (0629e02f12fe) * src/rcldb/searchdata.cpp: field values were not used in case term expansion was not performed (phrase or capitalized term) 2010-01-06 13:29 +0000 dockes <dockes> (e10bbaeefab5) * src/kde/kioslave/recoll/htmlif.cpp, src/query/recollq.cpp, src/query/xadump.cpp: adapt kio and recollq to the new internfile interface 2010-01-06 13:06 +0000 dockes <dockes> (0f2378be2603) * src/kde/kioslave/recoll/CMakeLists.txt: add libz 2010-01-05 15:00 +0000 dockes <dockes> (1ca577447878) * src/utils/closefrom.cpp, src/utils/fstreewalk.cpp, src/utils/fstreewalk.h, tests/Maildir/Maildir.txt, tests/andor/andor.sh, tests/andor/andor.txt, tests/cjk/cjk.sh, tests/cjk/cjk.txt, tests/mail/mail.txt, tests/msword/msword.txt, tests/txt/txt.txt, website/download.html: 1.13 tests txt mods + solaris port (FNM_LEADING_DIR) 2010-01-05 13:27 +0000 dockes <dockes> (0ab6a2dfc2c3) * website/BUGS.html, website/CHANGES.html, website/copydocs, website/credits.html, website/download.html, website/features.html, website/index.html.en, website/index.html.fr: web update for 1.13 2010-01-05 07:14 +0000 dockes <dockes> (cb08729afcd2) * src/INSTALL, src/README: 2010-01-05 07:14 +0000 dockes <dockes> (a1ba9ba640f7) * src/VERSION, src/common/rclconfig.cpp, src/doc/man/recoll.conf.5, src/doc/user/usermanual.sgml: 1.13.00: fixed doc ortographic typos 2009-12-31 08:20 +0000 dockes <dockes> (c2ae39772161) * src/INSTALL, src/README: 2009-12-31 08:15 +0000 dockes <dockes> (851e5b82f3d5) * src/VERSION: 1.13.0 2009-12-31 08:15 +0000 dockes <dockes> (04512125010e) * src/recollinstall.in: handle --without-gui config inside recollinstall.in 2009-12-20 14:31 +0000 dockes <dockes> (2cbda11286c5) * src/configure, src/configure.ac: typo in WIHOUT_X11 2009-12-17 20:23 +0000 dockes <dockes> (95eb8a010525) * src/doc/user/usermanual.sgml: There was an error in the mimemap format in the config example 2009-12-14 10:33 +0000 dockes <dockes> (1e774739395e) * src/INSTALL, src/README: 2009-12-14 10:33 +0000 dockes <dockes> (49cdfe826199) * src/ChangeLog: snapshot du jour 2009-12-14 10:23 +0000 dockes <dockes> (437be900fa14) * src/doc/user/Makefile, src/doc/user/usermanual.sgml: add --enable-camelcase doc + fix typo in doc Makefile comment 2009-12-14 10:10 +0000 dockes <dockes> (009ed00592fd) * src/common/autoconfig.h.in, src/common/textsplit.cpp, src/configure, src/configure.ac: add --enable-camelcase option to configure 2009-12-14 09:46 +0000 dockes <dockes> (1fabd736d16f) * src/doc/user/usermanual.sgml, src/index/fsindexer.cpp: use : as separator in localfields value before parsing as confsimple 2009-12-14 09:44 +0000 dockes <dockes> (2b09276dedc8) * src/utils/circache.cpp: fix pointer casting to make gcc happy 2009-12-14 09:44 +0000 dockes <dockes> (4ee0085fa59e) * src/sampleconf/fields: typo: keywords->keyword in prefixes 2009-12-14 09:43 +0000 dockes <dockes> (87b2caa6ec9c) * src/filters/rclabw, src/filters/rcldjvu, src/filters/rcldoc, src/filters/rcldvi, src/filters/rclflac, src/filters/rclgaim, src/filters/rclid3, src/filters/rclkwd, src/filters/rcllyx, src/filters/rclman, src/filters/rclogg, src/filters/rclopxml, src/filters/rclpdf, src/filters/rclppt, src/filters/rclps, src/filters/rclpurple, src/filters/rclrtf, src/filters/rclscribus, src/filters/rclsiduxman, src/filters/rclsoff, src/filters/rclsvg, src/filters/rcltex, src/filters/rcltext, src/filters/rclwpd, src/filters/rclxls, src/filters/recfiltcommon: iscmd: supplement -x with -d test not a dir 2009-12-14 07:26 +0000 dockes <dockes> (b8eceb552b3e) * src/INSTALL, src/README: 2009-12-14 07:25 +0000 dockes <dockes> (16dc2e0ed9fa) * src/makesrcdist.sh: 2009-12-14 07:13 +0000 dockes <dockes> (e5aae08ee26d) * src/Makefile.in: 2009-12-14 07:07 +0000 dockes <dockes> (c66c86594b35) * src/VERSION: 2009-12-14 07:06 +0000 dockes <dockes> (7229a431d686) * src/makesrcdist.sh: use different release name for beta versions 2009-12-13 21:40 +0000 dockes <dockes> (e0033b00df1e) * src/doc/user/usermanual.sgml: anacron 2009-12-13 16:16 +0000 dockes <dockes> (e148cd3f92c1) * src/sampleconf/recoll.conf.in: add localfields example 2009-12-13 16:13 +0000 dockes <dockes> (89ebf91076d8) * src/common/textsplit.cpp, src/internfile/mh_html.cpp, src/query/plaintorich.cpp, src/utils/base64.cpp: small amd64 fixes: 64 bits size_type, signed chars 2009-12-08 07:43 +0000 dockes <dockes> (026aa6df356f) * src/doc/man/recollindex.1, src/doc/man/recollq.1: clarify stemming options 2009-12-08 07:43 +0000 dockes <dockes> (0c698007055e) * src/query/recollq.cpp: add option -s to select stemming language 2009-12-08 07:42 +0000 dockes <dockes> (fcb5bca6adf8) * src/rcldb/stemdb.cpp: traces 2009-12-07 18:47 +0000 dockes <dockes> (6631c645c9df) * src/index/recollindex.cpp: use setpriority() to be a nice indexer 2009-12-07 17:43 +0000 dockes <dockes> (76128d18110e [RECOLL_1_13_0, RECOLL_20091214, RECOLL_1_13_01, RECOLL_1_13_00]) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: reimplemented Q3TextDocument::find() to be like the qt3 version 2009-12-07 14:32 +0000 dockes <dockes> (b02171ea3078) * src/qtgui/preview_w.cpp: switch preview qtextedit format back to plain text after loading so that selections copy plain text not html 2009-12-07 13:27 +0000 dockes <dockes> (3d37dc441cc9) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.cpp: Term expansion: handle field issues inside rcldb::termmatch, ensuring that we take the field name into account for all expansions. Ensures that File Name searches and filename: query language searches work the same, + overall better consistency 2009-12-07 13:24 +0000 dockes <dockes> (fe625ef90a21) * src/configure.ac: Israel G. Lugo: make sure that only one of inotify or FAM gets enabled, giving priority to inotify. 2009-11-30 10:04 +0000 dockes <dockes> (a75cd5af7c71) * src/VERSION, src/internfile/mimehandler.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/query/plaintorich.cpp, src/query/plaintorich.h, src/sampleconf/mimeconf: add <pre> tag to text/plain translated into qt html to preserve indentation. Removes need for rcltext (which did just this). Allow specifying any text/xxx as internal (allows having specific editor but no filter) 2009-11-30 06:34 +0000 dockes <dockes> (c4fdcda7df89) * src/index/rclmonrcv.cpp: compile either fam or inotify not both 2009-11-29 15:00 +0000 dockes <dockes> (6b9ed9ae0949) * src/doc/user/usermanual.sgml: change defaults for big text params 2009-11-29 12:56 +0000 dockes <dockes> (a04f5006fe89) * src/sampleconf/recoll.conf.in: add new 1.13 variables and defaults 2009-11-28 09:15 +0000 dockes <dockes> (4b56c2068545) * src/internfile/mh_execm.cpp, src/internfile/mh_text.cpp, src/query/docseqhist.cpp, src/utils/circache.cpp: new glibc missing includes 2009-11-28 08:45 +0000 dockes <dockes> (012b4b63e260) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_tr.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/i18n/recoll_xx.ts: 2009-11-28 08:14 +0000 dockes <dockes> (b0e70a20b7f1) * src/index/beaglequeue.cpp, src/internfile/mh_text.cpp, src/qtgui/confgui/confguiindex.cpp, src/qtgui/guiutils.cpp: set defaults usedesktoprefs, maxtext 20mb pagesz 1000k webcache 40m 2009-11-28 08:11 +0000 dockes <dockes> (ed3a574543f5) * src/doc/user/usermanual.sgml: clean-up + documented 1.13 new features 2009-11-28 06:39 +0000 dockes <dockes> (c45a690ee533) * src/internfile/mh_mbox.cpp: converted iostream to stdio because of mysterious read errors at the last position in the offsets file 2009-11-27 13:23 +0000 dockes <dockes> (7fa95cd57200) * src/internfile/mh_mail.cpp: add cnf(maildefcharset) to set specific mail default charset (mainly for readpst extracts which are utf-8 but have no charset set) 2009-11-27 13:11 +0000 dockes <dockes> (385305ee1820) * src/rcldb/rcldb.cpp: loglevel 2009-11-27 13:08 +0000 dockes <dockes> (8cc1ab099807) * src/internfile/mh_mbox.cpp: include sys/stat 2009-11-27 12:41 +0000 dockes <dockes> (c3039d4eab51) * src/internfile/Filter.h, src/internfile/internfile.cpp, src/internfile/mh_mbox.cpp, src/internfile/mh_mbox.h, src/internfile/mimehandler.h, src/query/docseqhist.cpp: implemented a cache for mbox message header offsets 2009-11-27 07:07 +0000 dockes <dockes> (a1a92e0952dd) * src/internfile/mh_mbox.cpp: Support From "bla bla" (quoted) From lines 2009-11-27 07:00 +0000 dockes <dockes> (64f09e3ad5a7) * src/internfile/internfile.cpp: update test driver 2009-11-26 14:03 +0000 dockes <dockes> (023c2a8520de) * src/aspell/rclaspell.cpp, src/aspell/rclaspell.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/query/docseq.h, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/reslistpager.cpp, src/query/reslistpager.h, src/rcldb/rcldb.cpp, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: suggest alternate spellings if no results 2009-11-26 13:52 +0000 dockes <dockes> (4270622aa3e0) * src/qtgui/guiutils.cpp: suppressed core dump at exit on unexisting config 2009-11-26 07:17 +0000 dockes <dockes> (f02bf2b6ea30) * src/rcldb/rcldb.cpp, src/rcldb/rclquery.cpp, src/rcldb/rclquery.h: use only match terms to build doc abstract, not all query terms (might save a little effort) 2009-11-26 07:15 +0000 dockes <dockes> (90776b10554c) * src/qtgui/rclmain_w.cpp: spell tool must be created even is USE_ASPELL is undefined 2009-11-25 14:37 +0000 dockes <dockes> (e3faedd237b8) * src/utils/md5.cpp: suppress unused parm warning 2009-11-25 11:07 +0000 dockes <dockes> (f8011c9579c8) * packaging/debian/debiankio/changelog, packaging/debian/debiankio/compat, packaging/debian/debiankio/control, packaging/debian/debiankio/copyright, packaging/debian/debiankio/dirs, packaging/debian/debiankio/docs, packaging/debian/debiankio/rules, packaging/debian/debiankio/watch, packaging/debian/debianrecoll/changelog, packaging/debian/debianrecoll/compat, packaging/debian/debianrecoll/control, packaging/debian/debianrecoll/copyright, packaging/debian/debianrecoll/docs, packaging/debian/debianrecoll/menu, packaging/debian/debianrecoll/rules, packaging/debian/debianrecoll/watch: new file. * packaging/debian/debiankio/changelog, packaging/debian/debiankio/compat, packaging/debian/debiankio/control, packaging/debian/debiankio/copyright, packaging/debian/debiankio/dirs, packaging/debian/debiankio/docs, packaging/debian/debiankio/rules, packaging/debian/debiankio/watch, packaging/debian/debianrecoll/changelog, packaging/debian/debianrecoll/compat, packaging/debian/debianrecoll/control, packaging/debian/debianrecoll/copyright, packaging/debian/debianrecoll/docs, packaging/debian/debianrecoll/menu, packaging/debian/debianrecoll/rules, packaging/debian/debianrecoll/watch: added debian dir to build kio-recoll 2009-11-24 10:25 +0000 dockes <dockes> (87057b6e2cba) * src/kde/kioslave/recoll/CMakeLists.txt: execute minimum recoll config inside cmakelists to create rclversion and autoconfig includes 2009-11-24 10:24 +0000 dockes <dockes> (a6e854084ffb) * src/utils/smallut.h: gcc4 2009-11-23 19:51 +0000 dockes <dockes> (42785e498950) * src/index/beaglequeue.cpp: store beagle fields before interning the file 2009-11-23 17:38 +0000 dockes <dockes> (aaccb7e813a8) * src/qtgui/preview_w.cpp: if text is empty, display fields by default 2009-11-23 17:37 +0000 dockes <dockes> (129654f22b3c) * src/internfile/internfile.cpp: in FileInterner::FileInterner(Rcl::Doc) (query), declare the BeagleQueue static so that the cache persists between FileInterner objects 2009-11-23 17:36 +0000 dockes <dockes> (2292efb797b4) * src/internfile/internfile.h: comments 2009-11-23 16:12 +0000 dockes <dockes> (a7ed9c85c313) * src/query/dynconf.cpp, src/query/dynconf.h: new file. * src/query/history.cpp, src/query/history.h: deleted file. * src/lib/Makefile, src/lib/mkMake, src/qtgui/guiutils.cpp, src/qtgui/main.cpp, src/qtgui/preview_w.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/recoll.h, src/query/docseqhist.cpp, src/query/docseqhist.h, src/query/dynconf.cpp, src/query/dynconf.h, src/query/history.cpp, src/query/history.h: revamped history feature to be udi-based while supporting old format 2009-11-23 16:11 +0000 dockes <dockes> (8a494a30e71f) * src/rcldb/rcldb.cpp: set udi in meta from getDoc(udi) 2009-11-23 16:10 +0000 dockes <dockes> (c432dcb83d8f) * src/index/beaglequeue.cpp, src/utils/circache.cpp, src/utils/circache.h: Beaglequeue: simplify index from cache now that udi entries are unique in cache 2009-11-22 17:27 +0000 dockes <dockes> (112515ddfd1b) * src/index/beaglequeue.cpp, src/utils/circache.cpp, src/utils/circache.h: only keep the latest entry for a given udi in the cache 2009-11-22 17:26 +0000 dockes <dockes> (c47346e105ac) * src/utils/smallut.h: added tempbuf class 2009-11-21 13:36 +0000 dockes <dockes> (d497773469db) * src/internfile/mimehandler.cpp, src/qtgui/rclmain_w.cpp: allow setting attrs on mimeview defs, factorize some code with mhExecFactory 2009-11-21 13:35 +0000 dockes <dockes> (77639dc8a584) * src/common/rclconfig.cpp, src/common/rclconfig.h: added valueSplitAttributes() method 2009-11-21 11:18 +0000 dockes <dockes> (50c2c8c764bb) * src/internfile/mimehandler.cpp: use a confsimple to parse the additional filter attributes 2009-11-21 11:14 +0000 dockes <dockes> (ba1b73290998) * src/qtgui/guiutils.h: add ipath to default paragraph format 2009-11-18 15:32 +0000 dockes <dockes> (132c512aacde) * src/kde/kioslave/recoll/CMakeLists.txt: added beaglequeue/circache to kio build because of internfile dependency 2009-11-18 14:27 +0000 dockes <dockes> (d1587dd98290) * src/utils/circache.cpp: warning 2009-11-18 14:26 +0000 dockes <dockes> (812296ef15d8) * src/rcldb/rclquery.cpp: query::getrescnt() would only work once following 1.13 mods (affects python api) 2009-11-18 14:25 +0000 dockes <dockes> (cc1924f2d969) * src/python/samples/recollq.py: 2009-11-18 14:03 +0000 dockes <dockes> (e60f229404a4) * src/python/recoll/pyrecoll.cpp: add some casts to avoid kwargs const warnings 2009-11-18 13:46 +0000 dockes <dockes> (0e29576743b0) * src/index/rclmonrcv.cpp: typo 2009-11-18 12:33 +0000 dockes <dockes> (da553b8d1e93) * src/filters/rclchm, src/filters/rclexecm.py, src/filters/rclics, src/internfile/mh_execm.cpp, src/internfile/mh_execm.h: handle REFILTERROR in execm 2009-11-18 10:26 +0000 dockes <dockes> (f28392bec173) * src/internfile/mh_mail.cpp, src/rcldb/rcldb.cpp: mh_mail: use truncate_to_word to avoid cutting an utf8 char. rcldb: logdeb text_to_word errors 2009-11-18 08:24 +0000 dockes <dockes> (c9b8704e7ffa) * src/index/beaglequeue.cpp, src/mk/FreeBSD: beaglequeue fully functional, small fixes remaining? 2009-11-18 07:57 +0000 dockes <dockes> (0f863324690f) * src/index/beaglequeue.cpp: ok with compression 2009-11-18 07:46 +0000 dockes <dockes> (7925e58ac0d9) * src/utils/circache.cpp, src/utils/circache.h: compression works 2009-11-17 14:52 +0000 dockes <dockes> (122d9a523dc7) * src/utils/circache.cpp, src/utils/circache.h: circache ok 2009-11-16 16:18 +0000 dockes <dockes> (88021fc84abd) * src/internfile/internfile.cpp: Lack of error checking after have_document() in preview case could lead to looping, and cancellation was not checked to make things worse 2009-11-16 16:16 +0000 dockes <dockes> (22e0540453bc) * src/configure: --without-gui 2009-11-16 16:12 +0000 dockes <dockes> (d3e16fb089de) * src/qt4gui/recoll.pro.in: stupid mistake in previous cosmetic change 2009-11-16 16:11 +0000 dockes <dockes> (a422d8f6d6fd) * src/index/fsindexer.cpp: make very sure ~/.beagle is in the skippedPaths 2009-11-16 16:10 +0000 dockes <dockes> (effac8983ab5) * src/internfile/mh_mail.cpp: reason msg 2009-11-16 12:50 +0000 dockes <dockes> (bfc0df6ab067) * src/Makefile.in, src/common/autoconfig.h.in, src/configure.ac, src/index/Makefile, src/mk/localdefs.in, src/utils/x11mon.cpp: add --without-gui configure option 2009-11-15 16:41 +0000 dockes <dockes> (81edb2c4cef7) * src/index/beaglequeue.cpp, src/index/fsindexer.cpp, src/utils/circache.cpp: catch cancel exceptions cast by internfile() 2009-11-15 14:39 +0000 dockes <dockes> (4539869b5761) * src/index/fsindexer.h, src/qtgui/rclmain_w.cpp, src/query/reslistpager.cpp, src/rcldb/rcldoc.cpp, src/rcldb/rcldoc.h, src/sampleconf/fields: changed apptag field name to rclaptg 2009-11-15 14:18 +0000 dockes <dockes> (b41678f5ad12) * src/qt4gui/recoll.pro.in, src/qtgui/recoll.pro.in: add -ldl -lX11 for binutils-gold 2009-11-15 08:38 +0000 dockes <dockes> (3801ee9a51c6) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/fsindexer.cpp, src/index/rclmonrcv.cpp, src/utils/fstreewalk.cpp, src/utils/fstreewalk.h, src/utils/smallut.cpp, src/utils/smallut.h: rationalized how we recompute things on setkeydir. recoll_noindex and skippedNames can now be changed at any point in the tree 2009-11-14 11:34 +0000 dockes <dockes> (a922eac98d16) * src/index/rclmonprc.cpp: monitor: accumulate mods during 30S before indexing 2009-11-14 10:29 +0000 dockes <dockes> (ea134de640e0) * src/index/beaglequeue.cpp, src/index/beaglequeue.h, src/index/fsindexer.cpp, src/index/indexer.cpp, src/index/rclmonrcv.cpp, src/index/recollindex.cpp, src/utils/circache.cpp: monitor the beagle queue 2009-11-14 10:25 +0000 dockes <dockes> (42421f027b94) * src/filters/rclchm, src/filters/rclics, src/filters/rcltext: emit helpernotfound 2009-11-14 08:21 +0000 dockes <dockes> (93baac7e87ac) * src/index/beaglequeue.cpp, src/index/beaglequeue.h, src/index/fsindexer.cpp, src/index/fsindexer.h, src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp, src/index/recollindex.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: beaglequeue indexFiles 2009-11-13 13:29 +0000 dockes <dockes> (7d0c4d7a917c) * src/index/beaglequeue.cpp, src/index/beaglequeue.h, src/internfile/internfile.cpp, src/internfile/internfile.h, src/qtgui/confgui/confguiindex.cpp, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain_w.cpp, src/rcldb/rcldoc.cpp, src/rcldb/rcldoc.h, src/rcldb/rclquery.cpp, src/sampleconf/fields: 1st beagle version with index/preview working 2009-11-13 09:08 +0000 dockes <dockes> (71f8c28cbeba) * src/qtgui/idxthread.cpp: integrate beaglequeueindexer for indexing. Work remains for indexfiles() at least 2009-11-13 09:08 +0000 dockes <dockes> (dda5121a7c45) * src/utils/circache.cpp, src/utils/circache.h: integrate beaglequeueindexer for indexing. Work remains for indexfiles() at least 2009-11-13 09:07 +0000 dockes <dockes> (364d46e16faf) * src/index/beaglequeue.cpp, src/index/beaglequeue.h, src/index/fsindexer.cpp, src/index/fsindexer.h, src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp: integrate beaglequeueindexer for indexing. Work remains for indexfiles() at least 2009-11-13 09:04 +0000 dockes <dockes> (7e32466740a7) * src/configure.ac: Israel G. Lugo: give priority to the user's PATH when looking for qmake (fixes detecting the wrong qmake when more than one exists). 2009-11-13 09:01 +0000 dockes <dockes> (3503bfba6b70) * src/rcldb/rcldoc.cpp, src/rcldb/rcldoc.h: make dump const 2009-11-13 09:01 +0000 dockes <dockes> (b4c8330037e7) * src/lib/Makefile, src/lib/mkMake: add beaglequeue, fsindexer 2009-11-13 08:58 +0000 dockes <dockes> (63ee628229e7) * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/confgui/confguiindex.cpp, src/qtgui/confgui/confguiindex.h: add panel for beaglequeue parameters + arrange so that a checkbox can enable/disable other params 2009-11-13 08:54 +0000 dockes <dockes> (5edf24b7552e) * src/sampleconf/fields: comments 2009-11-13 08:15 +0000 dockes <dockes> (a829fce15458) * src/filters/rclchm, src/filters/rclexecm.py, src/filters/rclics, src/filters/rclimg, src/filters/rclzip, src/internfile/mh_execm.cpp: dont use 0-sized doc to mean eof now 2009-11-11 18:09 +0000 dockes <dockes> (21b6ba1309c7) * src/filters/rclimg: send mimetype 2009-11-11 18:07 +0000 dockes <dockes> (7f2a7a7214fb) * src/internfile/mh_execm.cpp: set mimetype for the non-ipath case 2009-11-11 07:47 +0000 dockes <dockes> (75f9d10cf2f3) * src/index/fsindexer.cpp, src/index/fsindexer.h, src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp: moved common db code from fsindexer to confindexer 2009-11-10 18:11 +0000 dockes <dockes> (e079c8ce273f) * src/index/beaglequeue.cpp, src/index/beaglequeue.h: new file. * src/index/beaglequeue.cpp, src/index/beaglequeue.h: 2009-11-10 18:10 +0000 dockes <dockes> (698e70099ec0) * src/index/fsindexer.cpp, src/index/fsindexer.h, src/index/recollindex.h: new file. * src/index/fsindexer.cpp, src/index/fsindexer.h, src/index/indexer.cpp, src/index/indexer.h, src/index/rclmonprc.cpp, src/index/recollindex.cpp, src/index/recollindex.h: dbindexer->fsindexer, split into its own file 2009-11-10 17:42 +0000 dockes <dockes> (ccf674432104) * src/ChangeLog: 2009-11-10 17:42 +0000 dockes <dockes> (065c40b8964d) * src/index/recollindex.cpp: small cleanups and add option to call beaglequeue 2009-11-10 17:41 +0000 dockes <dockes> (d4ff290d1615) * src/index/indexer.cpp: small cleanups and comments 2009-11-10 17:39 +0000 dockes <dockes> (00c5f0c09ef9) * src/index/indexer.h: comments 2009-11-10 17:38 +0000 dockes <dockes> (02b632bcbeca) * src/index/rclmonrcv.cpp: remove indexer.h include 2009-11-10 17:38 +0000 dockes <dockes> (ba2255ec8b62) * src/common/rclinit.h: comment 2009-11-10 17:37 +0000 dockes <dockes> (915bf923b8da) * src/utils/fstreewalk.cpp, src/utils/fstreewalk.h: add nocanon option 2009-11-10 17:34 +0000 dockes <dockes> (29b753cd1f78) * src/utils/circache.cpp, src/utils/circache.h: intermediary checkpoint (things work, no index, no compression) 2009-11-10 17:32 +0000 dockes <dockes> (16e0d5965055) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: removed the useless keep_updated flag 2009-11-10 17:31 +0000 dockes <dockes> (75878eb08588) * src/rcldb/rcldoc.cpp, src/rcldb/rcldoc.h: added dump function 2009-11-10 17:30 +0000 dockes <dockes> (35b43d00db47) * src/query/recollq.cpp: added explicit flag parameter to Internfile constructeur for helping with beagle queue integration 2009-11-10 17:29 +0000 dockes <dockes> (75255bb8d7a0) * src/sampleconf/fields: add dc:description as keywords alias 2009-11-10 09:39 +0000 dockes <dockes> (ee6104876da9) * src/internfile/internfile.cpp, src/internfile/internfile.h, src/kde/kioslave/recoll/htmlif.cpp, src/qtgui/preview_w.cpp: added explicit flag parameter to Internfile constructeur for helping with beagle queue integration 2009-11-09 09:26 +0000 dockes <dockes> (7c3c0eed036b) * src/utils/circache.cpp, src/utils/circache.h: new file. * src/lib/Makefile, src/lib/mkMake, src/utils/Makefile, src/utils/circache.cpp, src/utils/circache.h: circache 2009-11-09 09:26 +0000 dockes <dockes> (877bb76973aa) * src/utils/conftree.cpp, src/utils/conftree.h: add some constness 2009-11-06 11:33 +0000 dockes <dockes> (944e0b9d1d53) * src/internfile/internfile.cpp, src/internfile/internfile.h, src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/query/docseq.h, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/docseqhist.cpp, src/query/docseqhist.h, src/utils/fileudi.h, src/utils/pathut.cpp, src/utils/pathut.h: allow opening parent/enclosing doc with native editor in reslist 2009-11-06 11:26 +0000 dockes <dockes> (1d9a5530d7bf) * src/sampleconf/mimeview: added okular as chm viewer 2009-11-04 13:52 +0000 dockes <dockes> (226d88ccb6c1) * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/confgui/confguiindex.cpp: store file names using local8bit qstring conversions 2009-11-04 13:43 +0000 dockes <dockes> (b57bd81d3e8e) * src/utils/fstreewalk.h: comment 2009-11-04 13:42 +0000 dockes <dockes> (2037ae120bcf) * src/utils/cancelcheck.h: comment 2009-10-31 09:00 +0000 dockes <dockes> (3ad7f6c85ce2) * src/internfile/mh_mail.cpp, src/internfile/mh_mail.h: extract msgid + generate abstract at start of txt, excluding headers 2009-10-31 08:59 +0000 dockes <dockes> (9e7ae93bd35b) * src/utils/mimeparse.cpp: change rfc2047 mail header decoding (=?iso-xx stuff) so that a start of encoding section can be recognized even not after white space 2009-10-30 19:05 +0000 dockes <dockes> (eb9ed35f9fe0) * src/qtgui/rclmain_w.cpp: allow substituting all doc fields in viewer command line 2009-10-30 19:04 +0000 dockes <dockes> (d8065c96ceae) * src/qtgui/viewaction.ui: clarify using desktop defs in action choice dialog 2009-10-30 10:16 +0000 dockes <dockes> (4a744302db21) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/qtgui/rclmain_w.cpp, src/query/reslistpager.cpp, src/sampleconf/fields: Allow setting fields in fs subtree. Use for an application tag used for starting a specialized viewer 2009-10-30 08:59 +0000 dockes <dockes> (bdd54ae7a182) * src/VERSION, src/common/rclconfig.h, src/index/indexer.cpp, src/index/indexer.h, src/internfile/mimehandler.cpp, src/rcldb/rcldb.cpp, src/rcldb/rclquery.cpp, src/utils/conftree.cpp, src/utils/conftree.h: Allow fields local to a subtree to be set in the configuration 2009-10-30 08:53 +0000 dockes <dockes> (aa8c442a67ec) * src/configure.ac: use /bin/sh to execute recollinstall instead of making it executable 2009-10-30 08:53 +0000 dockes <dockes> (0faf1f6ccf5f) * src/Makefile.in, src/configure: use /bin/sh to execute recollinstall instead of making it executable 2009-10-29 18:11 +0000 dockes <dockes> (2338d18226f2) * src/qtgui/uiprefs.ui: move the use-desktop-preference checkbox close to the choose editors button 2009-10-29 18:10 +0000 dockes <dockes> (4b6f29c1e3c3) * src/sampleconf/mimeconf: 2009-10-29 18:09 +0000 dockes <dockes> (2de2f1804086) * src/common/rclconfig.cpp, src/common/rclconfig.h: support wildcard filtering in getConfNames() + implement config checking function in test driver 2009-10-29 18:08 +0000 dockes <dockes> (78c287d1d2da) * src/utils/conftree.cpp, src/utils/conftree.h: bugfix: if last line ended with backslash, entry was ignored. new function: filter by wildcard expr in getNames() 2009-10-29 13:44 +0000 dockes <dockes> (26ae4011727a) * src/sampleconf/mimeconf, src/sampleconf/mimemap: chm+comments 2009-10-29 13:34 +0000 dockes <dockes> (178273f496f2) * src/sampleconf/recoll.conf.in: comment 2009-10-28 13:08 +0000 dockes <dockes> (9435a56f1962) * src/qtgui/reslist.cpp, src/qtgui/reslist.h: fix signal/slot type mismatch for setSortParams 2009-10-26 13:19 +0000 dockes <dockes> (2a369661c70c) * src/qtgui/uiprefs_w.cpp: disable app-choosing button when use-desktop-prefs is activated 2009-10-26 11:16 +0000 dockes <dockes> (8cdb908a253d) * src/qtgui/rclmain_w.cpp: qt4 sometimes doesnt display the status bar if its not created in init 2009-10-26 10:00 +0000 dockes <dockes> (758f39788d0c) * src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: arrange to send pageup/down and shift-home to the reslist 2009-10-24 15:02 +0000 dockes <dockes> (7d98b5c330c1) * src/rcldb/rcldb.cpp, src/rcldb/rcldb_p.h, src/rcldb/rclquery.cpp, src/rcldb/rclquery_p.h: unified retrying for databaseModified errors 2009-10-24 11:00 +0000 dockes <dockes> (9d49d2991eed) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rcldb_p.h, src/rcldb/rclquery.cpp: renamed fields for clarity 2009-10-24 06:37 +0000 dockes <dockes> (1486d8f630fc) * src/filters/rclchm, src/filters/rclics, src/filters/rclzip: cleanup 2009-10-24 06:17 +0000 dockes <dockes> (6a8a9821c17c) * src/filters/rclexecm.py, src/filters/rclzip: use python zipfile 2009-10-23 16:45 +0000 dockes <dockes> (436e03b2f0c1) * src/filters/rclchm: comments 2009-10-23 16:03 +0000 dockes <dockes> (be653b19dd28) * src/filters/rclchm: new file. * src/filters/rclchm: first working 2009-10-23 16:03 +0000 dockes <dockes> (99a819213c2a) * src/filters/rclzip: comment 2009-10-22 17:28 +0000 dockes <dockes> (e5f16d6d23db) * src/doc/user/usermanual.sgml: %(fldname) specs 2009-10-22 17:27 +0000 dockes <dockes> (3e37f6aac6c5) * src/doc/user/docbook.css: new freebsd version 2009-10-22 17:27 +0000 dockes <dockes> (1535c07dd8a6) * src/sampleconf/mimeconf: ics 2009-10-22 17:16 +0000 dockes <dockes> (deaef902d7e3) * src/sampleconf/mimeconf: add ics + more programming languages 2009-10-22 17:16 +0000 dockes <dockes> (98009bab1e61) * src/sampleconf/mimemap: add ics + more programming languages 2009-10-22 17:13 +0000 dockes <dockes> (bf9a0c5eeb5c) * src/filters/rclics: new file. * src/filters/rclexecm.py, src/filters/rclics: initial support for icalendar splitting 2009-10-22 12:24 +0000 dockes <dockes> (f97b91cb8153) * src/filters/rclexecm.py: new file. * src/filters/rclexecm.py, src/filters/rclzip: made rclexecm a class in a separate module 2009-10-22 11:58 +0000 dockes <dockes> (9361ab690eec) * src/filters/rclzip: fully extracted common code 2009-10-21 21:00 +0000 dockes <dockes> (39b12da95a76) * src/filters/rclzip: new file. * src/filters/rclzip: initial 2009-10-21 20:59 +0000 dockes <dockes> (ef17d33ea782) * website/download.html: 1.12.2 2009-10-21 12:02 +0000 dockes <dockes> (2baccf2235b6) * src/qtgui/rclmain_w.cpp, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/reslistpager.cpp, src/query/reslistpager.h, src/utils/Makefile, src/utils/smallut.cpp, src/utils/smallut.h: fix queryBuildAbstract option functionality. Allow substituting %(fieldname) in reslist paragraph format 2009-10-21 12:00 +0000 dockes <dockes> (30a02a6bada8) * src/internfile/mimehandler.h: warning 2009-10-21 11:58 +0000 dockes <dockes> (ebc82bec7704) * src/kde/kioslave/recoll/CMakeLists.txt, src/utils/closefrom.cpp: linux 2009-10-21 11:32 +0000 dockes <dockes> (1cc979921a0d) * src/internfile/mh_text.cpp, src/utils/closefrom.cpp: gcc43+linux compile 2009-10-21 07:48 +0000 dockes <dockes> (72168c28c9bb) * src/makestaticdist.sh: cleanup .svn directories 2009-10-21 07:24 +0000 dockes <dockes> (a550073d34d4) * src/makestaticdist.sh: get makestaticdist to work with qt4 2009-10-21 07:15 +0000 dockes <dockes> (e44497010880) * packaging/debian/changelog, packaging/debian/control, packaging/debian/menu, packaging/debian/rules, packaging/rpm/recollmdk.spec, tests/lyx/lyx.txt: 1.12.2 release fixes 2009-10-21 07:15 +0000 dockes <dockes> (cecbbb5e3c23) * website/pics/mario.png, website/pics/smile.png: new file. * website/mario.png, website/smile.png: deleted file. * website/BUGS.html, website/CHANGES.html, website/devel.html, website/download.html, website/features.html, website/index.html.en, website/index.html.fr, website/mario.png, website/pics/index.html, website/pics/mario.png, website/pics/recoll5-thumb.png, website/pics/recoll5.png, website/pics/smile.png, website/smile.png: 1.12.2 release 2009-10-19 16:20 +0000 dockes <dockes> (b2a9b0c5fc47) * src/lib/Makefile, src/lib/mkMake: add closefrom 2009-10-19 16:19 +0000 dockes <dockes> (5b3c0f9438a9) * src/README, src/doc/man/recoll.conf.5, src/doc/man/recollindex.1, src/doc/user/usermanual.sgml, src/filters/rclsvg: explict(e)ly errors again 2009-10-19 10:51 +0000 dockes <dockes> (70ed5ded2a5e) * src/qtgui/uiprefs.ui: move the use-desktop-preference checkbox close to the choose editors button 2009-10-19 07:30 +0000 dockes <dockes> (d25d7050d60c) * src/rcldb/rcldb.cpp, src/rcldb/rclquery.cpp: catch xapian exceptions in 2 more places. 2009-10-18 07:57 +0000 dockes <dockes> (cbcf397757a1) * src/qtgui/reslist.cpp: reslist: rightclick popup would not work inside table 2009-10-17 06:38 +0000 dockes <dockes> (cb08cd6b282b) * src/Makefile.in, src/index/recollindex.cpp, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: rclversion.h must not include xapian.h. Replace with Rcl::version_string() 2009-10-15 15:50 +0000 dockes <dockes> (6d01b54d3cf5) * src/qtgui/preview_w.cpp: compile with qt3 2009-10-15 12:32 +0000 dockes <dockes> (749d93d72709) * src/index/rclmonprc.cpp: only call x11IsAlive from the main thread 2009-10-15 12:32 +0000 dockes <dockes> (7339dd810b4c) * src/utils/Makefile, src/utils/conftree.cpp, src/utils/execmd.cpp: small linux include and makefile adjustments 2009-10-14 12:25 +0000 dockes <dockes> (4bfcb9f6483a) * src/utils/execmd.cpp, src/utils/execmd.h: m_cancelRequest->m_killRequest to avoid confusion with cancelcheck + close descriptors before exec 2009-10-14 12:24 +0000 dockes <dockes> (834b841865f0) * src/internfile/mh_exec.cpp: no timeout if filtermaxseconds is -1 2009-10-14 12:23 +0000 dockes <dockes> (894b94a986c2) * src/qtgui/confgui/confguiindex.cpp, src/sampleconf/recoll.conf.in: add filtermaxseconds to config 2009-10-14 12:22 +0000 dockes <dockes> (eec367c78b29) * src/utils/closefrom.cpp, src/utils/closefrom.h: new file. * src/utils/closefrom.cpp, src/utils/closefrom.h: 2009-10-14 06:21 +0000 dockes <dockes> (48782c4d99bd) * src/filters/rclimg, src/index/recollindex.cpp, src/internfile/mh_execm.cpp, src/internfile/mh_execm.h, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/utils/execmd.cpp: execm first working zip version 2009-10-13 17:32 +0000 dockes <dockes> (ac8388c11bcb) * src/utils/idfile.cpp, src/utils/idfile.h: allow working on memory string 2009-10-13 16:37 +0000 dockes <dockes> (25cd49e5f3b2) * src/internfile/mh_exec.cpp: comments 2009-10-13 12:22 +0000 dockes <dockes> (f8f821415451) * src/internfile/mh_exec.cpp: handle interrupt requests and set timeout on execs 2009-10-13 12:21 +0000 dockes <dockes> (0ec65928f00f) * src/utils/execmd.cpp: use process group to control/kill execd processes 2009-10-13 12:20 +0000 dockes <dockes> (ad3f88e0578e) * src/sampleconf/recoll.conf.in: added loop.ps to skippedFiles 2009-10-12 16:27 +0000 dockes <dockes> (2d5321b8e32c) * src/common/rclinit.cpp: also block USR1 USR2 2009-10-09 13:58 +0000 dockes <dockes> (9ef52b9903d4) * src/internfile/mh_execm.cpp, src/internfile/mh_execm.h: new file. * src/filters/rclimg, src/internfile/mh_exec.cpp, src/internfile/mh_exec.h, src/internfile/mh_execm.cpp, src/internfile/mh_execm.h, src/internfile/mimehandler.cpp, src/lib/Makefile, src/lib/mkMake, src/sampleconf/mimeconf: execm persistent filters 2009-10-09 13:57 +0000 dockes <dockes> (94243b4ecca6) * src/common/textsplit.cpp: process camelCase 2009-10-09 13:34 +0000 dockes <dockes> (9129980cfe0e) * src/utils/execmd.cpp, src/utils/execmd.h, src/utils/netcon.cpp, src/utils/netcon.h: Execmd: added count parameter to receive(), and new getline() function Netcon: fix receive() to properly handle the case where there is initially data in the line buffer 2009-10-04 13:25 +0000 dockes <dockes> (f81cdfd36952) * src/utils/readfile.cpp: 2009-10-04 13:24 +0000 dockes <dockes> (fe1c983b582e) * src/mk/commondefs: remove -I/usr/local/include from commondefs! 2009-09-30 15:53 +0000 dockes <dockes> (401a53878320) * src/internfile/mh_text.cpp: dont set ipath for the first page in text files to avoid dual records for files under the page size 2009-09-30 15:45 +0000 dockes <dockes> (1ce015f48d3a) * src/internfile/mh_text.cpp, src/internfile/mh_text.h, src/qtgui/confgui/confguiindex.cpp, src/sampleconf/recoll.conf.in, src/utils/readfile.cpp, src/utils/readfile.h: implemented paged text files 2009-09-29 15:58 +0000 dockes <dockes> (b288f2d22754) * src/internfile/mh_text.cpp, src/qtgui/confgui/confguiindex.cpp, src/sampleconf/recoll.conf.in: textfilemaxmbs 2009-09-29 15:58 +0000 dockes <dockes> (a41ae31020fa) * src/utils/execmd.cpp: loglevels 2009-09-29 14:49 +0000 dockes <dockes> (89ab6fcd4bef) * src/utils/netcon.cpp, src/utils/netcon.h: new file. * src/utils/netcon.cpp, src/utils/netcon.h: 2009-09-29 14:49 +0000 dockes <dockes> (254aad5cdd17) * src/utils/netcon.cpp, src/utils/netcon.h: deleted file. * src/utils/netcon.cpp, src/utils/netcon.h: 2009-09-29 08:47 +0000 dockes <dockes> (302c0dd0dfa0) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: got rid of the preview tabdata array 2009-09-29 07:48 +0000 dockes <dockes> (f65d40e808c6) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: make print a slot in the editor, not the preview 2009-09-28 18:19 +0000 dockes <dockes> (5c03bd6d7d00) * src/doc/user/usermanual.sgml, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: Preview printing 2009-09-28 17:53 +0000 dockes <dockes> (564c8022205f) * src/utils/execmd.cpp: adjust log levels 2009-09-26 09:30 +0000 dockes <dockes> (231f842cfa1a) * src/utils/netcon.cpp, src/utils/netcon.h: new file. * src/lib/Makefile, src/lib/mkMake, src/utils/execmd.cpp, src/utils/execmd.h, src/utils/netcon.cpp, src/utils/netcon.h: execmd uses netcon 2009-09-26 09:05 +0000 dockes <dockes> (3883518b318e) * src/rcldb/rclquery.cpp: dont abort on get_mset exception 2009-08-13 06:34 +0000 dockes <dockes> (71e1aa73c37e) * src/utils/refcntr.h: add release() method 2009-08-13 06:32 +0000 dockes <dockes> (75501a297534) * src/index/indexer.cpp, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h: xattrs: make them work with non-text files. Use ctime for up to date checks 2009-08-13 06:29 +0000 dockes <dockes> (45721e5ace5a) * src/common/autoconfig.h.in: allow choosing the "file" command from configure 2009-08-13 06:28 +0000 dockes <dockes> (817bbeb36f34) * src/qtgui/rclmain_w.cpp: Make sure db is open at all times (caused problems when sorting query started from the command line) 2009-08-13 06:27 +0000 dockes <dockes> (05b809bbb7d0) * src/qtgui/preview_w.cpp: 2009-08-13 06:26 +0000 dockes <dockes> (b5b49b39dc8a) * src/configure, src/configure.ac, src/index/mimetype.cpp: allow choosing the "file" command from configure 2009-08-13 06:24 +0000 dockes <dockes> (902b5dc99b09) * src/ChangeLog: 2009-08-13 06:23 +0000 dockes <dockes> (3ee15899a458) * src/sampleconf/recoll.conf.in: add indexedmimetypes to sample file 2009-07-02 13:26 +0000 dockes <dockes> (a0f0be9546bb) * src/filters/rclman: 2009-07-02 10:26 +0000 dockes <dockes> (82d09aa4b256) * src/index/indexer.cpp, src/qtgui/rclmain_w.cpp: improve periodic indexing status reporting and timer processing 2009-07-02 06:17 +0000 dockes <dockes> (b8cdf0ab08a9) * src/qtgui/main.cpp, src/rcldb/searchdata.h, src/utils/mimeparse.cpp, src/utils/mimeparse.h: explicitely->explicitly 2009-06-26 09:25 +0000 dockes <dockes> (98153ad73366) * src/filters/rclman, src/sampleconf/mimemap: improve man page handling 2009-06-22 16:41 +0000 dockes <dockes> (5003fe921249) * src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: moved periodic timer control from main.cpp to rclmain_w.cpp 2009-06-22 15:25 +0000 dockes <dockes> (a420554375c5) * src/qtgui/idxthread.cpp, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp: use proper locking/sleeping object for idx thread sync 2009-06-22 08:58 +0000 dockes <dockes> (d4fdc68fab47) * src/filters/rclman: use groff html output! 2009-06-22 08:57 +0000 dockes <dockes> (01a166e9f9e7) * src/index/indexer.cpp: debug trace 2009-06-01 06:32 +0000 dockes <dockes> (272067257953) * src/qtgui/main.cpp: fixed bug in handling remaining arguments as question pieces 2009-05-29 06:28 +0000 dockes <dockes> (091488ca1543) * src/bincimapmime/convert.h, src/utils/base64.cpp: change strchr() return parameter to const for new libc 2009-05-25 08:59 +0000 dockes <dockes> (6231c20d3e23) * src/filters/rcllyx: bug report from d.prost: spaces and accents in lyx file names 2009-05-04 08:06 +0000 dockes <dockes> (20f1f5746b3e) * src/qtgui/guiutils.cpp, src/qtgui/preview_w.h, src/qtgui/spell_w.cpp: gcc44 2009-04-27 11:49 +0000 dockes <dockes> (ba8db4a9fcf6) * packaging/rpm/recollfedora10.spec: new file. * packaging/rpm/recollfedora10.spec: 2009-04-27 11:42 +0000 dockes <dockes> (85e5723e268a) * tests/cjk/cjk.txt: new file. * tests/cjk/cjk.txt: 2009-04-27 09:40 +0000 dockes <dockes> (a7cf61bb3e6a) * website/BUGS.html, website/download.html, website/index.html.en, website/index.html.fr: 1.12 release changes 2009-04-27 09:15 +0000 dockes <dockes> (eb2d1da3c9ee) * website/BUGS.html: 2009-04-27 08:05 +0000 dockes <dockes> (c26df870665c) * src/utils/md5.cpp, src/utils/readfile.cpp: gcc 4.4 includes fixes 2009-04-27 08:03 +0000 dockes <dockes> (5e892d5aa963) * src/python/recoll/setup.py: pathhash->fileudi 2009-02-24 18:30 +0000 dockes <dockes> (d897d4f128ce) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain_w.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: implement option to display the catg filter as a toolbar combobox 2009-02-23 07:57 +0000 dockes <dockes> (5eb3b91eca18) * src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: new ru/uk translations from Michael 2009-02-06 16:49 +0000 dockes <dockes> (0946c032bea8) * src/utils/refcntr.h: make RefCntr(x*) explicit 2009-02-06 16:48 +0000 dockes <dockes> (1f50a0e7a3ac) * src/internfile/mimehandler.cpp: comments 2009-02-05 14:35 +0000 dockes <dockes> (1eb8b93ed85b) * src/utils/execmd.cpp, src/utils/execmd.h: 1st execcmd cleanup 2009-01-30 13:27 +0000 dockes <dockes> (55d06dfa9b04) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_tr.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/i18n/recoll_xx.ts, src/qtgui/reslist.cpp: small pbs with reslist translations 2009-01-30 11:43 +0000 dockes <dockes> (af28dae4f689) * src/INSTALL, src/README: 2009-01-30 11:43 +0000 dockes <dockes> (581a47458445 [RECOLL_1_12_0]) * website/BUGS.html, website/CHANGES.html: 1.12.0? 2009-01-30 11:42 +0000 dockes <dockes> (fd6cc84e76ce) * src/doc/user/usermanual.sgml: 1.12 manual 2009-01-30 10:22 +0000 dockes <dockes> (f683b3907dd1) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_tr.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/i18n/recoll_xx.ts: updated message files, translated french 2009-01-29 14:24 +0000 dockes <dockes> (f09b8b421535) * src/filters/rcltext: simplified rcltext. No need for awk and no assumptions on charset 2009-01-29 11:27 +0000 dockes <dockes> (c8b882dea260) * src/ChangeLog, website/CHANGES.html, website/doc.html: 2009-01-29 11:04 +0000 dockes <dockes> (0bf58162416f) * src/VERSION: 1.12.0 une 2009-01-29 10:47 +0000 dockes <dockes> (40e8e1f2f59b) * packaging/debian/changelog, packaging/rpm/recoll.spec, packaging/rpm/recollCooker.spec, packaging/rpm/recollfedora.spec, packaging/rpm/recollmdk.spec: 2009-01-29 10:08 +0000 dockes <dockes> (2af56852a361) * src/qtgui/main.cpp, src/qtgui/ssearch_w.cpp, src/qtgui/ssearch_w.h: have ssearch install the lang help section when needed 2009-01-28 17:41 +0000 dockes <dockes> (8654c9b9d56d) * src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp: erase history would crash with empty reslist docsource 2009-01-28 17:21 +0000 dockes <dockes> (8b56ccfdd91b) * src/qtgui/rclmain_w.cpp: fixed status bar messages (were cleared by periodic100 every 100ms) 2009-01-28 17:05 +0000 dockes <dockes> (b435cf90abb0) * src/qtgui/rclhelp.cpp, src/qtgui/rclhelp.h: new file. * src/qtgui/rclhelp.cpp, src/qtgui/rclhelp.h: F1 context-enhanced help 2009-01-28 16:56 +0000 dockes <dockes> (e5410627d9d5) * src/qt4gui/recoll.pro.in: F1 context-enhanced help 2009-01-28 16:56 +0000 dockes <dockes> (741df5618110) * src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/main.cpp, src/qtgui/preview_w.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/recoll.h, src/qtgui/recoll.pro.in, src/qtgui/reslist.cpp, src/qtgui/sort_w.cpp, src/qtgui/spell_w.cpp: F1 context-enhanced help 2009-01-28 14:58 +0000 dockes <dockes> (7e804d156dc5) * src/qtgui/rclmain_w.cpp: dont encode urls used for starting help browser 2009-01-28 14:22 +0000 dockes <dockes> (e696212a674c) * src/Makefile.in: add xapian version to version string 2009-01-28 08:45 +0000 dockes <dockes> (46251043fd88) * src/qtgui/advsearch.ui, src/qtgui/sort.ui, src/qtgui/spell.ui, src/qtgui/uiprefs.ui: avoid setting 0 sizes 2009-01-28 08:40 +0000 dockes <dockes> (1c551a065bdd) * src/configure, src/configure.ac: allow setting QMAKE in the environment 2009-01-27 18:12 +0000 dockes <dockes> (fb41a05985ed) * src/utils/pxattr.cpp: 2009-01-27 11:19 +0000 dockes <dockes> (3f5897bb4b8d) * tests/stemming/stemming.sh, tests/stemming/stemming.txt: new file. * tests/Maildir1/Maildir1.sh, tests/Maildir1/Maildir1.txt, tests/andor/andor.sh, tests/andor/andor.txt, tests/badsuffs/badsuffs.sh, tests/badsuffs/badsuffs.txt, tests/badsuffs1/badsuffs1.sh, tests/badsuffs1/badsuffs1.txt, tests/boolean/boolean.sh, tests/boolean/boolean.txt, tests/cjk/cjk.sh, tests/delete/delete.sh, tests/delete/delete.txt, tests/dirwithblanks/dirwithblanks.sh, tests/dirwithblanks/dirwithblanks.txt, tests/djvu/djvu.sh, tests/djvu/djvu.txt, tests/dvi/dvi.sh, tests/dvi/dvi.txt, tests/empty/empty.sh, tests/empty/empty.txt, tests/html/html.sh, tests/html/html.txt, tests/images/images.sh, tests/images/images.txt, tests/koi8r/koi8r.sh, tests/koi8r/koi8r.txt, tests/lyx/lyx.sh, tests/lyx/lyx.txt, tests/mail/mail.sh, tests/mail/mail.txt, tests/media/media.sh, tests/media/media.txt, tests/msword/msword.sh, tests/msword/msword.txt, tests/notypes/notypes.sh, tests/notypes/notypes.txt, tests/ooff/ooff.sh, tests/ooff/ooff.txt, tests/pdf/pdf.sh, tests/pdf/pdf.txt, tests/postscript/postscript.sh, tests/postscript/postscript.txt, tests/ppt/ppt.sh, tests/ppt/ppt.txt, tests/rfc2231/rfc2231.sh, tests/rfc2231/rfc2231.txt, tests/rtf/rtf.sh, tests/rtf/rtf.txt, tests/runtests.sh, tests/scribus/scribus.sh, tests/scribus/scribus.txt, tests/skipped/skipped.sh, tests/skipped/skipped.txt, tests/special/special.sh, tests/special/special.txt, tests/stemming/stemming.sh, tests/stemming/stemming.txt, tests/txt/txt.sh, tests/txt/txt.txt, tests/utf8/utf8.sh, tests/utf8/utf8.txt, tests/xls/xls.sh, tests/xls/xls.txt: remove recoll query text from compared test outputs 2009-01-27 10:25 +0000 dockes <dockes> (57dd90e8b55d) * src/common/textsplit.cpp, src/common/textsplit.h: Emit a_b intermediary span when splitting a_b.c 2009-01-26 18:30 +0000 dockes <dockes> (e2238061ec9d) * src/query/plaintorich.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.cpp: modified the time at which we unaccent so that we can do the Capitalized->nostemming test on single words (this had been broken by the change of noac/split order done earlier to get japanese to work) 2009-01-26 18:26 +0000 dockes <dockes> (8529cb7d58c7) * tests/cjk/cjk.sh: 2009-01-26 17:52 +0000 dockes <dockes> (8a5b4971a703) * tests/cjk/cjk.sh: new file. * tests/cjk/cjk.sh: 2009-01-26 17:34 +0000 dockes <dockes> (e65566ba6690) * website/BUGS.html, website/CHANGES.html, website/features.html, website/index.html.en, website/index.html.fr, website/pics/index.html, website/pics/recoll- HTML_search_results.html, website/pics/recoll0.html, website/pics/recoll0.txt, website/pics/recoll1.html, website/pics/recoll2.html, website/pics/recoll3.html, website/pics/recoll4.html, website/pics/recoll5.html, website/pics/recoll_chinese.html: website 2009-01-26 13:29 +0000 dockes <dockes> (61198659243f) * src/utils/smallut.cpp, src/utils/smallut.h: add overloaded neutchars with different parameters 2009-01-26 13:27 +0000 dockes <dockes> (61567bc09eab) * src/utils/transcode.cpp: tested and decided against caching iconv_open 2009-01-23 15:56 +0000 dockes <dockes> (1998b1608eb0) * src/ChangeLog, src/qtgui/advsearch_w.cpp, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/recoll.h: temp ckpt: need to test on real unix 2009-01-23 11:07 +0000 dockes <dockes> (3631372e04f1) * src/qtgui/uiprefs.ui: avoid name duplication 2009-01-23 11:03 +0000 dockes <dockes> (0dba2718e1aa) * src/qtgui/uiprefs.ui: one button for choosing native editors 2009-01-23 10:38 +0000 dockes <dockes> (167a153bcf3c) * src/kde/kioslave/recoll/data/searchable.html: simplified javascrip: no ie here! 2009-01-23 09:41 +0000 dockes <dockes> (b71166d61782) * src/qtgui/rclmain_w.cpp: toLocal8Bit->local8bit 2009-01-23 09:30 +0000 dockes <dockes> (c3565b4a7244) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/recoll.h, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h: use normal text/html ext app for viewing help 2009-01-23 09:27 +0000 dockes <dockes> (c025fa3fe99d) * src/utils/execmd.cpp, src/utils/execmd.h: accept additional path argument to execmd::which 2009-01-22 14:25 +0000 dockes <dockes> (967d5e013a33) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: allow toggle show text/fields in preview 2009-01-21 16:42 +0000 dockes <dockes> (f950b7d75e66) * src/internfile/internfile.cpp, src/internfile/internfile.h, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h: added saveToFile menu entry to reslist 2009-01-21 13:55 +0000 dockes <dockes> (033fe406a666) * src/utils/pxattr.cpp, src/utils/pxattr.h: new file. * src/common/autoconfig.h.in, src/common/rclconfig.cpp, src/common/rclconfig.h, src/configure, src/configure.ac, src/internfile/internfile.cpp, src/internfile/mh_exec.h, src/internfile/mh_html.cpp, src/internfile/mh_mail.cpp, src/internfile/mh_mbox.cpp, src/internfile/mh_text.cpp, src/internfile/mh_unknown.h, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/lib/Makefile, src/lib/mkMake, src/sampleconf/fields, src/utils/pxattr.cpp, src/utils/pxattr.h: added optional extended file attributes support 2009-01-21 11:11 +0000 dockes <dockes> (f269f00857ec) * src/sampleconf/mimeconf: comments 2009-01-21 11:11 +0000 dockes <dockes> (fda5a0a6fccb) * src/filters/rcldoc: try to use wvWare if present and antiword fails 2009-01-21 10:49 +0000 dockes <dockes> (394e160f7032) * src/utils/readfile.cpp: initialize the error buffer for gnu strerror_r 2009-01-21 10:24 +0000 dockes <dockes> (7580c4ed79ce) * src/utils/readfile.cpp: fix errno printing 2009-01-21 10:17 +0000 dockes <dockes> (f1dca213efee) * src/rcldb/rcldb.cpp: fixed typo that would prevent stopfile use 2009-01-17 14:57 +0000 dockes <dockes> (90f03bbd715c) * src/doc/man/recoll.conf.5, src/doc/user/usermanual.sgml: added compressedfilemaxkbs 2009-01-17 14:56 +0000 dockes <dockes> (78d1dd932d5b) * src/internfile/internfile.cpp, src/qtgui/confgui/confguiindex.cpp, src/sampleconf/recoll.conf.in: added compressedfilemaxkbs 2009-01-16 17:40 +0000 dockes <dockes> (fcc2539b18b4) * src/kde/kioslave/recoll/data/searchable.html: 2009-01-16 16:42 +0000 dockes <dockes> (11cc037db8a9) * src/kde/kioslave/recoll/00README.txt: 2009-01-16 11:32 +0000 dockes <dockes> (baaf38fdbca9) * src/kde/kioslave/recoll/00README.txt, src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/data/help.html, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/notes.txt: fixed docs + removed dead code 2009-01-15 17:07 +0000 dockes <dockes> (144b35bd64c0) * src/filters/rcluncomp, src/internfile/internfile.cpp: fixed handling of decompression errors, which was wrong but not catastrophly so in most cases 2009-01-15 17:05 +0000 dockes <dockes> (4b10b961d158) * src/qtgui/reslist.cpp: disable printing to tmp file 2009-01-15 14:37 +0000 dockes <dockes> (9392e278bb0a) * src/query/docseq.h, src/query/filtseq.cpp, src/query/filtseq.h, src/query/sortseq.cpp, src/query/sortseq.h: refactor operations delegated to subsequence by sortseq and filtspec into superclass 2009-01-15 09:47 +0000 dockes <dockes> (f02a34f835b4) * src/rcldb/rcldb.cpp: removed unused variable 2009-01-15 09:45 +0000 dockes <dockes> (2440f3259cd0) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/reslist.cpp, src/qtgui/uiprefs_w.cpp: ensure reslist parformat is refreshed after edit (1.11 bug) 2009-01-14 07:52 +0000 dockes <dockes> (b3c89a56c9a1) * src/qtgui/advsearch.ui, src/qtgui/rclmain_w.cpp, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h, src/qtgui/viewaction.ui, src/qtgui/viewaction_w.cpp, src/qtgui/viewaction_w.h: arrange so that the select action dialog is preselected on the right mime type after missing action 2009-01-13 16:03 +0000 dockes <dockes> (2d8517785a8e) * src/common/textsplit.cpp: add _ to wordsep/spanglue chars. Add non-ascii test to isCJK for optimization 2009-01-13 16:02 +0000 dockes <dockes> (cbfb1f939c9d) * src/common/uproplist.h: small fix : remove diaeresis from seps + comments 2009-01-13 08:56 +0000 dockes <dockes> (ee8989c89330) * src/doc/user/usermanual.sgml: 2009-01-13 08:49 +0000 dockes <dockes> (93e74953ed0b) * src/doc/user/usermanual.sgml: update version 2009-01-13 08:02 +0000 dockes <dockes> (051bf6d49898) * src/rcldb/rcldb.h, src/rcldb/rcldb_p.h, src/rcldb/rclquery.h: minor visibility cleanup 2009-01-13 08:01 +0000 dockes <dockes> (c550fb351f5f) * src/qtgui/ssearchb.ui: fix obsolete tooltip message 2009-01-12 18:31 +0000 dockes <dockes> (3cefac6eb52d) * src/doc/user/usermanual.sgml: doc: better adv search explanation + duplicates collapsing 2009-01-12 17:50 +0000 dockes <dockes> (f8cb21911962) * src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h: simplified dialog structure, apparently allowed to get rid of size hacks 2009-01-12 16:42 +0000 dockes <dockes> (48ca278dcd42) * src/qtgui/advsearch.ui: suppressed unused vbox 2009-01-12 15:55 +0000 dockes <dockes> (b5486bd5b85d) * src/qtgui/advsearch.ui, src/qtgui/searchclause_w.cpp, src/qtgui/searchclause_w.h: suppressed unused layout in searchClause 2009-01-09 14:56 +0000 dockes <dockes> (073523a33ffe) * src/internfile/mh_exec.cpp, src/internfile/mh_html.cpp, src/internfile/mh_mail.cpp, src/internfile/mh_text.cpp, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain_w.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rcldoc.cpp, src/rcldb/rcldoc.h, src/rcldb/rclquery.cpp, src/rcldb/rclquery.h: compute md5 checksums for all docs and optionally collapse duplicates in results 2009-01-09 12:23 +0000 dockes <dockes> (f89119e58f79) * src/qtgui/reslist.cpp: add space/backspace as pager keys for reslist 2009-01-09 12:23 +0000 dockes <dockes> (36eb326513d5) * src/utils/Makefile, src/utils/md5.cpp, src/utils/md5.h, src/utils/readfile.cpp, src/utils/readfile.h: implement md5 convenience file and string wrappers. Modify readfile to support this 2009-01-09 07:27 +0000 dockes <dockes> (de3507d26de4) * src/rcldb/pathhash.cpp, src/rcldb/pathhash.h: deleted file. * src/lib/Makefile, src/lib/mkMake, src/rcldb/pathhash.cpp, src/rcldb/pathhash.h, src/rcldb/rcldb.cpp: got rid of pathhash in rcldb, not used since 11.0 2009-01-08 09:55 +0000 dockes <dockes> (1fc0cdb06859) * src/excludefile: adapt to svn 2009-01-08 09:50 +0000 dockes <dockes> (867f1a9f6b02) * src/makesrcdist.sh: adapt distrib script to svn 2009-01-08 09:40 +0000 dockes <dockes> (33a7fbc42386 [RECOLL_1_12_1exp5]) * src/VERSION: 2009-01-06 18:48 +0000 dockes <dockes> (2e111dad7cba) * src/doc/user/bldloop: new file. * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- plist, src/doc/user/bldloop, tests/koi8r/koi8r.sh, tests/shared.sh: *** empty log message *** 2009-01-06 18:40 +0000 dockes <dockes> (c82fbe0ee8fc) * packaging/debian/changelog, packaging/rpm/recoll.spec, packaging/rpm/recollfedora.spec, packaging/rpm/recollmdk.spec, src/ChangeLog, website/devel.html, website/download.html: *** empty log message *** 2009-01-06 18:40 +0000 dockes <dockes> (7ebc18a8b4d7) * unac/builder.in, unac/unac.c, unac/unac.h: new unac approach for japanese: dont decompose at all 2009-01-06 17:40 +0000 dockes <dockes> (a0b7ed1f2bda) * website/xapUpg100.html: new file. * website/BUGS.html, website/BUGS.txt, website/CHANGES.html, website/doc.html, website/download.html, website/index.html.en, website/index.html.fr, website/xapUpg100.html: *** empty log message *** 2009-01-06 17:30 +0000 dockes <dockes> (636e0f9f2a77) * website/howtos/index.html, website/howtos/prevent_indexing_a_directory/index.html, website/howtos/use_multiple_indexes/index.html, website/pics/piclist.txt, website/pics/recoll-HTML_search_results- thumb.png, website/pics/recoll-HTML_search_results.html, website/pics/recoll-HTML_search_results.png, website/pics/recoll- HTML_search_results.txt, website/pics/recoll0-thumb.png, website/pics/recoll0.html, website/pics/recoll0.png, website/pics/recoll0.txt, website/pics/recoll1-thumb.png, website/pics/recoll1.html, website/pics/recoll1.png, website/pics/recoll1.txt, website/pics/recoll2-thumb.png, website/pics/recoll2.html, website/pics/recoll2.png, website/pics/recoll2.txt, website/pics/recoll3-thumb.png, website/pics/recoll3.html, website/pics/recoll3.png, website/pics/recoll3.txt, website/pics/recoll4-thumb.png, website/pics/recoll4.html, website/pics/recoll4.png, website/pics/recoll4.txt, website/pics/recoll5-thumb.png, website/pics/recoll5.html, website/pics/recoll5.png, website/pics/recoll5.txt, website/pics/recoll_chinese-thumb.png, website/pics/recoll_chinese.html, website/pics/recoll_chinese.png, website/pics/recoll_chinese.txt: new file. * website/howtos/index.html, website/howtos/prevent_indexing_a_directory/index.html, website/howtos/use_multiple_indexes/index.html, website/pics/piclist.txt, website/pics/recoll-HTML_search_results- thumb.png, website/pics/recoll-HTML_search_results.html, website/pics/recoll-HTML_search_results.png, website/pics/recoll- HTML_search_results.txt, website/pics/recoll0-thumb.png, website/pics/recoll0.html, website/pics/recoll0.png, website/pics/recoll0.txt, website/pics/recoll1-thumb.png, website/pics/recoll1.html, website/pics/recoll1.png, website/pics/recoll1.txt, website/pics/recoll2-thumb.png, website/pics/recoll2.html, website/pics/recoll2.png, website/pics/recoll2.txt, website/pics/recoll3-thumb.png, website/pics/recoll3.html, website/pics/recoll3.png, website/pics/recoll3.txt, website/pics/recoll4-thumb.png, website/pics/recoll4.html, website/pics/recoll4.png, website/pics/recoll4.txt, website/pics/recoll5-thumb.png, website/pics/recoll5.html, website/pics/recoll5.png, website/pics/recoll5.txt, website/pics/recoll_chinese-thumb.png, website/pics/recoll_chinese.html, website/pics/recoll_chinese.png, website/pics/recoll_chinese.txt: *** empty log message *** 2008-12-21 13:17 +0000 dockes <dockes> (74da01dd27c2) * src/unac/unac.c, src/unac/unac.h: new unac approach for japanese: dont decompose at all 2008-12-21 13:05 +0000 dockes <dockes> (273dad0916bb) * unac/unac_version.h: new file. * unac/unac_version.h: *** empty log message *** 2008-12-19 09:55 +0000 dockes <dockes> (1a2dd90e07b4) * src/rcldb/rcldb.h, src/rcldb/rclquery.cpp, src/rcldb/searchdata.cpp: getMainConfig not actually needed and possibly harmful 2008-12-19 09:44 +0000 dockes <dockes> (3a16629b24f5) * src/rcldb/searchdata.cpp, src/unac/unac.c, src/unac/unac.h: dont unaccent japanese + fix bug in unac/split ordering in searchdata 2008-12-19 08:39 +0000 dockes <dockes> (b895714a6500) * src/python/recoll/setup.py: pyrecoll: port to linux, update 2008-12-18 14:11 +0000 dockes <dockes> (33bffc499e78) * src/query/xadump.cpp: diag: prevent char combination by inserting spaces 2008-12-18 11:58 +0000 dockes <dockes> (a3863a0c1f62) * unac/builder.in, unac/unac.c: no going out of the basic plane! 2008-12-18 11:12 +0000 dockes <dockes> (ac1315d2a94f) * unac/unac.c: added recoll memory allocation checks 2008-12-18 11:05 +0000 dockes <dockes> (cfb4210ce7d5) * unac/CaseFolding-5.1.0.txt, unac/UnicodeData-5.1.0.txt: new file. * unac/CaseFolding-5.1.0.txt, unac/UnicodeData-5.1.0.txt: *** empty log message *** 2008-12-18 11:04 +0000 dockes <dockes> (cc609462a402) * unac/builder.in, unac/configure, unac/configure.ac, unac/unac.c, unac/unac.h: use unicode 5.1.0 + dont unaccent katakana/hiragana. Main change in unicode is that letters ae and o with stroke dont decompose anymore into a+e and o+e we may actually want to restore this if it proves a problem 2008-12-17 16:20 +0000 dockes <dockes> (65fd4fda84d3) * src/rcldb/rcldb.cpp: fix to previous abstract fix 2008-12-17 15:12 +0000 dockes <dockes> (9e9e84a23da6) * src/qtgui/reslist.cpp: use local hiliter 2008-12-17 14:26 +0000 dockes <dockes> (ada853f1e3b8) * src/common/Makefile, src/rcldb/rcldb.cpp: fix abstract generation when the match term is a multiword span (esp. for japanese) 2008-12-17 14:26 +0000 dockes <dockes> (9705bf172f13) * src/rcldb/searchdata.cpp: comment 2008-12-17 08:01 +0000 dockes <dockes> (42bc5b3b5abf) * src/index/indexer.cpp, src/index/indexer.h, src/kde/kioslave/recoll/kio_recoll.cpp, src/python/recoll/pyrecoll.cpp, src/qtgui/main.cpp, src/query/recollq.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: simplified db open by getting rid of the illusion that we could have several writeable dbs per config 2008-12-16 17:43 +0000 dockes <dockes> (1d040e634db3 [RECOLL_1_12_1exp4]) * src/README, src/VERSION, src/kde/kioslave/recoll/data/searchable.html, src/kde/kioslave/recoll/data/welcome.html: *** empty log message *** 2008-12-16 17:30 +0000 dockes <dockes> (18f65ef55dd6) * src/kde/kioslave/recoll/data/searchable.html: *** empty log message *** 2008-12-16 17:28 +0000 dockes <dockes> (e991bdd3d8c7) * src/kde/kioslave/recoll/data/searchable.html: new file. * src/kde/kioslave/recoll/data/searchable.html, src/kde/kioslave/recoll/data/welcome.html, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h: updated kioslave for small changes in reslistpager after main i/f integration. + javascript to search page 2008-12-16 14:20 +0000 dockes <dockes> (7bc14752b5f3) * src/qtgui/preview_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/query/plaintorich.h, src/query/reslistpager.cpp, src/query/reslistpager.h, src/utils/debuglog.cpp, src/utils/debuglog.h: converted qt reslist to reslistpager 2008-12-16 08:54 +0000 dockes <dockes> (c702627139c8) * src/query/wasastringtoquery.cpp: OR chain longer than 2 would swallow preceding AND terms 2008-12-15 15:04 +0000 dockes <dockes> (62e1b7eaa7b9) * src/kde/kioslave/recoll/htmlif.cpp, src/query/reslistpager.cpp: kio: use right ipath for preview 2008-12-15 14:39 +0000 dockes <dockes> (30b71a18e961) * src/query/xadump.cpp, src/rcldb/searchdata.cpp: make gcc happy 2008-12-15 13:51 +0000 dockes <dockes> (f93dda12024f) * website/howtos/template.html: *** empty log message *** 2008-12-15 11:20 +0000 dockes <dockes> (4a74871e9823) * website/howtos/buildindex.sh, website/howtos/fragend.html, website/howtos/fraghead.html, website/howtos/newdir.sh, website/howtos/template.html: new file. * website/BUGS.html, website/BUGS.txt, website/CHANGES.html, website/copydocs, website/download.html, website/howtos/buildindex.sh, website/howtos/fragend.html, website/howtos/fraghead.html, website/howtos/newdir.sh, website/howtos/template.html, website/index.html.en, website/index.html.fr, website/pics/index.html: *** empty log message *** 2008-12-15 09:33 +0000 dockes <dockes> (afc0ef4911b2) * src/doc/user/usermanual.sgml: more search tips 2008-12-15 09:24 +0000 dockes <dockes> (59cd1bdd4d3f) * src/rcldb/searchdata.cpp: reorganize code + add boost to phrase element to match boost of original user terms 2008-12-12 11:53 +0000 dockes <dockes> (4121cbc09d70) * src/common/textsplit.cpp, src/common/textsplit.h, src/rcldb/rcldb.cpp: dont insert space in cjk abstracts 2008-12-12 11:02 +0000 dockes <dockes> (37fd1c31af49) * src/rcldb/rcldb.cpp: message level 2008-12-12 11:01 +0000 dockes <dockes> (d2a8c016d05c) * src/qtgui/reslist.cpp: add %i for displaying ipath 2008-12-12 11:00 +0000 dockes <dockes> (151d6a590152) * src/qtgui/main.cpp: add all extra cmd line args to the question 2008-12-08 17:43 +0000 dockes <dockes> (90b62656b326) * src/kde/kioslave/recoll/htmlif.cpp: set name as preview title 2008-12-08 17:42 +0000 dockes <dockes> (5717c313d23a) * src/kde/kioslave/recoll/dirif.cpp: removed a few traces 2008-12-08 14:34 +0000 dockes <dockes> (de392f657f81) * src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/data/help.html, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/notes.txt: previews 2008-12-08 11:22 +0000 dockes <dockes> (877b674c328c) * src/utils/Makefile, src/utils/readfile.cpp: file_to_string: stat+reserve makes faster 2008-12-05 13:15 +0000 dockes <dockes> (19ef9198e3d5) * src/VERSION: branched maintenance for 1.11 kio devs on main now 1.12 2008-12-05 11:09 +0000 dockes <dockes> (b27d4070bbf8) * src/common/textsplit.cpp, src/common/textsplit.h, src/common/uproplist.h, src/kde/kioslave/recoll/kio_recoll.cpp, src/qtgui/ssearch_w.cpp, src/query/recollq.cpp, src/query/wasatorcl.cpp, src/rcldb/searchdata.cpp: take care of splitting user string with respect to unicode white space, not only ascii 2008-12-05 07:38 +0000 dockes <dockes> (d102970d3aee) * src/utils/smallut.h: comment 2008-12-04 12:41 +0000 dockes <dockes> (a3f25963b2da [RECOLL_1_11_1exp3]) * src/kde/kioslave/recoll/recollf.protocol: new file. * src/kde/kioslave/recoll/recollf.protocol: *** empty log message *** 2008-12-04 12:23 +0000 dockes <dockes> (adffbb42e449) * src/kde/kioslave/recoll/dirif.cpp: kde 4.0 compile 2008-12-04 11:50 +0000 dockes <dockes> (fef6cc6c4c97) * src/VERSION: *** empty log message *** 2008-12-04 11:49 +0000 dockes <dockes> (d1b1a426ddfa) * src/kde/kioslave/recoll/data/help.html, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/notes.txt, src/query/reslistpager.cpp, src/query/reslistpager.h, src/rcldb/rcldb.cpp, src/utils/pathut.h: kio_recoll: html/dir switching 2008-12-03 17:04 +0000 dockes <dockes> (a762165399a2) * src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/data/help.html, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/notes.txt: cleaned up virtual tree and url handling. Drag to desktop now works with appropriate name. recollf protocol 2008-12-03 10:02 +0000 dockes <dockes> (127dbb400363) * src/kde/kioslave/recoll/dirif.cpp: better stat 2008-12-02 13:41 +0000 dockes <dockes> (6e55b23fb64f) * src/kde/kioslave/recoll/dirif.cpp: *** empty log message *** 2008-12-02 13:38 +0000 dockes <dockes> (66b031be3559) * src/kde/kioslave/recoll/dirif.cpp: *** empty log message *** 2008-12-02 13:16 +0000 dockes <dockes> (619e41b1537b) * src/INSTALL, src/README, src/VERSION, src/doc/user/usermanual.sgml: *** empty log message *** 2008-12-02 13:14 +0000 dockes <dockes> (fff18d4ea953) * src/kde/kioslave/recoll/00README.txt, src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/data/welcome.html, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/notes.txt, src/query/reslistpager.cpp: kio goes to testing 2008-12-01 18:42 +0000 dockes <dockes> (714fdf15621e) * src/kde/kioslave/recoll/data/help.html, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h: small cleanups and comments. Still some weirdness 2008-12-01 15:37 +0000 dockes <dockes> (8d9ea1f1c645) * src/kde/kioslave/recoll/cleancmakestuff.sh: *** empty log message *** 2008-12-01 15:36 +0000 dockes <dockes> (8504e2e278dd) * src/kde/kioslave/recoll/data/help.html: new file. * src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/cleancmakestuff.sh, src/kde/kioslave/recoll/data/help.html, src/kde/kioslave/recoll/data/welcome.html, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/notes.txt: seems to work by re-rerunning search whenever it changes. Still had one crash, needs cleanup 2008-11-28 09:14 +0000 dockes <dockes> (ee6a7d32843e) * src/kde/kioslave/recoll/recollnolist.protocol: new file. * src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/notes.txt, src/kde/kioslave/recoll/recoll.protocol, src/kde/kioslave/recoll/recollnolist.protocol: ensured compatibility with kde4.0 2008-11-27 17:48 +0000 dockes <dockes> (d461029ef29c) * src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/notes.txt, src/kde/kioslave/recoll/recoll.protocol: bits of dual mode working 2008-11-27 14:05 +0000 dockes <dockes> (8cc177e8775a) * src/query/reslistpager.cpp: safety check 2008-11-27 13:35 +0000 dockes <dockes> (4d28c4942bc1) * src/sampleconf/mimeconf: *** empty log message *** 2008-11-27 09:49 +0000 dockes <dockes> (394d882caa0c) * src/sampleconf/mimeconf: remove obsolete [prefixes] section 2008-11-27 09:39 +0000 dockes <dockes> (0ec8260d8d7c) * src/doc/user/usermanual.sgml: *** empty log message *** 2008-11-26 15:03 +0000 dockes <dockes> (b6a62dc24003) * src/kde/kioslave/recoll/cleancmakestuff.sh, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/notes.txt: new file. * src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/cleancmakestuff.sh, src/kde/kioslave/recoll/dirif.cpp, src/kde/kioslave/recoll/htmlif.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/notes.txt, src/kde/kioslave/recoll/recoll.protocol: listdir doesnt work on kde 4.0 because on parent/child assumptions in kdirmodel have to check on kde 4.1 2008-11-24 17:42 +0000 dockes <dockes> (9333f13ac4c7) * src/VERSION: *** empty log message *** 2008-11-24 17:38 +0000 dockes <dockes> (a761936ec65e) * src/kde/kioslave/recoll/CMakeLists.txt: check for dlopen 2008-11-24 16:42 +0000 dockes <dockes> (0f7e0292212f) * src/kde/kioslave/recoll/CMakeLists.txt: have to cc the pic objects, cant use librcl 2008-11-24 15:47 +0000 dockes <dockes> (d06dd2891012) * src/Makefile.in, src/aspell/Makefile, src/common/Makefile, src/common/autoconfig.h.in, src/common/rclconfig.h, src/configure, src/configure.ac, src/index/Makefile, src/internfile/Makefile, src/lib/Makefile, src/lib/mkMake, src/mk/Darwin, src/mk/FreeBSD, src/mk/OpenBSD, src/mk/SunOS, src/mk/commondefs, src/qt4gui/recoll.pro.in, src/qtgui/recoll.pro.in, src/query/Makefile, src/unac/unac.c, src/utils/pathut.cpp: make it easier to maintain the kio cmake by moving as much stuff as possible to autoconfig.h, merging libmime and librcl etc. 2008-11-24 15:23 +0000 dockes <dockes> (7d9add059cc1) * src/qtgui/confgui/main.cpp, src/qtgui/guiutils.cpp, src/qtgui/main.cpp, src/qtgui/recoll.h: replace local variable recoll_datadir with access to config 2008-11-24 14:54 +0000 dockes <dockes> (7005bf515a0b) * src/unac/unac_version.h: new file. * src/unac/unac.c, src/unac/unac_version.h: *** empty log message *** 2008-11-21 16:43 +0000 dockes <dockes> (5c4559fa9d49) * src/makesrcdist.sh: *** empty log message *** 2008-11-21 16:37 +0000 dockes <dockes> (e92347cad84d) * src/kde/kioslave/recoll/00README.txt, src/makesrcdist.sh: ccmake cleanup in kio_recoll 2008-11-21 16:02 +0000 dockes <dockes> (f691d6ad3333) * src/excludefile: *** empty log message *** 2008-11-20 18:00 +0000 dockes <dockes> (5063f4280d8d) * src/kde/kioslave/recoll/Makefile: deleted file. * src/kde/kioslave/recoll/Makefile: *** empty log message *** 2008-11-20 15:10 +0000 dockes <dockes> (dc45badd0c45) * src/VERSION: *** empty log message *** 2008-11-20 14:16 +0000 dockes <dockes> (c653773059df) * src/kde/kioslave/recoll/00README.txt: *** empty log message *** 2008-11-20 13:10 +0000 dockes <dockes> (8b5eea7103b5) * src/kde/kioslave/recoll/data/welcome.html: new file. * src/kde/kioslave/recoll/00README.txt, src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/data/welcome.html, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/query/reslistpager.cpp, src/query/reslistpager.h: kioslave sort of works 2008-11-19 12:28 +0000 dockes <dockes> (93e6b483f5c4) * src/kde/kioslave/recoll/kio_recoll.cpp: *** empty log message *** 2008-11-19 12:19 +0000 dockes <dockes> (9b0d90b61574) * src/query/plaintorich.cpp, src/query/plaintorich.h, src/query/reslistpager.cpp, src/query/reslistpager.h: new file. * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h: deleted file. * src/lib/Makefile, src/lib/mkMake, src/qt4gui/recoll.pro.in, src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/recoll.pro.in, src/query/plaintorich.cpp, src/query/plaintorich.h, src/query/reslistpager.cpp, src/query/reslistpager.h: moved plaintorich from qtgui/ to query/ 2008-11-19 10:06 +0000 dockes <dockes> (350dd565c80d) * src/qtgui/reslist.cpp, src/utils/smallut.cpp, src/utils/smallut.h: moved code from qtgui to smallut 2008-11-18 13:51 +0000 dockes <dockes> (fae04b17c778) * src/utils/cancelcheck.h: comment 2008-11-18 13:25 +0000 dockes <dockes> (4d54c32dbee7) * src/index/csguess.cpp, src/index/mimetype.cpp, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp, src/query/wasatorcl.cpp: add a few includes for new gcc version 2008-11-18 13:24 +0000 dockes <dockes> (9455c0affe0a) * src/utils/cancelcheck.h: comments 2008-11-18 10:23 +0000 dockes <dockes> (d09d14bf2e24) * src/utils/debuglog.h: *** empty log message *** 2008-11-17 14:51 +0000 dockes <dockes> (9d4e9515342e) * src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/Makefile.kde3: new file. * src/kde/kioslave/recoll/CMakeLists.txt, src/kde/kioslave/recoll/Makefile, src/kde/kioslave/recoll/Makefile.kde3, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h: 1st kde test. cmake doesnt work need to use buildit script 2008-11-14 15:49 +0000 dockes <dockes> (13ca00d869a1) * src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h: *** empty log message *** 2008-11-13 10:57 +0000 dockes <dockes> (5cd3ce5481df) * src/kde/kioslave/recoll/00README.txt, src/kde/kioslave/recoll/Makefile, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.la, src/query/docseqdb.cpp: got the kio_slave working again 2008-11-08 11:00 +0000 dockes <dockes> (81b9fe1d7644) * src/qtgui/reslist.cpp: Copy entries in the rght clck menu copy both to selection and clipboard 2008-10-18 07:04 +0000 dockes <dockes> (33b4eec42ac8) * website/BUGS.html: new file. * website/BUGS.html, website/download.html: *** empty log message *** 2008-10-18 06:51 +0000 dockes <dockes> (b885092a2488) * website/CHANGES.html: new file. * website/CHANGES.txt: deleted file. * website/BUGS.txt, website/CHANGES.html, website/CHANGES.txt, website/download.html, website/index.html.en, website/index.html.fr: *** empty log message *** 2008-10-15 08:30 +0000 dockes <dockes> (6657f5e0f698) * src/sampleconf/recoll.conf.in: add .git .hg .bzr to skipped 2008-10-14 07:50 +0000 dockes <dockes> (2321044edfb9 [RECOLL_1_11_0]) * src/rcldb/searchdata.cpp, src/rcldb/searchdata.h, src/utils/refcntr.h: highlighting would not work with cat filt active because ClausSub did not implement getTerms 2008-10-14 06:07 +0000 dockes <dockes> (6ecc84bb82aa) * src/index/recollindex.cpp: print version in recollindex help 2008-10-13 11:46 +0000 dockes <dockes> (1cd1451bbb74) * src/excludefile, src/makesrcdist.sh: change in excludefile handling 2008-10-13 11:46 +0000 dockes <dockes> (609bbaa80120) * src/qtgui/ssearch_w.cpp, src/query/filtseq.cpp: warnings 2008-10-13 11:44 +0000 dockes <dockes> (809f8c3eb265) * src/qtgui/plaintorich.cpp: compil warn 2008-10-13 08:35 +0000 dockes <dockes> (a5d743b90fe8) * src/INSTALL, src/README: *** empty log message *** 2008-10-13 08:23 +0000 dockes <dockes> (5874f0e6fc82) * src/query/recollq.cpp: dont change recollq output, used for tests! 2008-10-13 07:57 +0000 dockes <dockes> (bf5637bbe652) * src/doc/user/usermanual.sgml, src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_tr.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/i18n/recoll_xx.ts, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp: messages and manual 2008-10-10 08:19 +0000 dockes <dockes> (d5a5fb9959b7) * src/doc/user/usermanual.sgml: added python api doc 2008-10-10 08:18 +0000 dockes <dockes> (4771280faa9c) * src/python/recoll/pyrecoll.cpp, src/python/samples/rclmbox.py, src/python/samples/recollqsd.py: fix executesd 2008-10-10 08:05 +0000 dockes <dockes> (f613511f9e1a) * src/python/recoll/pyrecoll.cpp: add delete purge 2008-10-10 08:04 +0000 dockes <dockes> (2547574a0242) * src/internfile/internfile.cpp: log levels 2008-10-09 09:36 +0000 dockes <dockes> (4fb973a50769) * src/python/recoll/pyrecoll.cpp: stemming went from query to searchdata 2008-10-09 09:21 +0000 dockes <dockes> (e112c834fca2) * src/filters/rclflac, src/filters/rclid3, src/sampleconf/mimeconf: improved mp3/flac filter. use pstotext directly 2008-10-09 09:19 +0000 dockes <dockes> (cf2e0559c3d9) * src/internfile/mh_exec.cpp, src/internfile/mimehandler.cpp: need to transcode text to utf-8 2008-10-09 09:19 +0000 dockes <dockes> (d250c2a0a26f) * src/utils/transcode.h: comments 2008-10-09 06:41 +0000 dockes <dockes> (721f4b3d08f4) * src/filters/rclimg: *** empty log message *** 2008-10-09 06:38 +0000 dockes <dockes> (e9d7fde008f9) * src/filters/rclimg: *** empty log message *** 2008-10-09 06:31 +0000 dockes <dockes> (4b76370655c3) * src/filters/rclimg: conform to filter error usual protocol 2008-10-08 16:15 +0000 dockes <dockes> (d60a26ce4397) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/filters/rcldvi, src/index/indexer.cpp, src/index/indexer.h, src/internfile/internfile.cpp, src/internfile/internfile.h, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/utils/smallut.cpp, src/utils/smallut.h: added menu to display missing helpers 2008-10-08 16:12 +0000 dockes <dockes> (30da9114943c) * src/doc/user/usermanual.sgml: *** empty log message *** 2008-10-08 08:27 +0000 dockes <dockes> (8a78ee8cc158) * src/filters/rclabw, src/filters/rcldjvu, src/filters/rclid3, src/filters/rclkwd, src/filters/rclogg, src/filters/rclopxml, src/filters/rclppt, src/filters/rclsoff, src/filters/rclsvg, src/filters/rclxls, src/sampleconf/fields, src/sampleconf/mimeconf: improved rclid3 and rclogg 2008-10-07 16:19 +0000 dockes <dockes> (c922f7984106) * src/qtgui/preview_w.cpp: message 2008-10-07 08:07 +0000 dockes <dockes> (7e7e59b8a48f) * src/doc/user/usermanual.sgml: query language precisions 2008-10-07 06:52 +0000 dockes <dockes> (0b46df2d0a1d) * src/query/wasatorcl.cpp: *** empty log message *** 2008-10-07 06:44 +0000 dockes <dockes> (a6e8f2583e65) * src/ChangeLog, src/common/rclconfig.cpp, src/python/recoll/pyrecoll.cpp, src/rcldb/rcldb.cpp, src/rcldb/rclquery.cpp, src/sampleconf/fields: let rclconfig take care of field name lowercasing 2008-10-06 06:22 +0000 dockes <dockes> (26eae5316b88) * src/internfile/mh_exec.cpp, src/internfile/mh_exec.h, src/internfile/mimehandler.cpp, src/utils/execmd.cpp: Disable filters with missing helpers for the whole indexing pass 2008-10-04 14:26 +0000 dockes <dockes> (556c7fa5998c) * src/index/indexer.cpp, src/internfile/Filter.h, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mh_exec.cpp, src/internfile/mh_exec.h, src/internfile/mh_html.h, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/internfile/mh_mbox.cpp, src/internfile/mh_mbox.h, src/internfile/mh_text.h, src/internfile/mh_unknown.h, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h: allow specifying format and charset for ext filters. Cache and reuse filters 2008-10-03 16:02 +0000 dockes <dockes> (6f5d875c2923) * src/utils/Makefile: *** empty log message *** 2008-10-03 16:02 +0000 dockes <dockes> (8d1e930cc9e2) * src/qtgui/preview_w.cpp: message 2008-10-03 08:19 +0000 dockes <dockes> (cf75be4a88cf) * src/common/rclconfig.cpp: *** empty log message *** 2008-10-03 08:09 +0000 dockes <dockes> (068bc565bf8b) * src/common/rclconfig.cpp, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: add option to preview html instead of plain text 2008-10-03 06:23 +0000 dockes <dockes> (bd1a6a560e25) * src/internfile/internfile.cpp, src/internfile/internfile.h: arrange for setting aside an html version when working for preview 2008-10-03 06:17 +0000 dockes <dockes> (b10d8b6906a0) * src/internfile/mh_html.cpp, src/internfile/mh_html.h: save transcoded html for preview 2008-10-02 13:30 +0000 dockes <dockes> (f469cf040425) * src/internfile/mh_exec.cpp, src/internfile/mh_exec.h: comments 2008-09-30 12:38 +0000 dockes <dockes> (6ff81f690928) * src/index/recollindex.cpp, src/qtgui/confgui/confguiindex.cpp, src/qtgui/idxthread.cpp, src/qtgui/idxthread.h, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb_p.h: added index format version checking 2008-09-29 11:33 +0000 dockes <dockes> (2691a6abf645) * src/kde/kioslave/recoll/kio_recoll.cpp, src/python/recoll/pyrecoll.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/query/docseq.h, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/filtseq.cpp, src/query/filtseq.h, src/query/recollq.cpp, src/query/sortseq.cpp, src/query/sortseq.h, src/rcldb/rclquery.cpp, src/rcldb/rclquery.h, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: move stemlang from RclQuery to SearchData. Allow DocSequences to do the sorting/filtering themselves 2008-09-29 08:59 +0000 dockes <dockes> (00bc43d91e91) * src/kde/kioslave/recoll/kio_recoll.cpp, src/python/recoll/pyrecoll.cpp, src/qtgui/reslist.cpp, src/query/docseq.cpp, src/query/docseq.h, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/docseqhist.cpp, src/query/docseqhist.h, src/query/filtseq.cpp, src/query/filtseq.h, src/query/recollq.cpp, src/query/sortseq.cpp, src/query/sortseq.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rclquery.cpp, src/rcldb/rclquery.h: doc.pc now only place where relevancy is stored 2008-09-29 07:13 +0000 dockes <dockes> (da809a196cc5) * src/qtgui/reslist.h: comments 2008-09-29 06:58 +0000 dockes <dockes> (dccf6cb38207) * src/python/recoll/pyrecoll.cpp, src/query/recollq.cpp, src/rcldb/rclquery.cpp, src/rcldb/rclquery.h, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: move sort params from searchdata to rclquery 2008-09-28 14:20 +0000 dockes <dockes> (0ce1cca8cac2) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/qtgui/sort_w.cpp, src/query/filtseq.h, src/query/sortseq.cpp, src/query/sortseq.h, src/sampleconf/mimeconf: 1st impl of catg filtering in reslist 2008-09-28 07:40 +0000 dockes <dockes> (5e29feefc554) * src/query/filtseq.cpp, src/query/filtseq.h: new file. * src/lib/Makefile, src/lib/mkMake, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/query/docseq.h, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/filtseq.cpp, src/query/filtseq.h: rearranged some reslist/rclmain functions + add but not use filtseq code 2008-09-25 09:08 +0000 dockes <dockes> (8588b8cc05d1) * src/python/samples/rcldlkp.py, src/python/samples/rclmbox.py: *** empty log message *** 2008-09-25 09:07 +0000 dockes <dockes> (40e028763fab) * src/python/xesam/xesam-recoll-service: arret apres hackfest 2008-09-25 06:17 +0000 dockes <dockes> (811009efeb96) * src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_tr.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/i18n/recoll_xx.ts: *** empty log message *** 2008-09-25 06:14 +0000 dockes <dockes> (ce29702ab7cc) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts: *** empty log message *** 2008-09-25 06:02 +0000 dockes <dockes> (a065c833e601) * src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: new russian/ukrainian translations 2008-09-25 06:00 +0000 dockes <dockes> (ba80af83d32f) * src/qtgui/advsearch_w.cpp, src/qtgui/confgui/confguiindex.cpp, src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_tr.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/i18n/recoll_xx.ts, src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/qtgui/uiprefs.ui: fixed typos 2008-09-24 06:50 +0000 dockes <dockes> (695914bd6d5d) * src/kde/recoll_applet/0README.Recoll: *** empty log message *** 2008-09-24 06:44 +0000 dockes <dockes> (48bbf0a115cc) * src/query/recollq.cpp: command line args must be processed as local 8 bit 2008-09-24 06:34 +0000 dockes <dockes> (e90ac2ed62fe) * src/doc/user/usermanual.sgml: *** empty log message *** 2008-09-24 05:35 +0000 dockes <dockes> (36e2522b06b2) * src/qtgui/main.cpp: command line args must be processed as local 8 bit 2008-09-24 05:31 +0000 dockes <dockes> (9b420f1d25f8) * src/qtgui/main.cpp: command line args must be processed as local 8 bit 2008-09-23 14:32 +0000 dockes <dockes> (cd440e5917d3) * src/configure, src/configure.ac: use $QMAKE not qmake when checking version 2008-09-16 10:19 +0000 dockes <dockes> (2bc72ad13a9b) * src/python/recoll/pyrecoll.cpp: fields, indexing i/f 2008-09-16 10:13 +0000 dockes <dockes> (ff10e8072c66) * src/qtgui/rclmain_w.cpp: have to setkeydir before calling internfile when opening 2008-09-16 08:18 +0000 dockes <dockes> (c78945994f7c) * src/python/samples/recollqsd.py: new file. * src/common/rclconfig.cpp, src/common/rclconfig.h, src/internfile/internfile.cpp, src/python/recoll/pyrecoll.cpp, src/python/recoll/setup.py, src/python/samples/recollqsd.py, src/python/xesam/xesam-recoll-service, src/query/recollq.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rcldb_p.h, src/rcldb/rcldoc.cpp, src/rcldb/rcldoc.h, src/rcldb/rclquery.cpp, src/rcldb/rclquery.h, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h, src/sampleconf/fields: general field name handling cleanup + sort facility in rclquery 2008-09-16 08:13 +0000 dockes <dockes> (bd4c0f6fd812) * src/internfile/mh_mail.cpp: emit field for recipients 2008-09-15 08:03 +0000 dockes <dockes> (11ba5592559e) * src/sampleconf/fields, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: added rcltext/python/purple 2008-09-15 08:02 +0000 dockes <dockes> (8af411ff9bf6) * src/filters/rclpurple: new file. * src/filters/rclpurple, src/qtgui/mtpics/README, src/utils/base64.cpp, src/utils/smallut.cpp, src/utils/smallut.h, src/utils/transcode.cpp: *** empty log message *** 2008-09-15 07:55 +0000 dockes <dockes> (49401228a5ef) * src/filters/rclpython, src/qtgui/mtpics/pidgin.png, src/qtgui/mtpics/text-x-python.png: new file. * src/filters/rclpython, src/qtgui/mtpics/pidgin.png, src/qtgui/mtpics/text-x-python.png: *** empty log message *** 2008-09-13 12:56 +0000 dockes <dockes> (299644545ca0) * src/python/xesam/xesam-recoll-service: new file. * src/python/xesam/xesam-recoll-service: *** empty log message *** 2008-09-12 11:35 +0000 dockes <dockes> (5c85f26d124d) * src/sampleconf/mimeconf: index c code with the new rcltext generic filter 2008-09-12 11:30 +0000 dockes <dockes> (b8277032f494) * src/filters/rcltext: new file. * src/filters/rcltext: *** empty log message *** 2008-09-09 12:58 +0000 dockes <dockes> (a3afe9b35b57) * src/rcldb/rcldb.cpp: debug messages 2008-09-08 16:49 +0000 dockes <dockes> (a18ab0c682a4) * src/rcldb/rcldoc.cpp, src/sampleconf/fields: new file. * src/common/rclconfig.cpp, src/common/rclconfig.h, src/internfile/internfile.cpp, src/lib/Makefile, src/lib/mkMake, src/python/recoll/pyrecoll.cpp, src/python/samples/recollq.py, src/qtgui/preview_w.cpp, src/qtgui/reslist.cpp, src/query/docseq.h, src/query/docseqdb.cpp, src/query/recollq.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldoc.cpp, src/rcldb/rcldoc.h, src/recollinstall.in, src/sampleconf/fields: foundation work for configurable stored/indexed fields 2008-09-08 15:47 +0000 dockes <dockes> (861e4211280b) * src/rcldb/searchdata.h: unused args warning 2008-09-08 15:47 +0000 dockes <dockes> (3f6468e20038) * src/utils/smallut.cpp: test driver 2008-09-08 15:46 +0000 dockes <dockes> (581ee503208b) * src/ChangeLog: *** empty log message *** 2008-09-07 07:22 +0000 dockes <dockes> (dfe4dd53d0b9) * src/Makefile.in, src/qt4gui/uifrom3: cleanup 2008-09-07 07:08 +0000 dockes <dockes> (95c2a94321a3) * src/Makefile.in: cleaning 2008-09-07 06:43 +0000 dockes <dockes> (6294638c2504) * src/Makefile.in, src/VERSION, src/mk/localdefs.in: improved cleaning 2008-09-05 11:45 +0000 dockes <dockes> (8532ebb84453) * src/rcldb/rclquery.cpp: gcc4.3 2008-09-05 10:36 +0000 dockes <dockes> (2ada099a7545) * src/internfile/internfile.cpp, src/internfile/internfile.h: strimline and restructure the doctree-exploring loop to make it close to understandable 2008-09-05 10:34 +0000 dockes <dockes> (404aa368d498) * src/rcldb/rcldb.cpp, src/rcldb/rcldb_p.h, src/rcldb/rclquery.cpp: add relevancyrating to the metadata when querying 2008-09-05 10:33 +0000 dockes <dockes> (bc0210deda18) * src/internfile/myhtmlparse.cpp: accept iso date format (2008-09-05T11:55:32) 2008-09-05 10:26 +0000 dockes <dockes> (4b17d6defb3c) * src/doc/man/recollindex.1: *** empty log message *** 2008-09-01 20:39 +0000 dockes <dockes> (39ff03712b54) * src/sampleconf/mimeconf, src/sampleconf/mimeview: openxml types 2008-09-01 17:31 +0000 dockes <dockes> (f0fde685acc8) * src/filters/rclopxml: sort of works 2008-09-01 17:21 +0000 dockes <dockes> (dfd3281994ff) * src/filters/rclopxml: new file. * src/filters/rclopxml: almost almost ok excepts outputs some formatting directives for ppt 2008-08-31 15:28 +0000 dockes <dockes> (7756d792699d [RECOLL_1_11_1exp1, RECOLL_1_11_1exp2, RECOLL_1_11_1exp]) * packaging/debian/changelog, packaging/debian/control, packaging/debian/rules: *** empty log message *** 2008-08-30 12:21 +0000 dockes <dockes> (60b122f6f4d6) * src/rcldb/rcldb.cpp: typo in xfsn len fix 2008-08-30 07:38 +0000 dockes <dockes> (d516181ad7a0) * src/rcldb/rcldb.cpp: truncate simple file names at max term length 2008-08-30 07:34 +0000 dockes <dockes> (59326d99e18d) * src/utils/smallut.cpp: utf8truncate 2008-08-30 07:31 +0000 dockes <dockes> (8f5c5fba53d1) * src/utils/smallut.cpp, src/utils/smallut.h: utf8truncate 2008-08-29 14:12 +0000 dockes <dockes> (41c405565cd4) * tests/boolean/boolean.sh: or->OR 2008-08-29 13:05 +0000 dockes <dockes> (6454f838026e) * src/internfile/mh_mbox.cpp: accept weird date format in From lines used by (old?) tbird 2008-08-29 09:51 +0000 dockes <dockes> (b830b6d6b04d) * src/index/recollindex.cpp: be more informative when monitoring not configured 2008-08-28 15:44 +0000 dockes <dockes> (27a9bf47f895) * src/python/recoll/pyrecoll.cpp, src/python/samples/rcldlkp.py, src/python/samples/rclmbox.py, src/rcldb/rcldb.cpp, src/sampleconf/mimeview: *** empty log message *** 2008-08-28 15:43 +0000 dockes <dockes> (d28eac37bdd9) * src/query/wasatorcl.cpp, src/rcldb/searchdata.h: use a refcntr for the sub SearchData 2008-08-28 15:42 +0000 dockes <dockes> (417a8f1346df) * src/rcldb/searchdata.cpp: ensure that a negative clause is not first or only in list 2008-08-27 12:34 +0000 dockes <dockes> (658ca4b955c8) * src/python/recoll/pyrecoll.cpp: reorganize+traces 2008-08-27 12:12 +0000 dockes <dockes> (37791b8e66aa) * src/python/recoll/pyrecoll.cpp: doc 2008-08-26 13:50 +0000 dockes <dockes> (af43f86ffe99) * src/query/wasastringtoquery.cpp: make AND and OR case-sensitive 2008-08-26 13:47 +0000 dockes <dockes> (bda91f767e32) * src/query/wasastringtoquery.cpp, src/query/wasastringtoquery.h, src/query/wasatorcl.cpp: try to parse the whole of Xesam user language 0.95 2008-08-26 07:56 +0000 dockes <dockes> (6a17726c7e41) * src/python/recoll/pyrecoll.cpp, src/python/recoll/setup.py, src/python/samples/rcldlkp.py, src/python/samples/rclmbox.py, src/python/samples/recollq.py: renamed a few things 2008-08-26 07:38 +0000 dockes <dockes> (c97de92889e3) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: copy author back from data record to Doc 2008-08-26 07:36 +0000 dockes <dockes> (d6e27e630844) * src/python/samples/rcldlkp.py, src/python/samples/rclmbox.py, src/python/samples/recollq.py: new file. * src/python/recoll/pyrecoll.cpp, src/python/recoll/setup.py, src/python/samples/rcldlkp.py, src/python/samples/rclmbox.py, src/python/samples/recollq.py: *** empty log message *** 2008-08-26 07:33 +0000 dockes <dockes> (1d6816c32358) * src/rcldb/rcldoc.h: comments 2008-08-26 07:33 +0000 dockes <dockes> (4e86d4c4f3d9) * src/internfile/internfile.cpp, src/internfile/internfile.h, src/qtgui/reslist.cpp: move ipath computations from reslist to internfile 2008-08-26 07:31 +0000 dockes <dockes> (b44f4950a084) * src/internfile/mh_exec.cpp, src/internfile/mh_exec.h: implement skip_to_document 2008-08-25 16:12 +0000 dockes <dockes> (936499917659) * src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: opxml formats 2008-07-30 13:16 +0000 dockes <dockes> (0f1387a8a565) * src/rcldb/stemdb.cpp: fixed inocuous but nasty bad string value test 2008-07-29 08:25 +0000 dockes <dockes> (a7888d48c2a6) * src/rcldb/rcldb.h, src/rcldb/rcldoc.h: comments 2008-07-29 06:25 +0000 dockes <dockes> (28ebb7cac39d) * src/index/indexer.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rcldb_p.h, src/rcldb/rcldoc.h: use explicit parent udi term instead of Qterm structure to express parent-child relationship 2008-07-28 12:24 +0000 dockes <dockes> (5cb926be362f) * src/index/indexer.cpp, src/lib/Makefile, src/lib/mkMake, src/query/docseqhist.cpp, src/rcldb/pathhash.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rcldoc.h, src/utils/Makefile, src/utils/fileudi.cpp: replaced path|ipath with unique doc id in rcldb i/f. Still depends on udi structure for parent/child 2008-07-28 10:20 +0000 dockes <dockes> (07bc933efb70) * src/utils/fileudi.cpp, src/utils/fileudi.h: new file. * src/utils/fileudi.cpp, src/utils/fileudi.h: *** empty log message *** 2008-07-28 08:42 +0000 dockes <dockes> (825cb66f8be3) * src/index/indexer.cpp, src/qtgui/uiprefs_w.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rcldb_p.h, src/rcldb/rcldoc.h, src/utils/base64.h: begin i/f cleanup: opacify doc uptodate sig (size+mtime) 2008-07-04 09:29 +0000 dockes <dockes> (6551cb55fa98 [RECOLL_1_10_3]) * src/qtgui/plaintorich.cpp: turn dbg off 2008-07-01 13:00 +0000 dockes <dockes> (19e926f99256) * src/ChangeLog, src/VERSION: 1.10.3: checkpoint for 1.10 branch maintenance 2008-07-01 12:11 +0000 dockes <dockes> (910f409cb0be) * src/bincimapmime/convert.h: suppressed a few wasteful string-cstr conversions 2008-07-01 11:57 +0000 dockes <dockes> (913963d84bc5) * src/bincimapmime/convert.cc, src/bincimapmime/convert.h, src/bincimapmime/mime-parseonlyheader.cc, src/bincimapmime/mime- printheader.cc: suppressed a few wasteful string-cstr conversions 2008-07-01 11:51 +0000 dockes <dockes> (54f3a868fb92) * src/bincimapmime/address.cc, src/internfile/mh_mail.cpp, src/query/wasastringtoquery.cpp, src/query/xadump.cpp, src/rcldb/rcldb.cpp, src/rcldb/rclquery.cpp, src/rcldb/searchdata.h, src/utils/conftree.cpp, src/utils/conftree.h, src/utils/idfile.cpp, src/utils/mimeparse.cpp, src/utils/pathut.cpp, src/utils/pathut.h, src/utils/smallut.cpp: suppressed a few wasteful string-cstr conversions 2008-07-01 10:29 +0000 dockes <dockes> (3e1aa9958af4) * src/index/mimetype.cpp, src/index/mimetype.h, src/internfile/mh_mail.cpp: mh_mail now uses mimetype() to try and better identify application /octet-stream 2008-07-01 08:31 +0000 dockes <dockes> (3665315a4fdd) * src/ChangeLog: *** empty log message *** 2008-07-01 08:31 +0000 dockes <dockes> (928e08cb2cc8) * src/rcldb/rclquery.cpp, src/rcldb/rclquery.h, src/rcldb/rclquery_p.h: small cleanups and comments 2008-07-01 08:28 +0000 dockes <dockes> (e5847d808877) * src/rcldb/rcldb.h: comments 2008-07-01 08:27 +0000 dockes <dockes> (97cd50050ecf) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/reslist.cpp: cleaned up plaintorich. Now a proper subclassable class + highlights multiple groups, not just the first 2008-07-01 08:27 +0000 dockes <dockes> (3ef1709e5955) * src/qtgui/confgui/confguiindex.cpp: typo 2008-07-01 08:26 +0000 dockes <dockes> (f6ddabbf59a2) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/utils/pathut.cpp, src/utils/pathut.h: moved printableUrl() to pathut 2008-07-01 08:24 +0000 dockes <dockes> (413a8a75c5af) * src/python/recoll/pyrecoll.cpp: added abstract i/f 2008-06-17 11:43 +0000 dockes <dockes> (009a912c3daf) * src/python/recoll/pyrecoll.cpp, src/python/recoll/setup.py: basic functionality ok, more funcs and options needed 2008-06-13 18:23 +0000 dockes <dockes> (58a4b54fa103) * src/utils/refcntr.h: separated rcldb and rclquery 2008-06-13 18:22 +0000 dockes <dockes> (a52ef2510839) * src/rcldb/rcldb_p.h, src/rcldb/rclquery.cpp, src/rcldb/rclquery.h, src/rcldb/rclquery_p.h: new file. * src/kde/kioslave/recoll/kio_recoll.cpp, src/lib/Makefile, src/lib/mkMake, src/python/recoll/pyrecoll.cpp, src/python/recoll/setup.py, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/query/docseq.h, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/recollq.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rcldb_p.h, src/rcldb/rclquery.cpp, src/rcldb/rclquery.h, src/rcldb/rclquery_p.h, src/rcldb/searchdata.h, src/utils/pathut.cpp, src/utils/refcntr.h: separated rcldb and rclquery 2008-06-13 18:14 +0000 dockes <dockes> (e39af9faad92) * src/common/autoconfig.h.in, src/configure, src/configure.ac, src/mk/Darwin, src/mk/FreeBSD, src/mk/Linux, src/mk/OpenBSD: move few things from the mk/sys files to autoconf 2008-06-10 06:30 +0000 dockes <dockes> (822b88ae3d1f) * src/qtgui/mtpics/License_sidux.txt: new file. * src/qtgui/mtpics/License_sidux.txt: *** empty log message *** 2008-06-09 09:14 +0000 dockes <dockes> (c9953f1a54ee) * src/filters/rclsiduxman, src/qtgui/mtpics/sidux-book.png: new file. * src/filters/rclsiduxman, src/qtgui/mtpics/sidux-book.png, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: sidux manual support 2008-05-27 10:46 +0000 dockes <dockes> (2afb8b8ec073) * 1.10.2 2008-05-27 10:45 +0000 dockes <dockes> (62c7f8ba0eb8) * packaging/debian/changelog, packaging/rpm/recoll.spec, packaging/rpm/recollfedora.spec, packaging/rpm/recollmdk.spec, src/python/recoll/pyrecoll.cpp, src/python/recoll/setup.py, website/BUGS.txt, website/CHANGES.txt, website/download.html, website/features.html, website/index.html.en, website/index.html.fr, website/styles/style.css: 1.10.2 2008-05-27 06:47 +0000 dockes <dockes> (b120e7a059cd [RECOLL_1_10_2]) * src/README: *** empty log message *** 2008-05-27 06:46 +0000 dockes <dockes> (70d9bb153b58) * src/VERSION: 1.10.2 2008-05-27 06:18 +0000 dockes <dockes> (305829599fb1) * src/utils/pathut.cpp: suppress warning 2008-05-27 05:40 +0000 dockes <dockes> (f611211f012a) * src/internfile/internfile.cpp: log message 2008-05-26 09:07 +0000 dockes <dockes> (dbb469971d76) * src/ChangeLog: *** empty log message *** 2008-05-21 07:21 +0000 dockes <dockes> (b1ee79619cca) * src/qtgui/advsearch_w.cpp, src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/preview_w.cpp, src/qtgui/reslist.cpp, src/utils/idfile.cpp: openSuse 11 compile issues 2008-05-20 10:09 +0000 dockes <dockes> (f047b0f61753) * src/qtgui/advsearch_w.cpp, src/rcldb/rcldb.cpp: *** empty log message *** 2008-05-20 10:09 +0000 dockes <dockes> (f2e76fada01c) * src/unac/unac.c: make strict gcc happy 2008-05-09 12:34 +0000 dockes <dockes> (be08db2c226e) * src/python/recoll/pyrecoll.cpp, src/python/recoll/setup.py: new file. * src/python/recoll/pyrecoll.cpp, src/python/recoll/setup.py: *** empty log message *** 2008-05-08 10:00 +0000 dockes <dockes> (2ff9f42dc279) * src/rcldb/searchdata.h: comments 2008-05-08 09:57 +0000 dockes <dockes> (bd6106d7f9ab) * src/utils/smallut.cpp, src/utils/smallut.h: *** empty log message *** 2008-05-08 09:31 +0000 dockes <dockes> (70f8eab20535) * src/ChangeLog: *** empty log message *** 2008-05-07 06:14 +0000 dockes <dockes> (f3d36126287d) * src/doc/user/usermanual.sgml: *** empty log message *** 2008-05-05 20:31 +0000 dockes <dockes> (d271616c4b99) * src/doc/user/usermanual.sgml: *** empty log message *** 2008-05-05 20:28 +0000 dockes <dockes> (02b1484f3eee) * src/doc/user/usermanual.sgml, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/preview_w.cpp, src/qtgui/reslist.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: allow setting query term highlight color in prefs 2008-05-05 16:38 +0000 dockes <dockes> (763298305d15) * src/qtgui/reslist.cpp: Edit -> Open in links 2008-05-05 13:13 +0000 dockes <dockes> (d2fc5c651024) * src/bincimapmime/mime-parsefull.cc: part data was sometimes truncated because of bad handling of consecutive mime boundaries. Most common symptom: error in base64 decoding 2008-04-18 11:41 +0000 dockes <dockes> (32155182993c) * src/mk/localdefs.in: get CXXFLAGS from autoconf 2008-04-18 11:39 +0000 dockes <dockes> (72073f033a45) * src/query/xadump.cpp: xadump would sometimes dump core with -b 2008-04-18 11:38 +0000 dockes <dockes> (ef6566c2ac8e) * src/qtgui/preview_w.cpp: walking the search terms hits backwards would go forward 2008-04-18 11:37 +0000 dockes <dockes> (018890cfdbd7) * src/utils/Makefile, src/utils/base64.cpp, src/utils/readfile.cpp: base64 testing code 2008-02-19 08:02 +0000 dockes <dockes> (34b45c5acd1c) * src/qtgui/main.cpp: make first sort after -q work 2008-02-19 08:02 +0000 dockes <dockes> (1293fc15412b) * src/qtgui/rclmain_w.cpp: comments+debug 2008-02-19 07:41 +0000 dockes <dockes> (efbaeed44ee9) * src/rcldb/rcldb.cpp: traces 2008-02-11 10:21 +0000 dockes <dockes> (81923201adc7) * src/utils/idfile.cpp: hack for Mark B.: allow treating (single-message) mbox files as message/rfc822 2008-02-08 08:37 +0000 dockes <dockes> (ddcce838e7d0) * src/qtgui/i18n/recoll_de.ts: update by Frank Thieme 2008-02-05 10:45 +0000 dockes <dockes> (51a501984fd4) * src/sampleconf/mimeconf: *** empty log message *** 2008-02-03 16:24 +0000 dockes <dockes> (825bb43d67ca) * src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: rclsvg 2008-02-03 16:05 +0000 dockes <dockes> (81794c3a6d9e) * src/filters/rclsvg: *** empty log message *** 2008-02-03 16:04 +0000 dockes <dockes> (40c35a7fb1bb) * src/filters/rclsvg: new file. * src/filters/rclsvg: *** empty log message *** 2008-01-29 10:14 +0000 dockes <dockes> (fd74eae7e8b4 [RECOLL_1_10_1]) * src/README: *** empty log message *** 2008-01-29 10:11 +0000 dockes <dockes> (a1fee09bfc3d) * src/rcldb/searchdata.h: m_haveWildCards was sometimes not init 2008-01-29 08:41 +0000 dockes <dockes> (ebc971754f92) * src/ChangeLog: *** empty log message *** 2008-01-24 09:34 +0000 dockes <dockes> (301425122a56) * src/qtgui/main.cpp: *** empty log message *** 2008-01-17 11:15 +0000 dockes <dockes> (af11c991aff3) * src/qtgui/idxthread.cpp, src/qtgui/idxthread.h, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: allow stopping indexing through menu action 2008-01-17 11:14 +0000 dockes <dockes> (4c108ac6227a) * src/query/wasastringtoquery.h: comment 2008-01-17 11:13 +0000 dockes <dockes> (7b2a9225dbef) * src/doc/user/usermanual.sgml: *** empty log message *** 2008-01-16 11:14 +0000 dockes <dockes> (ddfe49735bc2) * src/query/wasatorcl.cpp, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: express query language OR chains as rcldb subqueries so that field specs will work inside them 2008-01-16 10:52 +0000 dockes <dockes> (6487da12360f) * src/ChangeLog, src/doc/user/usermanual.sgml: *** empty log message *** 2008-01-16 08:43 +0000 dockes <dockes> (592d1258c5e4) * src/rcldb/searchdata.cpp: splitString filename queries 2007-12-20 09:08 +0000 dockes <dockes> (e99decc750eb) * src/index/indexer.cpp, src/rcldb/rcldb.cpp: ensure that the names of files with filter errors get indexed anyway 2007-12-13 06:58 +0000 dockes <dockes> (de422a0df409) * src/aspell/rclaspell.cpp, src/bincimapmime/convert.h, src/common/rclconfig.cpp, src/common/rclinit.cpp, src/common/textsplit.cpp, src/common/unacpp.cpp, src/index/csguess.cpp, src/index/indexer.cpp, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp, src/index/recollindex.cpp, src/internfile/htmlparse.cpp, src/internfile/mh_mail.cpp, src/internfile/mh_mbox.cpp, src/internfile/myhtmlparse.cpp, src/query/docseqhist.cpp, src/query/history.cpp, src/query/recollq.cpp, src/rcldb/pathhash.cpp, src/rcldb/rcldb.cpp, src/utils/base64.cpp, src/utils/conftree.cpp, src/utils/copyfile.cpp, src/utils/fstreewalk.cpp, src/utils/idfile.cpp, src/utils/mimeparse.cpp, src/utils/pathut.cpp, src/utils/readfile.cpp, src/utils/wipedir.cpp: gcc 4 compat, thanks to Kartik Mistry 2007-12-04 10:17 +0000 dockes <dockes> (f2bd537aad87) * src/qtgui/rclmain_w.cpp: directly open editor action choice dialog when user says so 2007-12-04 10:16 +0000 dockes <dockes> (9a289ca30889) * src/qtgui/uiprefs_w.cpp, src/utils/utf8iter.cpp: *** empty log message *** 2007-11-25 07:29 +0000 dockes <dockes> (3782c85019d4) * src/qtgui/i18n/recoll_tr.ts: *** empty log message *** 2007-11-24 16:51 +0000 dockes <dockes> (a41099c58ac0) * src/qtgui/i18n/recoll_fr.ts: accents 2007-11-24 16:43 +0000 dockes <dockes> (eecb572a0935) * src/qtgui/confgui/confguiindex.h: make conftoppanelw a q_object for translations to work 2007-11-24 10:41 +0000 dockes <dockes> (343184d41f3b) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_tr.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/i18n/recoll_xx.ts: *** empty log message *** 2007-11-21 16:34 +0000 dockes <dockes> (966333a903a9) * src/VERSION: *** empty log message *** 2007-11-21 16:34 +0000 dockes <dockes> (aed5f0389421) * 1.10.0 2007-11-21 16:34 +0000 dockes <dockes> (4918fce7a71a) * packaging/debian/changelog, packaging/debian/control, packaging/debian/menu, packaging/debian/rules, packaging/rpm/recoll.spec, packaging/rpm/recollfedora.spec, packaging/rpm/recollmdk.spec, tests/shared.sh, website/CHANGES.txt, website/devel.html, website/download.html, website/features.html, website/fr/features.html, website/index.html.en, website/index.html.fr, website/pics/index.html, website/styles/style.css: 1.10.0 2007-11-21 14:15 +0000 dockes <dockes> (9c57d53ad305 [RECOLL_1_10_0]) * src/qtgui/confgui/confguiindex.cpp, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/recoll.h: allow opening config gui if no index on first start 2007-11-21 09:42 +0000 dockes <dockes> (b1db39055b6d) * src/excludefile: *** empty log message *** 2007-11-21 09:34 +0000 dockes <dockes> (cca64d1bdb79) * src/utils/conftree.cpp: explicitly detect lines beginning with # 2007-11-21 09:00 +0000 dockes <dockes> (2cb85a4bd555) * src/INSTALL, src/README: *** empty log message *** 2007-11-16 15:20 +0000 dockes <dockes> (1f90c7302746) * src/doc/user/usermanual.sgml: *** empty log message *** 2007-11-16 14:28 +0000 dockes <dockes> (d7f21b7adf20) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/internfile/internfile.cpp, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h: indexedmimetypes 2007-11-16 12:21 +0000 dockes <dockes> (8221e8f1ce4f) * src/query/wasastringtoquery.cpp, src/query/wasatorcl.cpp: very small effort to look like xesam simple query 2007-11-16 07:34 +0000 dockes <dockes> (1398d49de21d) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: *** empty log message *** 2007-11-16 07:19 +0000 dockes <dockes> (eedcef5d56b7) * src/qtgui/i18n/recoll_tr.ts: *** empty log message *** 2007-11-15 18:44 +0000 dockes <dockes> (99e585288200) * src/qtgui/preview_w.cpp: comment 2007-11-15 18:39 +0000 dockes <dockes> (1ee213030954) * src/qt4gui/q3richtext_p.h: new file. * src/qt4gui/q3richtext_p.h, src/qt4gui/recoll.pro.in: qt4 movetoanchor 2007-11-15 18:39 +0000 dockes <dockes> (335db8a5c8cb) * src/qtgui/i18n/recoll_it.ts: *** empty log message *** 2007-11-15 18:34 +0000 dockes <dockes> (b3bb7b017f2a) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: moveToAnchor qt4 2007-11-15 18:05 +0000 dockes <dockes> (1fe63dd4f268) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/recoll.pro.in: finally got anchors to work. qt3 2007-11-15 18:05 +0000 dockes <dockes> (3158e59fd92e) * src/qtgui/reslist.cpp: *** empty log message *** 2007-11-13 18:42 +0000 dockes <dockes> (1a7029e2dd4e) * src/doc/man/recoll.1: *** empty log message *** 2007-11-13 18:40 +0000 dockes <dockes> (a5a94cfbfa7d) * src/query/recollq.cpp: keep format constant 2007-11-13 18:40 +0000 dockes <dockes> (09f615e1a305) * src/doc/user/usermanual.sgml: text 2007-11-13 18:39 +0000 dockes <dockes> (ce5a12bb92bd) * tests/badsuffs1/badsuffs1.txt, tests/html/html.txt, tests/mail/mail.txt, tests/ooff/ooff.txt, tests/special/special.txt: 1.10+small changes in dataset 2007-11-13 15:35 +0000 dockes <dockes> (3a8d3f5af0e8) * src/ChangeLog: *** empty log message *** 2007-11-13 15:34 +0000 dockes <dockes> (d23b6a94f4c0) * src/VERSION: 1.10.0? 2007-11-13 10:07 +0000 dockes <dockes> (f3338fa8cb4e) * src/doc/man/recollq.1: new file. * src/doc/man/recollq.1: *** empty log message *** 2007-11-09 18:48 +0000 dockes <dockes> (7859ad070bfc) * src/qtgui/i18n/recoll_fr.ts: 1.9 ? 2007-11-09 18:07 +0000 dockes <dockes> (557d4b9ce60a) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_tr.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/i18n/recoll_xx.ts: *** empty log message *** 2007-11-09 15:56 +0000 dockes <dockes> (2c201bdce017) * src/filters/rcltex: *** empty log message *** 2007-11-09 15:46 +0000 dockes <dockes> (7960c1dd4d0a) * src/kde/kioslave/recoll/00README.txt, src/kde/kioslave/recoll/Makefile, src/kde/kioslave/recoll/kio_recoll.cpp: get things to compile with recoll 1.9 and suse + kde 3.5.5 2007-11-09 13:44 +0000 dockes <dockes> (6196dbaf0aec) * src/sampleconf/mimeview: tex 2007-11-09 11:55 +0000 dockes <dockes> (10ce7112596d) * src/filters/rcltex: new file. * src/filters/rclmedia: deleted file. * src/filters/rclmedia, src/filters/rcltex, src/sampleconf/mimeconf, src/sampleconf/mimemap: added support for indexing TeX text 2007-11-09 11:54 +0000 dockes <dockes> (5a35ec87ecf2) * src/filters/rclid3: comments 2007-11-08 09:35 +0000 dockes <dockes> (bdde14acf3bd) * src/query/recollq.h: new file. * src/lib/Makefile, src/lib/mkMake, src/qtgui/main.cpp, src/qtgui/recoll.pro.in, src/query/Makefile, src/query/recollq.cpp, src/query/recollq.h: allow recoll to be used as a recollq driver 2007-11-08 09:34 +0000 dockes <dockes> (06e94674b8e2) * src/utils/execmd.cpp: include pthread 2007-11-08 09:34 +0000 dockes <dockes> (d6e84478935d) * src/rcldb/stemdb.cpp: debug 2007-11-08 09:32 +0000 dockes <dockes> (9f3349e7358b) * src/qt4gui/recoll.pro.in: turkish 2007-11-08 09:31 +0000 dockes <dockes> (cd6b8b7d2a36) * src/mk/OpenBSD: *** empty log message *** 2007-11-08 07:54 +0000 dockes <dockes> (6e986b6d1e64) * src/query/recollq.cpp: add -b option to only output url list 2007-11-06 11:55 +0000 dockes <dockes> (2b0e2fc0dd88) * src/qtgui/i18n/recoll_tr.ts: new file. * src/qtgui/i18n/recoll_tr.ts: *** empty log message *** 2007-10-27 16:40 +0000 dockes <dockes> (e8ac0b8f6c46) * src/rcldb/rcldb.cpp: comment 2007-10-27 08:40 +0000 dockes <dockes> (2ccaf4ef243e) * src/ChangeLog, src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: *** empty log message *** 2007-10-27 08:40 +0000 dockes <dockes> (e647e4592daa) * src/filters/rcluncomp: allow uncompressing suffix-less files 2007-10-27 08:40 +0000 dockes <dockes> (dc6d97a86685) * src/internfile/internfile.cpp: use pcSubst 2007-10-27 08:39 +0000 dockes <dockes> (54ba3ef75586) * src/rcldb/rcldb.cpp: adjust MatchDecider return type according to xapian version 2007-10-27 07:06 +0000 dockes <dockes> (54b798d7fa02) * src/qtgui/i18n/recoll_xx.ts: sent to ning 2007-10-26 10:42 +0000 dockes <dockes> (acfa4e6c24ba) * src/doc/user/usermanual.sgml: index config gui 2007-10-25 15:51 +0000 dockes <dockes> (12d12311134a) * src/qtgui/confgui/confguiindex.cpp: labels 2007-10-25 15:51 +0000 dockes <dockes> (2a1d29582446) * src/qtgui/confgui/confgui.cpp: use new style combobox constructor 2007-10-25 15:50 +0000 dockes <dockes> (8b45d32c605c) * src/internfile/mh_exec.h: cleanup 2007-10-25 08:04 +0000 dockes <dockes> (0bf8540b6c22) * src/doc/user/usermanual.sgml: *** empty log message *** 2007-10-25 07:27 +0000 dockes <dockes> (5d57c38993af) * src/query/recollq.cpp, src/query/wasatorcl.cpp: added option to query language for filtering on directory 2007-10-25 07:09 +0000 dockes <dockes> (d1adc7006d08) * src/rcldb/rcldb.cpp: add filter topdir to query description 2007-10-24 15:38 +0000 dockes <dockes> (5f1863c33239) * src/rcldb/rcldb.cpp: use a Xapian MatchDecider to filter on dir path 2007-10-24 08:42 +0000 dockes <dockes> (2d337545271f) * src/rcldb/rcldb.cpp: make filter a xapian::MatchDecider, dont change mechanism 2007-10-19 15:25 +0000 dockes <dockes> (935a92d6db39) * src/qtgui/ssearch_w.cpp, src/utils/smallut.cpp: consider cr and lf as whitespace when splitting strings 2007-10-19 14:31 +0000 dockes <dockes> (bb88b5f4fc25) * src/qtgui/confgui/confgui.h, src/qtgui/confgui/confguiindex.cpp: small sizing adjustments 2007-10-18 10:39 +0000 dockes <dockes> (f34f0260a62a) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview_w.cpp, src/qtgui/reslist.cpp: let plaintorich do the chunking, easier to make sure we dont confuse textedit by cutting inside a tag 2007-10-18 10:15 +0000 dockes <dockes> (7c46f29559fe) * src/qtgui/confgui/confguiindex.cpp: qt3 2007-10-17 16:12 +0000 dockes <dockes> (41f711edeb0b) * src/qtgui/plaintorich.cpp: replaced utf8 cgj with good ole bel 2007-10-17 11:40 +0000 dockes <dockes> (102fcc4aa169) * src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/utils/mimeparse.cpp: text/plain attachments were not transcoded to utf-8 2007-10-17 09:57 +0000 dockes <dockes> (dd33128e3a59) * src/common/rclconfig.cpp, src/internfile/internfile.cpp: *** empty log message *** 2007-10-15 13:08 +0000 dockes <dockes> (0a095e89bfb9) * src/kde/recoll_applet/0README.Recoll, src/kde/recoll_applet/AUTHORS, src/kde/recoll_applet/COPYING, src/kde/recoll_applet/ChangeLog, src/kde/recoll_applet/Doxyfile, src/kde/recoll_applet/INSTALL, src/kde/recoll_applet/Makefile.am, src/kde/recoll_applet/Makefile.cvs, src/kde/recoll_applet/Makefile.in, src/kde/recoll_applet/NEWS, src/kde/recoll_applet/README, src/kde/recoll_applet/TODO, src/kde/recoll_applet/acinclude.m4, src/kde/recoll_applet/aclocal.m4, src/kde/recoll_applet/admin/Doxyfile.am, src/kde/recoll_applet/admin/Doxyfile.global, src/kde/recoll_applet/admin/Makefile.common, src/kde/recoll_applet/admin/acinclude.m4.in, src/kde/recoll_applet/admin/am_edit, src/kde/recoll_applet/admin/bcheck.pl, src/kde/recoll_applet/admin/compile, src/kde/recoll_applet/admin/conf.change.pl, src/kde/recoll_applet/admin/config.guess, src/kde/recoll_applet/admin/config.pl, src/kde/recoll_applet/admin/config.sub, src/kde/recoll_applet/admin/configure.in.bot.end, src/kde/recoll_applet/admin/configure.in.min, src/kde/recoll_applet/admin/cvs.sh, src/kde/recoll_applet/admin/debianrules, src/kde/recoll_applet/admin/depcomp, src/kde/recoll_applet/admin/deps.am, src/kde/recoll_applet/admin /detect-autoconf.pl, src/kde/recoll_applet/admin/doxygen.sh, src/kde/recoll_applet/admin/install-sh, src/kde/recoll_applet/admin/libtool.m4.in, src/kde/recoll_applet/admin/ltmain.sh, src/kde/recoll_applet/admin/missing, src/kde/recoll_applet/admin/mkinstalldirs, src/kde/recoll_applet/admin/nmcheck, src/kde/recoll_applet/admin/oldinclude.m4.in, src/kde/recoll_applet/admin/pkg.m4.in, src/kde/recoll_applet/admin/ylwrap, src/kde/recoll_applet/config.h.in, src/kde/recoll_applet/configure, src/kde/recoll_applet/configure.files, src/kde/recoll_applet/configure.in, src/kde/recoll_applet/configure.in.in, src/kde/recoll_applet/doc/Makefile.am, src/kde/recoll_applet/doc/Makefile.in, src/kde/recoll_applet/doc/en/Makefile.am, src/kde/recoll_applet/doc/en/Makefile.in, src/kde/recoll_applet/doc/en/index.docbook, src/kde/recoll_applet/po/Makefile.am, src/kde/recoll_applet/po/Makefile.in, src/kde/recoll_applet/src/Makefile.am, src/kde/recoll_applet/src/Makefile.in, src/kde/recoll_applet/src/kpixmapcombo.cpp, src/kde/recoll_applet/src/kpixmapcombo.h, src/kde/recoll_applet/src/recoll_applet.cpp, src/kde/recoll_applet/src/recoll_applet.desktop, src/kde/recoll_applet/src/recoll_applet.h, src/kde/recoll_applet/src/recoll_applet.lsm, src/kde/recoll_applet/stamp-h.in, src/kde/recoll_applet/subdirs: new file. * src/kde/recoll_applet/0README.Recoll, src/kde/recoll_applet/AUTHORS, src/kde/recoll_applet/COPYING, src/kde/recoll_applet/ChangeLog, src/kde/recoll_applet/Doxyfile, src/kde/recoll_applet/INSTALL, src/kde/recoll_applet/Makefile.am, src/kde/recoll_applet/Makefile.cvs, src/kde/recoll_applet/Makefile.in, src/kde/recoll_applet/NEWS, src/kde/recoll_applet/README, src/kde/recoll_applet/TODO, src/kde/recoll_applet/acinclude.m4, src/kde/recoll_applet/aclocal.m4, src/kde/recoll_applet/admin/Doxyfile.am, src/kde/recoll_applet/admin/Doxyfile.global, src/kde/recoll_applet/admin/Makefile.common, src/kde/recoll_applet/admin/acinclude.m4.in, src/kde/recoll_applet/admin/am_edit, src/kde/recoll_applet/admin/bcheck.pl, src/kde/recoll_applet/admin/compile, src/kde/recoll_applet/admin/conf.change.pl, src/kde/recoll_applet/admin/config.guess, src/kde/recoll_applet/admin/config.pl, src/kde/recoll_applet/admin/config.sub, src/kde/recoll_applet/admin/configure.in.bot.end, src/kde/recoll_applet/admin/configure.in.min, src/kde/recoll_applet/admin/cvs.sh, src/kde/recoll_applet/admin/debianrules, src/kde/recoll_applet/admin/depcomp, src/kde/recoll_applet/admin/deps.am, src/kde/recoll_applet/admin /detect-autoconf.pl, src/kde/recoll_applet/admin/doxygen.sh, src/kde/recoll_applet/admin/install-sh, src/kde/recoll_applet/admin/libtool.m4.in, src/kde/recoll_applet/admin/ltmain.sh, src/kde/recoll_applet/admin/missing, src/kde/recoll_applet/admin/mkinstalldirs, src/kde/recoll_applet/admin/nmcheck, src/kde/recoll_applet/admin/oldinclude.m4.in, src/kde/recoll_applet/admin/pkg.m4.in, src/kde/recoll_applet/admin/ylwrap, src/kde/recoll_applet/config.h.in, src/kde/recoll_applet/configure, src/kde/recoll_applet/configure.files, src/kde/recoll_applet/configure.in, src/kde/recoll_applet/configure.in.in, src/kde/recoll_applet/doc/Makefile.am, src/kde/recoll_applet/doc/Makefile.in, src/kde/recoll_applet/doc/en/Makefile.am, src/kde/recoll_applet/doc/en/Makefile.in, src/kde/recoll_applet/doc/en/index.docbook, src/kde/recoll_applet/po/Makefile.am, src/kde/recoll_applet/po/Makefile.in, src/kde/recoll_applet/src/Makefile.am, src/kde/recoll_applet/src/Makefile.in, src/kde/recoll_applet/src/kpixmapcombo.cpp, src/kde/recoll_applet/src/kpixmapcombo.h, src/kde/recoll_applet/src/recoll_applet.cpp, src/kde/recoll_applet/src/recoll_applet.desktop, src/kde/recoll_applet/src/recoll_applet.h, src/kde/recoll_applet/src/recoll_applet.lsm, src/kde/recoll_applet/stamp-h.in, src/kde/recoll_applet/subdirs: *** empty log message *** 2007-10-14 16:07 +0000 dockes <dockes> (aea3ceac265d) * src/doc/user/usermanual.sgml: *** empty log message *** 2007-10-09 14:08 +0000 dockes <dockes> (008fb8da2cfe) * src/qt4gui/recoll.pro.in, src/qtgui/confgui/confguiindex.cpp, src/qtgui/confgui/confguiindex.h, src/qtgui/idxthread.cpp, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/recoll.pro.in: indexing confgui seems to sort of work 2007-10-09 11:08 +0000 dockes <dockes> (8e165638db48) * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/confgui/confguiindex.cpp, src/qtgui/confgui/confguiindex.h, src/qtgui/confgui/conflinkrcl.h, src/qtgui/confgui/main.cpp: *** empty log message *** 2007-10-09 09:43 +0000 dockes <dockes> (bda697547b28) * src/common/rclconfig.cpp, src/common/rclconfig.h: modified mechanism for confgui updates 2007-10-09 09:40 +0000 dockes <dockes> (314568630e50) * src/utils/conftree.h: *** empty log message *** 2007-10-07 20:22 +0000 dockes <dockes> (a4407de529dc) * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confguiindex.cpp: *** empty log message *** 2007-10-06 07:44 +0000 dockes <dockes> (e12dcaba9422) * src/sampleconf/mimeconf: *** empty log message *** 2007-10-06 07:26 +0000 dockes <dockes> (8c03c83a6353) * src/ChangeLog, src/INSTALL, src/README, src/VERSION: *** empty log message *** 2007-10-06 07:13 +0000 dockes <dockes> (80c8e77d75e3) * src/qtgui/i18n/recoll_xx.ts: new file. * src/doc/user/usermanual.sgml, src/qtgui/i18n/recoll_xx.ts: *** empty log message *** 2007-10-05 14:00 +0000 dockes <dockes> (3f47738c7b7f) * src/query/wasatorcl.cpp: add rclcat prefix to query languages + adapt find_applet to use it 2007-10-05 08:03 +0000 dockes <dockes> (eb9ae456f872) * src/qtgui/main.cpp, src/qtgui/ssearch_w.cpp, src/qtgui/ssearch_w.h: add cmd line option to run query when starting 2007-10-04 12:26 +0000 dockes <dockes> (479712bd069b) * src/rcldb/searchdata.cpp: when search includes composite spans + other terms, increase slack instead of switching to word split 2007-10-04 12:21 +0000 dockes <dockes> (67c23cd41df2) * src/common/rclconfig.cpp, src/common/textsplit.cpp, src/common/textsplit.h: make cjk ngramlen configurable 2007-10-04 12:20 +0000 dockes <dockes> (e9e128bf43ab) * src/index/indexer.cpp: trace 2007-10-03 14:53 +0000 dockes <dockes> (b8852ea7a80c) * src/internfile/Makefile, src/internfile/mh_mbox.cpp, src/internfile/mh_mbox.h: Improve From_ line detection 2007-10-02 14:25 +0000 dockes <dockes> (3379ab8d9013) * src/doc/user/docbook.css, src/doc/user/usermanual.sgml: *** empty log message *** 2007-10-02 14:22 +0000 dockes <dockes> (29a402a23d12) * src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: a few more image files 2007-10-02 14:00 +0000 dockes <dockes> (d0e7241eeb0e) * src/filters/rclflac, src/filters/rclogg: new file. * src/filters/rcljpeg: deleted file. * src/filters/rclflac, src/filters/rcljpeg, src/filters/rclogg: *** empty log message *** 2007-10-02 13:56 +0000 dockes <dockes> (e180ca729bea) * src/filters/rclimg: comments,GPL 2007-10-02 11:39 +0000 dockes <dockes> (7777fdc5d30a) * src/common/rclconfig.cpp, src/common/textsplit.cpp, src/common/textsplit.h: add flag to disable cjk processing 2007-10-01 17:56 +0000 dockes <dockes> (29b1aeb75d23) * src/filters/rclimg: new file. * src/filters/rclimg: initial version from Cedric Scott 2007-10-01 15:57 +0000 dockes <dockes> (b3aeb47d6a43) * src/utils/conftree.cpp: added updates/erase tests 2007-10-01 06:35 +0000 dockes <dockes> (b29617933c16) * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/confgui/confguiindex.cpp, src/qtgui/confgui/main.cpp, src/qtgui/confgui/trconf.pro: qt4 port 2007-10-01 06:19 +0000 dockes <dockes> (78068b236681) * src/VERSION, src/common/rclconfig.cpp, src/common/rclconfig.h, src/qtgui/confgui/confgui.cpp, src/utils/conftree.cpp, src/utils/conftree.h: config update enabling functions 2007-09-29 09:06 +0000 dockes <dockes> (e38c26097ece) * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/confgui/confguiindex.cpp, src/qtgui/confgui/confguiindex.h, src/qtgui/confgui/conflinkrcl.h, src/qtgui/confgui/main.cpp, src/qtgui/confgui/trconf.pro: *** empty log message *** 2007-09-27 15:47 +0000 dockes <dockes> (9ac07bf91591) * src/qtgui/confgui/confguiindex.cpp, src/qtgui/confgui/confguiindex.h, src/qtgui/confgui/conflinkrcl.h: new file. * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/confgui/confguiindex.cpp, src/qtgui/confgui/confguiindex.h, src/qtgui/confgui/conflinkrcl.h, src/qtgui/confgui/main.cpp, src/qtgui/confgui/trconf.pro: *** empty log message *** 2007-09-27 11:03 +0000 dockes <dockes> (436530279a09) * src/utils/conftree.h: comment 2007-09-27 11:02 +0000 dockes <dockes> (a466c387c485) * src/utils/conftree.cpp, src/utils/conftree.h: avoid adding unneeded entries in confstack. fix erase-add resulting in duplicate 2007-09-26 12:16 +0000 dockes <dockes> (8e1e4edb4f4a) * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/confgui/main.cpp, src/qtgui/confgui/trconf.pro: new file. * src/qtgui/confgui/confgui.cpp, src/qtgui/confgui/confgui.h, src/qtgui/confgui/main.cpp, src/qtgui/confgui/trconf.pro: *** empty log message *** 2007-09-22 08:51 +0000 dockes <dockes> (8072f3278663) * src/common/textsplit.cpp, src/utils/utf8iter.h: include assert.h when needed 2007-09-21 16:45 +0000 dockes <dockes> (d85479652341) * src/INSTALL, src/README, src/VERSION, src/doc/user/usermanual.sgml, src/qtgui/recoll.pro.in: *** empty log message *** 2007-09-20 12:22 +0000 dockes <dockes> (28a9c536ebba) * src/common/textsplit.cpp: logs 2007-09-20 08:45 +0000 dockes <dockes> (415256bd7508) * src/common/textsplit.cpp, src/common/textsplit.h, src/utils/utf8iter.h: initial cjk support 2007-09-20 08:43 +0000 dockes <dockes> (66200ff61f31) * src/rcldb/searchdata.cpp: comments,formatting 2007-09-20 08:42 +0000 dockes <dockes> (750b59dea1e9) * src/qtgui/rclmain_w.cpp: restore cursor if cant start query 2007-09-18 20:35 +0000 dockes <dockes> (1d01904f2b55) * src/common/textsplit.cpp, src/common/textsplit.h: use m_ prefix for members 2007-09-18 20:34 +0000 dockes <dockes> (49381b7f40f6) * src/qt4gui/recoll.pro.in: add recoll_xx.ts 2007-09-18 07:01 +0000 dockes <dockes> (7dea06d57ada) * src/qtgui/i18n/recoll_it.ts: changes by Giovanni Cannizzaro 2007-09-11 08:23 +0000 dockes <dockes> (615a70a64b94 [RECOLL_1_9_0]) * src/desktop/recoll-searchgui.desktop: desktop file corrected as per Kartik Mistry patch 2007-09-10 05:44 +0000 dockes <dockes> (78b0c9bd47bb) * src/qtgui/i18n/recoll_fr.ts: long menu labels cause pbs at least on macosx 2007-09-08 17:26 +0000 dockes <dockes> (ef2964b2e49e) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: *** empty log message *** 2007-09-08 17:25 +0000 dockes <dockes> (000b2b01844d) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/preview_w.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: change hghlight text size limit to configurable value 2007-09-08 17:21 +0000 dockes <dockes> (c0ab1e961f0a) * src/qtgui/viewaction_w.cpp: added missing space in string 2007-09-08 17:21 +0000 dockes <dockes> (f70ce9c4c753) * src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp: renamed preferencesQuery_PrefsAction to queryPrefsAction 2007-09-08 17:19 +0000 dockes <dockes> (17eefeb77500 [RECOLL_1_9_1cjk2, RECOLL_1_9_1cjk1]) * src/qtgui/plaintorich.cpp: comment 2007-09-08 09:44 +0000 dockes <dockes> (8aabe9bc2d85) * src/utils/readfile.cpp: small pb in solaris fix 2007-09-08 08:07 +0000 dockes <dockes> (4b862559adbb) * src/mk/SunOS, src/utils/pathut.cpp, src/utils/readfile.cpp: SunOS 2.8 fixes 2007-09-07 14:58 +0000 dockes <dockes> (f0b17af1f5d7) * src/configure, src/configure.ac: always add lz to lxapian 2007-09-07 12:39 +0000 dockes <dockes> (b10ac30fe130) * website/CHANGES.txt: *** empty log message *** 2007-09-07 08:05 +0000 dockes <dockes> (f031116372e8) * src/rcldb/rcldb.cpp: improve purge error message printing 2007-09-07 08:04 +0000 dockes <dockes> (276b259f9ec6) * src/qtgui/i18n/recoll_it.ts: new 1.9 translation by C. Rigamont 2007-09-07 08:04 +0000 dockes <dockes> (450e1342467c) * src/sampleconf/mimemap: fix wordperfect spurious extensions 2007-09-07 08:03 +0000 dockes <dockes> (624a100107be [RECOLL_1_9_1cjk]) * website/BUGS.txt: update xapian near to 1.0.2 2007-09-07 08:03 +0000 dockes <dockes> (a0d360caf71e) * website/copydocs: to_mac 2007-09-01 19:12 +0000 dockes <dockes> (3ebdb5af664f) * src/qt4gui/recoll.pro.in, src/qtgui/i18n/recoll_de.ts, src/qtgui/recoll.pro.in: *** empty log message *** 2007-08-31 09:04 +0000 dockes <dockes> (32533d0d11d0) * src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h: pressing CR in advsearch would run query twice because of start autodefault 2007-08-31 07:23 +0000 dockes <dockes> (bb17fa4cfaca) * src/qtgui/images/d_firstpage.png, src/qtgui/images/firstpage.png: new file. * src/qtgui/images/d_firstpage.png, src/qtgui/images/firstpage.png: *** empty log message *** 2007-08-30 10:11 +0000 dockes <dockes> (c75b5f42b33d) * src/INSTALL, src/README, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: *** empty log message *** 2007-08-30 10:00 +0000 dockes <dockes> (7c4ccceae2a7) * website/BUGS.txt, website/CHANGES.txt, website/download.html, website/features.html: *** empty log message *** 2007-08-30 09:01 +0000 dockes <dockes> (687cad7b46de) * src/doc/user/usermanual.sgml, src/index/indexer.cpp, src/index/indexer.h, src/index/rclmonrcv.cpp, src/sampleconf/recoll.conf.in, src/utils/fstreewalk.cpp, src/utils/fstreewalk.h: add followLinks option 2007-08-30 08:39 +0000 dockes <dockes> (6af3a2216074) * src/doc/user/usermanual.sgml: add followLinks option 2007-08-28 08:12 +0000 dockes <dockes> (6385c6a9c88e) * src/index/indexer.cpp: allow symlinks in topdirs 2007-08-28 08:08 +0000 dockes <dockes> (a3df89087437) * src/utils/fstreewalk.cpp, src/utils/fstreewalk.h: follow top (entry) symlinks even if nofollow is set 2007-08-28 08:07 +0000 dockes <dockes> (19ac4f90b7e7) * src/internfile/internfile.cpp: error msg 2007-08-26 13:52 +0000 dockes <dockes> (fa08f95a4d95) * src/doc/user/usermanual.sgml: add wordperfect ext app info 2007-08-26 13:34 +0000 dockes <dockes> (ac877cc2e3ad) * src/filters/rclwpd: new file. * src/filters/rclwpd, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: added wordperfect support 2007-08-26 13:34 +0000 dockes <dockes> (7472abcdbc4a) * src/sampleconf/recoll.conf.in: add commented entries for daem* 2007-08-07 08:45 +0000 dockes <dockes> (ad6dad566902) * src/qtgui/rclmain_w.cpp: *** empty log message *** 2007-08-07 08:42 +0000 dockes <dockes> (2040417c73e4) * src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp: qt3 adjustments 2007-08-07 08:26 +0000 dockes <dockes> (55c7dc79c190) * src/aspell/rclaspell.cpp, src/sampleconf/recoll.conf.in: *** empty log message *** 2007-08-05 05:55 +0000 dockes <dockes> (3acd192c01d1) * src/utils/conftree.h: comments 2007-08-05 05:49 +0000 dockes <dockes> (afee970ae166) * src/utils/conftree.h: *** empty log message *** 2007-08-04 07:22 +0000 dockes <dockes> (9afb2050f462) * src/utils/conftree.cpp, src/utils/conftree.h: Allow updates in confstacks 2007-08-03 07:50 +0000 dockes <dockes> (28ae2e572dcf) * src/utils/Makefile, src/utils/conftree.cpp, src/utils/conftree.h: have conftree preserve comments and ordering 2007-08-02 06:33 +0000 dockes <dockes> (4da8b2dbcaa6) * src/qt4gui/recoll.qrc, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/recoll.pro.in, src/qtgui/reslist.cpp, src/qtgui/reslist.h: added gotofirstpage action 2007-08-01 10:04 +0000 dockes <dockes> (c91831fab8a0) * src/qtgui/guiutils.h, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/ssearch_w.cpp, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h, src/rcldb/stemdb.cpp, src/rcldb/stemdb.h: Allow stem expansion for several (all) stemming languages at a time 2007-08-01 07:55 +0000 dockes <dockes> (5d13d87e6e14) * src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h: allow setting stemlang from prefs menu 2007-07-20 14:50 +0000 dockes <dockes> (573069870fd4) * src/configure, src/configure.ac: check for uic3 during qt4 configure 2007-07-20 14:43 +0000 dockes <dockes> (32ae47904cca) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: preview: dont search for anchors if we have none 2007-07-20 14:32 +0000 dockes <dockes> (eac614c9a725) * src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/qtgui/reslist.h: *** empty log message *** 2007-07-20 11:44 +0000 dockes <dockes> (6133f68f886f) * src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: factorize previewNext/Prev 2007-07-20 11:38 +0000 dockes <dockes> (4dfc3942351a) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: more preview window interface cleanup 2007-07-20 10:55 +0000 dockes <dockes> (d57bd5e6cb2d) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain_w.cpp: cleaned up preview window interface 2007-07-14 16:53 +0000 dockes <dockes> (35087158d61f) * src/common/autoconfig.h.in, src/configure, src/configure.ac, src/mk/AIX, src/mk/Darwin, src/mk/Linux, src/mk/SunOS, src/utils/execmd.cpp: handle putenv arg constness in configure 2007-07-13 10:24 +0000 dockes <dockes> (98774298901d) * src/INSTALL, src/README, src/doc/man/recoll.conf.5, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts, website/BUGS.txt, website/CHANGES.txt, website/download.html: *** empty log message *** 2007-07-13 07:17 +0000 dockes <dockes> (d2c5a6098bbd) * src/doc/user/docbook.css, src/doc/user/usermanual.sgml: 1.9 changes 2007-07-13 07:10 +0000 dockes <dockes> (2569115962c0) * src/qtgui/uiprefs.ui: msg 2007-07-13 07:00 +0000 dockes <dockes> (2bd0371b8e12) * src/qtgui/plaintorich.cpp: adjust term beacon for better finding ? 2007-07-13 06:31 +0000 dockes <dockes> (f7d41e95166c) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain_w.cpp: better handle preview close during load 2007-07-12 17:28 +0000 dockes <dockes> (5b6f1204d077) * src/rcldb/rcldb.cpp: *** empty log message *** 2007-07-12 17:13 +0000 dockes <dockes> (9345d3db5ff2) * src/filters/rclpdf: dont use anchored regexps for stripping whitespace, ubuntu mawk ignores the anchor 2007-07-12 13:41 +0000 dockes <dockes> (1fb4e582fe5b) * src/utils/cancelcheck.h: *** empty log message *** 2007-07-12 10:53 +0000 dockes <dockes> (eb352f6c17ae) * src/index/rclmonrcv.cpp, src/utils/fstreewalk.cpp, src/utils/fstreewalk.h: monitor: dont add watch on created dir if in skippedXXX 2007-07-12 10:13 +0000 dockes <dockes> (d55862505674) * src/ChangeLog, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: fix v1.8 default format string if we find it 2007-07-12 08:34 +0000 dockes <dockes> (b69b14b67cd2) * src/rcldb/rcldb.cpp: use uniform code for Xapian exception catching + catch a few more, esp. databaseModified cases 2007-07-12 08:23 +0000 dockes <dockes> (ffe9a12f9237) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/reslist.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: icon now part of paragraph format 2007-07-11 10:05 +0000 dockes <dockes> (a8d4da32f304) * src/qtgui/reslist.cpp: dont create popup in irrelevant areas 2007-07-10 09:24 +0000 dockes <dockes> (993776d69bab) * src/sampleconf/recoll.conf.in: idxflushnb default 10 2007-07-10 09:23 +0000 dockes <dockes> (e800d4e4d1de) * src/doc/man/recollindex.1, src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: recollindex -l 2007-07-10 05:44 +0000 dockes <dockes> (7247df0336ab) * src/sampleconf/recoll.conf.in: *** empty log message *** 2007-07-09 17:21 +0000 dockes <dockes> (ef8eddb1b94a) * src/doc/user/usermanual.sgml: *** empty log message *** 2007-07-01 06:52 +0000 dockes <dockes> (03cb707d9122) * src/filters/rclid3: new file. * src/filters/rclid3, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: audio tags support improvement: flac+ogg. use FORPREVIEW 2007-06-26 17:07 +0000 dockes <dockes> (ec5b66db8aea) * src/qtgui/i18n/recoll_de.ts, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: *** empty log message *** 2007-06-26 16:58 +0000 dockes <dockes> (34658791397a) * *** empty log message *** 2007-06-26 16:58 +0000 dockes <dockes> (26a811724423) * packaging/rpm/recollCooker.spec, website/fr/features.html, website/mario.png, website/perfs.html, website/smile.png: new file. * packaging/rpm/recollCooker.spec, src/doc/user/usermanual.sgml, website/BUGS.txt, website/CHANGES.txt, website/credits.html, website/doc.html, website/download.html, website/features.html, website/fr/features.html, website/index.html.en, website/index.html.fr, website/mario.png, website/perfs.html, website/rclidxfmt.html, website/smile.png, website/styles/style.css: *** empty log message *** 2007-06-26 16:09 +0000 dockes <dockes> (d4a3058d613e) * src/internfile/internfile.cpp, src/internfile/internfile.h: comments 2007-06-26 16:08 +0000 dockes <dockes> (7115d37ab33d) * src/configure, src/configure.ac, src/mk/Darwin, src/qtgui/reslist.cpp, src/recollinstall.in: get things to sort of compile / install on macosx 2007-06-26 15:38 +0000 dockes <dockes> (02621fd62ca0) * src/ChangeLog: new file. * src/ChangeLog: *** empty log message *** 2007-06-26 11:59 +0000 dockes <dockes> (51061217635d) * src/excludefile, src/makesrcdist.sh: small mkdist fixes 2007-06-25 18:31 +0000 dockes <dockes> (5f173fcd227f) * src/INSTALL, src/README: *** empty log message *** 2007-06-25 10:25 +0000 dockes <dockes> (048658cd678b) * src/rcldb/rcldb.cpp: simplified and hopefully improved abstract generation 2007-06-25 10:13 +0000 dockes <dockes> (14ecb9d719e7) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/reslist.cpp: plaintorich: only setup beacons if needed 2007-06-22 06:14 +0000 dockes <dockes> (0584daa67e7c) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/query/wasatorcl.cpp, src/rcldb/rcldb.cpp, src/rcldb/searchdata.cpp, src/sampleconf/mimeconf: handle mime: and ext: in qlang 2007-06-21 11:56 +0000 dockes <dockes> (e5102468f77e) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: slightly reorganized Db::close/~Db code 2007-06-21 11:14 +0000 dockes <dockes> (e360a50fdaa5) * src/common/rclconfig.cpp: beware of unsigneds diffs when comparing to 0 ! 2007-06-20 13:16 +0000 dockes <dockes> (e515c5541bd4) * src/qtgui/preview_w.cpp, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/query/history.cpp, src/query/history.h: menu entry to reset document history 2007-06-19 16:19 +0000 dockes <dockes> (675d2fed7a32) * src/qtgui/rclmain_w.cpp, src/qtgui/sort_w.cpp: fix sort state restoration which didnt work 2007-06-19 15:48 +0000 dockes <dockes> (36fa1c12d616) * src/rcldb/rcldb.cpp: try to better print delete exception messages 2007-06-19 15:47 +0000 dockes <dockes> (304862edc545) * src/query/xadump.cpp: option X 2007-06-19 15:47 +0000 dockes <dockes> (23a728d3cdd7) * src/query/recollq.cpp: compile 2007-06-19 12:27 +0000 dockes <dockes> (5ee1b5e9168e) * src/internfile/internfile.cpp, src/internfile/internfile.h: get test driver to compile 2007-06-19 12:17 +0000 dockes <dockes> (8974a52d2baa) * src/internfile/htmlparse.h, src/internfile/mh_html.cpp, src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h: renamed the html charset values to stick to omega usage 2007-06-19 10:28 +0000 dockes <dockes> (e66870aeadb6) * src/internfile/htmlparse.cpp, src/internfile/htmlparse.h, src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h: updated html parser to omega 1.0.1 + moved entity decoder to myhtmlparse to minimize amount of diffs 2007-06-19 08:36 +0000 dockes <dockes> (e2533617731d) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/internfile/internfile.cpp, src/internfile/mh_html.cpp, src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h, src/qtgui/preview_w.cpp, src/qtgui/reslist.cpp, src/query/docseq.h, src/query/docseqdb.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/rcldoc.h, src/rcldb/searchdata.cpp, src/sampleconf/mimeconf: added open-ended field name handling 2007-06-19 07:52 +0000 dockes <dockes> (73ccb629ad66) * src/common/autoconfig.h.in, src/configure, src/configure.ac, src/index/csguess.cpp, src/utils/transcode.cpp: added test for iconv parm 2 constness 2007-06-18 13:04 +0000 dockes <dockes> (bb1262134776) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.cpp, src/sampleconf/mimeconf: implement dynamic field name to prefix translation, query side 2007-06-15 11:41 +0000 dockes <dockes> (5eccc05a2ae7) * src/filters/rclabw, src/filters/rclsoff, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: added abiword + some oofice cleanup 2007-06-15 09:25 +0000 dockes <dockes> (f5b1666a10e6) * src/filters/rclabw: new file. * src/filters/rclabw: *** empty log message *** 2007-06-14 08:20 +0000 dockes <dockes> (dc698e7b3c84) * src/rcldb/rcldb.cpp: removed the "weak" date, not used and not in omega anymore 2007-06-13 17:03 +0000 dockes <dockes> (3d509dbc275c) * src/qtgui/reslist.cpp: textedit autext sometimes switched to plain at eol? 2007-06-12 13:31 +0000 dockes <dockes> (793abec1cee4) * src/qtgui/plaintorich.cpp, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/qtgui/reslist.h: somewhat fixed qt4 selection problems 2007-06-12 10:33 +0000 dockes <dockes> (261ca6c11087) * src/qtgui/ssearch_w.cpp: adjust event handling for qt4, get esc-spc to work 2007-06-12 10:32 +0000 dockes <dockes> (97c9f158e297) * src/qtgui/plaintorich.cpp: comments 2007-06-12 08:50 +0000 dockes <dockes> (28d503078074) * src/qtgui/rclmain_w.cpp: *** empty log message *** 2007-06-12 08:46 +0000 dockes <dockes> (d3f305e57522) * src/query/recollq.cpp: getMainConfig 2007-06-11 08:33 +0000 dockes <dockes> (5542196b466a) * src/qtgui/rclmain_w.cpp: set busy cursor while search runs 2007-06-11 05:51 +0000 dockes <dockes> (bf5090aed2fd) * src/Makefile.in: *** empty log message *** 2007-06-11 05:49 +0000 dockes <dockes> (9327b736d7ff) * src/Makefile.in, src/qt4gui/uifrom3: *** empty log message *** 2007-06-11 05:45 +0000 dockes <dockes> (cbb602782461) * src/desktop/recoll.png, src/desktop/recoll.xcf: new file. * src/desktop/recoll-searchgui.png, src/desktop/recoll-searchgui.xcf: deleted file. * src/desktop/recoll-searchgui.desktop, src/desktop/recoll- searchgui.png, src/desktop/recoll-searchgui.xcf, src/desktop/recoll.png, src/desktop/recoll.xcf, src/makestaticdist.sh, src/recollinstall.in: icon named recoll.png 2007-06-11 05:38 +0000 dockes <dockes> (9268fba2c65c) * src/index/indexer.cpp: changed level of missing helpers message 2007-06-10 12:26 +0000 dockes <dockes> (f5b6dcd36de0) * src/mk/OpenBSD: new file. * src/mk/OpenBSD: *** empty log message *** 2007-06-08 16:47 +0000 dockes <dockes> (96f2807957dd) * src/common/rclconfig.h, src/index/indexer.cpp, src/index/recollindex.cpp, src/mk/FreeBSD, src/qtgui/main.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: added file system usage check 2007-06-08 16:46 +0000 dockes <dockes> (0c11deb1a678) * src/doc/user/usermanual.sgml: *** empty log message *** 2007-06-08 16:46 +0000 dockes <dockes> (4f2c0d45e15b) * src/desktop/recoll-searchgui.desktop, src/desktop/recoll- searchgui.png, src/desktop/recoll-searchgui.xcf: new icon 2007-06-08 16:05 +0000 dockes <dockes> (6835d2fbb56c) * src/rcldb/rcldb.h: comments and ordering 2007-06-08 15:30 +0000 dockes <dockes> (aeffac1f3f2d) * src/utils/pathut.cpp, src/utils/pathut.h: fsocc 2007-06-08 14:01 +0000 dockes <dockes> (7c47d8aae3cc) * src/filters/rclkwd: new file. * src/filters/rclkwd, src/sampleconf/mimeview: kword support 2007-06-08 13:51 +0000 dockes <dockes> (53a1012a564f) * src/filters/rcldjvu, src/filters/rcldoc, src/filters/rcldvi, src/filters/rclgaim, src/filters/rcljpeg, src/filters/rcllyx, src/filters/rclman, src/filters/rclmedia, src/filters/rclpdf, src/filters/rclppt, src/filters/rclps, src/filters/rclrtf, src/filters/rclscribus, src/filters/rclsoff, src/filters/rclxls, src/filters/recfiltcommon, src/sampleconf/mimeconf, src/sampleconf/mimemap: kword support 2007-06-08 12:33 +0000 dockes <dockes> (a56bc180327b) * src/query/recollq.cpp: added stopfile parameter 2007-06-08 12:32 +0000 dockes <dockes> (7b3710f69cd0) * src/filters/rcljpeg: new file. * src/filters/rcljpeg, src/sampleconf/mimeconf: rcljpeg 2007-06-08 12:31 +0000 dockes <dockes> (0b20447d105e) * src/common/rclconfig.cpp: improve message about bad config 2007-06-02 08:30 +0000 dockes <dockes> (dfa3e5682035) * src/rcldb/Makefile, src/rcldb/stoplist.cpp, src/rcldb/stoplist.h: new file. * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/indexer.cpp, src/lib/Makefile, src/lib/mkMake, src/qtgui/main.cpp, src/rcldb/Makefile, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.cpp, src/rcldb/stoplist.cpp, src/rcldb/stoplist.h, src/utils/readfile.cpp, src/utils/readfile.h: minimal experimental stopword functionality 2007-06-01 05:44 +0000 dockes <dockes> (b9f3d4b61852) * src/qtgui/preview_w.cpp: preview: space and backspace bound to pgdown/pgup 2007-05-30 12:31 +0000 dockes <dockes> (105744d9f609) * src/index/indexer.cpp, src/internfile/mh_html.cpp, src/internfile/mh_html.h, src/qtgui/plaintorich.cpp, src/query/xadump.cpp, src/utils/transcode.cpp: improve transcode error printing 2007-05-30 12:30 +0000 dockes <dockes> (d4fa167018eb) * src/doc/user/usermanual.sgml: *** empty log message *** 2007-05-30 12:30 +0000 dockes <dockes> (6027fd8afb12) * src/rcldb/rcldb.cpp: improve add_document error message printing 2007-05-30 12:29 +0000 dockes <dockes> (234dc300c26b) * src/qtgui/reslist.cpp, src/qtgui/reslist.h: escape possibly not html-safe text 2007-05-24 09:35 +0000 dockes <dockes> (ec684a070c43) * src/rcldb/stemdb.cpp: comment 2007-05-24 07:48 +0000 dockes <dockes> (deedeff93a6e) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/sort_w.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: optionally remember sorting state between invocations 2007-05-24 07:47 +0000 dockes <dockes> (e6bb3bced970) * src/configure, src/configure.ac, src/qt4gui/uifrom3: make uifrom3 a makefile 2007-05-23 09:19 +0000 dockes <dockes> (4f9ab7436818) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview.ui, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: in preview window if search line empty look for search terms 2007-05-23 08:29 +0000 dockes <dockes> (644c4e20106b) * src/internfile/internfile.cpp: *** empty log message *** 2007-05-23 08:28 +0000 dockes <dockes> (1927522b5826) * src/common/rclinit.cpp, src/utils/execmd.cpp: cant block sigcld globally cause qt needs it 2007-05-22 08:33 +0000 dockes <dockes> (2c0d94ae674a) * src/internfile/internfile.cpp, src/internfile/mh_html.cpp: let email attachments inherit date and author from parent message 2007-05-22 07:40 +0000 dockes <dockes> (fc644359e793) * src/index/indexer.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: implemented adjustable indexing flush threshold 2007-05-21 14:26 +0000 dockes <dockes> (d70d7b6988f0) * src/qtgui/rclmain_w.cpp: reopen db for each search during query 2007-05-21 13:30 +0000 dockes <dockes> (7f65a405e028) * src/common/rclinit.cpp, src/common/rclinit.h, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp, src/index/recollindex.cpp, src/qtgui/idxthread.cpp, src/qtgui/main.cpp, src/rcldb/rcldb.cpp, src/utils/execmd.cpp, src/utils/execmd.h: make sure signals are only handled by the main thread. Fix bus error on rclmon exit (double delete) 2007-05-21 12:03 +0000 dockes <dockes> (7af2d0c361be) * src/utils/smallut.h: *** empty log message *** 2007-05-21 09:00 +0000 dockes <dockes> (9ee50650bd6f) * src/index/indexer.cpp, src/index/rclmonprc.cpp: better handle aspell errors: dont exit from monitor on aux db creation failure, and dont retry forever 2007-05-21 07:24 +0000 dockes <dockes> (53f18ed9c2f8) * src/VERSION, src/configure, src/configure.ac, src/doc/user/usermanual.sgml, src/qt4gui/uifrom3, src/sampleconf/recoll.conf.in: removed --enable-qt4, rely on qmake output instead 2007-05-21 06:46 +0000 dockes <dockes> (d6267bb0e30f) * website/BUGS.txt, website/CHANGES.txt, website/devel.html, website/doc.html, website/download.html, website/index.html.en, website/index.html.fr: *** empty log message *** 2007-05-19 07:32 +0000 dockes <dockes> (cbbd4158e0a8) * website/doc.html: new file. * website/doc.html: *** empty log message *** 2007-05-18 12:05 +0000 dockes <dockes> (75610b300ee1 [RECOLL_1_8_2]) * src/recollinstall.in: qt4 install glitches 2007-05-18 11:16 +0000 dockes <dockes> (c9a0be6210be) * src/README: *** empty log message *** 2007-05-18 07:49 +0000 dockes <dockes> (eaf500145dd5) * src/VERSION: 1.8.2 2007-05-18 07:49 +0000 dockes <dockes> (fc64434e87c0) * packaging/debian/changelog, tests/runtests.sh, website/BUGS.txt, website/CHANGES.txt, website/download.html: *** empty log message *** 2007-05-18 07:41 +0000 dockes <dockes> (6bec0784b8fd) * src/doc/user/usermanual.sgml: doc fix 2007-05-18 07:41 +0000 dockes <dockes> (022d354a0a2f) * src/sampleconf/recoll.conf.in: add .beagle to stops 2007-05-18 07:41 +0000 dockes <dockes> (451a13663a00) * src/rcldb/rcldb.cpp, src/rcldb/stemdb.cpp: change method name deprecated in xap 1.0 2007-05-18 07:40 +0000 dockes <dockes> (54bfc83a6186) * src/query/Makefile: *** empty log message *** 2007-05-18 07:40 +0000 dockes <dockes> (ef599af3e2e7) * src/configure.ac: use $libdir instead of /usr/lib (64bits machs) 2007-05-16 11:28 +0000 dockes <dockes> (2cced3d0aa32) * src/qtgui/i18n/recoll_it.ts: *** empty log message *** 2007-04-22 07:36 +0000 dockes <dockes> (8628fca949e7) * src/qtgui/i18n/recoll_de.ts: new file. * src/qtgui/i18n/recoll_de.ts: *** empty log message *** 2007-03-28 19:30 +0000 dockes <dockes> (51c5bdb227cd) * website/BUGS.txt, website/download.html, website/index.html.en, website/index.html.fr: *** empty log message *** 2007-03-08 12:24 +0000 dockes <dockes> (0efcbb1564f2) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo: 1.8.1 2007-03-08 12:04 +0000 dockes <dockes> (813c82bcc951 [RECOLL_1_8_1]) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- plist, packaging/debian/changelog, packaging/rpm/recoll.spec, packaging/rpm/recollfedora.spec, packaging/rpm/recollmdk.spec, src/VERSION, src/makestaticdist.sh, website/BUGS.txt, website/CHANGES.txt, website/download.html: version 1.8.1 ? 2007-02-20 09:30 +0000 dockes <dockes> (817cdab71c1c [RECOLL_1_8_0]) * src/recollinstall.in: go back to not using xdg 2007-02-20 07:57 +0000 dockes <dockes> (d1f1b31e4a58) * website/index.html.en, website/index.html.fr: new file. * website/index.html: deleted file. * packaging/debian/changelog, website/BUGS.txt, website/CHANGES.txt, website/credits.html, website/download.html, website/features.html, website/index.html, website/index.html.en, website/index.html.fr: *** empty log message *** 2007-02-20 07:43 +0000 dockes <dockes> (1f4b07f4cb62) * src/qtgui/recoll.pro.in: *** empty log message *** 2007-02-20 07:33 +0000 dockes <dockes> (dc922603c639) * src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_it.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: *** empty log message *** 2007-02-20 07:19 +0000 dockes <dockes> (d6b63dc759cd) * src/INSTALL, src/README: *** empty log message *** 2007-02-19 18:15 +0000 dockes <dockes> (a1331ff143f7) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h: make shift-arrow in preview work with qt4 and avoid reentrancy while loading a file 2007-02-19 18:14 +0000 dockes <dockes> (66c79bcff30e) * src/utils/execmd.cpp: block sigcld, it sometimes causes eintrs during the select() call 2007-02-19 18:05 +0000 dockes <dockes> (db9e1830a040) * src/internfile/internfile.cpp: check file name not empty on return from uncomp exec 2007-02-19 16:28 +0000 dockes <dockes> (19982a948347) * src/qtgui/spell_w.cpp: stemming language choice was not observed in term explorer 2007-02-19 16:10 +0000 dockes <dockes> (26815f6c7ce0) * src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h: cleanup ign file types handling/saving 2007-02-14 15:02 +0000 dockes <dockes> (714300cb7780) * tests/empty/empty.sh, tests/empty/empty.txt, tests/html/html.sh, tests/html/html.txt, tests/images/images.sh, tests/images/images.txt, tests/koi8r/koi8r.sh, tests/koi8r/koi8r.txt, tests/mail/mail.sh, tests/mail/mail.txt, tests/notypes/notypes.sh, tests/notypes/notypes.txt, tests/rfc2231/rfc2231.sh, tests/rfc2231/rfc2231.txt, tests/special/special.sh, tests/special/special.txt, tests/txt/txt.sh, tests/txt/txt.txt, tests/utf8/utf8.sh, tests/utf8/utf8.txt: new file. * tests/empty/empty.sh, tests/empty/empty.txt, tests/html/html.sh, tests/html/html.txt, tests/images/images.sh, tests/images/images.txt, tests/koi8r/koi8r.sh, tests/koi8r/koi8r.txt, tests/lyx/lyx.txt, tests/mail/mail.sh, tests/mail/mail.txt, tests/notypes/notypes.sh, tests/notypes/notypes.txt, tests/rfc2231/rfc2231.sh, tests/rfc2231/rfc2231.txt, tests/special/special.sh, tests/special/special.txt, tests/txt/txt.sh, tests/txt/txt.txt, tests/utf8/utf8.sh, tests/utf8/utf8.txt: *** empty log message *** 2007-02-14 11:52 +0000 dockes <dockes> (b3f3859ce5e5) * tests/boolean/boolean.sh, tests/boolean/boolean.txt, tests/delete/delete.sh, tests/delete/delete.txt, tests/dirwithblanks/dirwithblanks.sh, tests/dirwithblanks/dirwithblanks.txt, tests/djvu/djvu.sh, tests/djvu/djvu.txt, tests/dvi/dvi.sh, tests/dvi/dvi.txt, tests/lyx/lyx.sh, tests/lyx/lyx.txt, tests/media/media.sh, tests/media/media.txt, tests/msword/msword.sh, tests/msword/msword.txt, tests/ooff/ooff.sh, tests/ooff/ooff.txt, tests/pdf/pdf.sh, tests/pdf/pdf.txt, tests/postscript/postscript.sh, tests/postscript/postscript.txt, tests/ppt/ppt.sh, tests/ppt/ppt.txt, tests/rtf/rtf.sh, tests/rtf/rtf.txt, tests/scribus/scribus.sh, tests/scribus/scribus.txt, tests/xls/xls.sh, tests/xls/xls.txt: new file. * tests/Maildir1/Maildir1.txt, tests/andor/andor.txt, tests/badsuffs1/badsuffs1.txt, tests/boolean/boolean.sh, tests/boolean/boolean.txt, tests/delete/delete.sh, tests/delete/delete.txt, tests/dirwithblanks/dirwithblanks.sh, tests/dirwithblanks/dirwithblanks.txt, tests/djvu/djvu.sh, tests/djvu/djvu.txt, tests/dvi/dvi.sh, tests/dvi/dvi.txt, tests/lyx/lyx.sh, tests/lyx/lyx.txt, tests/media/media.sh, tests/media/media.txt, tests/msword/msword.sh, tests/msword/msword.txt, tests/ooff/ooff.sh, tests/ooff/ooff.txt, tests/pdf/pdf.sh, tests/pdf/pdf.txt, tests/postscript/postscript.sh, tests/postscript/postscript.txt, tests/ppt/ppt.sh, tests/ppt/ppt.txt, tests/rtf/rtf.sh, tests/rtf/rtf.txt, tests/scribus/scribus.sh, tests/scribus/scribus.txt, tests/shared.sh, tests/skipped/skipped.sh, tests/skipped/skipped.txt, tests/xls/xls.sh, tests/xls/xls.txt: *** empty log message *** 2007-02-14 10:10 +0000 dockes <dockes> (04c3156fd4dd) * src/doc/user/usermanual.sgml, src/makestaticdist.sh, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain_w.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp, src/recollinstall.in, src/sampleconf/mimeview: add user pref to use xdg-open for all document edits 2007-02-14 10:09 +0000 dockes <dockes> (7886dd99d419) * src/rcldb/rcldb.cpp: during indexing use simple file name as title if this is empty. This allows storing the sfn for subdocs for which the url sfn doesnt make sense as title 2007-02-14 10:08 +0000 dockes <dockes> (fb42e10e5a7b) * src/query/recollq.cpp: adjust format to help the test set scripts 2007-02-14 08:54 +0000 dockes <dockes> (5e02666b38db) * src/desktop/xdg-utils-1.0.1/scripts/xdg-open: new file. * src/desktop/xdg-utils-1.0.1/scripts/xdg-open: *** empty log message *** 2007-02-14 08:16 +0000 dockes <dockes> (eb0fd52ef15a) * tests/Maildir/Maildir.sh, tests/Maildir/Maildir.txt, tests/Maildir1/Maildir1.sh, tests/Maildir1/Maildir1.txt, tests/andor/andor.sh, tests/andor/andor.txt, tests/badsuffs/badsuffs.sh, tests/badsuffs/badsuffs.txt, tests/badsuffs1/badsuffs1.sh, tests/badsuffs1/badsuffs1.txt, tests/runtests.sh, tests/shared.sh, tests/skipped/skipped.sh, tests/skipped/skipped.txt: new file. * tests/Maildir/Maildir.sh, tests/Maildir/Maildir.txt, tests/Maildir1/Maildir1.sh, tests/Maildir1/Maildir1.txt, tests/andor/andor.sh, tests/andor/andor.txt, tests/badsuffs/badsuffs.sh, tests/badsuffs/badsuffs.txt, tests/badsuffs1/badsuffs1.sh, tests/badsuffs1/badsuffs1.txt, tests/runtests.sh, tests/shared.sh, tests/skipped/skipped.sh, tests/skipped/skipped.txt: *** empty log message *** 2007-02-13 10:58 +0000 dockes <dockes> (19c29e100995) * src/query/wasatorcl.cpp, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: propagate wasa nostem modifier 2007-02-12 18:16 +0000 dockes <dockes> (bf3060f2e259) * src/query/wasastringtoquery.cpp, src/query/wasastringtoquery.h: add wasabi modifiers 2007-02-12 18:14 +0000 dockes <dockes> (6ae625065d64) * src/qtgui/guiutils.cpp: dont set Helvetica as default font 2007-02-08 17:05 +0000 dockes <dockes> (f23e18da0362) * src/index/indexer.cpp, src/index/indexer.h, src/internfile/internfile.cpp, src/internfile/internfile.h, src/qtgui/preview_w.cpp, src/utils/smallut.cpp, src/utils/smallut.h: improve handling of missing helpers messages 2007-02-08 17:03 +0000 dockes <dockes> (f53e952b71cd) * src/filters/rcldvi: typos 2007-02-08 12:25 +0000 dockes <dockes> (ba982598a66f) * src/internfile/internfile.h, src/query/recollq.cpp: clarify temp dir usage in internfile 2007-02-08 09:03 +0000 dockes <dockes> (876ec27bd9c0) * src/qtgui/reslist.cpp, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h: qt4 compilation glitches 2007-02-07 17:18 +0000 dockes <dockes> (2f05854b010a) * src/filters/injectcommon.sh, src/filters/recfiltcommon: new file. * src/filters/injectcommon.sh, src/filters/recfiltcommon: *** empty log message *** 2007-02-07 17:17 +0000 dockes <dockes> (39e4d9e07461) * src/recoll.desktop, src/recoll.png, src/recoll.xcf: deleted file. * src/recoll.desktop, src/recoll.png, src/recoll.xcf, src/recollinstall.in: use xdg scripts to install desktop file and icon 2007-02-07 17:17 +0000 dockes <dockes> (3161a2dabc0a) * src/common/rclconfig.cpp, src/doc/user/usermanual.sgml: dont autocreate config specified with -c or RECOLL_CONFDIR 2007-02-07 16:31 +0000 dockes <dockes> (f89cbedba93f) * src/desktop/recoll-searchgui.desktop, src/desktop/recoll- searchgui.png, src/desktop/recoll-searchgui.xcf, src/desktop/xdg- utils-1.0.1/LICENSE, src/desktop/xdg-utils-1.0.1/scripts/xdg- desktop-menu, src/desktop/xdg-utils-1.0.1/scripts/xdg-icon-resource: new file. * src/desktop/recoll-searchgui.desktop, src/desktop/recoll- searchgui.png, src/desktop/recoll-searchgui.xcf, src/desktop/xdg- utils-1.0.1/LICENSE, src/desktop/xdg-utils-1.0.1/scripts/xdg- desktop-menu, src/desktop/xdg-utils-1.0.1/scripts/xdg-icon-resource, src/query/recollq.cpp: *** empty log message *** 2007-02-07 16:31 +0000 dockes <dockes> (2494c5157c22) * src/aspell/rclaspell.cpp: improve db creation error message 2007-02-07 12:00 +0000 dockes <dockes> (3c02ca709886) * src/query/recollq.cpp: new file. * src/query/Makefile, src/query/recollq.cpp, src/query/wasatorcl.cpp, src/query/wasatorcl.h: recollq 2007-02-06 18:01 +0000 dockes <dockes> (1992c71741c0) * src/index/indexer.cpp, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mh_exec.cpp, src/qtgui/preview_w.cpp: arrange for error info about missing helpers to trickle up to the user 2007-02-06 18:01 +0000 dockes <dockes> (d5e12cec5aeb) * src/sampleconf/mimeconf, src/sampleconf/mimemap: added config+filter for man pages 2007-02-06 15:08 +0000 dockes <dockes> (ef2eef3c33e9) * src/filters/rclman: new file. * src/filters/rcldjvu, src/filters/rcldoc, src/filters/rcldvi, src/filters/rclgaim, src/filters/rcllyx, src/filters/rclman, src/filters/rclmedia, src/filters/rclpdf, src/filters/rclppt, src/filters/rclps, src/filters/rclrtf, src/filters/rclscribus, src/filters/rclsoff, src/filters/rclxls: factored out filter script common code 2007-02-06 14:18 +0000 dockes <dockes> (7812fc3157a4) * src/common/rclconfig.cpp, src/utils/pathut.cpp, src/utils/pathut.h: make sure the -c argument is turned absolute before use 2007-02-06 10:19 +0000 dockes <dockes> (243d1fffdfb9) * src/qtgui/ssearch_w.cpp: no space in query -> phrase 2007-02-06 10:18 +0000 dockes <dockes> (d1b8dd6a7182) * src/qtgui/reslist.cpp: try to make sure that the old reslist is cleared while searching 2007-02-06 10:18 +0000 dockes <dockes> (24163d1804e5) * src/qt4gui/uifrom3: link images/ from qtgui to qt4gui 2007-02-03 16:46 +0000 dockes <dockes> (d27849ad572f) * website/styles/style.css: *** empty log message *** 2007-02-02 10:27 +0000 dockes <dockes> (3c82d463b36c) * src/doc/user/usermanual.sgml: add skippedPaths and daemSkippedPaths config variables 2007-02-02 10:12 +0000 dockes <dockes> (0232602ba055) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/indexer.cpp, src/index/rclmonrcv.cpp, src/utils/fstreewalk.cpp: add skippedPaths and daemSkippedPaths config variables 2007-02-02 10:10 +0000 dockes <dockes> (12a2a255dedc) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: sort and uniquify termMatch results out of stem expansion 2007-02-02 10:09 +0000 dockes <dockes> (344b11ebced1) * src/index/recollindex.cpp: do x11 check between sleeping and starting in recollindex -m 2007-02-02 10:06 +0000 dockes <dockes> (3a9bb20130c8) * src/doc/user/usermanual-italian.html: new file. * src/doc/user/usermanual-italian.html, src/qtgui/i18n/recoll_it.ts: *** empty log message *** 2007-02-02 10:06 +0000 dockes <dockes> (9a6092dbecea) * src/lib/Makefile, src/lib/mkMake: fix $(depth) usage for easier kio compilation 2007-02-02 10:05 +0000 dockes <dockes> (a645eeae729a) * src/doc/user/usermanual.sgml: added config examples 2007-02-02 10:01 +0000 dockes <dockes> (a0640e49ab3a) * src/recollinstall.in: removed old filter in examples cleanup 2007-02-01 15:01 +0000 dockes <dockes> (db53657c868d) * src/aspell/rclaspell.cpp, src/mk/localdefs.in: use configure libdir to search for aspell lib (mainly for 64 bits machines) 2007-02-01 12:43 +0000 dockes <dockes> (7f3d33405e53) * src/kde/kioslave/recoll/Makefile, src/kde/kioslave/recoll/kio_recoll.cpp: fixed kio compilation. Dont know if it works 2007-01-30 11:39 +0000 dockes <dockes> (1aa8e8c3d93a) * src/filters/rcldjvu, src/filters/rcldoc, src/filters/rcldvi, src/filters/rclpdf, src/filters/rclps, src/filters/rclrtf, src/filters/rclsoff: hide awk BEGIN statements - make debian linda happy 2007-01-29 13:51 +0000 dockes <dockes> (f207a83f0617) * src/rcldb/searchdata.cpp: more field name synonyms 2007-01-25 15:50 +0000 dockes <dockes> (ba53fd450dc5) * src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: better wildcards handling. Tuning of user term boosting 2007-01-25 15:47 +0000 dockes <dockes> (026e24e9aafc) * src/doc/user/usermanual.sgml: *** empty log message *** 2007-01-25 15:47 +0000 dockes <dockes> (31cd60d81a3a) * src/rcldb/rcldb.cpp: dont explicitly anchor regexp in termMatch 2007-01-25 15:46 +0000 dockes <dockes> (8c7afe9df6fb) * src/qtgui/ssearch_w.cpp: Dont add auto phrase if there are wildcards 2007-01-25 15:45 +0000 dockes <dockes> (d35369f54699) * src/query/wasatorcl.cpp: comment 2007-01-25 15:40 +0000 dockes <dockes> (2d7b13ebd2c8) * src/common/textsplit.cpp: [] are also wildcard chars 2007-01-25 12:04 +0000 dockes <dockes> (27310036f46c) * src/qtgui/i18n/recoll_it.ts: *** empty log message *** 2007-01-25 08:27 +0000 dockes <dockes> (876d5192bdde) * src/qtgui/i18n/recoll_it.ts: new file. * src/qtgui/i18n/recoll_it.ts: *** empty log message *** 2007-01-24 12:40 +0000 dockes <dockes> (dd470677dbf2) * src/qtgui/guiutils.cpp: make AND the initial default for ssearch 2007-01-24 11:20 +0000 dockes <dockes> (623c6533e0f0) * src/qtgui/uiprefs.ui, src/qtgui/viewaction.ui: change MyDialog and Form1 dialog captions 2007-01-24 11:15 +0000 dockes <dockes> (9dc93d749ea8) * src/filters/rclscribus: transate \r to <br> (for older scribus files) 2007-01-24 11:00 +0000 dockes <dockes> (7ea73b206760) * src/sampleconf/mimeconf, src/sampleconf/mimemap: scribus scd files 2007-01-23 07:23 +0000 dockes <dockes> (0f9e96c72d1c) * src/filters/rcllyx: *** empty log message *** 2007-01-23 07:22 +0000 dockes <dockes> (5fc9550be90c) * src/filters/rcllyx: *** empty log message *** 2007-01-23 07:16 +0000 dockes <dockes> (55734c5d16c2) * src/filters/rcllyx: *** empty log message *** 2007-01-23 07:14 +0000 dockes <dockes> (dafabbcdaf1a) * src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/mimeview: lyx filter 2007-01-23 07:14 +0000 dockes <dockes> (d5ac3c0cf64f) * src/filters/rcllyx: new file. * src/filters/rcllyx: lyx filter 2007-01-22 16:34 +0000 dockes <dockes> (e76e39a890d0) * src/sampleconf/mimeconf, src/sampleconf/mimemap: added scribus support 2007-01-22 16:32 +0000 dockes <dockes> (0b142f40e0c7) * src/filters/rclscribus: new file. * src/filters/rclscribus: added scribus support 2007-01-21 16:41 +0000 dockes <dockes> (8e06e0f7914e) * src/filters/rclsoff: fix shell syntax for debian 2007-01-19 15:22 +0000 dockes <dockes> (084098d57a50) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/query/docseq.h, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/docseqhist.h, src/query/sortseq.cpp, src/query/sortseq.h: try to limit the places which use Rcl:: stuff 2007-01-19 15:19 +0000 dockes <dockes> (1d1bdf98f176) * src/rcldb/stemdb.cpp: make sure that both the user term and the stem are in the expanded list 2007-01-19 10:32 +0000 dockes <dockes> (757f49c23d93) * src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/docseqhist.cpp, src/query/docseqhist.h: new file. * src/lib/Makefile, src/lib/mkMake, src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/qtgui/ssearch_w.cpp, src/query/docseq.cpp, src/query/docseq.h, src/query/docseqdb.cpp, src/query/docseqdb.h, src/query/docseqhist.cpp, src/query/docseqhist.h, src/query/sortseq.cpp, src/query/sortseq.h: cleanup docseq, arrange things so that we can page reslist past the initial result count estimate if there are more 2007-01-19 10:23 +0000 dockes <dockes> (d4ecd356406a) * src/rcldb/searchdata.cpp: the relevance-boosted original term needs a prefix too 2007-01-19 10:23 +0000 dockes <dockes> (cacb9b50f1cf) * src/rcldb/rcldb.cpp: adjust makeAbstract for prefixed terms 2007-01-19 10:22 +0000 dockes <dockes> (95d569102c37) * src/query/wasatorcl.cpp, src/query/wasatorcl.h: add direct qstring to rcl function 2007-01-18 14:23 +0000 dockes <dockes> (157d8676b256) * src/utils/mimeparse.cpp: debug msg 2007-01-18 12:09 +0000 dockes <dockes> (b0647b310dec) * src/common/textsplit.cpp, src/common/textsplit.h, src/rcldb/searchdata.cpp: handle wildcards in search terms 2007-01-17 14:06 +0000 dockes <dockes> (65d2617d690c) * src/query/Makefile, src/query/wasatorcl.cpp: *** empty log message *** 2007-01-17 13:53 +0000 dockes <dockes> (82c00cf9d054) * src/internfile/internfile.cpp, src/internfile/mh_html.cpp, src/internfile/mh_mail.cpp, src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h, src/lib/Makefile, src/lib/mkMake, src/query/Makefile, src/query/wasastringtoquery.cpp, src/query/wasastringtoquery.h, src/query/wasatorcl.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldoc.h, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: added field/prefixes for author and title + command line query language 2007-01-16 10:58 +0000 dockes <dockes> (f56d8a303798) * src/sampleconf/recoll.conf.in: add recollrc to skipped 2007-01-16 10:58 +0000 dockes <dockes> (a28d7ea5359b) * website/BUGS.txt, website/CHANGES.txt, website/download.html, website/index.html: 1.7.5 2007-01-16 10:58 +0000 dockes <dockes> (83b10dc2e5ea) * src/bincimapmime/trbinc.cc, src/utils/debuglog.cpp: wrong copyrights 2007-01-16 10:56 +0000 dockes <dockes> (e51d7ee21ffd) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/rpm/recoll.spec, packaging/rpm/recollfedora.spec, packaging/rpm/recollmdk.spec: 1.7.5 packaging 2007-01-16 09:22 +0000 dockes <dockes> (7f8fea3bed13) * packaging/debian/manpages: deleted file. * packaging/debian/changelog, packaging/debian/control, packaging/debian/copyright, packaging/debian/manpages, packaging/debian/rules: 2007-01-12 comments 2007-01-15 19:16 +0000 dockes <dockes> (740528a1cd7d) * packaging/debian/dirs: deleted file. * packaging/debian/dirs: *** empty log message *** 2007-01-15 13:06 +0000 dockes <dockes> (12e31e690f9e) * src/internfile/internfile.cpp, src/internfile/internfile.h: dont stop processing a complex document on the first next_document error: pop level and go on 2007-01-15 13:03 +0000 dockes <dockes> (6d3f8a71e602) * src/doc/user/usermanual.sgml: *** empty log message *** 2007-01-13 15:21 +0000 dockes <dockes> (b04adc5188d5) * src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h: improved external index dialog with listview 2007-01-13 14:41 +0000 dockes <dockes> (f9567f0fed32) * src/query/xadump.cpp: add option to dump raw terms 2007-01-13 10:28 +0000 dockes <dockes> (fc890008108f) * src/internfile/mh_mail.cpp: handle multipart/signed 2007-01-12 09:01 +0000 dockes <dockes> (1782d39f9d4d) * src/qtgui/reslist.cpp: Use sample from Rcl::Doc if makeAbstract() fails 2007-01-12 06:42 +0000 dockes <dockes> (8223a4aa9ad4) * packaging/debian/copyright: include gpl statement 2007-01-10 16:03 +0000 dockes <dockes> (66247acdb470) * packaging/debian/changelog, packaging/debian/compat, packaging/debian/control, packaging/debian/copyright, packaging/debian/dirs, packaging/debian/docs, packaging/debian/manpages, packaging/debian/menu, packaging/debian/rules, packaging/debian/watch: new file. * packaging/debian/changelog, packaging/debian/compat, packaging/debian/control, packaging/debian/copyright, packaging/debian/dirs, packaging/debian/docs, packaging/debian/manpages, packaging/debian/menu, packaging/debian/rules, packaging/debian/watch, src/Makefile.in, src/VERSION: *** empty log message *** 2007-01-10 12:27 +0000 dockes <dockes> (733bc11b5526) * packaging/FreeBSD/recoll/Makefile: *** empty log message *** 2007-01-09 15:34 +0000 dockes <dockes> (9583ff723edf [RECOLL_1_7_3]) * packaging/rpm/recollfedora.spec: new file. * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- descr, packaging/FreeBSD/recoll/pkg-plist, packaging/rpm/recoll.spec, packaging/rpm/recollfedora.spec, packaging/rpm/recollmdk.spec: 1.7.3 2007-01-09 14:39 +0000 dockes <dockes> (f108471cd099) * website/BUGS.txt, website/CHANGES.txt, website/download.html: 1.7.3 2007-01-09 07:25 +0000 dockes <dockes> (e0c1d14a73c5) * src/VERSION: *** empty log message *** 2007-01-09 07:25 +0000 dockes <dockes> (f06dbc019ff4) * src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: french messages 2007-01-08 15:21 +0000 dockes <dockes> (906e56e99e81) * src/VERSION, src/qtgui/main.cpp: initial indexation with gui would not work 2007-01-08 13:00 +0000 dockes <dockes> (44d2b5d58ac6 [RECOLL_1_7_1]) * src/VERSION: 1.7.1 2007-01-08 12:43 +0000 dockes <dockes> (2cb748432b10) * src/qtgui/advsearch.ui: lost sizers? 2007-01-08 10:11 +0000 dockes <dockes> (67c4375292e5) * src/qtgui/rclmain_w.cpp: fix the previous icon fix 2007-01-08 10:01 +0000 dockes <dockes> (8eb24fe9db4f) * src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h: reslist menu openParent opens containing folder if not a subdoc 2007-01-08 09:40 +0000 dockes <dockes> (b38401a650c3) * src/qtgui/uiprefs.ui: fix resizing of prefs dialog 2007-01-08 07:02 +0000 dockes <dockes> (5a2fb87a2c55) * src/qtgui/rclmain_w.cpp: get rid of messages about missing d_xxx images 2007-01-08 07:01 +0000 dockes <dockes> (8a2c6d2cba46) * src/qtgui/reslist.cpp: synthetic abstracts not displayed 2006-12-24 08:07 +0000 dockes <dockes> (e42dca990bea [RECOLL_1_7_0]) * src/Makefile.in: better cleanup -> 1.7.0 NOW 2006-12-24 08:02 +0000 dockes <dockes> (916d6e831996 [RECOLL_1_7_2]) * packaging/FreeBSD/recoll/pkg-plist, src/excludefile, website/BUGS.txt, website/CHANGES.txt, website/download.html, website/features.html, website/index.html, website/pics/index.html, website/rclidxfmt.html: 1.7.0 2006-12-24 07:53 +0000 dockes <dockes> (b37a6e3566b9) * src/INSTALL, src/README, src/doc/user/usermanual.sgml: *** empty log message *** 2006-12-24 07:40 +0000 dockes <dockes> (69573fe97b89) * src/configure, src/configure.ac, src/doc/man/recollindex.1, src/doc/user/usermanual.sgml, src/index/rclmon.h, src/index/rclmonprc.cpp, src/index/recollindex.cpp: option -x to disable x11 session monitoring 2006-12-23 13:07 +0000 dockes <dockes> (fb731b7d3ab1) * src/configure, src/configure.ac, src/index/Makefile, src/index/rclmonprc.cpp, src/lib/Makefile, src/lib/mkMake, src/mk/localdefs.in, src/utils/pathut.cpp: x11 session end detection 2006-12-23 12:23 +0000 dockes <dockes> (00532204c17f) * src/utils/x11mon.cpp, src/utils/x11mon.h: new file. * src/utils/Makefile, src/utils/x11mon.cpp, src/utils/x11mon.h: *** empty log message *** 2006-12-22 16:48 +0000 dockes <dockes> (ee878b9d311e) * src/qt4gui/recoll.qrc, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/spell_w.cpp: get all icons out of .ui files to avoid qt4 startup messages 2006-12-22 11:01 +0000 dockes <dockes> (078acb3ab4fd) * src/doc/man/recollindex.1, src/doc/user/usermanual.sgml, src/qtgui/spell_w.h: *** empty log message *** 2006-12-21 10:08 +0000 dockes <dockes> (d36d26d5b5d5) * src/index/rclmonprc.cpp: try to be more responsive to interruptions 2006-12-21 09:22 +0000 dockes <dockes> (818387de5d92) * src/index/indexer.cpp, src/index/rclmonrcv.cpp, src/index/recollindex.cpp, src/sampleconf/mimemap, src/utils/fstreewalk.cpp: always skip indexing of confdir and dbdir. start index monitor with normal indexing pass 2006-12-21 09:21 +0000 dockes <dockes> (13c7229ee6dc) * src/qtgui/advsearch.ui, src/qtgui/ssearchb.ui: tooltips 2006-12-21 08:22 +0000 dockes <dockes> (c1e9892c3ba1) * src/utils/fstreewalk.cpp, src/utils/fstreewalk.h: add skipped paths 2006-12-20 14:28 +0000 dockes <dockes> (f580e9aa026a) * src/internfile/internfile.cpp: msg 2006-12-20 14:09 +0000 dockes <dockes> (e3d7f975546f) * src/qtgui/preview_w.cpp: try to improve error message for internfile failure 2006-12-20 13:55 +0000 dockes <dockes> (0a07075dd464) * src/qtgui/preview_w.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h: reslist: added menu entry to see parent doc of attachment 2006-12-20 13:12 +0000 dockes <dockes> (733a59947cfb) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/sampleconf/mimeconf: mime categories 2006-12-20 10:47 +0000 dockes <dockes> (591625eb1d38) * src/INSTALL, src/README: *** empty log message *** 2006-12-20 09:54 +0000 dockes <dockes> (c563fb138893) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/doc/user/usermanual.sgml, src/index/mimetype.cpp: changed stopsuffixes processing 2006-12-20 09:41 +0000 dockes <dockes> (64488c2687be) * src/index/recollindex.cpp: opt -e 2006-12-19 12:38 +0000 dockes <dockes> (6d4a0c0f8cc3) * src/qtgui/spell_w.cpp: qt4 2006-12-19 12:11 +0000 dockes <dockes> (a3e7c86f79d7) * src/qtgui/spell.ui, src/qtgui/spell_w.cpp, src/qtgui/spell_w.h, src/qtgui/ssearch_w.cpp, src/qtgui/viewaction.ui, src/qtgui/viewaction_w.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.cpp, src/rcldb/stemdb.cpp, src/rcldb/stemdb.h, src/utils/smallut.cpp, src/utils/smallut.h: merge stemExpand into termExpand. return term frequencies from there and display in spellW 2006-12-19 08:40 +0000 dockes <dockes> (3bbff3062a89) * src/common/rclconfig.cpp, src/index/indexer.cpp, src/index/mimetype.cpp, src/index/mimetype.h, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mimehandler.cpp, src/qtgui/preview_w.cpp, src/sampleconf/mimeconf, src/sampleconf/mimeview: index directory names 2006-12-19 07:48 +0000 dockes <dockes> (7301c237649a) * src/qtgui/mtpics/folder.png: new file. * src/qtgui/mtpics/folder.png: *** empty log message *** 2006-12-18 16:45 +0000 dockes <dockes> (0a640477a752) * src/qt4gui/recoll.pro.in, src/qtgui/viewaction.ui, src/qtgui/viewaction_w.cpp, src/qtgui/viewaction_w.h: qt4 2006-12-18 12:06 +0000 dockes <dockes> (5f17ab347621) * src/doc/user/usermanual.sgml, src/internfile/mh_mail.cpp, src/utils/smallut.cpp, src/utils/smallut.h: mh_mail needs to lowercase contentypes 2006-12-18 12:05 +0000 dockes <dockes> (03363b562546) * src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/viewaction_w.cpp, src/qtgui/viewaction_w.h: dblclick to edit in viewAction 2006-12-16 15:39 +0000 dockes <dockes> (a3027dd4b920) * src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mh_html.h, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/internfile/mh_text.h, src/internfile/mimehandler.h, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: mail attachments sort of ok 2006-12-16 15:31 +0000 dockes <dockes> (7d335e595c2b) * src/utils/pathut.cpp, src/utils/pathut.h: added TempFile class 2006-12-16 15:30 +0000 dockes <dockes> (89fed05a6ace) * src/internfile/Filter.h: *** empty log message *** 2006-12-16 15:30 +0000 dockes <dockes> (5f74c84fa800) * src/index/indexer.cpp: dont clobber utf8fn from filter 2006-12-16 15:30 +0000 dockes <dockes> (b5f77fb6530b) * src/common/rclconfig.cpp, src/common/rclconfig.h: added getSuffixFromMimeType() 2006-12-16 07:15 +0000 dockes <dockes> (ef72575e285c) * src/internfile/Filter.h: *** empty log message *** 2006-12-15 16:33 +0000 dockes <dockes> (df6232340341) * src/index/indexer.cpp, src/internfile/Filter.h, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mh_html.cpp, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/internfile/myhtmlparse.h: test data indexing result same terms as 1.6.3 2006-12-15 12:40 +0000 dockes <dockes> (5156a319f219) * src/internfile/Filter.h, src/internfile/mh_mbox.cpp, src/internfile/mh_mbox.h: new file. * src/internfile/Filter.h, src/internfile/Makefile, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mh_exec.cpp, src/internfile/mh_exec.h, src/internfile/mh_html.cpp, src/internfile/mh_html.h, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/internfile/mh_mbox.cpp, src/internfile/mh_mbox.h, src/internfile/mh_text.cpp, src/internfile/mh_text.h, src/internfile/mh_unknown.h, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/internfile/myhtmlparse.h, src/lib/Makefile, src/lib/mkMake, src/utils/smallut.cpp, src/utils/smallut.h: Dijon filters 1st step: mostly working needs check and optim 2006-12-14 14:54 +0000 dockes <dockes> (2f7d4fb90b31) * src/rcldb/rcldoc.h: new file. * src/rcldb/rcldb.h, src/rcldb/rcldoc.h: split rcldb.h -> rcldoc.h 2006-12-14 13:53 +0000 dockes <dockes> (839454238284) * src/qtgui/viewaction.ui, src/qtgui/viewaction_w.cpp, src/qtgui/viewaction_w.h, src/sampleconf/mimeview: new file. * src/common/rclconfig.cpp, src/common/rclconfig.h, src/doc/user/usermanual.sgml, src/index/indexer.cpp, src/qtgui/preview_w.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/recoll.pro.in, src/qtgui/ssearch_w.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h, src/qtgui/viewaction.ui, src/qtgui/viewaction_w.cpp, src/qtgui/viewaction_w.h, src/recollinstall.in, src/sampleconf/mimeconf, src/sampleconf/mimeview, src/utils/Makefile, src/utils/conftree.cpp, src/utils/conftree.h, src/utils/execmd.cpp, src/utils/execmd.h, src/utils/pathut.cpp, src/utils/pathut.h, src/utils/smallut.cpp, src/utils/smallut.h: created mimeview and the viewer conf edit dialog 2006-12-13 09:13 +0000 dockes <dockes> (ca4c21f5ad44) * src/common/rclconfig.cpp, src/internfile/internfile.cpp, src/internfile/mh_exec.cpp, src/internfile/mimehandler.cpp: move findFilter usage out of mh_exec 2006-12-11 14:56 +0000 dockes <dockes> (dd4f283c9753 [BEFORE_Dijon20061215]) * src/qtgui/plaintorich.cpp: not calling textsplit with onlyspans improves highlighting 2006-12-11 14:50 +0000 dockes <dockes> (c6d552528f6c) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/mimetype.cpp, src/utils/smallut.cpp, src/utils/smallut.h: rationalize stopsuffix list usage 2006-12-11 09:05 +0000 dockes <dockes> (87037320fddf) * packaging/FreeBSD/recoll/pkg-plist: try to cleanup the share/icons tree 2006-12-10 17:03 +0000 dockes <dockes> (0d0fec69b4e4 [MAPSTRMAPSTRSTR]) * src/query/wasastringtoquery.cpp, src/query/wasastringtoquery.h, src/query/wasatorcl.cpp, src/query/wasatorcl.h: added sort and type specs parsing 2006-12-08 17:18 +0000 dockes <dockes> (9d443b2ad416) * src/query/wasatorcl.h: 1st query 2006-12-08 10:54 +0000 dockes <dockes> (dc4914858b42) * src/query/wasastringtoquery.cpp, src/query/wasastringtoquery.h: *** empty log message *** 2006-12-08 07:11 +0000 dockes <dockes> (df1ce4c7c9bf) * src/common/textsplit.cpp, src/common/textsplit.h, src/qtgui/ssearch_w.cpp: only autophrase if query has several terms 2006-12-08 06:45 +0000 dockes <dockes> (6b96cd852343) * src/qtgui/ssearch_w.cpp: make autophrase do the right thing: add a subclause, not modify the query string 2006-12-07 16:38 +0000 dockes <dockes> (e0b7c11d4054) * src/query/qtry.cpp, src/query/qxtry.cpp: deleted file. * src/query/Makefile, src/query/qtry.cpp, src/query/qxtry.cpp, src/query/xadump.cpp: removed qtry and merged qxtry into xadump 2006-12-07 13:24 +0000 dockes <dockes> (11f50dc2ced9) * src/rcldb/rcldb.cpp: comment 2006-12-07 13:14 +0000 dockes <dockes> (0137bc80c8a5) * website/rclidxfmt.html: new file. * website/rclidxfmt.html: *** empty log message *** 2006-12-07 13:02 +0000 dockes <dockes> (e36e165c1055) * src/rcldb/rcldb.cpp: comments 2006-12-07 08:23 +0000 dockes <dockes> (6bdb3421d1ca) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- plist: 1.6.3 2006-12-07 08:06 +0000 dockes <dockes> (2ca80dafce2a) * src/internfile/mh_mail.cpp: fix bug with bad message "From " delimiter detection 2006-12-07 07:07 +0000 dockes <dockes> (92354b8e641a) * src/qtgui/rclmain_w.cpp, src/utils/mimeparse.h, src/utils/smallut.cpp, src/utils/smallut.h: fix pb with executing viewer for files with single-quotes in pathnames 2006-12-07 07:06 +0000 dockes <dockes> (b415958c3148) * src/internfile/mh_mail.cpp: fix bug with bad message "From " delimiter detection 2006-12-05 15:25 +0000 dockes <dockes> (451489717e47) * src/internfile/mh_mail.cpp: use regexp to better discriminate From delimiter lines in mbox. Avoid reading mboxes twice 2006-12-05 15:23 +0000 dockes <dockes> (282880e83069) * src/qtgui/advsearch.ui, src/qtgui/main.cpp, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/qtgui/sort.ui, src/qtgui/sort_w.cpp, src/qtgui/sort_w.h: avoid generating abstracts before theyre needed (ie: not during sort). have the sort tools redisplay the results when sort criteria are applied 2006-12-05 15:18 +0000 dockes <dockes> (069f87c83682) * src/query/sortseq.cpp, src/query/sortseq.h: use refcntr to access docsequence 2006-12-05 15:17 +0000 dockes <dockes> (f7bad3e61904) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: expose abstract synthesis to let users decide when they want it done 2006-12-05 15:17 +0000 dockes <dockes> (57148c851c44) * src/rcldb/searchdata.h: clauseCount 2006-12-05 15:16 +0000 dockes <dockes> (d6d5ee7b750b) * src/utils/refcntr.h: fix pbs with empty object 2006-12-04 09:56 +0000 dockes <dockes> (1173f38c9de4) * src/configure, src/configure.ac, src/qtgui/advsearch_w.h, src/qtgui/main.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/sort_w.h, src/qtgui/spell_w.h, src/qtgui/ssearch_w.h, src/qtgui/uiprefs_w.h, src/utils/refcntr.h: qt4 compiles and sort of works 2006-12-04 09:49 +0000 dockes <dockes> (00bc69d47f20) * src/qt4gui/recoll.qrc, src/qt4gui/uifrom3: new file. * src/qt4gui/recoll.pro.in, src/qt4gui/recoll.qrc, src/qt4gui/uifrom3: *** empty log message *** 2006-12-04 08:17 +0000 dockes <dockes> (c92f84765756) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/sort_w.cpp, src/qtgui/ssearch_w.cpp, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h: compiles (doesnt work) on qt4 2006-12-04 06:19 +0000 dockes <dockes> (5a7d6794967e) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/guiutils.cpp, src/qtgui/main.cpp, src/qtgui/preview.ui, src/qtgui/preview_w.h, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/qtgui/searchclause_w.cpp, src/qtgui/searchclause_w.h, src/qtgui/sort_w.h, src/qtgui/spell.ui, src/qtgui/spell_w.h, src/qtgui/ssearch_w.h, src/qtgui/ssearchb.ui, src/qtgui/uiprefs_w.h: qt4 ckpt 2006-12-02 07:32 +0000 dockes <dockes> (45564d318a93) * src/utils/idfile.cpp: improved tests to check for mail 2006-12-01 10:05 +0000 dockes <dockes> (8c3b51bc117f) * src/query/xadump.cpp: *** empty log message *** 2006-11-30 18:12 +0000 dockes <dockes> (e41c0db701ae) * src/query/wasastringtoquery.cpp, src/query/wasastringtoquery.h, src/query/wasatorcl.cpp, src/query/wasatorcl.h: new file. * src/query/wasastringtoquery.cpp, src/query/wasastringtoquery.h, src/query/wasatorcl.cpp, src/query/wasatorcl.h: *** empty log message *** 2006-11-30 13:44 +0000 dockes <dockes> (5ef831ae4659) * website/download.html: *** empty log message *** 2006-11-30 13:38 +0000 dockes <dockes> (6e49658236c6) * src/qtgui/images/cancel.png, src/qtgui/images/close.png: new file. * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- plist, packaging/rpm/recoll.spec, packaging/rpm/recollmdk.spec, src/README, src/aspell/rclaspell.cpp, src/doc/user/usermanual.sgml, src/index/indexer.cpp, src/index/indexer.h, src/makesrcdist.sh, src/makestaticdist.sh, src/mk/SunOS, src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/images/cancel.png, src/qtgui/images/close.png, src/qtgui/main.cpp, src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview.ui, src/qtgui/preview_w.cpp, src/qtgui/rclmain.ui, src/qtgui/recoll.pro.in, src/qtgui/reslist.cpp, src/qtgui/searchclause_w.cpp, src/qtgui/spell_w.cpp, src/qtgui/ssearch_w.cpp, src/qtgui/ssearchb.ui, src/rcldb/searchdata.cpp, src/recoll.desktop, src/recollinstall.in, src/sampleconf/recoll.conf.in, src/utils/execmd.cpp, src/utils/mimeparse.cpp, src/utils/smallut.cpp, website/BUGS.txt, website/CHANGES.txt, website/download.html, website/features.html, website/index.html: merged 1.6 maint branch modifs up to MERGED_TO_TRUNK_20061130 2006-11-22 09:29 +0000 dockes <dockes> (568c34cf75e9) * src/VERSION, website/BUGS.txt, website/CHANGES.txt, website/credits.html, website/download.html: *** empty log message *** 2006-11-21 14:00 +0000 dockes <dockes> (f247e019bf08 [RECOLL_1_6_0]) * src/INSTALL, src/README: *** empty log message *** 2006-11-21 13:05 +0000 dockes <dockes> (23604b23773d) * src/aspell/rclaspell.cpp, src/configure, src/configure.ac: mdk 2006 aspell quirks 2006-11-21 09:18 +0000 dockes <dockes> (17597459707c) * packaging/FreeBSD/recoll/pkg-plist, packaging/rpm/recoll.spec, packaging/rpm/recollmdk.spec, src/doc/user/usermanual.sgml, src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts, website/BUGS.txt, website/CHANGES.txt, website/copydocs, website/download.html, website/index.html: *** empty log message *** 2006-11-21 08:47 +0000 dockes <dockes> (f434e776fec8) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/spell.ui, src/qtgui/spell_w.cpp, src/qtgui/spell_w.h, src/qtgui/uiprefs_w.cpp: added stem expansion mode to term explorer 2006-11-20 18:07 +0000 dockes <dockes> (bc85af9f678c) * src/doc/man/recoll.conf.5, src/doc/man/recollindex.1, src/doc/user/usermanual.sgml: doc 2006-11-20 17:46 +0000 dockes <dockes> (28cb0d8c325a) * src/doc/user/usermanual.sgml: *** empty log message *** 2006-11-20 17:06 +0000 dockes <dockes> (9252428377e4) * src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: *** empty log message *** 2006-11-20 15:35 +0000 dockes <dockes> (192c101b8b7c) * src/qtgui/advsearch.ui: tooltip 2006-11-20 15:29 +0000 dockes <dockes> (73ca6e78a1dd) * src/filters/rclxls, src/utils/transcode.cpp: *** empty log message *** 2006-11-20 15:28 +0000 dockes <dockes> (9bb875d3bfcf) * src/rcldb/rcldb.cpp: clear abstract if its only ... 2006-11-20 15:28 +0000 dockes <dockes> (5ef1b603c3be) * src/common/rclconfig.cpp: test driver 2006-11-20 15:28 +0000 dockes <dockes> (1c4807a363f9) * src/common/rclconfig.h: fix defaultcharset reset 2006-11-20 11:17 +0000 dockes <dockes> (ef95275586d1) * src/common/Makefile, src/common/textsplit.cpp, src/common/textsplit.h: improved textsplit speed (needs utf8iter modifs too 2006-11-20 11:16 +0000 dockes <dockes> (e05653621eb4) * src/utils/Makefile, src/utils/utf8iter.cpp, src/utils/utf8iter.h: cleaned and speeded up utf8iter 2006-11-19 18:37 +0000 dockes <dockes> (756bc7569b34) * src/common/textsplit.cpp, src/common/textsplit.h: optim ckpt 2006-11-18 12:56 +0000 dockes <dockes> (bf6e4de3a902) * src/qtgui/plaintorich.cpp: firsttermocc init was not always done 2006-11-18 12:31 +0000 dockes <dockes> (1703e5a7b03e) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview_w.cpp, src/qtgui/reslist.cpp: improve positioning on term groups by storing/passing an occurrence index 2006-11-18 12:30 +0000 dockes <dockes> (f065c8063ff3) * src/rcldb/searchdata.cpp: correctly generate highlighting term groups when stem-expanding NEAR queries 2006-11-17 15:26 +0000 dockes <dockes> (ee4a13877b24) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h: Save adv search clause list + add delete button 2006-11-17 12:55 +0000 dockes <dockes> (679a2cb3d3e7) * src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h: get shift+clicklink to open new preview window instead of tab 2006-11-17 12:32 +0000 dockes <dockes> (51f7db5eff83) * src/qtgui/plaintorich.cpp: small opts + fixed near region detection code 2006-11-17 12:31 +0000 dockes <dockes> (c0ba08efc3dd) * src/qtgui/plaintorich.h: comments 2006-11-17 12:31 +0000 dockes <dockes> (e54183706237) * src/utils/utf8iter.h: removed not strictly needed error checking code 2006-11-17 10:09 +0000 dockes <dockes> (7e44d4280e2d) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/qtgui/reslist.h: Remember searchData and use it in plaintorich for phrase/group highlighting 2006-11-17 10:08 +0000 dockes <dockes> (c175799e9e72) * src/qtgui/advsearch_w.cpp: better data encap in searchdata 2006-11-17 10:06 +0000 dockes <dockes> (0ea302968170) * src/rcldb/rcldb.cpp, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: added code to remember search terms and term groups in searchdata 2006-11-15 14:57 +0000 dockes <dockes> (188b5b28427d) * src/common/Makefile, src/lib/Makefile, src/lib/mkMake, src/mk/commondefs, src/qtgui/recoll.pro.in, src/rcldb/pathhash.cpp, src/rcldb/pathhash.h, src/rcldb/rcldb.cpp, src/rcldb/searchdata.h, src/rcldb/stemdb.h: distributed files from common/ into rcld, internfile, common 2006-11-15 07:27 +0000 dockes <dockes> (5bfc0444c072) * src/internfile/Makefile: new file. * src/internfile/Makefile: *** empty log message *** 2006-11-14 18:29 +0000 dockes <dockes> (c45d7a2b1c63) * src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h: got rid of the static clause names 2006-11-14 18:17 +0000 dockes <dockes> (e4789b229585) * src/qtgui/advsearch.ui: *** empty log message *** 2006-11-14 17:56 +0000 dockes <dockes> (ae916c13c591) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp: added conjunction choice in advsearch 2006-11-14 17:41 +0000 dockes <dockes> (dfc71f06c1ce) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/searchclause_w.cpp, src/qtgui/searchclause_w.h, src/rcldb/searchdata.cpp: use SearchClauseW for all advsearch fields 2006-11-14 15:13 +0000 dockes <dockes> (300f3705d6cf) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp: *** empty log message *** 2006-11-14 14:58 +0000 dockes <dockes> (c5f65c6f8fb9) * src/qtgui/recoll.pro.in: *** empty log message *** 2006-11-14 13:55 +0000 dockes <dockes> (9e98c3d86016) * src/qtgui/searchclause_w.cpp, src/qtgui/searchclause_w.h: new file. * src/doc/user/usermanual.sgml, src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/rclmain_w.cpp, src/qtgui/searchclause_w.cpp, src/qtgui/searchclause_w.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: added dynamic clauses to adv search. Still needs work 2006-11-13 14:51 +0000 dockes <dockes> (5c9db8d08690) * src/rcldb/rcldb.cpp: *** empty log message *** 2006-11-13 14:48 +0000 dockes <dockes> (edec86240778) * src/rcldb/rcldb.cpp: use wdfs for better selection of doc extracts in makeAbstract 2006-11-13 11:59 +0000 dockes <dockes> (fdf0f43cd03e) * src/utils/smallut.h: *** empty log message *** 2006-11-13 08:58 +0000 dockes <dockes> (c48e54f96603) * src/utils/refcntr.h: new file. * src/utils/refcntr.h: *** empty log message *** 2006-11-13 08:58 +0000 dockes <dockes> (40853ad94507) * src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/reslist.cpp, src/qtgui/reslist.h, src/qtgui/ssearch_w.cpp, src/qtgui/ssearch_w.h: make searchdata a more flexible struct 2006-11-13 08:50 +0000 dockes <dockes> (e585bfd6e725) * src/rcldb/searchdata.cpp: new file. * src/lib/Makefile, src/lib/mkMake, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.cpp, src/rcldb/searchdata.h: make searchdata a more flexible struct 2006-11-13 08:49 +0000 dockes <dockes> (db3490f9b522) * src/kde/kioslave/recoll/kio_recoll.cpp: *** empty log message *** 2006-11-13 08:15 +0000 dockes <dockes> (7240ec62ffac) * src/qtgui/plaintorich.cpp: new splitter interface 2006-11-12 08:35 +0000 dockes <dockes> (ff9f3aed6a5b) * src/common/textsplit.cpp, src/common/textsplit.h, src/rcldb/rcldb.cpp: phrase queries with bot spans and words must be split as words only 2006-11-11 15:30 +0000 dockes <dockes> (25647c7c5aac) * src/qtgui/reslist.cpp, src/qtgui/uiprefs.ui: have more compact list header + %N 2006-11-10 17:53 +0000 dockes <dockes> (d423490bea37) * src/qtgui/reslist.cpp: Really use the rich abstracts 2006-11-10 17:18 +0000 dockes <dockes> (9fc1a2d1b7af) * src/rcldb/rcldb.cpp: optimized abstract building: bybye big vector 2006-11-10 13:32 +0000 dockes <dockes> (8cfbbddd355a) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain_w.cpp, src/qtgui/reslist.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: make result list paragraph format user-adjustable 2006-11-10 13:30 +0000 dockes <dockes> (933509968125) * src/utils/smallut.cpp, src/utils/smallut.h: pcSubst() 2006-11-10 13:29 +0000 dockes <dockes> (17e0ecfb5834) * src/internfile/internfile.cpp: errlog 2006-11-09 19:04 +0000 dockes <dockes> (814af75ba7a8) * src/qtgui/preview_w.cpp: better handle the situation of mbox file name matching search 2006-11-09 17:38 +0000 dockes <dockes> (3c996d97497a) * src/Makefile.in: *** empty log message *** 2006-11-09 17:37 +0000 dockes <dockes> (c13aab8ac186) * src/qtgui/reslist.cpp: nbsp to prevent line date wrap before tz 2006-11-09 17:37 +0000 dockes <dockes> (573934250b27) * src/rcldb/rcldb.cpp: dont continue adding ellipsis into the abstract when its maxlen! 2006-11-09 08:59 +0000 dockes <dockes> (baafe52b9d1b) * src/utils/mimeparse.cpp: test driver modifs 2006-11-08 15:34 +0000 dockes <dockes> (025fa484738a) * src/common/rclinit.cpp, src/utils/debuglog.cpp, src/utils/debuglog.h: fix pb with special log file names 2006-11-08 15:32 +0000 dockes <dockes> (a32333c18e9f) * src/configure, src/configure.ac: aspell help string 2006-11-08 13:04 +0000 dockes <dockes> (f5f0e953f42e) * src/qtgui/plaintorich.cpp: use vector instead of list for positions 2006-11-08 07:22 +0000 dockes <dockes> (de7777528655) * src/common/rclinit.cpp, src/common/rclinit.h, src/index/recollindex.cpp: allow daemon-specific log parameters 2006-11-08 06:56 +0000 dockes <dockes> (451b31555bc2) * src/utils/conftree.cpp, src/utils/conftree.h: volatile conf 2006-11-08 06:49 +0000 dockes <dockes> (ba1e8fb12e39) * src/recollinstall.in: install rclmon.sh 2006-11-07 18:28 +0000 dockes <dockes> (fb26679a6cec) * src/qtgui/reslist.cpp, src/utils/mimeparse.cpp: 1.5.9: fix bad tz correction in email dates + display tz in reslist 2006-11-07 16:51 +0000 dockes <dockes> (affd0b42e8ae) * src/index/rclmon.h, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp: traces 2006-11-07 12:02 +0000 dockes <dockes> (70ed645d27f3) * src/rcldb/rcldb.cpp: use both size and mtime changes as updateneeding indicator 2006-11-07 09:11 +0000 dockes <dockes> (2491b468f55d) * src/qtgui/uiprefs.ui: improved autophrase tooltip 2006-11-07 09:04 +0000 dockes <dockes> (ba7c28e1a205) * src/qtgui/uiprefs_w.cpp: Cancel did not reset uiprefs dialog to stored state 2006-11-07 08:57 +0000 dockes <dockes> (c9f2b8c02171) * src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h: Cancel did not reset uiprefs dialog to stored state 2006-11-07 06:41 +0000 dockes <dockes> (fea8781e4829) * src/index/indexer.cpp: record/show mtime instead of ctime 2006-11-06 17:37 +0000 dockes <dockes> (a82b3932ac69) * src/doc/user/usermanual.sgml, src/qtgui/rclmain.ui, src/qtgui/spell_w.cpp, src/rcldb/rcldb.cpp: wrote manual for term explorer and fixed a few problems 2006-11-05 21:10 +0000 dockes <dockes> (f4fc6544cb74) * src/bincimapmime/mime-parsefull.cc: fix binc imap infinite loop on multipart with null boundary 2006-11-05 18:02 +0000 dockes <dockes> (9306096cb34f) * src/bincimapmime/depot.h, src/bincimapmime/session.h: deleted file. * src/bincimapmime/address.cc, src/bincimapmime/address.h, src/bincimapmime/convert.cc, src/bincimapmime/convert.h, src/bincimapmime/depot.h, src/bincimapmime/iodevice.cc, src/bincimapmime/iodevice.h, src/bincimapmime/iofactory.h, src/bincimapmime/mime-getpart.cc, src/bincimapmime/mime- inputsource.h, src/bincimapmime/mime-parsefull.cc, src/bincimapmime /mime-parseonlyheader.cc, src/bincimapmime/mime-printbody.cc, src/bincimapmime/mime-printdoc.cc, src/bincimapmime/mime- printheader.cc, src/bincimapmime/mime-utils.h, src/bincimapmime/mime.cc, src/bincimapmime/mime.h, src/bincimapmime/session.h: included bincimap 1.3.3 to 1.3.4 diffs (mostly cosmetic) 2006-11-04 17:09 +0000 dockes <dockes> (3e0e0d4b152f) * src/qtgui/spell.ui, src/qtgui/spell_w.cpp: fix aspell version of term explorer 2006-11-04 14:49 +0000 dockes <dockes> (7f914235875b) * src/qtgui/ssearch_w.cpp, src/qtgui/ssearch_w.h: change ctrl-tab to esc-spc 2006-10-30 12:59 +0000 dockes <dockes> (2454d0c418a2) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain_w.cpp, src/qtgui/spell.ui, src/qtgui/spell_w.cpp, src/qtgui/ssearch_w.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: Turn spell tool into multimode spell/wild/regexp 2006-10-25 11:50 +0000 dockes <dockes> (66843e6e167c) * src/index/indexer.cpp: make tmpdir only once 2006-10-25 10:52 +0000 dockes <dockes> (10c2b1b74822) * src/index/indexer.cpp, src/index/rclmon.h, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp, src/rcldb/rcldb.cpp: added some debugging msgs (too much) 2006-10-24 15:16 +0000 dockes <dockes> (1fc3f90d5ee3) * src/mk/Darwin: *** empty log message *** 2006-10-24 14:28 +0000 dockes <dockes> (33512e5ceddb) * src/index/indexer.cpp, src/index/indexer.h, src/index/rclmon.h, src/index/rclmonprc.cpp, src/index/recollindex.cpp: create stemming db on queue timeout if needed 2006-10-24 13:22 +0000 dockes <dockes> (21df8a0f4856) * src/index/rclmon.sh: new file. * src/index/rclmon.sh: *** empty log message *** 2006-10-24 12:48 +0000 dockes <dockes> (4af32c44f8ea) * src/index/rclmon.h, src/index/rclmonprc.cpp: setup lockfile for monitor 2006-10-24 11:42 +0000 dockes <dockes> (f922b4dda121) * src/query/Makefile: *** empty log message *** 2006-10-24 11:42 +0000 dockes <dockes> (f1da6521f1ff) * src/qtgui/ssearch_w.cpp: explain error for C-TAB too many expansions 2006-10-24 09:28 +0000 dockes <dockes> (3228b6b8093a) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: fix slowness in needUpdate by using Database instead of WritableDatabase 2006-10-24 09:09 +0000 dockes <dockes> (0d72c341e2eb) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/indexer.cpp, src/index/rclmonrcv.cpp: centralize skippedNames computation to add dbdir always 2006-10-23 15:01 +0000 dockes <dockes> (6a07dc59db99) * src/index/rclmon.h, src/index/rclmonrcv.cpp: handle directory creation 2006-10-23 15:00 +0000 dockes <dockes> (672e4b4bfe51) * src/utils/pathut.cpp, src/utils/pathut.h: add path_isdir() 2006-10-23 14:29 +0000 dockes <dockes> (d395ca679c7a) * src/common/autoconfig.h.in, src/configure, src/configure.ac, src/index/rclmonrcv.cpp: raw inotify support 2006-10-22 15:55 +0000 dockes <dockes> (de0702a6c5e2) * src/mk/Linux: *** empty log message *** 2006-10-22 15:54 +0000 dockes <dockes> (35832011eaf9) * src/rcldb/rcldb.cpp: simplify needUpdate test 2006-10-22 14:47 +0000 dockes <dockes> (733c7646ca29) * src/configure, src/configure.ac, src/index/Makefile, src/index/indexer.cpp, src/index/indexer.h, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp, src/index/recollindex.cpp, src/mk/localdefs.in, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: monitor: purge docs for deleted files from db 2006-10-20 08:31 +0000 dockes <dockes> (6d54039efe79) * src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: new Ukrainian+Russian messages from Michael 2006-10-20 08:29 +0000 dockes <dockes> (ebcb12870038) * src/qtgui/advsearch.ui, src/qtgui/rclmain.ui, src/qtgui/uiprefs.ui: small fixes on label strings 2006-10-17 14:41 +0000 dockes <dockes> (ea77c15d81a6) * src/common/autoconfig.h.in, src/configure, src/configure.ac, src/index/rclmon.h, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp, src/index/recollindex.cpp: fam autoconfig 2006-10-16 15:33 +0000 dockes <dockes> (aa570fc97bf9) * src/index/rclmon.h, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp: new file. * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/Makefile, src/index/indexer.cpp, src/index/indexer.h, src/index/rclmon.h, src/index/rclmonprc.cpp, src/index/rclmonrcv.cpp, src/index/recollindex.cpp: 1st version of real time monitor 2006-10-15 13:07 +0000 dockes <dockes> (aa97f764d4a6) * src/qtgui/rclmain_w.cpp, src/qtgui/spell_w.cpp, src/qtgui/spell_w.h: dbl click in spell win to add to ssearch 2006-10-12 14:46 +0000 dockes <dockes> (78a3a37209ae) * src/configure.ac, src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp: recollindex -i now checks that the files are descendants of topdirs 2006-10-12 08:39 +0000 dockes <dockes> (ab66430f3f7d) * src/doc/user/usermanual.sgml: *** empty log message *** 2006-10-11 16:09 +0000 dockes <dockes> (1cf66e2b486f) * src/aspell/rclaspell.cpp, src/utils/execmd.cpp, src/utils/execmd.h: improve execcmd to avoid allocating an allterms buffer when creating dico 2006-10-11 14:16 +0000 dockes <dockes> (26e08a8fc135) * src/qtgui/images/d_spell.png, src/qtgui/images/spell.png, src/qtgui/spell.ui, src/qtgui/spell_w.cpp, src/qtgui/spell_w.h: new file. * src/aspell/rclaspell.cpp, src/aspell/rclaspell.h, src/common/autoconfig.h.in, src/common/rclconfig.h, src/configure, src/configure.ac, src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp, src/mk/commondefs, src/mk/localdefs.in, src/qtgui/images/d_spell.png, src/qtgui/images/spell.png, src/qtgui/main.cpp, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/recoll.h, src/qtgui/recoll.pro.in, src/qtgui/spell.ui, src/qtgui/spell_w.cpp, src/qtgui/spell_w.h, src/sampleconf/recoll.conf.in, src/utils/smallut.cpp, src/utils/smallut.h: 1st full version of aspell support 2006-10-10 10:58 +0000 dockes <dockes> (ed60d657e8e9) * src/aspell/aspell-local.h, src/common/autoconfig.h.in: new file. * src/aspell/aspell-local.h, src/common/autoconfig.h.in: *** empty log message *** 2006-10-09 16:37 +0000 dockes <dockes> (93d9009c4d51) * src/VERSION, src/aspell/rclaspell.cpp, src/aspell/rclaspell.h, src/index/Makefile, src/lib/Makefile, src/lib/mkMake, src/makesrcdist.sh, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/stemdb.cpp, src/utils/execmd.cpp, src/utils/execmd.h: aspell checkpoint 2006-10-09 14:05 +0000 dockes <dockes> (711360b10738) * src/aspell/Makefile, src/aspell/rclaspell.cpp, src/aspell/rclaspell.h: new file. * src/aspell/Makefile, src/aspell/rclaspell.cpp, src/aspell/rclaspell.h: *** empty log message *** 2006-10-03 08:34 +0000 dockes <dockes> (4033d57b83da) * packaging/rpm/recoll.spec, packaging/rpm/recollmdk.spec: 1.5 2006-10-02 13:30 +0000 dockes <dockes> (562f54fc8029) * src/VERSION, src/makestaticdist.sh: small glitches in makestaticdist 2006-10-02 12:33 +0000 dockes <dockes> (7be5b4b7d6c0) * src/VERSION, src/configure, src/configure.ac, src/query/history.h: small glitches detected on suse / gcc 4.1 2006-10-02 11:25 +0000 dockes <dockes> (8731b62606fe [RECOLL-1_5_8, RECOLL-1_5_3, RECOLL-1_5_6, RECOLL-1_5_7, RECOLL-1_5_4, RECOLL-1_5_5]) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- descr, packaging/FreeBSD/recoll/pkg-plist: 1.5.2 2006-10-02 08:38 +0000 dockes <dockes> (ee82de281263) * src/VERSION: *** empty log message *** 2006-10-02 08:26 +0000 dockes <dockes> (bafb3e762d82) * src/sampleconf/mimeconf: added 2 icons 2006-10-02 08:25 +0000 dockes <dockes> (c3d47772ea99) * src/qtgui/mtpics/image.png, src/qtgui/mtpics/source.png: new file. * src/qtgui/mtpics/image.png, src/qtgui/mtpics/source.png: *** empty log message *** 2006-10-02 07:50 +0000 dockes <dockes> (fe9700b5a6fe [RECOLL-1_5_2, RECOLL-1_5_1]) * src/INSTALL, src/README, website/BUGS.txt, website/CHANGES.txt, website/download.html, website/index.html: *** empty log message *** 2006-10-02 07:45 +0000 dockes <dockes> (bc3c93581184) * src/VERSION, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts: 1.5.1 2006-09-29 11:43 +0000 dockes <dockes> (bd51dd85dc0f) * src/doc/user/usermanual.sgml: aspell pass 2006-09-29 08:26 +0000 dockes <dockes> (026d0b177533) * src/rcldb/rcldb.cpp: syntabs: remove size limit. Handle overlapping chunks. Make sure we use only one term per position 2006-09-29 08:24 +0000 dockes <dockes> (49342357f800) * src/qtgui/reslist.cpp: reset curPvDoc on setDocSource 2006-09-29 08:23 +0000 dockes <dockes> (a9cef42dd219) * src/qtgui/uiprefs.ui: bump up limits on max abstract size parameters 2006-09-29 07:13 +0000 dockes <dockes> (7973023d7c1b) * src/qtgui/ssearch_w.cpp: bad/unneeded conversion to utf8 while saving ssearch history would cause some string sizes in history to double at each program invocation 2006-09-28 14:32 +0000 dockes <dockes> (a5bba26b0ac0) * src/qtgui/i18n/recoll_fr.ts: 1.5 2006-09-28 14:31 +0000 dockes <dockes> (71d5895b7848) * src/qtgui/ssearchb.ui: improved tip 2006-09-28 14:30 +0000 dockes <dockes> (903f443a7150) * src/doc/user/usermanual.sgml: reordered the tips 2006-09-28 11:55 +0000 dockes <dockes> (3bf818bd1e39) * src/qtgui/main.cpp: debug messages 2006-09-23 13:32 +0000 dockes <dockes> (3cf269bf18f0) * website/BUGS.txt, website/CHANGES.txt, website/download.html, website/features.html, website/index.html: *** empty log message *** 2006-09-23 13:13 +0000 dockes <dockes> (06ed627f182d [RECOLL-1_5_0]) * src/INSTALL, src/README, src/makesrcdist.sh: *** empty log message *** 2006-09-23 13:11 +0000 dockes <dockes> (0533f47f1c34) * src/INSTALL, src/doc/user/usermanual.sgml: *** empty log message *** 2006-09-23 13:09 +0000 dockes <dockes> (9b8fdf62ad07) * src/doc/user/usermanual.sgml: *** empty log message *** 2006-09-23 07:39 +0000 dockes <dockes> (33e469ad3a2e) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/preview_w.cpp, src/qtgui/reslist.cpp: fix file name display in tooltips 2006-09-23 07:39 +0000 dockes <dockes> (e47ebb4e22ce) * src/internfile/mh_mail.cpp: fix newlines 2006-09-23 07:21 +0000 dockes <dockes> (b5b530ea2ec9) * src/rcldb/rcldb.cpp: message 2006-09-22 14:11 +0000 dockes <dockes> (3ef29a8417c7) * src/qtgui/i18n/recoll_fr.ts, src/rcldb/rcldb.cpp: msg 2006-09-22 10:46 +0000 dockes <dockes> (9ade76cc0df5) * src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/uiprefs_w.cpp: messages 2006-09-22 08:19 +0000 dockes <dockes> (c228a1515468) * src/qtgui/recoll.pro.in: *** empty log message *** 2006-09-22 07:51 +0000 dockes <dockes> (df858f3508f4) * src/qtgui/recoll.pro.in: names cleanup 2006-09-22 07:42 +0000 dockes <dockes> (de54384ab321) * src/utils/mimeparse.h: comment 2006-09-22 07:41 +0000 dockes <dockes> (f37052248b5b) * src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h: new file. * src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/recoll_fr.ts, src/qtgui/recoll_ru.ts, src/qtgui/recoll_uk.ts, src/qtgui/recollmain.ui: deleted file. * src/qtgui/i18n/recoll_fr.ts, src/qtgui/i18n/recoll_ru.ts, src/qtgui/i18n/recoll_uk.ts, src/qtgui/main.cpp, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/rclmain.ui, src/qtgui/rclmain_w.cpp, src/qtgui/rclmain_w.h, src/qtgui/recoll_fr.ts, src/qtgui/recoll_ru.ts, src/qtgui/recoll_uk.ts, src/qtgui/recollmain.ui: names cleanup: rclmain, translations 2006-09-22 07:38 +0000 dockes <dockes> (c17bf757689b) * src/recollinstall.in: names cleanup: translations 2006-09-22 07:29 +0000 dockes <dockes> (2d749704a22b) * src/qtgui/reslist.cpp, src/qtgui/reslist.h: new file. * src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h: deleted file. * src/qtgui/rclmain.cpp, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h, src/qtgui/recollmain.ui, src/qtgui/reslist.cpp, src/qtgui/reslist.h: names cleanup: reslist 2006-09-22 07:22 +0000 dockes <dockes> (cd9f046bf5e3) * src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h: clarified preview paragraph coloring in reslist 2006-09-22 07:19 +0000 dockes <dockes> (c700f9f95168) * src/internfile/mh_mail.cpp: clarified depth processing and increased limit 2006-09-21 12:56 +0000 dockes <dockes> (334ef2914129) * src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h: synchronize preview tab and colored paragraph in result list 2006-09-21 09:37 +0000 dockes <dockes> (43c279d4d112) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/sort_w.cpp, src/qtgui/sort_w.h, src/query/sortseq.cpp, src/query/sortseq.h: remember sort criteria 2006-09-21 05:59 +0000 dockes <dockes> (e5e9c1ffa44c) * src/internfile/myhtmlparse.cpp: dont throw away text even if html is weird 2006-09-21 05:59 +0000 dockes <dockes> (4e99ebec009f) * src/common/textsplit.cpp: 132.jpg was not split 2006-09-21 05:57 +0000 dockes <dockes> (3bc572456a49) * src/common/Makefile, src/utils/Makefile: *** empty log message *** 2006-09-20 06:21 +0000 dockes <dockes> (5829221e8612) * src/rcldb/stemdb.cpp: comments 2006-09-19 14:30 +0000 dockes <dockes> (598f2c534c4c) * src/rcldb/stemdb.cpp: Stems with unique parent must be in db too so that one can search on stem (which is not a term) 2006-09-19 14:30 +0000 dockes <dockes> (98cd92c958bd) * src/internfile/mh_mail.cpp, src/internfile/mh_mail.h: walk the full mime tree instead of staying at level 1 2006-09-19 14:19 +0000 dockes <dockes> (12fcb57186c2) * src/configure, src/configure.ac: *** empty log message *** 2006-09-19 14:19 +0000 dockes <dockes> (88bbc8f18b9e) * src/utils/mimeparse.cpp: disable date debug msgs 2006-09-19 14:18 +0000 dockes <dockes> (a0016b0e9969) * src/query/xadump.cpp: add option to dump a recoll stemdb 2006-09-18 12:17 +0000 dockes <dockes> (e662e0bbe85e) * src/README: *** empty log message *** 2006-09-15 16:50 +0000 dockes <dockes> (315f1c1d3dd3) * src/VERSION, src/doc/user/usermanual.sgml, src/internfile/mh_mail.cpp, src/utils/mimeparse.cpp, src/utils/mimeparse.h: Use own code to parse rfc822 dates, strptime() cant do 2006-09-15 16:49 +0000 dockes <dockes> (ca133771bc5b) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/recoll.pro.in, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: small typo fixes 2006-09-15 12:36 +0000 dockes <dockes> (47354227e577) * src/INSTALL, src/README: *** empty log message *** 2006-09-14 07:13 +0000 dockes <dockes> (b717321f9de4 [RECOLL-1_4_4]) * *** empty log message *** 2006-09-14 07:13 +0000 dockes <dockes> (919e6e0dfc56) * website/BUGS.txt, website/CHANGES.txt, website/copydocs, website/credits.html, website/devel.html, website/download.html, website/features.html, website/index.html, website/pics/index.html, website/styles/style.css: new file. * website/BUGS.txt, website/CHANGES.txt, website/copydocs, website/credits.html, website/devel.html, website/download.html, website/features.html, website/index.html, website/pics/index.html, website/styles/style.css: *** empty log message *** 2006-09-13 15:31 +0000 dockes <dockes> (9bd2431eaa66) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/ssearch_w.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: autophrase parameter 2006-09-13 14:57 +0000 dockes <dockes> (0d952f522055) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/rclreslist.cpp, src/query/docseq.cpp, src/query/docseq.h: colorize search terms in abstracts 2006-09-13 13:53 +0000 dockes <dockes> (5980807171a8) * src/index/indexer.cpp, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/main.cpp, src/qtgui/rclmain.cpp, src/qtgui/ssearch_w.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/sampleconf/recoll.conf.in: make constant lengths for abstracts config params 2006-09-13 08:13 +0000 dockes <dockes> (6e43869ceb61) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/main.cpp, src/qtgui/rclmain.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs_w.cpp: add feature to save asearch ignored file types as startup default 2006-09-12 10:11 +0000 dockes <dockes> (9b323c436beb) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h, src/qtgui/ssearch_w.cpp, src/qtgui/ssearchb.ui: allow paging through results inside a preview window with shift-up shift-down 2006-09-11 14:22 +0000 dockes <dockes> (f0dd93428e23) * src/doc/user/usermanual.sgml, src/qtgui/advsearch.ui: try to make clearer that adv search fields will accept phrases as well as single words 2006-09-11 12:05 +0000 dockes <dockes> (f455fbc6a42a) * src/qtgui/advsearch.ui, src/qtgui/advsearch_w.cpp, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/ssearch_w.cpp, src/qtgui/ssearchb.ui, src/query/history.cpp, src/query/history.h: remember history of restrict subdirs in adv search 2006-09-11 09:08 +0000 dockes <dockes> (ad274e633ffb) * src/qtgui/guiutils.cpp, src/qtgui/main.cpp, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/recoll.h, src/qtgui/uiprefs.ui, src/query/docseq.h, src/query/history.cpp, src/query/history.h: use the (should be renamed) history file to store external databases lists 2006-09-11 07:10 +0000 dockes <dockes> (6cb09384f54a) * src/qtgui/ssearch_w.cpp, src/qtgui/ssearchb.ui: maintain ssearches listbox in mru order 2006-09-11 06:58 +0000 dockes <dockes> (b62d0be5650e) * src/qtgui/advsearch.ui, src/qtgui/sort.ui, src/qtgui/uiprefs.ui: ensure dialogs are sized according to font size 2006-09-08 09:02 +0000 dockes <dockes> (a5a31c9b0a37) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/common/rclinit.cpp, src/common/rclinit.h, src/doc/man/recoll.1, src/doc/man/recoll.conf.5, src/doc/man/recollindex.1, src/doc/user/usermanual.sgml, src/index/recollindex.cpp, src/qtgui/guiutils.cpp, src/qtgui/main.cpp: Add -c <confdir> option to recoll and recollindex 2006-09-08 08:51 +0000 dockes <dockes> (315e0865ec26) * src/sampleconf/recoll.conf.in: The dbdir default value is now relative to the cnf dir 2006-09-06 09:50 +0000 dockes <dockes> (e696d98fe7fe) * src/qtgui/uiprefs_w.cpp: Used to reset the buildAbstract replaceAbstract options because of setDown instead of setChecked 2006-09-06 09:14 +0000 dockes <dockes> (0dedd735c86e) * src/utils/mimeparse.cpp, src/utils/mimeparse.h: implement rfc2231 decoding for mime parameter values 2006-09-05 17:09 +0000 dockes <dockes> (95fd6b3a5b9a) * src/internfile/mh_mail.cpp: let mimeparse handle decoding or param values 2006-09-05 09:52 +0000 dockes <dockes> (44182523e711) * src/filters/rclppt, src/filters/rclxls: new file. * src/filters/rclppt, src/filters/rclxls, src/sampleconf/mimeconf, src/sampleconf/mimemap: added support for ppt and xls via catdoc 2006-09-05 08:05 +0000 dockes <dockes> (587719349228) * src/internfile/mh_mail.cpp, src/internfile/mh_mail.h: index and display attachment file names 2006-09-05 08:04 +0000 dockes <dockes> (6f8b09a74d14) * src/utils/mimeparse.cpp, src/utils/mimeparse.h: comments only 2006-09-04 15:13 +0000 dockes <dockes> (0f11e18480b2) * src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/preview.ui, src/qtgui/preview.ui.h, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/sort_w.cpp, src/qtgui/sort_w.h, src/qtgui/ssearch_w.cpp, src/qtgui/ssearch_w.h, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h: new file. * src/qtgui/preview/preview.pro, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/preview/pvmain.cpp: deleted file. * src/qtgui/advsearch.ui, src/qtgui/advsearch.ui.h, src/qtgui/advsearch_w.cpp, src/qtgui/advsearch_w.h, src/qtgui/guiutils.cpp, src/qtgui/main.cpp, src/qtgui/preview.ui, src/qtgui/preview.ui.h, src/qtgui/preview/preview.pro, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/preview/pvmain.cpp, src/qtgui/preview_w.cpp, src/qtgui/preview_w.h, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/recoll.pro.in, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/qtgui/sort.ui, src/qtgui/sort.ui.h, src/qtgui/sort_w.cpp, src/qtgui/sort_w.h, src/qtgui/ssearch_w.cpp, src/qtgui/ssearch_w.h, src/qtgui/ssearchb.ui, src/qtgui/ssearchb.ui.h, src/qtgui/uiprefs.ui, src/qtgui/uiprefs.ui.h, src/qtgui/uiprefs_w.cpp, src/qtgui/uiprefs_w.h: mostly cosmetic changes to prepare for a future qt4 port: better separate form design from code 2006-06-29 11:05 +0000 dockes <dockes> (8f28af2cb548) * src/qt4gui/recollmain.ui: *** empty log message *** 2006-06-24 09:56 +0000 dockes <dockes> (fb2180e4d577) * src/qt4gui/recollmain.ui: qt4 cleanup: merged back rclmainbase and rclmain 2006-06-24 07:40 +0000 dockes <dockes> (e1b5ffd88b25) * src/Makefile.in, src/VERSION, src/configure, src/configure.ac, src/doc/user/usermanual.sgml, src/qt4gui/recoll.pro.in, src/qt4gui/recollmain.ui, src/recollinstall.in: more qt4, unfinished 2006-06-23 08:07 +0000 dockes <dockes> (46a46e406504) * src/qt4gui/recoll.pro.in, src/qt4gui/recollmain.ui: new file. * src/qt4gui/recoll.pro.in, src/qt4gui/recollmain.ui: added qt4gui code from Gennadi Sushko 2006-05-22 07:04 +0000 dockes <dockes> (2663a50d4760) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo: 1.4.3 2006-05-09 10:15 +0000 dockes <dockes> (c9a62f0cb289) * src/rcldb/rcldb.cpp: perform stem expansion using all active dbs 2006-05-09 07:56 +0000 dockes <dockes> (29feec461985) * src/qtgui/preview/preview.ui.h, src/qtgui/rclreslist.cpp: esc quits preview + prev/next links 2006-05-08 07:08 +0000 dockes <dockes> (185da0be6900) * src/recollinstall.in: install icon 2006-05-08 07:08 +0000 dockes <dockes> (7dbebc260389) * src/qtgui/rclreslist.cpp: *** empty log message *** 2006-05-07 14:18 +0000 dockes <dockes> (2f273f645a91) * packaging/rpm/recoll.spec: 1.4.3 2006-05-07 14:18 +0000 dockes <dockes> (16b38a704d8e) * packaging/rpm/recoll.spec, packaging/rpm/recollmdk.spec: 1.3.3 2006-05-07 14:10 +0000 dockes <dockes> (4ab20caea142 [RECOLL-1_4_3]) * src/VERSION: Release 1.4.3 2006-05-06 17:25 +0000 dockes <dockes> (e7b4fd0f97fa) * src/recoll.png, src/recoll.xcf: new file. * src/qtgui/recoll.pro.in, src/recoll.png, src/recoll.xcf: *** empty log message *** 2006-05-06 17:24 +0000 dockes <dockes> (aae37ad598a9) * src/qtgui/recoll_ru.ts, src/qtgui/recoll_uk.ts: new from michael 2006-05-02 09:49 +0000 dockes <dockes> (fb5bb4665925 [RECOLL-1_4_2]) * src/qtgui/guiutils.cpp, src/rcldb/stemdb.cpp, src/unac/unac.c: more fbsd4 tweaks: Release 1.4.2 2006-04-30 07:44 +0000 dockes <dockes> (d686e45d4b5e) * src/rcldb/rcldb.cpp: fbsd4 tweaks 2006-04-30 07:39 +0000 dockes <dockes> (b889e57b87d6) * src/VERSION, src/index/indexer.cpp, src/index/indexer.h, src/lib/Makefile, src/lib/mkMake: fbsd4 tweaks 2006-04-30 07:26 +0000 dockes <dockes> (72f2881955d1 [RECOLL-1_4_1]) * src/Makefile.in, src/README: *** empty log message *** 2006-04-30 07:23 +0000 dockes <dockes> (172a9e09b77c) * src/Makefile.in: *** empty log message *** 2006-04-30 07:20 +0000 dockes <dockes> (7be76a62e017) * src/qtgui/recoll_fr.ts, src/qtgui/recoll_ru.ts, src/qtgui/recoll_uk.ts: lupdate+french 2006-04-28 07:54 +0000 dockes <dockes> (5b44017502c3) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/indexer.cpp, src/index/recollindex.cpp, src/kde/kioslave/recoll/kio_recoll.cpp, src/qtgui/main.cpp, src/query/qtry.cpp: centralize dbdir computation in rclconfig+cat with conffdir if not absolute 2006-04-28 07:23 +0000 dockes <dockes> (436f58f83459) * src/utils/transcode.cpp: change debug log trace 2006-04-27 09:23 +0000 dockes <dockes> (3df68e37cdd9) * src/doc/user/usermanual.sgml, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain.cpp, src/qtgui/ssearchb.ui, src/qtgui/ssearchb.ui.h: make ssearch a combobox 2006-04-27 06:12 +0000 dockes <dockes> (e7c0f6cd73f0) * src/configure, src/configure.ac, src/lib/Makefile, src/lib/mkMake: fix pb with .deps not existing 2006-04-27 06:12 +0000 dockes <dockes> (83e1c6a16ca6) * src/qtgui/preview/preview.ui.h, src/qtgui/rclmain.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: use getmatchingterms instead of getqueryterms for highlighting etc. in preview 2006-04-26 11:51 +0000 dockes <dockes> (fa1cc55f05e9) * src/INSTALL, src/README: *** empty log message *** 2006-04-26 11:29 +0000 dockes <dockes> (d92273eb3274) * src/qtgui/rclmain.cpp, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h: dblclck in reslist adds to search lineedit 2006-04-25 09:59 +0000 dockes <dockes> (16f32a4eda4c) * src/index/indexer.cpp, src/rcldb/rcldb.cpp: new way for doc unique terms: only path for monodoc, only path+ipath for doc inside multidoc, add pseudo-doc for file itself 2006-04-25 08:17 +0000 dockes <dockes> (4c947b29c23c) * src/common/textsplit.cpp, src/rcldb/rcldb.cpp: fixed small glitch in abstract text splitting 2006-04-23 13:37 +0000 dockes <dockes> (ea8caddeb344) * src/lib/mkMake: new file. * src/lib/mkMake: *** empty log message *** 2006-04-22 06:27 +0000 dockes <dockes> (1b0dd24cad31) * src/qtgui/main.cpp, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h, src/qtgui/ssearchb.ui, src/qtgui/ssearchb.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.h: turn-off abst. build for fname search (no terms) + prototype query expansion (xapian e-set on chosen doc) + dbl-click in preview adds term to ssearch 2006-04-20 09:20 +0000 dockes <dockes> (4b9c3c7bcb49) * src/common/rclconfig.cpp, src/lib/Makefile, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/qtgui/sort.ui, src/qtgui/sort.ui.h, src/qtgui/ssearchb.ui.h, src/query/sortseq.cpp, src/query/sortseq.h: mode 700 on .recoll. move showquerydetails to rclreslist 2006-04-19 08:26 +0000 dockes <dockes> (9ec7ff1d0d53) * src/rcldb/searchdata.h: new file. * src/qtgui/advsearch.ui, src/qtgui/advsearch.ui.h, src/qtgui/main.cpp, src/qtgui/rclmain.h, src/qtgui/rclreslist.cpp, src/qtgui/ssearchb.ui, src/qtgui/ssearchb.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/rcldb/searchdata.h: compacted res list + completions in ssearch + additional or field 2006-04-18 08:53 +0000 dockes <dockes> (7c4352949f19) * src/index/recollindex.cpp, src/lib/Makefile, src/qtgui/advsearch.ui, src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/rclmain.cpp, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h, src/qtgui/recollmain.ui, src/qtgui/ssearchb.ui, src/qtgui/ssearchb.ui.h, src/qtgui/uiprefs.ui, src/qtgui/uiprefs.ui.h: new libs Makefile+autoSearchOnWS 2006-04-15 17:15 +0000 dockes <dockes> (cc178f316e64) * src/qtgui/main.cpp, src/query/Makefile: small aix tweaks 2006-04-15 16:51 +0000 dockes <dockes> (356148054ef1) * src/mk/AIX: new file. * src/mk/AIX: *** empty log message *** 2006-04-13 09:50 +0000 dockes <dockes> (fe982a2684e4) * src/rcldb/stemdb.cpp, src/rcldb/stemdb.h: new file. * src/lib/Makefile, src/rcldb/rcldb.cpp, src/rcldb/stemdb.cpp, src/rcldb/stemdb.h: extracted stem database from rcldb to make it smaller 2006-04-12 10:41 +0000 dockes <dockes> (6892025a5c8e) * src/index/indexer.cpp, src/index/indexer.h, src/qtgui/idxthread.cpp, src/qtgui/idxthread.h, src/qtgui/rclmain.cpp, src/qtgui/recollmain.ui, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: improve indexing status reporting 2006-04-12 07:26 +0000 dockes <dockes> (44ac63815611) * src/qtgui/rclmain.cpp, src/rcldb/rcldb.cpp: Fix history not working after thread index run 2006-04-11 07:14 +0000 dockes <dockes> (bd453ed96f6a) * src/rcldb/rcldb.cpp: fix rare case where stem itself was forgotten in list of possible derivatives 2006-04-11 06:49 +0000 dockes <dockes> (dd7f793fdf8e) * src/common/textsplit.cpp, src/rcldb/rcldb.cpp, src/utils/smallut.cpp, src/utils/smallut.h: comments and moving some util routines out of rcldb.cpp 2006-04-08 14:00 +0000 dockes <dockes> (d60e656c348f) * src/VERSION, src/doc/user/usermanual.sgml: *** empty log message *** 2006-04-07 13:10 +0000 dockes <dockes> (dc4ff4178b85) * src/qtgui/advsearch.ui.h, src/rcldb/rcldb.cpp: check for and forbid pure negative query 2006-04-07 13:08 +0000 dockes <dockes> (7da00eb0c7aa) * src/qtgui/guiutils.cpp: RECOLL_EXTRA_DBS environment variable 2006-04-07 13:07 +0000 dockes <dockes> (f040ff3bf0aa) * src/doc/user/usermanual.sgml: use indexing instead of indexation 2006-04-07 08:51 +0000 dockes <dockes> (52451e342e49) * src/internfile/mh_mail.cpp, src/internfile/mh_mail.h: comments+conventions 2006-04-06 17:39 +0000 dockes <dockes> (7d2906a0371d) * packaging/FreeBSD/recoll/pkg-plist: merge modif from committer 2006-04-06 14:28 +0000 dockes <dockes> (52d4a2c2a341) * src/sampleconf/recoll.conf.in: stem only for english by default 2006-04-06 13:09 +0000 dockes <dockes> (fa565da09aa7 [RECOLL-1_4_0]) * src/VERSION: 1.4.0 2006-04-06 13:08 +0000 dockes <dockes> (1436c843e74e) * src/VERSION, src/rcldb/rcldb.h, src/recollinstall.in: rpmlint wants 755 for execs 2006-04-06 12:34 +0000 dockes <dockes> (687369f6736c) * src/INSTALL, src/README: *** empty log message *** 2006-04-05 15:41 +0000 dockes <dockes> (359711d5fbe3) * src/recollinstall.in: fix the installed file perms 2006-04-05 13:39 +0000 dockes <dockes> (fdd0d33d5c2e) * src/qtgui/guiutils.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: small fixes for extra db selection 2006-04-05 13:30 +0000 dockes <dockes> (3277935457e9) * src/doc/user/usermanual.sgml: small fixes 2006-04-05 12:50 +0000 dockes <dockes> (98d8d7d74aee) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/main.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: additional search databases 2006-04-05 06:26 +0000 dockes <dockes> (e8f1cc7c2bbf) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: simplified class structure 2006-04-04 16:03 +0000 dockes <dockes> (ddb66052e3e8 [RECOLL-1_4_0pre1]) * src/filters/rcldjvu, src/filters/rclmedia, src/filters/rclps, src/filters/rclsoff: handle paths with embedded spaces 2006-04-04 15:44 +0000 dockes <dockes> (933f89f10033) * src/filters/rcluncomp: handle paths with embedded spaces 2006-04-04 13:49 +0000 dockes <dockes> (dec4932652ae) * src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp, src/qtgui/idxthread.cpp, src/qtgui/idxthread.h, src/qtgui/rclmain.cpp: make indexation more easily cancellable 2006-04-04 12:37 +0000 dockes <dockes> (17342a5b330b) * src/doc/user/usermanual.sgml, src/index/indexer.cpp, src/index/indexer.h: check for symlinks in the topdirs list. Generate diags in confIndexer 2006-04-04 10:38 +0000 dockes <dockes> (ec632eb29364) * src/qtgui/guiutils.cpp, src/qtgui/rclreslist.cpp: setup initial default window size smaller 2006-04-04 09:36 +0000 dockes <dockes> (d175998d9270) * src/common/unacpp.cpp, src/unac/unac.c: clarify/clean up mem buffer handling 2006-04-04 09:35 +0000 dockes <dockes> (b14f9df37817) * src/utils/conftree.h: fix potential minor memory leak when copying conftrees 2006-04-04 09:34 +0000 dockes <dockes> (aaddcbb06c7a) * src/index/indexer.cpp, src/index/indexer.h: cosmetic: add m_ prefix to private vars 2006-04-04 07:55 +0000 dockes <dockes> (1bda0dcafd17) * src/qtgui/main.cpp, src/qtgui/preview/preview.ui.h, src/qtgui/rclmain.cpp: Get things to compile with QT_NO_STL 2006-04-03 14:16 +0000 dockes <dockes> (4957b439000e) * packaging/FreeBSD/recoll/Makefile: add mods from port tree 2006-04-03 12:59 +0000 dockes <dockes> (62a51f626e46) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo: *** empty log message *** 2006-04-03 11:43 +0000 dockes <dockes> (0449d2ba4099 [RECOLL-1_3_3]) * src/VERSION: 1.3.3 2006-04-03 11:43 +0000 dockes <dockes> (6b54e368d2d1) * src/common/rclconfig.cpp: small port fixes for fbsd4 and solaris 2006-04-03 09:42 +0000 dockes <dockes> (cd9fe4976fae) * src/utils/execmd.cpp: warning 2006-04-01 21:02 +0000 dockes <dockes> (eef792b97ce8 [RECOLL-1_3_2]) * src/VERSION, src/qtgui/rclmain.cpp: limit max length of displayed query details. 1.3.2 2006-04-01 09:15 +0000 dockes <dockes> (315da01fb1a5 [RECOLL-1_3_1]) * src/INSTALL, src/README: *** empty log message *** 2006-04-01 09:15 +0000 dockes <dockes> (e9f0a85fc18e) * src/INSTALL, src/README, src/qtgui/rclreslist.cpp, src/qtgui/ssearchb.ui: updated INSTALL+README. Fix tab focus in main window 2006-04-01 08:07 +0000 dockes <dockes> (916faf93fb66) * src/qtgui/rclmain.cpp: urlencode file name before executing ext app with url param 2006-04-01 07:48 +0000 dockes <dockes> (adbde9cd60b9) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- plist: 1.3.1 2006-04-01 07:34 +0000 dockes <dockes> (4cd3e4e4074c) * src/sampleconf/mimeconf, src/sampleconf/mimemap: Allow ext edit for c/c++ files. 1.3.1 2? 2006-03-31 17:19 +0000 dockes <dockes> (35db6f17bdd8) * src/VERSION, src/qtgui/rclmain.cpp, src/qtgui/rclreslist.cpp, src/qtgui/recoll_ru.ts, src/qtgui/recoll_uk.ts: fixed reslist header charset issues. 1.3.1 first 2006-03-31 09:02 +0000 dockes <dockes> (ae3d9c9deb6d) * src/INSTALL, src/README: *** empty log message *** 2006-03-31 07:51 +0000 dockes <dockes> (0fccf51c6905 [RECOLL-1_3_1pre3]) * src/qtgui/recoll_uk.ts: new file. * src/qtgui/recoll_uk.ts: *** empty log message *** 2006-03-30 13:00 +0000 dockes <dockes> (b41828dda0ac) * src/common/Makefile: cleanup rclconfig 2006-03-30 10:31 +0000 dockes <dockes> (afbdbc31ff1c) * src/sampleconf/recoll.conf.in: dont set defaultcharset to 8859-1: will let nls info be used 2006-03-30 10:31 +0000 dockes <dockes> (582fa2a09db3) * src/doc/user/usermanual.sgml: *** empty log message *** 2006-03-30 08:19 +0000 dockes <dockes> (89efa1c78c3c [RECOLL-1_3_1pre2]) * src/qtgui/recoll_fr.ts, src/qtgui/recoll_ru.ts: lupdate 2006-03-30 07:54 +0000 dockes <dockes> (0b236faa0b9d) * src/qtgui/advsearch.ui: cleaned up layout 2006-03-29 17:31 +0000 dockes <dockes> (7cb115f5789c) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h, src/qtgui/main.cpp, src/qtgui/ssearchb.ui, src/qtgui/ssearchb.ui.h: gui: replaced checkboxes for all/filename in simple search with droplist 2006-03-29 13:08 +0000 dockes <dockes> (ce199bb02759) * src/VERSION, src/common/Makefile, src/common/rclconfig.cpp, src/common/rclconfig.h, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/qtgui/rclreslist.cpp, src/sampleconf/mimeconf, src/sampleconf/mimemap: result list: show preview and edit links only when they can be used 2006-03-29 11:18 +0000 dockes <dockes> (5f22b93705b4) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/common/rclinit.cpp, src/index/indexer.cpp, src/qtgui/preview/preview.ui.h, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/pathut.cpp, src/utils/pathut.h, src/utils/transcode.cpp, src/utils/transcode.h: try to better handle non-ascii file names 2006-03-28 12:49 +0000 dockes <dockes> (a081a1b65de1) * src/doc/man/recoll.conf.5, src/doc/user/usermanual.sgml, src/qtgui/recoll.pro.in, src/recollinstall.in, src/sampleconf/recoll.conf.in: 1.3.1pre1 2006-03-28 12:18 +0000 dockes <dockes> (7429c22d162b) * src/INSTALL, src/README: *** empty log message *** 2006-03-28 09:38 +0000 dockes <dockes> (25e1ed25acc5) * src/filters/rclmedia, src/qtgui/mtpics/sownd.png: new file. * src/filters/rclmedia, src/qtgui/mtpics/sownd.png, src/sampleconf/mimeconf, src/sampleconf/mimemap: filter for indexing mp3 tags 2006-03-28 09:36 +0000 dockes <dockes> (fb852147db29) * src/internfile/mh_unknown.h: new file. * src/internfile/mh_unknown.h: added code to specifically index/search file names 2006-03-22 16:24 +0000 dockes <dockes> (4467274ce405) * src/index/indexer.cpp, src/index/indexer.h, src/qtgui/idxthread.cpp, src/qtgui/idxthread.h, src/qtgui/rclmain.cpp, src/qtgui/recollmain.ui: show current filename as feedback during indexation 2006-03-22 14:25 +0000 dockes <dockes> (5dae5f8a140d) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/utils/conftree.cpp, src/utils/conftree.h: Replace user config with central values + override 2006-03-22 11:17 +0000 dockes <dockes> (1f04e3bfeb4a) * src/qtgui/rclreslist.cpp: fix size display 2006-03-21 15:11 +0000 dockes <dockes> (88d6359d2739) * src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h: implement right click menu in result list 2006-03-21 13:46 +0000 dockes <dockes> (56610f5d03b3) * src/qtgui/rclmain.cpp, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h: replaced (double)clicks in the result list with links 2006-03-21 13:27 +0000 dockes <dockes> (cc41e73a4f5a) * src/qtgui/rclreslist.cpp: ckpt 2006-03-21 11:04 +0000 dockes <dockes> (b1dc67961a45) * src/index/mimetype.cpp: sanity check on file -i return 2006-03-21 09:15 +0000 dockes <dockes> (8589c7c01f25) * src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h: new file. * src/qtgui/reslistb.ui, src/qtgui/reslistb.ui.h: deleted file. * src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/rclreslist.cpp, src/qtgui/rclreslist.h, src/qtgui/recollmain.ui, src/qtgui/reslistb.ui, src/qtgui/reslistb.ui.h: reslistb form replaced by object derived from QTextBrowser 2006-03-20 16:05 +0000 dockes <dockes> (70c0ec0275a9) * src/VERSION, src/index/indexer.cpp, src/internfile/internfile.cpp, src/internfile/mimehandler.cpp, src/qtgui/advsearch.ui, src/qtgui/advsearch.ui.h, src/qtgui/ssearchb.ui, src/qtgui/ssearchb.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: added code to specifically index/search file names 2006-03-20 15:14 +0000 dockes <dockes> (86bb2d64fdd9) * src/internfile/mh_text.cpp: get rid of unused temp 2006-03-20 09:54 +0000 dockes <dockes> (fea74448199d) * src/utils/pathut.h: comments 2006-03-20 09:54 +0000 dockes <dockes> (bf4772fd96ff) * src/sampleconf/mimemap: add # to ignd suffixes 2006-03-20 09:51 +0000 dockes <dockes> (218c67bcb769) * src/common/rclconfig.cpp, src/common/rclconfig.h: try to get default charset from LANG if not in config 2006-03-20 09:50 +0000 dockes <dockes> (2d633e45c451) * src/makestaticdist.sh: desktop file 2006-03-16 14:00 +0000 dockes <dockes> (b45dd89bb177) * src/recoll.desktop: new file. * src/recoll.desktop: initial version from Michael Shigorin 2006-03-16 13:49 +0000 dockes <dockes> (e3e216dfacb6) * src/qtgui/recoll_ru.ts: new file. * src/qtgui/recoll_ru.ts: initial version from Michael Shigorin 2006-03-04 10:09 +0000 dockes <dockes> (983d0984e972 [RECOLL-1_2_3]) * src/VERSION, src/doc/user/usermanual.sgml: 1.2.3 2006-02-21 12:57 +0000 dockes <dockes> (29500b27662b) * src/INSTALL, src/README: *** empty log message *** 2006-02-21 12:56 +0000 dockes <dockes> (0bc6bf836dfe) * src/Makefile.in, src/configure, src/configure.ac: ensure Makefile uses same qmake as configure 2006-02-21 12:52 +0000 dockes <dockes> (9a69d49b1448) * src/query/docseq.h, src/query/sortseq.h: sorted sequence title would never show 2006-02-07 10:26 +0000 dockes <dockes> (8881db16fe21) * src/qtgui/reslistb.ui.h, src/query/docseq.cpp, src/query/docseq.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: fix problems with doc fetch sequence (have to know where to stop) 2006-02-07 09:44 +0000 dockes <dockes> (fbfb30458fc2) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h: replace computation of term positions in editor text with search for 1st query term 2006-02-03 11:47 +0000 dockes <dockes> (1dbf9bcedfc0) * src/filters/rcldvi: option to use catdvi 2006-02-03 10:53 +0000 dockes <dockes> (f219261a580b) * src/filters/rcldjvu, src/filters/rcldvi: new file. * src/filters/rcldjvu, src/filters/rcldvi, src/filters/rclps, src/sampleconf/mimeconf, src/sampleconf/mimemap: added dvi and djvu support 2006-02-02 09:45 +0000 dockes <dockes> (71a4b9e391e0) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo: 1.2.2 2006-02-02 08:58 +0000 dockes <dockes> (c2f3b36a7169 [RECOLL-1_2_2]) * src/rcldb/rcldb.cpp, src/utils/pathut.cpp: suppress 2 compilation warnings (one was actual 64bits bug but inocuous 2006-02-02 08:35 +0000 dockes <dockes> (4d473bd0d9a8) * src/Makefile.in, src/VERSION, src/configure, src/configure.ac, src/qtgui/main.cpp, src/qtgui/preview/preview.ui.h: fix small cc glitches: qt3.1, xapian-config 2006-02-01 14:34 +0000 dockes <dockes> (a4deac6ede77 [RECOLL-1_2_1]) * src/qtgui/guiutils.cpp, src/qtgui/reslistb.ui.h: fbsd4 cc 2006-02-01 14:27 +0000 dockes <dockes> (b005945089dc) * src/VERSION: *** empty log message *** 2006-02-01 14:18 +0000 dockes <dockes> (1f6da4b2f946) * src/common/textsplit.cpp: use string::erase() not clear() 2006-02-01 09:00 +0000 dockes <dockes> (09b3a24a6173 [RECOLL-1_2_0]) * src/recollinstall.in: *** empty log message *** 2006-02-01 08:19 +0000 dockes <dockes> (bef8d87339d0) * packaging/rpm/recoll.spec: *** empty log message *** 2006-02-01 07:14 +0000 dockes <dockes> (5c4deca7b177) * src/excludefile, src/utils/base64.cpp: *** empty log message *** 2006-02-01 07:12 +0000 dockes <dockes> (77e021af3fa0) * src/INSTALL, src/README, src/doc/user/usermanual.sgml, src/excludefile, src/makesrcdist.sh: *** empty log message *** 2006-01-31 11:39 +0000 dockes <dockes> (73f22e91d844) * src/qtgui/reslistb.ui.h: Clicking on "No results found" will also display the expanded query 2006-01-31 11:39 +0000 dockes <dockes> (c225bd05e9c1) * src/qtgui/recoll.h: close/reopen db by default: let us see results of recollindex -i 2006-01-30 12:51 +0000 dockes <dockes> (cd40d5627d38) * src/qtgui/recoll_fr.ts: *** empty log message *** 2006-01-30 11:15 +0000 dockes <dockes> (962649c706ef) * src/common/rclconfig.h, src/common/rclinit.h, src/common/textsplit.h, src/common/unacpp.h, src/common/uproplist.h, src/index/csguess.h, src/index/indexer.h, src/index/mimetype.h, src/internfile/htmlparse.cpp, src/internfile/htmlparse.h, src/internfile/indextext.h, src/internfile/internfile.h, src/internfile/mh_exec.h, src/internfile/mh_html.h, src/internfile/mh_mail.h, src/internfile/mh_text.h, src/internfile/mimehandler.h, src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h, src/qtgui/advsearch.ui.h, src/qtgui/guiutils.h, src/qtgui/idxthread.h, src/qtgui/plaintorich.h, src/qtgui/preview/preview.ui.h, src/qtgui/rclmain.h, src/qtgui/recoll.h, src/qtgui/recollmain.ui.h, src/qtgui/reslistb.ui.h, src/qtgui/sort.ui.h, src/qtgui/ssearchb.ui.h, src/qtgui/uiprefs.ui.h, src/query/docseq.h, src/query/history.h, src/query/sortseq.h, src/rcldb/pathhash.h, src/rcldb/rcldb.h, src/utils/base64.h, src/utils/cancelcheck.h, src/utils/conftree.h, src/utils/copyfile.h, src/utils/debuglog.h, src/utils/execmd.h, src/utils/fstreewalk.h, src/utils/idfile.h, src/utils/mimeparse.h, src/utils/pathut.h, src/utils/readfile.h, src/utils/smallut.h, src/utils/transcode.h, src/utils/utf8iter.h, src/utils/wipedir.h: *** empty log message *** 2006-01-30 10:01 +0000 dockes <dockes> (f683194d38a4) * src/qtgui/preview/preview.ui.h: dont highlight terms in very big docs: too slow 2006-01-30 09:32 +0000 dockes <dockes> (dc8cbf051f54) * src/mk/localdefs.in: -O2 2006-01-30 09:28 +0000 dockes <dockes> (af56f00261eb) * src/qtgui/guiutils.cpp, src/qtgui/main.cpp, src/qtgui/rclmain.cpp, src/qtgui/uiprefs.ui, src/qtgui/uiprefs.ui.h: help browser selection in prefs 2006-01-30 09:28 +0000 dockes <dockes> (df275d18bee6) * src/utils/execmd.cpp: *** empty log message *** 2006-01-30 09:28 +0000 dockes <dockes> (6d7b08c3bba0) * src/common/textsplit.cpp: moved span cleanup where it belonged 2006-01-28 15:36 +0000 dockes <dockes> (b65e6344a9e4) * src/common/textsplit.cpp, src/common/textsplit.h: *** empty log message *** 2006-01-28 10:23 +0000 dockes <dockes> (507b05e72779) * src/common/textsplit.cpp, src/common/textsplit.h, src/configure, src/configure.ac, src/query/xadump.cpp, src/utils/utf8iter.h: more textsplit tweaking 2006-01-27 13:43 +0000 dockes <dockes> (8ed38cba7965) * src/utils/cancelcheck.h: new file. * src/utils/cancelcheck.h: *** empty log message *** 2006-01-27 13:43 +0000 dockes <dockes> (fa13d8fe2fc9) * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h: new file. * src/qtgui/guiutils.cpp, src/qtgui/guiutils.h: extracted code from main and others 2006-01-27 13:42 +0000 dockes <dockes> (96572eee9528) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/rclmain.cpp: implement cancellation in preview loading 2006-01-27 13:38 +0000 dockes <dockes> (3ad2458e654a) * src/internfile/myhtmlparse.cpp: strip whitespace and newlines (as the original version), except in pre tags 2006-01-27 13:37 +0000 dockes <dockes> (80dbbac5b981) * src/filters/rcldoc, src/filters/rclpdf, src/filters/rclps, src/filters/rclsoff: fix to output <br> when needed + other misc pbs 2006-01-27 13:34 +0000 dockes <dockes> (538235c10cd7) * src/rcldb/rcldb.cpp: define some constants and increase abstract context width 2006-01-27 11:25 +0000 dockes <dockes> (1d381cea9ec3) * src/internfile/htmlparse.cpp: missing amp entity translation 2006-01-26 17:59 +0000 dockes <dockes> (15b82e0f9689) * src/internfile/mh_exec.cpp: check for cancellation 2006-01-26 17:59 +0000 dockes <dockes> (81f5d1264b7d) * src/utils/execmd.cpp, src/utils/execmd.h: also test cancel on select timeout 2006-01-26 17:44 +0000 dockes <dockes> (77efdf7b7e93) * src/utils/execmd.cpp, src/utils/execmd.h: make execCmd exception-safe 2006-01-26 14:02 +0000 dockes <dockes> (ffd1ec38fb9f) * src/qtgui/main.cpp, src/qtgui/rclmain.cpp, src/qtgui/recoll.h, src/qtgui/uiprefs.ui, src/qtgui/uiprefs.ui.h: abstract params 2006-01-26 14:01 +0000 dockes <dockes> (c34965eaaa05) * src/qtgui/reslistb.ui, src/qtgui/reslistb.ui.h: abstracts + doc sizes 2006-01-26 12:30 +0000 dockes <dockes> (c3718d2ceeae) * src/query/docseq.cpp: let the db do whats needed to get a result count 2006-01-26 12:29 +0000 dockes <dockes> (bc0a233de310) * src/utils/smallut.cpp, src/utils/smallut.h: chrono 2006-01-26 12:28 +0000 dockes <dockes> (69be9a0edd98) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: abstract building from position data 2006-01-26 07:03 +0000 dockes <dockes> (2c5403cbdbc1) * src/qtgui/recoll.pro: deleted file. * src/qtgui/recoll.pro: replaced by recoll.pro.in 2006-01-26 07:03 +0000 dockes <dockes> (7a03d26ad54d) * src/qtgui/recoll.pro, src/qtgui/recoll.pro.in, src/utils/smallut.h: *** empty log message *** 2006-01-26 07:02 +0000 dockes <dockes> (de94ebf3cb51) * src/index/indexer.cpp: pass size info to db.add 2006-01-25 08:39 +0000 dockes <dockes> (fc5ab7249caa) * src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h: reenable stripping newlines 2006-01-25 08:09 +0000 dockes <dockes> (1ce613930379) * src/query/Makefile, src/query/xadump.cpp: xadump improvements 2006-01-24 12:22 +0000 dockes <dockes> (6a16d14c076e) * src/qtgui/rclmain.cpp: fix signal type 2006-01-24 12:22 +0000 dockes <dockes> (322a0f010b59) * src/utils/execmd.cpp, src/utils/execmd.h: add feedback and possible cancellation 2006-01-23 17:21 +0000 dockes <dockes> (d16bcca9bc1e) * src/qtgui/images/d_nextpage.png, src/qtgui/images/d_prevpage.png: new file. * src/qtgui/images/d_nextpage.png, src/qtgui/images/d_prevpage.png, src/qtgui/rclmain.cpp, src/qtgui/recoll.pro, src/qtgui/recollmain.ui: slightly improved the icon situation 2006-01-23 16:52 +0000 dockes <dockes> (a51e0cfa77db) * src/qtgui/images/asearch.png, src/qtgui/images/history.png, src/qtgui/images/nextpage.png, src/qtgui/images/prevpage.png, src/qtgui/images/sortparms.png: *** empty log message *** 2006-01-23 15:43 +0000 dockes <dockes> (907a44f71ddc) * src/qtgui/images/editcopy, src/qtgui/images/editcut, src/qtgui/images/editpaste, src/qtgui/images/filenew, src/qtgui/images/fileopen, src/qtgui/images/filesave, src/qtgui/images/print, src/qtgui/images/redo, src/qtgui/images/searchfind, src/qtgui/images/undo: deleted file. * src/qtgui/images/editcopy, src/qtgui/images/editcut, src/qtgui/images/editpaste, src/qtgui/images/filenew, src/qtgui/images/fileopen, src/qtgui/images/filesave, src/qtgui/images/print, src/qtgui/images/redo, src/qtgui/images/searchfind, src/qtgui/images/undo: *** empty log message *** 2006-01-23 13:32 +0000 dockes <dockes> (b27df12a0147) * src/common/rclconfig.cpp, src/common/rclinit.cpp, src/common/textsplit.cpp, src/common/unacpp.cpp, src/index/csguess.cpp, src/index/indexer.cpp, src/index/mimetype.cpp, src/index/recollindex.cpp, src/internfile/internfile.cpp, src/internfile/mh_exec.cpp, src/internfile/mh_mail.cpp, src/internfile/mh_text.cpp, src/internfile/mimehandler.cpp, src/query/docseq.cpp, src/query/history.cpp, src/query/qtry.cpp, src/query/qxtry.cpp, src/query/sortseq.cpp, src/query/xadump.cpp, src/rcldb/pathhash.cpp, src/rcldb/rcldb.cpp, src/utils/base64.cpp, src/utils/conftree.cpp, src/utils/copyfile.cpp, src/utils/debuglog.cpp, src/utils/execmd.cpp, src/utils/fstreewalk.cpp, src/utils/idfile.cpp, src/utils/mimeparse.cpp, src/utils/pathut.cpp, src/utils/readfile.cpp, src/utils/smallut.cpp, src/utils/transcode.cpp, src/utils/utf8iter.cpp, src/utils/wipedir.cpp: reference to GPL in all .cpp files 2006-01-23 13:32 +0000 dockes <dockes> (c2c52e3c568f) * src/qtgui/idxthread.cpp, src/qtgui/main.cpp, src/qtgui/plaintorich.cpp, src/qtgui/rclmain.cpp, src/qtgui/recoll.h, src/qtgui/recoll.pro, src/qtgui/reslistb.ui.h, src/qtgui/uiprefs.ui.h: more refactoring 2006-01-23 07:15 +0000 dockes <dockes> (639d2208e231) * src/qtgui/rclmain.cpp: *** empty log message *** 2006-01-23 07:07 +0000 dockes <dockes> (29cad268f7ba) * src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/reslistb.ui, src/qtgui/reslistb.ui.h, src/qtgui/ssearchb.ui.h: more modularization 2006-01-22 18:46 +0000 dockes <dockes> (c329a0d633e1) * src/qtgui/recoll.pro.in: new file. * src/qtgui/main.cpp, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/recoll.pro, src/qtgui/recoll.pro.in, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/qtgui/reslistb.ui, src/qtgui/reslistb.ui.h, src/qtgui/ssearchb.ui: extract functionality from main window 2006-01-22 15:16 +0000 dockes <dockes> (f8f81a690e3d) * src/qtgui/reslistb.ui, src/qtgui/reslistb.ui.h: *** empty log message *** 2006-01-22 13:56 +0000 dockes <dockes> (b62fca0983d3) * src/qtgui/reslistb.ui, src/qtgui/reslistb.ui.h, src/qtgui/ssearchb.ui.h: new file. * src/qtgui/reslistb.ui, src/qtgui/reslistb.ui.h, src/qtgui/ssearchb.ui.h: *** empty log message *** 2006-01-22 07:41 +0000 dockes <dockes> (50553b4f8d29 [T1_2]) * src/qtgui/ssearchb.ui: *** empty log message *** 2006-01-22 07:25 +0000 dockes <dockes> (f5ecee171cca) * src/qtgui/ssearchb.ui: new file. * src/qtgui/ssearchb.ui: *** empty log message *** 2006-01-21 15:36 +0000 dockes <dockes> (283be80e303b) * src/configure: *** empty log message *** 2006-01-21 15:36 +0000 dockes <dockes> (57061cf4c252) * src/Makefile.in, src/configure, src/configure.ac, src/makesrcdist.sh, src/qtgui/recoll.pro: enable building from inside qtgui/ 2006-01-21 15:25 +0000 dockes <dockes> (ce790ab8e905) * packaging/rpm/recollmdk.spec: new file. * packaging/rpm/recollmdk.spec: *** empty log message *** 2006-01-21 10:47 +0000 dockes <dockes> (47b92b35b369) * src/INSTALL, src/README: *** empty log message *** 2006-01-20 14:58 +0000 dockes <dockes> (9dfcca9b0073) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/qtgui/main.cpp, src/qtgui/preview/preview.ui.h, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/recoll.h: qt main program cleanup 2006-01-20 12:46 +0000 dockes <dockes> (04782d3c08bb) * src/qtgui/rclmain.cpp, src/qtgui/rclmain.h: new file. * src/qtgui/main.cpp, src/qtgui/rclmain.cpp, src/qtgui/rclmain.h, src/qtgui/recoll.pro, src/qtgui/recoll_fr.ts, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: separated code from design by subclassing recollmain 2006-01-20 10:01 +0000 dockes <dockes> (f1c90fc5dd19) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/kde/kioslave/recoll/00README.txt, src/mk/commondefs, src/mk/localdefs.in, src/qtgui/idxthread.cpp, src/qtgui/idxthread.h, src/qtgui/main.cpp, src/utils/conftree.h, src/utils/debuglog.h: cleanup 2006-01-19 17:11 +0000 dockes <dockes> (b6465d0ee08c) * src/Makefile.in: new file. * src/Makefile: deleted file. * src/Makefile, src/Makefile.in, src/common/rclconfig.cpp, src/common/rclconfig.h, src/configure, src/configure.ac, src/internfile/internfile.cpp, src/internfile/mh_exec.cpp, src/mk/localdefs.in, src/qtgui/main.cpp, src/qtgui/recoll.pro, src/recollinstall.in: slight config cleanup 2006-01-19 15:08 +0000 dockes <dockes> (211c1066ac8f) * src/kde/kioslave/recoll/00README.txt: new file. * src/kde/kioslave/recoll/00README.txt: end of test, doesnt look very useful 2006-01-19 14:57 +0000 dockes <dockes> (302ee688e96a) * src/kde/kioslave/recoll/kio_recoll.la: new file. * src/kde/kioslave/recoll/Makefile, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/kio_recoll.la: end of initial experimentation 2006-01-19 12:03 +0000 dockes <dockes> (ffb549062074) * src/utils/Makefile: *** empty log message *** 2006-01-19 12:01 +0000 dockes <dockes> (0e6b7d796f28) * packaging/FreeBSD/recoll/Makefile, src/Makefile, src/VERSION, src/bincimapmime/Makefile, src/common/Makefile, src/doc/user/usermanual.sgml, src/index/Makefile, src/kde/kioslave/recoll/Makefile, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/lib/Makefile, src/makestaticdist.sh, src/mk/Darwin, src/mk/FreeBSD, src/mk/Linux, src/mk/SunOS, src/mk/commondefs, src/mk/localdefs.in, src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/recollmain.ui.h, src/query/Makefile, src/utils/Makefile, src/utils/smallut.cpp, src/utils/smallut.h: misc small mods to help with building kio_recoll 2006-01-18 13:41 +0000 dockes <dockes> (ebf94c8fc21c) * src/kde/kioslave/recoll/Makefile, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/recoll.protocol: new file. * src/kde/kioslave/recoll/Makefile, src/kde/kioslave/recoll/kio_recoll.cpp, src/kde/kioslave/recoll/kio_recoll.h, src/kde/kioslave/recoll/recoll.protocol: *** empty log message *** 2006-01-17 10:08 +0000 dockes <dockes> (9784891fd0a7) * src/utils/mimeparse.h: comments and clarification 2006-01-17 09:31 +0000 dockes <dockes> (08549e5e4a9e) * src/index/indexer.cpp, src/utils/fstreewalk.cpp, src/utils/fstreewalk.h: cleanup and comments 2006-01-14 13:09 +0000 dockes <dockes> (d7ac146b7dd5) * src/configure, src/configure.ac: do a better search for qt configuration 2006-01-14 11:48 +0000 dockes <dockes> (d073ecc93317) * src/Makefile, src/configure, src/configure.ac: do a better search for qt configuration 2006-01-12 09:29 +0000 dockes <dockes> (2dfd16f6a9a4 [RECOLL-1_1_0]) * src/qtgui/recoll_fr.ts: *** empty log message *** 2006-01-12 09:16 +0000 dockes <dockes> (deb6607d43bf) * src/README: *** empty log message *** 2006-01-12 09:13 +0000 dockes <dockes> (7635781b18c5) * src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp: handle removed docs in history 2006-01-11 17:41 +0000 dockes <dockes> (bd54a740def9) * src/qtgui/recollmain.ui.h: *** empty log message *** 2006-01-11 15:09 +0000 dockes <dockes> (108917b10bf3) * src/qtgui/uiprefs.ui.h: new file. * src/qtgui/uiprefs.ui.h: *** empty log message *** 2006-01-11 15:08 +0000 dockes <dockes> (a03b6696412a) * src/doc/user/usermanual.sgml, src/index/Makefile, src/qtgui/recoll_fr.ts, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/query/docseq.h, src/query/sortseq.cpp, src/query/sortseq.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: translation of result list title. Show query details when clicking on header 2006-01-10 17:46 +0000 dockes <dockes> (d4cc3428e381) * src/recollinstall.in: *** empty log message *** 2006-01-10 14:53 +0000 dockes <dockes> (c873b3133cdd) * packaging/rpm/recoll.spec: *** empty log message *** 2006-01-10 13:52 +0000 dockes <dockes> (ab4934e066f9) * src/recollinstall.in: *** empty log message *** 2006-01-10 13:41 +0000 dockes <dockes> (23d6e8ae7155) * src/recollinstall.in: *** empty log message *** 2006-01-10 13:32 +0000 dockes <dockes> (526cfe52f2e1) * src/recollinstall.in: *** empty log message *** 2006-01-10 13:27 +0000 dockes <dockes> (a2f47b62ca03) * src/recollinstall.in: *** empty log message *** 2006-01-10 13:16 +0000 dockes <dockes> (72d6ccffea15) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- plist, src/recollinstall.in: install man pages 2006-01-10 12:58 +0000 dockes <dockes> (3a7d0fd4ceb7) * src/Makefile, src/common/rclconfig.cpp: warning 2006-01-10 12:55 +0000 dockes <dockes> (aaeb49f89a98) * src/rcldb/rcldb.cpp: include unistd 2006-01-10 12:06 +0000 dockes <dockes> (9b804748017f) * src/INSTALL, src/README: *** empty log message *** 2006-01-10 11:07 +0000 dockes <dockes> (01e4fe9772b0) * src/common/rclconfig.h, src/doc/user/usermanual.sgml, src/index/recollindex.cpp, src/sampleconf/recoll.conf.in: doc + got rid of unused defaultlanguage config param 2006-01-10 09:10 +0000 dockes <dockes> (34638d9bd009) * src/doc/man/recoll.conf.5: new file. * src/doc/man/recoll.conf.5: *** empty log message *** 2006-01-10 08:14 +0000 dockes <dockes> (a9b485ada811) * src/doc/man/recoll.1, src/doc/man/recollindex.1: new file. * src/doc/man/recoll.1, src/doc/man/recollindex.1: *** empty log message *** 2006-01-09 16:53 +0000 dockes <dockes> (29f37b7888d3) * src/excludefile, src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/Makefile, src/utils/pathut.cpp, src/utils/pathut.h: allow independent creation / deletion of stem dbs 2006-01-06 13:55 +0000 dockes <dockes> (8831260252d9) * src/rcldb/rcldb.cpp: do a better test for a capitalized query term (no stem expand) 2006-01-06 13:20 +0000 dockes <dockes> (82e02042773f) * src/qtgui/uiprefs.ui: new file. * src/qtgui/uiprefs.ui: *** empty log message *** 2006-01-06 13:19 +0000 dockes <dockes> (29cdbe2390e4) * src/utils/CaseFolding.txt, src/utils/caseconvert.cpp, src/utils/caseconvert.h, src/utils/gencasefold.sh: deleted file. * src/lib/Makefile, src/rcldb/rcldb.cpp, src/unac/unac.c, src/unac/unac.h, src/utils/CaseFolding.txt, src/utils/Makefile, src/utils/caseconvert.cpp, src/utils/caseconvert.h, src/utils/gencasefold.sh: integrated case-folding into unac for better performance 2006-01-06 13:18 +0000 dockes <dockes> (7840fc0ec48b) * src/common/Makefile, src/common/unacpp.cpp, src/common/unacpp.h, src/rcldb/rcldb.cpp: integrated case-folding into unac for better performance 2006-01-06 13:10 +0000 dockes <dockes> (15e715082e40) * unac/CaseFolding-3.2.0.txt: new file. * unac/CaseFolding-3.2.0.txt, unac/builder.in, unac/unac.c, unac/unac.h: implemented additional case-folding 2006-01-06 13:08 +0000 dockes <dockes> (f27aa43e32ef [UNAC_1_7_0]) * unac/.version, unac/AUTHORS, unac/COPYING, unac/ChangeLog, unac/INSTALL, unac/Makefile.am, unac/Makefile.in, unac/NEWS, unac/README, unac/THANKS, unac/UnicodeData-3.2.0.txt, unac/acinclude.m4, unac/aclocal.m4, unac/builder.in, unac/config.guess, unac/config.h.in, unac/config.sub, unac/configure, unac/configure.ac, unac/depcomp, unac/getopt.c, unac/getopt.h, unac/install-sh, unac/ltconfig, unac/ltmain.sh, unac/missing, unac/mkinstalldirs, unac/stamp-h.in, unac/t_unac.in, unac/unac.3, unac/unac.c, unac/unac.h, unac/unac.pc.in, unac/unac.spec.in, unac/unaccent.1, unac/unaccent.c, unac/unactest.c, unac/unactest1.c: new file. * unac/.version, unac/AUTHORS, unac/COPYING, unac/ChangeLog, unac/INSTALL, unac/Makefile.am, unac/Makefile.in, unac/NEWS, unac/README, unac/THANKS, unac/UnicodeData-3.2.0.txt, unac/acinclude.m4, unac/aclocal.m4, unac/builder.in, unac/config.guess, unac/config.h.in, unac/config.sub, unac/configure, unac/configure.ac, unac/depcomp, unac/getopt.c, unac/getopt.h, unac/install-sh, unac/ltconfig, unac/ltmain.sh, unac/missing, unac/mkinstalldirs, unac/stamp-h.in, unac/t_unac.in, unac/unac.3, unac/unac.c, unac/unac.h, unac/unac.pc.in, unac/unac.spec.in, unac/unaccent.1, unac/unaccent.c, unac/unactest.c, unac/unactest1.c: initial import 2006-01-06 07:59 +0000 dockes <dockes> (52c86ee701fd) * src/index/Makefile, src/qtgui/recoll.pro: ensure relink for changed lib 2006-01-05 16:37 +0000 dockes <dockes> (a2ef019b6308) * src/common/unacpp.cpp, src/common/unacpp.h, src/lib/Makefile, src/rcldb/rcldb.cpp, src/utils/Makefile: Use proper unicode lowercasing 2006-01-05 16:16 +0000 dockes <dockes> (158267ddbcb6) * src/utils/CaseFolding.txt, src/utils/caseconvert.cpp, src/utils/caseconvert.h, src/utils/gencasefold.sh: new file. * src/utils/CaseFolding.txt, src/utils/caseconvert.cpp, src/utils/caseconvert.h, src/utils/gencasefold.sh: *** empty log message *** 2006-01-05 10:27 +0000 dockes <dockes> (f1af15efef34) * packaging/rpm/recoll.spec: new file. * packaging/rpm/recoll.spec: *** empty log message *** 2006-01-05 10:24 +0000 dockes <dockes> (55284d2ed66e) * src/Makefile, src/recollinstall.in: install tweaks for rpm compatibility 2006-01-04 11:33 +0000 dockes <dockes> (236c587eb180) * src/VERSION, src/common/rclconfig.h, src/makesrcdist.sh, src/qtgui/main.cpp, src/qtgui/recoll.h, src/qtgui/recoll_fr.ts, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/recollinstall.in, src/utils/smallut.cpp, src/utils/smallut.h: add menu entry to start browser on html doc 2006-01-04 11:16 +0000 dockes <dockes> (2075c2a6d71e) * src/INSTALL, src/README: *** empty log message *** 2006-01-04 11:09 +0000 dockes <dockes> (9a4cceb219aa) * src/doc/user/Makefile, src/doc/user/docbook.css, src/doc/user/usermanual.sgml: new file. * src/doc/user/Makefile, src/doc/user/docbook.css, src/doc/user/usermanual.sgml: *** empty log message *** 2006-01-03 11:35 +0000 dockes <dockes> (188ffc87b7d3) * src/INSTALL, src/README: *** empty log message *** 2005-12-16 10:08 +0000 dockes <dockes> (789da9d2380c) * src/query/Makefile, src/query/xadump.cpp, src/unac/unac.c, src/utils/mimeparse.cpp: 64 bits fixes 2005-12-16 10:06 +0000 dockes <dockes> (cf18fa6d2a7b) * src/Makefile, src/mk/localdefs.in, src/qtgui/main.cpp, src/qtgui/recoll.pro, src/recollinstall.in: get prefix to really work 2005-12-16 08:00 +0000 dockes <dockes> (cca6b156e460) * src/excludefile: dont copy localdefs 2005-12-16 07:58 +0000 dockes <dockes> (7b20df9408ce) * src/mk/localdefs: deleted file. * src/mk/localdefs: *** empty log message *** 2005-12-15 14:39 +0000 dockes <dockes> (959564d835fd) * src/qtgui/main.cpp, src/qtgui/recoll.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/sampleconf/recoll.conf.in: user prefs tweaks. Allow switching stemlang from ui 2005-12-15 13:41 +0000 dockes <dockes> (bf3c45bf931d) * src/qtgui/main.cpp: *** empty log message *** 2005-12-14 16:15 +0000 dockes <dockes> (229d1902798e) * src/qtgui/main.cpp, src/qtgui/recoll.h, src/qtgui/recoll.pro, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: user interface preferences settable from ui 2005-12-14 11:00 +0000 dockes <dockes> (3e5f6f1c000d) * src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp, src/internfile/mh_html.cpp, src/internfile/mh_text.cpp, src/qtgui/preview/preview.ui.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/query/Makefile: allow indexing individual files. Fix pb with preview and charsets (local defcharset ignored) 2005-12-13 17:20 +0000 dockes <dockes> (0895be2b8196) * src/qtgui/main.cpp, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: add allTerms checkbutton, save state in settings 2005-12-13 17:20 +0000 dockes <dockes> (b522d74e613c) * src/qtgui/advsearch.ui: avoid activating random buttons when typing CR... 2005-12-13 12:43 +0000 dockes <dockes> (0448daf8c23e) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- descr, packaging/FreeBSD/recoll/pkg-plist, src/common/rclconfig.cpp, src/doc/prog/Doxyfile, src/doc/prog/Makefile, src/doc/prog/filters.txt, src/qtgui/main.cpp, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/utils/fstreewalk.cpp, src/utils/pathut.cpp, src/utils/pathut.h, src/utils/smallut.cpp, src/utils/wipedir.cpp: pgup/down in result list 2005-12-08 08:44 +0000 dockes <dockes> (ec006d171797) * src/common/Makefile, src/internfile/htmlparse.cpp, src/internfile/htmlparse.h, src/internfile/internfile.cpp, src/internfile/mh_html.cpp, src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h: process text from html files without a </body> tag 2005-12-07 15:41 +0000 dockes <dockes> (a44bf0c6a081 [RECOLL-1_0_14]) * src/VERSION, src/internfile/mh_mail.cpp, src/qtgui/preview/preview.ui.h, src/qtgui/recollmain.ui.h, src/query/docseq.cpp, src/utils/utf8iter.h: freebsd 4 port 2005-12-06 15:59 +0000 dockes <dockes> (812bc8f9232b) * packaging/FreeBSD/recoll/distinfo: 0.13 really now 2005-12-06 15:41 +0000 dockes <dockes> (3a7b74624ff4) * src/recollinstall.in: strip execs 2005-12-06 15:20 +0000 dockes <dockes> (fa8c19799a41) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- plist: recoll-0.13 2005-12-06 15:10 +0000 dockes <dockes> (b6df28b0d0e3) * src/README: *** empty log message *** 2005-12-06 15:10 +0000 dockes <dockes> (e66dba4d628c [RECOLL-1_0_13]) * src/Makefile, src/VERSION, src/recollinstall.in: no recollinstall install 2005-12-06 12:55 +0000 dockes <dockes> (cbfcc5627111) * packaging/FreeBSD/recoll/pkg-descr: *** empty log message *** 2005-12-06 10:30 +0000 dockes <dockes> (d132e05e40ac) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- descr, packaging/FreeBSD/recoll/pkg-plist: *** empty log message *** 2005-12-06 09:40 +0000 dockes <dockes> (f93d6a9b2336) * src/internfile/mh_html.cpp, src/internfile/myhtmlparse.cpp: previous html fix didnt work 2005-12-06 08:35 +0000 dockes <dockes> (a3eec94f6861) * src/VERSION, src/internfile/internfile.cpp, src/internfile/mh_html.cpp, src/internfile/myhtmlparse.cpp: fix nasty html parse bug introduced in 1.0.9 2005-12-06 07:16 +0000 dockes <dockes> (c1ccf42bf359 [RECOLL-1_0_12, RECOLL-1_0_11]) * src/qtgui/recollmain.ui: move search/clear buttons to the left side 2005-12-05 17:47 +0000 dockes <dockes> (37952b251aee) * src/VERSION: 1.0.11? 2005-12-05 16:45 +0000 dockes <dockes> (eecd7a311e8f) * src/qtgui/recollmain.ui.h: no %F on solaris8 2005-12-05 16:13 +0000 dockes <dockes> (cd9899dcdec1) * src/utils/copyfile.cpp: *** empty log message *** 2005-12-05 16:13 +0000 dockes <dockes> (7e7e675138b2) * src/query/docseq.cpp, src/query/docseq.h: avoid unneeded getDoc(0) + normalize private var names 2005-12-05 15:00 +0000 dockes <dockes> (6aa562bb0180) * src/INSTALL, src/Makefile, src/README, src/qtgui/main.cpp: *** empty log message *** 2005-12-05 14:09 +0000 dockes <dockes> (d3954ac2c5ec) * src/utils/copyfile.cpp, src/utils/copyfile.h: new file. * src/common/rclconfig.cpp, src/lib/Makefile, src/mk/localdefs, src/mk/localdefs.in, src/utils/copyfile.cpp, src/utils/copyfile.h: create personal config if it does not exist 2005-12-05 12:02 +0000 dockes <dockes> (6d38fb24e3b1) * src/qtgui/images/asearch.png, src/qtgui/images/history.png, src/qtgui/images/nextpage.png, src/qtgui/images/prevpage.png, src/qtgui/images/sortparms.png: new file. * src/qtgui/images/asearch.png, src/qtgui/images/history.png, src/qtgui/images/nextpage.png, src/qtgui/images/prevpage.png, src/qtgui/images/sortparms.png, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/qtgui/sort.ui.h, src/query/docseq.cpp, src/query/sortseq.cpp: use toolbar buttons for prev/next + misc cleanups 2005-12-05 10:39 +0000 dockes <dockes> (55a212b17808) * src/rcldb/rcldb.cpp: also index file path as terms 2005-12-04 17:10 +0000 dockes <dockes> (a4005adeece9) * src/qtgui/recoll.pro: more 2005-12-04 17:10 +0000 dockes <dockes> (15ce414ea700) * src/common/textsplit.cpp: split stdin 2005-12-04 14:58 +0000 dockes <dockes> (369372321681) * src/qtgui/recollmain.ui: *** empty log message *** 2005-12-02 16:18 +0000 dockes <dockes> (b8ea8500fe26) * src/qtgui/preview/preview.ui.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/qtgui/sort.ui, src/qtgui/sort.ui.h, src/query/sortseq.cpp, src/query/sortseq.h, src/rcldb/rcldb.h: 1st version of sorting interface. Needs polishing 2005-12-02 16:17 +0000 dockes <dockes> (fba2b55c4ba7) * src/filters/rclpdf: new version of pdftotext broke us 2005-12-02 16:17 +0000 dockes <dockes> (a31234c89a73) * src/common/rclconfig.cpp, src/recollinstall.in, src/sampleconf/recoll.conf.in: install filters to /usr/local 2005-12-02 14:18 +0000 dockes <dockes> (7b585689ce4a) * src/query/sortseq.cpp: *** empty log message *** 2005-12-01 16:23 +0000 dockes <dockes> (c7393c3bc8b5) * src/qtgui/sort.ui, src/qtgui/sort.ui.h, src/query/sortseq.cpp, src/query/sortseq.h: new file. * src/lib/Makefile, src/qtgui/recoll.pro, src/qtgui/recollmain.ui.h, src/qtgui/sort.ui, src/qtgui/sort.ui.h, src/query/sortseq.cpp, src/query/sortseq.h: sorting 1st steps 2005-11-30 18:37 +0000 dockes <dockes> (ddba9ec4f65f) * src/configure: make recollinstall executable 2005-11-30 18:28 +0000 dockes <dockes> (35f236d5ad1f) * src/configure.ac: make recollinstall executable 2005-11-30 18:26 +0000 dockes <dockes> (580ae261b629) * src/sampleconf/recoll.conf.in: keep log level at 4 for index feedback 2005-11-30 18:20 +0000 dockes <dockes> (2fb51c4552fb) * src/sampleconf/recoll.conf.in: decrease log level 2005-11-30 18:10 +0000 dockes <dockes> (0ad46d9bcaa5) * src/query/history.cpp: *** empty log message *** 2005-11-30 18:05 +0000 dockes <dockes> (653e0a145731) * src/qtgui/form1.ui.h: deleted file. * src/excludefile, src/qtgui/form1.ui.h: *** empty log message *** 2005-11-30 18:04 +0000 dockes <dockes> (dfff4ecb1918) * src/qtgui/form1.ui: deleted file. * src/excludefile, src/qtgui/form1.ui: *** empty log message *** 2005-11-30 18:01 +0000 dockes <dockes> (a63a8d7c49f3) * src/excludefile: *** empty log message *** 2005-11-30 17:58 +0000 dockes <dockes> (7676c325de57) * src/excludefile: *** empty log message *** 2005-11-30 17:58 +0000 dockes <dockes> (6ddc4c210c87) * src/utils/transcode.cpp: try harder to convert bad encodings 2005-11-30 10:36 +0000 dockes <dockes> (7e0aab848f91 [RECOLL-1_0_10]) * src/README, src/qtgui/recollmain.ui: *** empty log message *** 2005-11-30 10:35 +0000 dockes <dockes> (1f97b79ea735) * src/VERSION: v1.0.10 2005-11-30 10:25 +0000 dockes <dockes> (3c2bcb1ec527) * src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: disable/enable buttons dep. on state 2005-11-30 09:46 +0000 dockes <dockes> (a75b091acbae) * src/qtgui/advsearch.ui, src/qtgui/advsearch.ui.h, src/sampleconf/mimeconf: *** empty log message *** 2005-11-30 09:46 +0000 dockes <dockes> (30a527e5014f) * src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp, src/rcldb/rcldb.cpp: add option to rezero db before index 2005-11-28 15:31 +0000 dockes <dockes> (d9e31422258b) * src/qtgui/main.cpp, src/qtgui/recoll.h, src/qtgui/recollmain.ui.h, src/query/docseq.cpp, src/query/docseq.h, src/query/history.cpp, src/query/history.h: store and display dates in history. Needs more work 2005-11-25 14:36 +0000 dockes <dockes> (18bc54d4e426) * src/qtgui/recollmain.ui.h, src/query/history.cpp, src/utils/conftree.cpp, src/utils/smallut.cpp, src/utils/smallut.h: show history newest first + prune duplicate entries 2005-11-25 10:26 +0000 dockes <dockes> (3ad346d3f29e) * src/qtgui/main.cpp, src/qtgui/recoll_fr.ts, src/recollinstall.in: install translations to share/recoll/translations 2005-11-25 10:02 +0000 dockes <dockes> (6ed5669a337b) * src/query/docseq.cpp, src/query/docseq.h: new file. * src/lib/Makefile, src/qtgui/main.cpp, src/qtgui/recoll.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/query/docseq.cpp, src/query/docseq.h: 1st version of doc history 2005-11-25 09:14 +0000 dockes <dockes> (69bab5c09012) * src/index/indexer.cpp, src/index/mimetype.cpp, src/sampleconf/mimeconf: *** empty log message *** 2005-11-25 09:13 +0000 dockes <dockes> (55e99bcc0a46) * src/common/rclconfig.cpp, src/common/rclconfig.h: get all mime list from mimeconf, not mimemap 2005-11-25 09:12 +0000 dockes <dockes> (87febfb9c3be) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: added method to retrieve doc from path/ipath 2005-11-25 08:53 +0000 dockes <dockes> (756168516697) * src/Makefile, src/internfile/mimehandler.cpp, src/rcldb/rcldb.h: *** empty log message *** 2005-11-25 08:50 +0000 dockes <dockes> (6fda25d19678) * src/utils/conftree.cpp, src/utils/conftree.h, src/utils/smallut.cpp, src/utils/smallut.h: add methods useful for history. move stuff to smallut 2005-11-25 08:49 +0000 dockes <dockes> (bd6b75c162a5) * src/utils/base64.cpp: Strip extra null byte that we were appending 2005-11-24 18:21 +0000 dockes <dockes> (ba604719481c) * src/query/history.cpp, src/query/history.h: new file. * src/query/Makefile, src/query/history.cpp, src/query/history.h, src/query/qtry.cpp, src/query/xadump.cpp: *** empty log message *** 2005-11-24 07:16 +0000 dockes <dockes> (1fc7382994a5) * src/recollinstall.in, src/sampleconf/recoll.conf.in: new file. * src/bincimapmime/address.cc, src/bincimapmime/convert.cc, src/bincimapmime/iodevice.cc, src/bincimapmime/iofactory.cc, src/bincimapmime/mime-getpart.cc, src/bincimapmime/mime- parsefull.cc, src/bincimapmime/mime-parseonlyheader.cc, src/bincimapmime/mime-printbody.cc, src/bincimapmime/mime- printdoc.cc, src/bincimapmime/mime-printheader.cc, src/bincimapmime /mime-utils.h, src/bincimapmime/mime.cc, src/bincimapmime/trbinc.cc, src/common/rclconfig.cpp, src/common/textsplit.cpp, src/common/unacpp.cpp, src/configure, src/configure.ac, src/index/csguess.cpp, src/index/indexer.cpp, src/index/mimetype.cpp, src/internfile/htmlparse.cpp, src/internfile/htmlparse.h, src/internfile/internfile.cpp, src/internfile/mh_exec.cpp, src/internfile/mh_html.cpp, src/internfile/mh_mail.cpp, src/internfile/mh_text.cpp, src/internfile/mimehandler.cpp, src/makestaticdist.sh, src/qtgui/advsearch.ui.h, src/qtgui/main.cpp, src/qtgui/plaintorich.cpp, src/qtgui/preview/preview.ui.h, src/qtgui/preview/pvmain.cpp, src/qtgui/recollmain.ui.h, src/query/qtry.cpp, src/query/qxtry.cpp, src/query/xadump.cpp, src/rcldb/pathhash.cpp, src/rcldb/rcldb.cpp, src/recollinstall.in, src/sampleconf/recoll.conf.in, src/utils/base64.cpp, src/utils/execmd.cpp, src/utils/fstreewalk.cpp, src/utils/idfile.cpp, src/utils/mimeparse.cpp, src/utils/pathut.cpp, src/utils/readfile.cpp, src/utils/smallut.cpp, src/utils/smallut.h, src/utils/transcode.cpp, src/utils/utf8iter.cpp, src/utils/wipedir.cpp: *** empty log message *** 2005-11-23 13:12 +0000 dockes <dockes> (a8ff464ec720) * src/recollinstall, src/sampleconf/recoll.conf: deleted file. * src/recollinstall, src/sampleconf/recoll.conf: *** empty log message *** 2005-11-23 11:11 +0000 dockes <dockes> (4ba2ad248537) * src/utils/execmd.cpp: *** empty log message *** 2005-11-23 11:00 +0000 dockes <dockes> (66cac25635e1) * src/INSTALL, src/README, src/rcldb/rcldb.cpp: *** empty log message *** 2005-11-23 10:57 +0000 dockes <dockes> (45f106d04652 [RECOLL-1_0_9]) * src/Makefile: use prefix instead of PREFIX 2005-11-23 10:19 +0000 dockes <dockes> (e7a6edd38c56) * src/Makefile, src/VERSION, src/filters/rclrtf, src/index/Makefile, src/index/mimetype.cpp, src/utils/debuglog.cpp, src/utils/mimeparse.cpp: *** empty log message *** 2005-11-23 10:18 +0000 dockes <dockes> (4e530d6556d2) * src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/recollmain.ui.h: document already shown test was wrong, wouldnt show more docs from same file 2005-11-23 10:17 +0000 dockes <dockes> (9944ac86338d) * src/utils/execmd.cpp: need to do _exit not exit after exec failure 2005-11-23 10:16 +0000 dockes <dockes> (085c66533884) * src/internfile/mh_html.cpp, src/utils/smallut.cpp, src/utils/smallut.h: improve charset name comparison 2005-11-21 17:18 +0000 dockes <dockes> (9c398b7ee69e [RECOLL-1_0_8]) * src/configure, src/configure.ac, src/index/mimetype.cpp, src/internfile/myhtmlparse.cpp, src/mk/Linux, src/mk/SunOS, src/recollinstall, src/utils/execmd.cpp: glitches in linux/solaris compil. + install 2005-11-21 16:16 +0000 dockes <dockes> (7594b3dd0dc5) * src/README: *** empty log message *** 2005-11-21 16:06 +0000 dockes <dockes> (8a82b3826a4a) * src/VERSION: *** empty log message *** 2005-11-21 16:05 +0000 dockes <dockes> (9cc42706006d) * src/filters/rclrtf: new file. * src/filters/rclrtf, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/recoll.conf: add support for rtf 2005-11-21 16:04 +0000 dockes <dockes> (8169ca3ae210) * src/Makefile, src/makestaticdist.sh, src/recollinstall: install pics and samples to $PREFIX/local/recoll 2005-11-21 14:32 +0000 dockes <dockes> (f0aaac1df843) * src/filters/rclgaim: just needs awk 2005-11-21 14:31 +0000 dockes <dockes> (88649af9a0ac [RECOLL-1_0_7]) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/indexer.cpp, src/index/mimetype.cpp, src/index/mimetype.h, src/internfile/internfile.cpp, src/internfile/mh_html.cpp, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h, src/lib/Makefile, src/qtgui/recollmain.ui.h, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/recoll.conf: mimemap processing recentered in rclconfig. Handle directory-local suffix to mime-type definitions. Implement gaim log handling 2005-11-18 17:03 +0000 dockes <dockes> (ae7d483398d2) * src/filters/rclgaim: new file. * src/filters/rclgaim: *** empty log message *** 2005-11-18 15:19 +0000 dockes <dockes> (9c8cb27e5749) * src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mh_exec.cpp, src/internfile/mimehandler.h, src/utils/execmd.h: misc cleanup + tell filters if working for preview or index 2005-11-18 13:52 +0000 dockes <dockes> (9d83fd6a7d8c) * src/utils/execmd.cpp, src/utils/execmd.h: add putenv interface 2005-11-18 13:23 +0000 dockes <dockes> (c3d0cfc77a9f) * src/internfile/mh_exec.cpp, src/internfile/mh_exec.h, src/internfile/mh_text.cpp, src/internfile/mh_text.h: new file. * src/internfile/mh_exec.cpp, src/internfile/mh_exec.h, src/internfile/mh_html.cpp, src/internfile/mh_html.h, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/internfile/mh_text.cpp, src/internfile/mh_text.h, src/internfile/mimehandler.cpp, src/lib/Makefile: restructuring on mimehandler files 2005-11-17 17:39 +0000 dockes <dockes> (e530dcacaf42) * src/VERSION: *** empty log message *** 2005-11-17 17:36 +0000 dockes <dockes> (64437283f61f) * src/rcldb/rcldb.cpp: use OP_FILTER instead of OP_AND to filter on file types 2005-11-17 12:47 +0000 dockes <dockes> (e9efe66d79c0) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/qtgui/main.cpp, src/qtgui/recollmain.ui, src/sampleconf/recoll.conf, src/utils/conftree.cpp, src/utils/conftree.h: allow tilde expansion for section names in config file 2005-11-16 18:31 +0000 dockes <dockes> (e319e6fa047d) * src/qtgui/recollmain.ui.h: *** empty log message *** 2005-11-16 17:30 +0000 dockes <dockes> (70dbf29f84e0) * src/excludefile: *** empty log message *** 2005-11-16 17:29 +0000 dockes <dockes> (4c957598f6fd [RECOLL-1_0_6]) * src/mk/localdefs, src/rcldb/rcldb.cpp: use and_maybe in adv search 2005-11-16 15:07 +0000 dockes <dockes> (a19870cd6761) * src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/qtgui/main.cpp, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/recoll.h, src/qtgui/recollmain.ui.h, src/sampleconf/mimeconf, src/sampleconf/recoll.conf: Optionnally show mime type icons in result list 2005-11-16 15:05 +0000 dockes <dockes> (6464421540ca) * src/qtgui/mtpics/README, src/qtgui/mtpics/document.png, src/qtgui/mtpics/drawing.png, src/qtgui/mtpics/html.png, src/qtgui/mtpics/message.png, src/qtgui/mtpics/mozilla_doc.png, src/qtgui/mtpics/pdf.png, src/qtgui/mtpics/postscript.png, src/qtgui/mtpics/presentation.png, src/qtgui/mtpics/soffice.png, src/qtgui/mtpics/spreadsheet.png, src/qtgui/mtpics/txt.png, src/qtgui/mtpics/wordprocessing.png: new file. * src/qtgui/mtpics/README, src/qtgui/mtpics/document.png, src/qtgui/mtpics/drawing.png, src/qtgui/mtpics/html.png, src/qtgui/mtpics/message.png, src/qtgui/mtpics/mozilla_doc.png, src/qtgui/mtpics/pdf.png, src/qtgui/mtpics/postscript.png, src/qtgui/mtpics/presentation.png, src/qtgui/mtpics/soffice.png, src/qtgui/mtpics/spreadsheet.png, src/qtgui/mtpics/txt.png, src/qtgui/mtpics/wordprocessing.png: *** empty log message *** 2005-11-16 11:22 +0000 dockes <dockes> (f29236269564) * src/qtgui/plaintorich.cpp, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/recollmain.ui.h: Implemented better feedback during preview loading 2005-11-16 08:17 +0000 dockes <dockes> (44b8c2233623) * src/Makefile, src/VERSION, src/qtgui/main.cpp, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/recoll_fr.ts, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: about dialog, remember previous mainwin geometry 2005-11-14 09:59 +0000 dockes <dockes> (f196f00bd521) * src/internfile/internfile.cpp, src/internfile/internfile.h, src/qtgui/recollmain.ui.h: fix rare case where indexed file could not be previewed because of change in file identification config param 2005-11-14 09:57 +0000 dockes <dockes> (5610887cf602) * src/index/indexer.cpp: comment 2005-11-14 09:56 +0000 dockes <dockes> (b6c7dd9504b9) * src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: stem expansion was never done for adv search 2005-11-12 14:36 +0000 dockes <dockes> (87b02b667eef) * src/README, src/mk/Linux: *** empty log message *** 2005-11-12 14:33 +0000 dockes <dockes> (5743f1558790) * src/mk/localdefs.in: typo 2005-11-12 14:31 +0000 dockes <dockes> (0dd4948b5c2f) * src/Makefile, src/configure, src/configure.ac: more config tweaks 2005-11-12 14:24 +0000 dockes <dockes> (6d47a227c1b2) * src/utils/conftree.cpp, src/utils/conftree.h, src/utils/debuglog.cpp, src/utils/debuglog.h: new file. * src/utils/conftree.cpp, src/utils/conftree.h, src/utils/debuglog.cpp, src/utils/debuglog.h: local versions of utility files 2005-11-12 14:23 +0000 dockes <dockes> (c77e47fdc6fb) * src/Makefile: *** empty log message *** 2005-11-12 14:19 +0000 dockes <dockes> (49499e32e341) * src/configure.ac, src/mk/localdefs, src/mk/localdefs.in: new file. * src/Makefile, src/configure, src/configure.ac, src/mk/Darwin, src/mk/FreeBSD, src/mk/Linux, src/mk/SunOS, src/mk/commondefs, src/mk/localdefs, src/mk/localdefs.in: introduced some autoconf 2005-11-12 11:26 +0000 dockes <dockes> (b13e733c2796) * src/Makefile, src/bincimapmime/Makefile, src/common/Makefile, src/index/Makefile, src/lib/Makefile, src/mk/commondefs, src/qtgui/recoll.pro, src/query/Makefile, src/sampleconf/mimemap, src/utils/Makefile: cleaned-up makes 2005-11-10 08:47 +0000 dockes <dockes> (06490e6e7dc1) * src/index/Makefile, src/index/indexer.cpp, src/index/indexer.h, src/index/mimetype.cpp, src/index/mimetype.h, src/internfile/internfile.cpp, src/sampleconf/recoll.conf: add config parameter to decide if we use the file command as a final step of mimetype identification 2005-11-10 08:46 +0000 dockes <dockes> (d9a64999d22d) * src/sampleconf/mimeconf, src/sampleconf/mimemap: add .Z compressed files 2005-11-09 21:40 +0000 dockes <dockes> (1dd753a59d1c) * src/sampleconf/mimemap: add .odt -> openoffice. Add .php and others to ignored types 2005-11-09 21:39 +0000 dockes <dockes> (a8b54cf24c83) * src/common/rclinit.cpp: test cleanup and sigcleanup not zero for small uts that dont need this 2005-11-08 21:02 +0000 dockes <dockes> (344fc56239c8) * src/internfile/internfile.cpp, src/internfile/mh_html.cpp, src/internfile/mh_html.h, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h: renamed MimeHandler::worker to mkDoc + comments for doxygen 2005-11-08 21:02 +0000 dockes <dockes> (1ac76bfea47d) * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- descr, packaging/FreeBSD/recoll/pkg-plist, src/doc/prog/filters.txt, src/doc/prog/top.txt: new file. * packaging/FreeBSD/recoll/Makefile, packaging/FreeBSD/recoll/distinfo, packaging/FreeBSD/recoll/pkg- descr, packaging/FreeBSD/recoll/pkg-plist, src/doc/prog/Doxyfile, src/doc/prog/Makefile, src/doc/prog/filters.txt, src/doc/prog/top.txt: *** empty log message *** 2005-11-08 21:00 +0000 dockes <dockes> (54bcdfd186f1) * src/doc/prog/Doxyfile, src/doc/prog/Makefile: new file. * src/doc/prog/Doxyfile, src/doc/prog/Makefile: *** empty log message *** 2005-11-07 15:52 +0000 dockes <dockes> (a0bde5fbc55b [RECOLL-1_0_5]) * src/INSTALL, src/Makefile, src/README, src/excludefile, src/makesrcdist.sh, src/makestaticdist.sh: *** empty log message *** 2005-11-07 15:37 +0000 dockes <dockes> (c6a8f5375981) * src/README: *** empty log message *** 2005-11-07 15:36 +0000 dockes <dockes> (5ca00f4db306) * src/INSTALL, src/README: *** empty log message *** 2005-11-07 15:11 +0000 dockes <dockes> (8ae633ae4194) * src/VERSION: *** empty log message *** 2005-11-07 15:06 +0000 dockes <dockes> (6be191f54656) * src/Makefile, src/mk/commondefs, src/recollinstall: fixed installation script 2005-11-07 11:21 +0000 dockes <dockes> (e48ddf065716) * src/VERSION: *** empty log message *** 2005-11-06 15:07 +0000 dockes <dockes> (fef6e5d66e29 [RECOLL-1_05]) * src/qtgui/idxthread.cpp, src/qtgui/main.cpp, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/utils/base64.cpp: slightly better status printing while loading preview 2005-11-06 11:16 +0000 dockes <dockes> (0fa0ac2c3e5b) * src/rcldb/pathhash.cpp, src/rcldb/pathhash.h, src/utils/base64.cpp, src/utils/base64.h, src/utils/md5.cpp, src/utils/md5.h: new file. * src/lib/Makefile, src/rcldb/pathhash.cpp, src/rcldb/pathhash.h, src/rcldb/rcldb.cpp, src/utils/base64.cpp, src/utils/base64.h, src/utils/md5.cpp, src/utils/md5.h, src/utils/mimeparse.cpp: limit path therm length through hashing 2005-11-05 15:30 +0000 dockes <dockes> (eea6ede9ce9a) * src/INSTALL, src/README, src/VERSION: *** empty log message *** 2005-11-05 15:29 +0000 dockes <dockes> (c99e6c9d50df) * src/rcldb/rcldb.cpp: debug message 2005-11-05 15:17 +0000 dockes <dockes> (a3463f8f8c63) * src/mk/commondefs: unused def 2005-11-05 14:40 +0000 dockes <dockes> (47c04f4507d0 [RECOLL-1_04]) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/common/rclinit.cpp, src/common/rclinit.h, src/index/indexer.cpp, src/index/recollindex.cpp, src/internfile/mh_mail.cpp, src/mk/SunOS, src/qtgui/main.cpp, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: separate file and document dates (mainly for email folders). Better check configuration at startup 2005-11-02 12:36 +0000 dockes <dockes> (e0d52b43cd5c) * src/lib/Makefile, src/mk/commondefs: add def for RANLIB 2005-11-01 10:55 +0000 dockes <dockes> (2b858432af00) * src/mk/Darwin: new file. * src/mk/Darwin: *** empty log message *** 2005-10-31 08:59 +0000 dockes <dockes> (65fd4f89de80) * src/internfile/mh_mail.cpp, src/utils/mimeparse.cpp: fixed base64 decoding of email parts: str[x] = ch does not adjust length! and be more lenient with encoding errors 2005-10-22 13:10 +0000 dockes <dockes> (9a5b142d31f3) * src/README: *** empty log message *** 2005-10-22 13:10 +0000 dockes <dockes> (d5ccf5480db1 [RECOLL-1_03]) * src/VERSION, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: update status line when starting lengthy operations 2005-10-22 07:29 +0000 dockes <dockes> (df8ad947685b) * src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: get clicks in res list to behave: drag->no click. dblclick->no single click 2005-10-22 05:35 +0000 dockes <dockes> (c566d157cfd3) * src/qtgui/recoll_fr.ts: new file. * src/qtgui/main.cpp, src/qtgui/recoll.pro, src/qtgui/recoll_fr.ts: i8n 2005-10-21 15:45 +0000 dockes <dockes> (34b797e01868) * src/mk/commondefs: new file. * src/mk/commondefs: *** empty log message *** 2005-10-21 15:41 +0000 dockes <dockes> (08f9ad818cb3 [RECOLL-1_02]) * src/makestaticdist.sh, src/recollinstall: more verbosity in install 2005-10-21 15:22 +0000 dockes <dockes> (aa642ead5a8e) * src/INSTALL: *** empty log message *** 2005-10-21 15:11 +0000 dockes <dockes> (1c74d6d926b7) * src/INSTALL: *** empty log message *** 2005-10-21 14:14 +0000 dockes <dockes> (662fe9bab837) * src/excludefile: *** empty log message *** 2005-10-21 14:11 +0000 dockes <dockes> (1856de4bf3f6) * src/makestaticdist.sh: new file. * src/Makefile, src/makestaticdist.sh: static bin dists 2005-10-21 13:34 +0000 dockes <dockes> (0c861c8b6029) * src/INSTALL, src/README: *** empty log message *** 2005-10-21 13:33 +0000 dockes <dockes> (7256b6e4e2ff) * src/Makefile, src/excludefile, src/index/Makefile, src/makesrcdist.sh, src/mk/FreeBSD, src/mk/Linux, src/mk/SunOS, src/qtgui/recoll.pro: rearrange make includes+prepare bin static distrib 2005-10-21 12:15 +0000 dockes <dockes> (a9773a1a4715) * src/unac/unac.c: fix args to iconv to get rid of warnings 2005-10-21 08:14 +0000 dockes <dockes> (f50d252ec29b) * src/Makefile, src/VERSION, src/excludefile, src/mk/FreeBSD, src/mk/Linux, src/qtgui/preview/pvmain.cpp, src/utils/smallut.cpp: more small build tweaks. use mkdtemp if available 2005-10-20 16:20 +0000 dockes <dockes> (b5fe53035720 [RECOLL-1_01]) * src/qtgui/advsearch.ui, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h: CR->search in advanced dialog. ^W close tab in preview 2005-10-20 15:42 +0000 dockes <dockes> (a9e9ecfba2d2) * src/filters/rcldoc, src/filters/rclpdf, src/filters/rclps, src/filters/rclsoff, src/mk/SunOS: small fixes for SunOS 2005-10-20 12:17 +0000 dockes <dockes> (bc70bba2564c) * src/README, src/makesrcdist.sh: *** empty log message *** 2005-10-20 12:17 +0000 dockes <dockes> (4e8de2aee40d) * src/INSTALL, src/README: *** empty log message *** 2005-10-20 12:17 +0000 dockes <dockes> (39b33b1f4e36) * src/INSTALL, src/README: *** empty log message *** 2005-10-20 12:16 +0000 dockes <dockes> (45a324ad4baa) * src/INSTALL, src/README: *** empty log message *** 2005-10-20 12:16 +0000 dockes <dockes> (73b1f99aef21) * src/INSTALL, src/README: *** empty log message *** 2005-10-20 12:12 +0000 dockes <dockes> (b3a8d1bceb51) * src/INSTALL, src/README: *** empty log message *** 2005-10-20 11:38 +0000 dockes <dockes> (5966cd48c62c) * src/sampleconf/recoll.conf: defaultlanguage->english 2005-10-20 11:33 +0000 dockes <dockes> (4ba3bd42973e) * src/recollinstall: new file. * src/bincimapmime/Makefile, src/filters/rcldoc, src/filters/rclpdf, src/filters/rclps, src/filters/rclsoff, src/qtgui/preview/preview.ui.h, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/recollinstall: small installation tweaks 2005-10-20 08:34 +0000 dockes <dockes> (8ce6cff4ca9c) * src/Makefile, src/VERSION, src/bincimapmime/Makefile, src/excludefile, src/lib/Makefile, src/mk/FreeBSD, src/qtgui/plaintorich.cpp, src/qtgui/preview/preview.ui.h, src/qtgui/recoll.pro: small warning and compilation adjustments 2005-10-20 07:51 +0000 dockes <dockes> (b6f58b26d846 [RECOLL-1_0]) * src/configure: new file. * src/README, src/configure: *** empty log message *** 2005-10-19 16:29 +0000 dockes <dockes> (46a91fdb7a8e) * src/INSTALL, src/README: *** empty log message *** 2005-10-19 16:27 +0000 dockes <dockes> (92e16891b11d) * src/INSTALL: *** empty log message *** 2005-10-19 16:09 +0000 dockes <dockes> (0dda1bd16921) * src/README, src/VERSION: *** empty log message *** 2005-10-19 15:22 +0000 dockes <dockes> (88cadb2e703e) * src/qtgui/recollmain.ui: *** empty log message *** 2005-10-19 14:14 +0000 dockes <dockes> (61cd7c267dec) * src/common/rclconfig.cpp, src/qtgui/advsearch.ui.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/idfile.cpp, src/utils/idfile.h: implemented filtering on file subtree 2005-10-19 10:21 +0000 dockes <dockes> (598116a30bfb) * src/common/textsplit.cpp, src/common/textsplit.h, src/filters/rcldoc, src/filters/rclpdf, src/filters/rclps, src/filters/rclsoff, src/qtgui/advsearch.ui, src/qtgui/advsearch.ui.h, src/qtgui/main.cpp, src/qtgui/plaintorich.cpp, src/qtgui/recoll.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: most of adv search working. Still need subtree/filename filters 2005-10-17 13:36 +0000 dockes <dockes> (6ce40ecb81f6) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/qtgui/advsearch.ui, src/qtgui/advsearch.ui.h, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/preview/pvmain.cpp, src/qtgui/recoll.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/utils/Makefile: implemented dialog/glue for advanced search 2005-10-15 12:18 +0000 dockes <dockes> (b57626e188f9) * src/index/indexer.cpp, src/internfile/mh_mail.cpp, src/utils/mimeparse.cpp, src/utils/mimeparse.h: decode encoded mail headers, plus use message date instead of file mtime 2005-10-10 13:25 +0000 dockes <dockes> (3797f12a0832) * src/common/textsplit.h: comments 2005-10-10 13:24 +0000 dockes <dockes> (a339c123dcb9) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: ckpt 2005-10-10 12:29 +0000 dockes <dockes> (e88bad1f996b) * src/qtgui/advsearch.ui, src/qtgui/main.cpp, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/preview/pvmain.cpp, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: ckpt 2005-09-27 06:20 +0000 dockes <dockes> (8b147a42b660) * src/qtgui/preview/preview.pro, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/preview/pvmain.cpp: new file. * src/qtgui/preview/preview.pro, src/qtgui/preview/preview.ui, src/qtgui/preview/preview.ui.h, src/qtgui/preview/pvmain.cpp: *** empty log message *** 2005-09-26 16:17 +0000 dockes <dockes> (783900fcd3e7) * src/qtgui/recoll.pro: *** empty log message *** 2005-09-22 16:22 +0000 dockes <dockes> (1e6ccf2c2fdc) * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h: new file. * src/qtgui/plaintorich.cpp, src/qtgui/plaintorich.h: *** empty log message *** 2005-09-22 15:00 +0000 dockes <dockes> (db2d876f2a2b) * src/qtgui/recollmain.ui.h: *** empty log message *** 2005-09-22 14:09 +0000 dockes <dockes> (4455c0eeffd4) * src/common/textsplit.cpp: adjust start/end of word when trimming 2005-09-22 11:10 +0000 dockes <dockes> (3b9d4fc5b507) * src/common/textsplit.cpp: fix problems with word followed by . 2005-05-18 08:42 +0000 dockes <dockes> (03bc1f1290cd) * src/qtgui/recoll.pro: *** empty log message *** 2005-05-17 11:46 +0000 dockes <dockes> (cff6e901fde8) * src/qtgui/advsearch.ui, src/qtgui/advsearch.ui.h: new file. * src/qtgui/advsearch.ui, src/qtgui/advsearch.ui.h, src/qtgui/recoll.pro: *** empty log message *** 2005-05-17 06:30 +0000 dockes <dockes> (9a44703bd049 [RECOLL-0_7]) * src/README: *** empty log message *** 2005-05-17 06:30 +0000 dockes <dockes> (d2265051082d) * src/qtgui/recollmain.ui.h: escape < to < in rich text 2005-04-08 07:32 +0000 dockes <dockes> (3917ab1cc937) * src/README: *** empty log message *** 2005-04-08 07:32 +0000 dockes <dockes> (2f2439c9590a) * src/mk/SunOS: new file. * src/Makefile, src/mk/SunOS, src/utils/Makefile: works on solaris8 2005-04-07 09:05 +0000 dockes <dockes> (0264f1839b92) * src/utils/idfile.cpp, src/utils/idfile.h: new file. * src/index/mimetype.cpp, src/lib/Makefile, src/sampleconf/mimemap, src/utils/Makefile, src/utils/idfile.cpp, src/utils/idfile.h: replaced /usr/bin/file exec with internal code 2005-04-06 10:20 +0000 dockes <dockes> (ba9162debe5a) * src/bincimapmime/AUTHORS, src/bincimapmime/COPYING: new file. * src/INSTALL, src/VERSION, src/bincimapmime/AUTHORS, src/bincimapmime/COPYING, src/bincimapmime/mime-inputsource.h, src/index/indexer.cpp, src/internfile/mh_mail.cpp, src/makesrcdist.sh, src/mk/FreeBSD, src/mk/Linux, src/qtgui/main.cpp, src/rcldb/rcldb.cpp, src/sampleconf/recoll.conf, src/utils/smallut.h, src/utils/wipedir.cpp: re-port to linux 2005-04-06 09:18 +0000 dockes <dockes> (d8add828aa6b) * src/README: *** empty log message *** 2005-04-06 09:13 +0000 dockes <dockes> (7d5759a43255) * src/README: *** empty log message *** 2005-04-05 09:35 +0000 dockes <dockes> (6232ca052972) * src/common/rclinit.cpp, src/common/rclinit.h: new file. * src/common/rclinit.cpp, src/common/rclinit.h, src/index/mimetype.cpp, src/index/recollindex.cpp, src/internfile/internfile.cpp, src/internfile/mh_mail.cpp, src/lib/Makefile, src/qtgui/main.cpp, src/rcldb/rcldb.cpp, src/sampleconf/mimemap: *** empty log message *** 2005-04-04 13:18 +0000 dockes <dockes> (e69c810eb5b1) * src/index/indexer.cpp, src/index/mimetype.cpp, src/internfile/mh_html.cpp, src/internfile/mh_mail.cpp, src/rcldb/rcldb.cpp, src/sampleconf/mimeconf, src/utils/fstreewalk.cpp, src/utils/fstreewalk.h: *** empty log message *** 2005-03-31 10:04 +0000 dockes <dockes> (9428bb11ff77) * src/bincimapmime/mime-inputsource.h, src/bincimapmime/mime- parsefull.cc, src/bincimapmime/mime-parseonlyheader.cc, src/bincimapmime/mime-printbody.cc, src/bincimapmime/mime.h, src/bincimapmime/trbinc.cc, src/common/rclconfig.cpp, src/internfile/mh_html.cpp, src/internfile/mh_html.h, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/rcldb/rcldb.cpp: mail handling 1st working version 2005-03-25 09:40 +0000 dockes <dockes> (408a2650e963 [RECOLL-0_6]) * src/bincimapmime/00README.recoll, src/bincimapmime/trbinc.cc, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h: new file. * src/bincimapmime/00README.recoll, src/bincimapmime/mime- printbody.cc, src/bincimapmime/mime.h, src/bincimapmime/trbinc.cc, src/common/Makefile, src/index/Makefile, src/index/indexer.cpp, src/index/mimetype.cpp, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mh_html.cpp, src/internfile/mh_html.h, src/internfile/mh_mail.cpp, src/internfile/mh_mail.h, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/internfile/myhtmlparse.cpp, src/lib/Makefile, src/mk/FreeBSD, src/qtgui/recoll.pro, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.h, src/sampleconf/mimeconf, src/utils/mimeparse.cpp, src/utils/mimeparse.h: mail ckpt 2005-03-17 15:35 +0000 dockes <dockes> (55a0c15039bf) * src/index/indexer.cpp, src/index/indexer.h, src/internfile/internfile.cpp, src/internfile/mh_html.h: only comments. Before multidoc files 2005-03-17 14:02 +0000 dockes <dockes> (b1f57902f3c1) * src/bincimapmime/Makefile, src/bincimapmime/iodevice.cc, src/index/indexer.cpp, src/qtgui/recollmain.ui.h, src/sampleconf/mimeconf, src/utils/execmd.cpp, src/utils/execmd.h, src/utils/mimeparse.cpp, src/utils/smallut.cpp, src/utils/smallut.h, src/utils/utf8iter.h: checkpoint after long pause 2005-03-16 07:35 +0000 dockes <dockes> (4d4d71cd89ea) * src/bincimapmime/Makefile, src/bincimapmime/address.cc, src/bincimapmime/address.h, src/bincimapmime/config.h, src/bincimapmime/convert.cc, src/bincimapmime/convert.h, src/bincimapmime/depot.h, src/bincimapmime/iodevice.cc, src/bincimapmime/iodevice.h, src/bincimapmime/iofactory.cc, src/bincimapmime/iofactory.h, src/bincimapmime/mime-getpart.cc, src/bincimapmime/mime-inputsource.h, src/bincimapmime/mime- parsefull.cc, src/bincimapmime/mime-parseonlyheader.cc, src/bincimapmime/mime-printbody.cc, src/bincimapmime/mime- printdoc.cc, src/bincimapmime/mime-printheader.cc, src/bincimapmime /mime-utils.h, src/bincimapmime/mime.cc, src/bincimapmime/mime.h, src/bincimapmime/session.h: new file. * src/bincimapmime/Makefile, src/bincimapmime/address.cc, src/bincimapmime/address.h, src/bincimapmime/config.h, src/bincimapmime/convert.cc, src/bincimapmime/convert.h, src/bincimapmime/depot.h, src/bincimapmime/iodevice.cc, src/bincimapmime/iodevice.h, src/bincimapmime/iofactory.cc, src/bincimapmime/iofactory.h, src/bincimapmime/mime-getpart.cc, src/bincimapmime/mime-inputsource.h, src/bincimapmime/mime- parsefull.cc, src/bincimapmime/mime-parseonlyheader.cc, src/bincimapmime/mime-printbody.cc, src/bincimapmime/mime- printdoc.cc, src/bincimapmime/mime-printheader.cc, src/bincimapmime /mime-utils.h, src/bincimapmime/mime.cc, src/bincimapmime/mime.h, src/bincimapmime/session.h: initial import from bincimap-1.3.3 2005-02-11 11:48 +0000 dockes <dockes> (7b2bdc5c6ed9) * src/README, src/makesrcdist.sh: *** empty log message *** 2005-02-11 11:48 +0000 dockes <dockes> (ffca521040c2) * src/README: *** empty log message *** 2005-02-11 11:20 +0000 dockes <dockes> (7c54c58f0fd1) * src/common/uproplist.h, src/utils/utf8testin.txt: new file. * src/common/textsplit.cpp, src/common/uproplist.h, src/utils/Makefile, src/utils/utf8iter.cpp, src/utils/utf8iter.h, src/utils/utf8testin.txt: improved word extraction a bit (unicode punctuation) 2005-02-10 19:52 +0000 dockes <dockes> (ba4dd19f41c4) * src/utils/utf8iter.cpp, src/utils/utf8iter.h: new file. * src/common/textsplit.cpp, src/utils/Makefile, src/utils/utf8iter.cpp, src/utils/utf8iter.h: *** empty log message *** 2005-02-10 15:21 +0000 dockes <dockes> (44892bfc8d49) * src/index/indexer.cpp, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/execmd.cpp, src/utils/fstreewalk.cpp, src/utils/fstreewalk.h, src/utils/smallut.cpp, src/utils/smallut.h: implemented stem databases 2005-02-09 13:34 +0000 dockes <dockes> (7517469a76b5) * src/index/Makefile, src/index/mimetype.cpp: *** empty log message *** 2005-02-09 12:07 +0000 dockes <dockes> (e5d0612227af) * src/filters/rcldoc, src/filters/rclsoff, src/utils/wipedir.cpp, src/utils/wipedir.h: new file. * src/VERSION, src/filters/rcldoc, src/filters/rclsoff, src/index/indexer.cpp, src/index/mimetype.cpp, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/myhtmlparse.cpp, src/lib/Makefile, src/qtgui/main.cpp, src/qtgui/recoll.h, src/qtgui/recollmain.ui.h, src/sampleconf/mimeconf, src/sampleconf/mimemap, src/utils/Makefile, src/utils/smallut.cpp, src/utils/smallut.h, src/utils/wipedir.cpp, src/utils/wipedir.h: added support for openoffice and word + optimized decomp temp dir usage 2005-02-08 17:35 +0000 dockes <dockes> (e5a6d4a27e1f) * src/INSTALL, src/README: *** empty log message *** 2005-02-08 15:08 +0000 dockes <dockes> (f89783d5d828) * src/excludefile, src/makesrcdist.sh: new file. * src/README, src/excludefile, src/makesrcdist.sh: *** empty log message *** 2005-02-08 15:03 +0000 dockes <dockes> (4ec10decb898) * src/README: *** empty log message *** 2005-02-08 14:55 +0000 dockes <dockes> (07541712859f) * src/README: *** empty log message *** 2005-02-08 14:54 +0000 dockes <dockes> (fa3ad0590138 [RECOLL-0_5]) * src/Makefile, src/README, src/qtgui/recoll.pro, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/sampleconf/recoll.conf: *** empty log message *** 2005-02-08 14:45 +0000 dockes <dockes> (d04d78bb1af4) * src/INSTALL, src/internfile/myhtmlparse.cpp, src/qtgui/recoll.pro: *** empty log message *** 2005-02-08 11:59 +0000 dockes <dockes> (b5f33d8a83cb) * src/common/textsplit.cpp, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: fixed next/prev screen pb + pb with accents when matching in preview 2005-02-08 10:56 +0000 dockes <dockes> (ea8c32a3b71e) * src/common/textsplit.cpp, src/common/textsplit.h, src/rcldb/rcldb.cpp: phrases ok except for preview position 2005-02-08 09:34 +0000 dockes <dockes> (8f72bd8ca147) * src/common/textsplit.cpp, src/common/textsplit.h, src/qtgui/recoll.pro, src/qtgui/recollmain.ui.h, src/query/xadump.cpp, src/rcldb/rcldb.cpp, src/utils/execmd.cpp: fixes in textsplit 2005-02-07 13:17 +0000 dockes <dockes> (3e10d31a55a9) * src/common/textsplit.cpp, src/common/textsplit.h, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: simple term highlighting in query preview 2005-02-04 14:21 +0000 dockes <dockes> (77a59732f8aa) * src/COPYING, src/INSTALL, src/README, src/VERSION, src/filters/rcluncomp, src/internfile/internfile.cpp, src/internfile/internfile.h, src/mk/Linux, src/qtgui/images/editcopy, src/qtgui/images/editcut, src/qtgui/images/editpaste, src/qtgui/images/filenew, src/qtgui/images/fileopen, src/qtgui/images/filesave, src/qtgui/images/print, src/qtgui/images/redo, src/qtgui/images/searchfind, src/qtgui/images/undo: new file. * src/COPYING, src/INSTALL, src/Makefile, src/README, src/VERSION, src/common/Makefile, src/common/unacpp.cpp, src/filters/rcluncomp, src/index/Makefile, src/index/csguess.cpp, src/index/indexer.cpp, src/internfile/internfile.cpp, src/internfile/internfile.h, src/internfile/mimehandler.cpp, src/lib/Makefile, src/mk/FreeBSD, src/mk/Linux, src/qtgui/idxthread.cpp, src/qtgui/images/editcopy, src/qtgui/images/editcut, src/qtgui/images/editpaste, src/qtgui/images/filenew, src/qtgui/images/fileopen, src/qtgui/images/filesave, src/qtgui/images/print, src/qtgui/images/redo, src/qtgui/images/searchfind, src/qtgui/images/undo, src/qtgui/recoll.pro, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/query/Makefile, src/rcldb/rcldb.cpp, src/utils/Makefile, src/utils/pathut.cpp, src/utils/transcode.cpp: uncompression+linux port 2005-02-04 09:39 +0000 dockes <dockes> (482687ce34da) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/index/indexer.cpp, src/internfile/mh_html.cpp, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/lib/Makefile, src/qtgui/recollmain.ui.h, src/utils/smallut.cpp, src/utils/smallut.h: *** empty log message *** 2005-02-04 09:30 +0000 dockes <dockes> (5fac5dd8a1c4) * src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/recoll.conf: *** empty log message *** 2005-02-04 09:21 +0000 dockes <dockes> (2ad004ec5fd7) * src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/recoll.conf: new file. * src/sampleconf/mimeconf, src/sampleconf/mimemap, src/sampleconf/recoll.conf: *** empty log message *** 2005-02-02 17:57 +0000 dockes <dockes> (4819b0b410e7) * src/filters/rclps: new file. * src/filters/rclps: *** empty log message *** 2005-02-01 17:52 +0000 dockes <dockes> (023ac2c1c87f) * src/internfile/mimehandler.cpp, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp: *** empty log message *** 2005-02-01 17:20 +0000 dockes <dockes> (4eb8337baa03) * src/filters/rclpdf, src/internfile/mh_html.h, src/mk/FreeBSD, src/qtgui/idxthread.h, src/qtgui/recoll.h: new file. * src/filters/rclpdf, src/index/indexer.cpp, src/internfile/mh_html.cpp, src/internfile/mh_html.h, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/lib/Makefile, src/mk/FreeBSD, src/qtgui/idxthread.h, src/qtgui/recoll.h, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/utils/Makefile, src/utils/execmd.cpp: added external filters and pdf handling 2005-02-01 08:42 +0000 dockes <dockes> (b82908e25c6b) * src/common/Makefile, src/index/Makefile, src/index/recollindex.cpp, src/lib/Makefile, src/qtgui/idxthread.cpp, src/qtgui/main.cpp, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/query/Makefile, src/rcldb/rcldb.cpp, src/utils/Makefile: *** empty log message *** 2005-01-31 14:31 +0000 dockes <dockes> (c8a32d0e0056) * src/index/indexer.cpp, src/qtgui/idxthread.cpp, src/utils/smallut.cpp, src/utils/smallut.h: new file. * src/common/rclconfig.cpp, src/index/indexer.cpp, src/index/indexer.h, src/index/recollindex.cpp, src/lib/Makefile, src/qtgui/idxthread.cpp, src/qtgui/main.cpp, src/qtgui/recoll.pro, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/query/qtry.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/Makefile, src/utils/pathut.cpp, src/utils/pathut.h, src/utils/smallut.cpp, src/utils/smallut.h: first incarnation of indexing thread 2005-01-29 15:41 +0000 dockes <dockes> (3dd05c65d8ed) * src/index/recollindex.cpp, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/lib/Makefile, src/qtgui/main.cpp, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/Makefile: external viewer+ deleted doc purging 2005-01-28 15:25 +0000 dockes <dockes> (8c6b04552a34) * src/Makefile, src/internfile/indextext.h: new file. * src/Makefile, src/internfile/indextext.h, src/internfile/myhtmlparse.cpp, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: ckpt 2005-01-28 09:37 +0000 dockes <dockes> (bf2c00ad72d0) * src/internfile/mh_html.cpp, src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h, src/lib/Makefile, src/rcldb/rcldb.cpp: merged modifs from xapian/omega 0.8.5 2005-01-28 08:56 +0000 dockes <dockes> (a5e2a08ce1b8) * src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h: import from xapian 0.8.5 2005-01-28 08:50 +0000 dockes <dockes> (73f5b0ed50d8) * src/internfile/htmlparse.cpp, src/internfile/htmlparse.h: Initial recoll modifs for utf8 2005-01-28 08:46 +0000 dockes <dockes> (04f0053d01e4) * src/internfile/mh_html.cpp, src/rcldb/rcldb.cpp: xapian 0.8.3 2005-01-28 08:45 +0000 dockes <dockes> (ec7863976555) * src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h: new file. * src/internfile/myhtmlparse.cpp, src/internfile/myhtmlparse.h: *** empty log message *** 2005-01-28 08:41 +0000 dockes <dockes> (c5c570040571) * src/internfile/htmlparse.cpp, src/internfile/htmlparse.h: new file. * src/internfile/htmlparse.cpp, src/internfile/htmlparse.h: xapian 0.8.3 2005-01-26 13:03 +0000 dockes <dockes> (5a37e2aa9a53) * src/index/recollindex.cpp, src/internfile/mh_html.cpp, src/internfile/mimehandler.cpp, src/rcldb/rcldb.cpp: sort of indexes html 2005-01-26 11:47 +0000 dockes <dockes> (eec829a74f2d) * src/internfile/mh_html.cpp: new file. * src/internfile/mh_html.cpp, src/internfile/mimehandler.cpp, src/internfile/mimehandler.h, src/lib/Makefile, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/query/qtry.cpp, src/rcldb/rcldb.cpp, src/utils/Makefile: ckpt 2005-01-26 11:45 +0000 dockes <dockes> (1c17d5d56a6b) * src/utils/mimeparse.cpp, src/utils/mimeparse.h: new file. * src/utils/mimeparse.cpp, src/utils/mimeparse.h: mime header parsing embryo 2005-01-25 14:37 +0000 dockes <dockes> (1d5b47c225bf) * src/internfile/mimehandler.cpp, src/internfile/mimehandler.h: new file. * src/internfile/mimehandler.cpp, src/internfile/mimehandler.h: *** empty log message *** 2005-01-25 14:37 +0000 dockes <dockes> (46d42849ee3a) * src/lib/Makefile, src/qtgui/recoll.pro, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h: new file. * src/common/Makefile, src/common/rclconfig.cpp, src/index/Makefile, src/index/indexer.h, src/index/recollindex.cpp, src/lib/Makefile, src/qtgui/main.cpp, src/qtgui/recoll.pro, src/qtgui/recollmain.ui, src/qtgui/recollmain.ui.h, src/query/Makefile, src/query/qtry.cpp, src/query/xadump.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/Makefile, src/utils/transcode.h: gui connected to rcldb (init) 2005-01-24 13:17 +0000 dockes <dockes> (e0104075bdd3) * src/common/Makefile, src/index/Makefile, src/qtgui/form1.ui, src/qtgui/form1.ui.h, src/qtgui/main.cpp, src/query/qtry.cpp, src/query/qxtry.cpp, src/utils/Makefile: new file. * src/common/Makefile, src/common/rclconfig.cpp, src/common/textsplit.h, src/common/unacpp.cpp, src/index/Makefile, src/qtgui/form1.ui, src/qtgui/form1.ui.h, src/qtgui/main.cpp, src/query/Makefile, src/query/qtry.cpp, src/query/qxtry.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/Makefile: *** empty log message *** 2004-12-17 15:50 +0000 dockes <dockes> (4f7a0a26f6d7) * src/query/xadump.cpp, src/rcldb/rcldb.cpp: very basic indexing working 2004-12-17 15:36 +0000 dockes <dockes> (325aea11f893) * src/common/unacpp.cpp, src/common/unacpp.h: new file. * src/common/unacpp.cpp, src/common/unacpp.h, src/rcldb/rcldb.cpp: *** empty log message *** 2004-12-17 15:04 +0000 dockes <dockes> (930a5f50b45e) * src/unac/AUTHORS, src/unac/COPYING, src/unac/README, src/unac/README.recoll, src/unac/unac.c, src/unac/unac.h: new file. * src/unac/AUTHORS, src/unac/COPYING, src/unac/README, src/unac/README.recoll, src/unac/unac.c, src/unac/unac.h: unac 1.7.0 2004-12-17 13:01 +0000 dockes <dockes> (70ded59ba246) * src/query/Makefile, src/query/xadump.cpp: new file. * src/common/rclconfig.h, src/common/textsplit.cpp, src/common/textsplit.h, src/index/recollindex.cpp, src/query/Makefile, src/query/xadump.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h: *** empty log message *** 2004-12-15 15:00 +0000 dockes <dockes> (1e3483587b45) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/common/textsplit.cpp, src/index/csguess.cpp, src/index/csguess.h, src/index/indexer.h, src/index/mimetype.cpp, src/index/recollindex.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/transcode.cpp: warnings cleanup 2004-12-15 09:43 +0000 dockes <dockes> (502752de59d5) * src/utils/transcode.cpp, src/utils/transcode.h: new file. * src/utils/transcode.cpp, src/utils/transcode.h: *** empty log message *** 2004-12-15 08:21 +0000 dockes <dockes> (520f5e294f10) * src/index/csguess.cpp, src/index/csguess.h: new file. * src/index/csguess.cpp, src/index/csguess.h: just converted (indent+comments) from estraier 2004-12-14 17:54 +0000 dockes <dockes> (12a23501eee8) * src/common/rclconfig.cpp, src/common/rclconfig.h, src/common/textsplit.h, src/index/indexer.h, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/readfile.cpp, src/utils/readfile.h: new file. * src/common/rclconfig.cpp, src/common/rclconfig.h, src/common/textsplit.cpp, src/common/textsplit.h, src/index/indexer.h, src/index/mimetype.cpp, src/index/mimetype.h, src/index/recollindex.cpp, src/rcldb/rcldb.cpp, src/rcldb/rcldb.h, src/utils/execmd.cpp, src/utils/pathut.cpp, src/utils/pathut.h, src/utils/readfile.cpp, src/utils/readfile.h: *** empty log message *** 2004-12-13 15:42 +0000 dockes <dockes> (8c1fce132f19) * src/common/textsplit.cpp, src/index/mimetype.cpp, src/index/mimetype.h, src/index/recollindex.cpp: new file. * src/common/textsplit.cpp, src/index/mimetype.cpp, src/index/mimetype.h, src/index/recollindex.cpp: *** empty log message *** 2004-12-12 08:58 +0000 dockes <dockes> (17a132340425) * src/utils/execmd.cpp, src/utils/execmd.h: new file. * src/utils/execmd.cpp, src/utils/execmd.h, src/utils/fstreewalk.cpp: *** empty log message *** 2004-12-10 18:13 +0000 dockes <dockes> (1de12131e4a4) * src/utils/fstreewalk.cpp, src/utils/fstreewalk.h, src/utils/pathut.cpp, src/utils/pathut.h: new file. * src/utils/fstreewalk.cpp, src/utils/fstreewalk.h, src/utils/pathut.cpp, src/utils/pathut.h: *** empty log message *** 2004-12-10 18:13 +0000 unknown <unknown> (318176766db7) * Standard project directories initialized by cvs2svn. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/aclocal.m4����������������������������������������������������������������������������0000644�0001750�0001750�00000370436�14515753277�011700� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# generated automatically by aclocal 1.16.5 -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.71],, [m4_warning([this file was generated for autoconf 2.71. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # host-cpu-c-abi.m4 serial 13 dnl Copyright (C) 2002-2020 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible and Sam Steingold. dnl Sets the HOST_CPU variable to the canonical name of the CPU. dnl Sets the HOST_CPU_C_ABI variable to the canonical name of the CPU with its dnl C language ABI (application binary interface). dnl Also defines __${HOST_CPU}__ and __${HOST_CPU_C_ABI}__ as C macros in dnl config.h. dnl dnl This canonical name can be used to select a particular assembly language dnl source file that will interoperate with C code on the given host. dnl dnl For example: dnl * 'i386' and 'sparc' are different canonical names, because code for i386 dnl will not run on SPARC CPUs and vice versa. They have different dnl instruction sets. dnl * 'sparc' and 'sparc64' are different canonical names, because code for dnl 'sparc' and code for 'sparc64' cannot be linked together: 'sparc' code dnl contains 32-bit instructions, whereas 'sparc64' code contains 64-bit dnl instructions. A process on a SPARC CPU can be in 32-bit mode or in 64-bit dnl mode, but not both. dnl * 'mips' and 'mipsn32' are different canonical names, because they use dnl different argument passing and return conventions for C functions, and dnl although the instruction set of 'mips' is a large subset of the dnl instruction set of 'mipsn32'. dnl * 'mipsn32' and 'mips64' are different canonical names, because they use dnl different sizes for the C types like 'int' and 'void *', and although dnl the instruction sets of 'mipsn32' and 'mips64' are the same. dnl * The same canonical name is used for different endiannesses. You can dnl determine the endianness through preprocessor symbols: dnl - 'arm': test __ARMEL__. dnl - 'mips', 'mipsn32', 'mips64': test _MIPSEB vs. _MIPSEL. dnl - 'powerpc64': test _BIG_ENDIAN vs. _LITTLE_ENDIAN. dnl * The same name 'i386' is used for CPUs of type i386, i486, i586 dnl (Pentium), AMD K7, Pentium II, Pentium IV, etc., because dnl - Instructions that do not exist on all of these CPUs (cmpxchg, dnl MMX, SSE, SSE2, 3DNow! etc.) are not frequently used. If your dnl assembly language source files use such instructions, you will dnl need to make the distinction. dnl - Speed of execution of the common instruction set is reasonable across dnl the entire family of CPUs. If you have assembly language source files dnl that are optimized for particular CPU types (like GNU gmp has), you dnl will need to make the distinction. dnl See <https://en.wikipedia.org/wiki/X86_instruction_listings>. AC_DEFUN([gl_HOST_CPU_C_ABI], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([gl_C_ASM]) AC_CACHE_CHECK([host CPU and C ABI], [gl_cv_host_cpu_c_abi], [case "$host_cpu" in changequote(,)dnl i[34567]86 ) changequote([,])dnl gl_cv_host_cpu_c_abi=i386 ;; x86_64 ) # On x86_64 systems, the C compiler may be generating code in one of # these ABIs: # - 64-bit instruction set, 64-bit pointers, 64-bit 'long': x86_64. # - 64-bit instruction set, 64-bit pointers, 32-bit 'long': x86_64 # with native Windows (mingw, MSVC). # - 64-bit instruction set, 32-bit pointers, 32-bit 'long': x86_64-x32. # - 32-bit instruction set, 32-bit pointers, 32-bit 'long': i386. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if (defined __x86_64__ || defined __amd64__ \ || defined _M_X64 || defined _M_AMD64) int ok; #else error fail #endif ]])], [AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __ILP32__ || defined _ILP32 int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=x86_64-x32], [gl_cv_host_cpu_c_abi=x86_64])], [gl_cv_host_cpu_c_abi=i386]) ;; changequote(,)dnl alphaev[4-8] | alphaev56 | alphapca5[67] | alphaev6[78] ) changequote([,])dnl gl_cv_host_cpu_c_abi=alpha ;; arm* | aarch64 ) # Assume arm with EABI. # On arm64 systems, the C compiler may be generating code in one of # these ABIs: # - aarch64 instruction set, 64-bit pointers, 64-bit 'long': arm64. # - aarch64 instruction set, 32-bit pointers, 32-bit 'long': arm64-ilp32. # - 32-bit instruction set, 32-bit pointers, 32-bit 'long': arm or armhf. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#ifdef __aarch64__ int ok; #else error fail #endif ]])], [AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __ILP32__ || defined _ILP32 int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=arm64-ilp32], [gl_cv_host_cpu_c_abi=arm64])], [# Don't distinguish little-endian and big-endian arm, since they # don't require different machine code for simple operations and # since the user can distinguish them through the preprocessor # defines __ARMEL__ vs. __ARMEB__. # But distinguish arm which passes floating-point arguments and # return values in integer registers (r0, r1, ...) - this is # gcc -mfloat-abi=soft or gcc -mfloat-abi=softfp - from arm which # passes them in float registers (s0, s1, ...) and double registers # (d0, d1, ...) - this is gcc -mfloat-abi=hard. GCC 4.6 or newer # sets the preprocessor defines __ARM_PCS (for the first case) and # __ARM_PCS_VFP (for the second case), but older GCC does not. echo 'double ddd; void func (double dd) { ddd = dd; }' > conftest.c # Look for a reference to the register d0 in the .s file. AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS $gl_c_asm_opt conftest.c) >/dev/null 2>&1 if LC_ALL=C grep 'd0,' conftest.$gl_asmext >/dev/null; then gl_cv_host_cpu_c_abi=armhf else gl_cv_host_cpu_c_abi=arm fi rm -f conftest* ]) ;; hppa1.0 | hppa1.1 | hppa2.0* | hppa64 ) # On hppa, the C compiler may be generating 32-bit code or 64-bit # code. In the latter case, it defines _LP64 and __LP64__. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#ifdef __LP64__ int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=hppa64], [gl_cv_host_cpu_c_abi=hppa]) ;; ia64* ) # On ia64 on HP-UX, the C compiler may be generating 64-bit code or # 32-bit code. In the latter case, it defines _ILP32. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#ifdef _ILP32 int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=ia64-ilp32], [gl_cv_host_cpu_c_abi=ia64]) ;; mips* ) # We should also check for (_MIPS_SZPTR == 64), but gcc keeps this # at 32. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined _MIPS_SZLONG && (_MIPS_SZLONG == 64) int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=mips64], [# In the n32 ABI, _ABIN32 is defined, _ABIO32 is not defined (but # may later get defined by <sgidefs.h>), and _MIPS_SIM == _ABIN32. # In the 32 ABI, _ABIO32 is defined, _ABIN32 is not defined (but # may later get defined by <sgidefs.h>), and _MIPS_SIM == _ABIO32. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if (_MIPS_SIM == _ABIN32) int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=mipsn32], [gl_cv_host_cpu_c_abi=mips])]) ;; powerpc* ) # Different ABIs are in use on AIX vs. Mac OS X vs. Linux,*BSD. # No need to distinguish them here; the caller may distinguish # them based on the OS. # On powerpc64 systems, the C compiler may still be generating # 32-bit code. And on powerpc-ibm-aix systems, the C compiler may # be generating 64-bit code. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __powerpc64__ || defined _ARCH_PPC64 int ok; #else error fail #endif ]])], [# On powerpc64, there are two ABIs on Linux: The AIX compatible # one and the ELFv2 one. The latter defines _CALL_ELF=2. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined _CALL_ELF && _CALL_ELF == 2 int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=powerpc64-elfv2], [gl_cv_host_cpu_c_abi=powerpc64]) ], [gl_cv_host_cpu_c_abi=powerpc]) ;; rs6000 ) gl_cv_host_cpu_c_abi=powerpc ;; riscv32 | riscv64 ) # There are 2 architectures (with variants): rv32* and rv64*. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if __riscv_xlen == 64 int ok; #else error fail #endif ]])], [cpu=riscv64], [cpu=riscv32]) # There are 6 ABIs: ilp32, ilp32f, ilp32d, lp64, lp64f, lp64d. # Size of 'long' and 'void *': AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __LP64__ int ok; #else error fail #endif ]])], [main_abi=lp64], [main_abi=ilp32]) # Float ABIs: # __riscv_float_abi_double: # 'float' and 'double' are passed in floating-point registers. # __riscv_float_abi_single: # 'float' are passed in floating-point registers. # __riscv_float_abi_soft: # No values are passed in floating-point registers. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __riscv_float_abi_double int ok; #else error fail #endif ]])], [float_abi=d], [AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __riscv_float_abi_single int ok; #else error fail #endif ]])], [float_abi=f], [float_abi='']) ]) gl_cv_host_cpu_c_abi="${cpu}-${main_abi}${float_abi}" ;; s390* ) # On s390x, the C compiler may be generating 64-bit (= s390x) code # or 31-bit (= s390) code. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __LP64__ || defined __s390x__ int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=s390x], [gl_cv_host_cpu_c_abi=s390]) ;; sparc | sparc64 ) # UltraSPARCs running Linux have `uname -m` = "sparc64", but the # C compiler still generates 32-bit code. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __sparcv9 || defined __arch64__ int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi=sparc64], [gl_cv_host_cpu_c_abi=sparc]) ;; *) gl_cv_host_cpu_c_abi="$host_cpu" ;; esac ]) dnl In most cases, $HOST_CPU and $HOST_CPU_C_ABI are the same. HOST_CPU=`echo "$gl_cv_host_cpu_c_abi" | sed -e 's/-.*//'` HOST_CPU_C_ABI="$gl_cv_host_cpu_c_abi" AC_SUBST([HOST_CPU]) AC_SUBST([HOST_CPU_C_ABI]) # This was # AC_DEFINE_UNQUOTED([__${HOST_CPU}__]) # AC_DEFINE_UNQUOTED([__${HOST_CPU_C_ABI}__]) # earlier, but KAI C++ 3.2d doesn't like this. sed -e 's/-/_/g' >> confdefs.h <<EOF #ifndef __${HOST_CPU}__ #define __${HOST_CPU}__ 1 #endif #ifndef __${HOST_CPU_C_ABI}__ #define __${HOST_CPU_C_ABI}__ 1 #endif EOF AH_TOP([/* CPU and C ABI indicator */ #ifndef __i386__ #undef __i386__ #endif #ifndef __x86_64_x32__ #undef __x86_64_x32__ #endif #ifndef __x86_64__ #undef __x86_64__ #endif #ifndef __alpha__ #undef __alpha__ #endif #ifndef __arm__ #undef __arm__ #endif #ifndef __armhf__ #undef __armhf__ #endif #ifndef __arm64_ilp32__ #undef __arm64_ilp32__ #endif #ifndef __arm64__ #undef __arm64__ #endif #ifndef __hppa__ #undef __hppa__ #endif #ifndef __hppa64__ #undef __hppa64__ #endif #ifndef __ia64_ilp32__ #undef __ia64_ilp32__ #endif #ifndef __ia64__ #undef __ia64__ #endif #ifndef __m68k__ #undef __m68k__ #endif #ifndef __mips__ #undef __mips__ #endif #ifndef __mipsn32__ #undef __mipsn32__ #endif #ifndef __mips64__ #undef __mips64__ #endif #ifndef __powerpc__ #undef __powerpc__ #endif #ifndef __powerpc64__ #undef __powerpc64__ #endif #ifndef __powerpc64_elfv2__ #undef __powerpc64_elfv2__ #endif #ifndef __riscv32__ #undef __riscv32__ #endif #ifndef __riscv64__ #undef __riscv64__ #endif #ifndef __riscv32_ilp32__ #undef __riscv32_ilp32__ #endif #ifndef __riscv32_ilp32f__ #undef __riscv32_ilp32f__ #endif #ifndef __riscv32_ilp32d__ #undef __riscv32_ilp32d__ #endif #ifndef __riscv64_ilp32__ #undef __riscv64_ilp32__ #endif #ifndef __riscv64_ilp32f__ #undef __riscv64_ilp32f__ #endif #ifndef __riscv64_ilp32d__ #undef __riscv64_ilp32d__ #endif #ifndef __riscv64_lp64__ #undef __riscv64_lp64__ #endif #ifndef __riscv64_lp64f__ #undef __riscv64_lp64f__ #endif #ifndef __riscv64_lp64d__ #undef __riscv64_lp64d__ #endif #ifndef __s390__ #undef __s390__ #endif #ifndef __s390x__ #undef __s390x__ #endif #ifndef __sh__ #undef __sh__ #endif #ifndef __sparc__ #undef __sparc__ #endif #ifndef __sparc64__ #undef __sparc64__ #endif ]) ]) dnl Sets the HOST_CPU_C_ABI_32BIT variable to 'yes' if the C language ABI dnl (application binary interface) is a 32-bit one, to 'no' if it is a 64-bit dnl one, or to 'unknown' if unknown. dnl This is a simplified variant of gl_HOST_CPU_C_ABI. AC_DEFUN([gl_HOST_CPU_C_ABI_32BIT], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_CACHE_CHECK([32-bit host C ABI], [gl_cv_host_cpu_c_abi_32bit], [if test -n "$gl_cv_host_cpu_c_abi"; then case "$gl_cv_host_cpu_c_abi" in i386 | x86_64-x32 | arm | armhf | arm64-ilp32 | hppa | ia64-ilp32 | mips | mipsn32 | powerpc | riscv*-ilp32* | s390 | sparc) gl_cv_host_cpu_c_abi_32bit=yes ;; x86_64 | alpha | arm64 | hppa64 | ia64 | mips64 | powerpc64 | powerpc64-elfv2 | riscv*-lp64* | s390x | sparc64 ) gl_cv_host_cpu_c_abi_32bit=no ;; *) gl_cv_host_cpu_c_abi_32bit=unknown ;; esac else case "$host_cpu" in # CPUs that only support a 32-bit ABI. arc \ | bfin \ | cris* \ | csky \ | epiphany \ | ft32 \ | h8300 \ | m68k \ | microblaze | microblazeel \ | nds32 | nds32le | nds32be \ | nios2 | nios2eb | nios2el \ | or1k* \ | or32 \ | sh | sh[1234] | sh[1234]e[lb] \ | tic6x \ | xtensa* ) gl_cv_host_cpu_c_abi_32bit=yes ;; # CPUs that only support a 64-bit ABI. changequote(,)dnl alpha | alphaev[4-8] | alphaev56 | alphapca5[67] | alphaev6[78] \ | mmix ) changequote([,])dnl gl_cv_host_cpu_c_abi_32bit=no ;; changequote(,)dnl i[34567]86 ) changequote([,])dnl gl_cv_host_cpu_c_abi_32bit=yes ;; x86_64 ) # On x86_64 systems, the C compiler may be generating code in one of # these ABIs: # - 64-bit instruction set, 64-bit pointers, 64-bit 'long': x86_64. # - 64-bit instruction set, 64-bit pointers, 32-bit 'long': x86_64 # with native Windows (mingw, MSVC). # - 64-bit instruction set, 32-bit pointers, 32-bit 'long': x86_64-x32. # - 32-bit instruction set, 32-bit pointers, 32-bit 'long': i386. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if (defined __x86_64__ || defined __amd64__ \ || defined _M_X64 || defined _M_AMD64) \ && !(defined __ILP32__ || defined _ILP32) int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=no], [gl_cv_host_cpu_c_abi_32bit=yes]) ;; arm* | aarch64 ) # Assume arm with EABI. # On arm64 systems, the C compiler may be generating code in one of # these ABIs: # - aarch64 instruction set, 64-bit pointers, 64-bit 'long': arm64. # - aarch64 instruction set, 32-bit pointers, 32-bit 'long': arm64-ilp32. # - 32-bit instruction set, 32-bit pointers, 32-bit 'long': arm or armhf. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __aarch64__ && !(defined __ILP32__ || defined _ILP32) int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=no], [gl_cv_host_cpu_c_abi_32bit=yes]) ;; hppa1.0 | hppa1.1 | hppa2.0* | hppa64 ) # On hppa, the C compiler may be generating 32-bit code or 64-bit # code. In the latter case, it defines _LP64 and __LP64__. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#ifdef __LP64__ int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=no], [gl_cv_host_cpu_c_abi_32bit=yes]) ;; ia64* ) # On ia64 on HP-UX, the C compiler may be generating 64-bit code or # 32-bit code. In the latter case, it defines _ILP32. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#ifdef _ILP32 int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=yes], [gl_cv_host_cpu_c_abi_32bit=no]) ;; mips* ) # We should also check for (_MIPS_SZPTR == 64), but gcc keeps this # at 32. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined _MIPS_SZLONG && (_MIPS_SZLONG == 64) int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=no], [gl_cv_host_cpu_c_abi_32bit=yes]) ;; powerpc* ) # Different ABIs are in use on AIX vs. Mac OS X vs. Linux,*BSD. # No need to distinguish them here; the caller may distinguish # them based on the OS. # On powerpc64 systems, the C compiler may still be generating # 32-bit code. And on powerpc-ibm-aix systems, the C compiler may # be generating 64-bit code. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __powerpc64__ || defined _ARCH_PPC64 int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=no], [gl_cv_host_cpu_c_abi_32bit=yes]) ;; rs6000 ) gl_cv_host_cpu_c_abi_32bit=yes ;; riscv32 | riscv64 ) # There are 6 ABIs: ilp32, ilp32f, ilp32d, lp64, lp64f, lp64d. # Size of 'long' and 'void *': AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __LP64__ int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=no], [gl_cv_host_cpu_c_abi_32bit=yes]) ;; s390* ) # On s390x, the C compiler may be generating 64-bit (= s390x) code # or 31-bit (= s390) code. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __LP64__ || defined __s390x__ int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=no], [gl_cv_host_cpu_c_abi_32bit=yes]) ;; sparc | sparc64 ) # UltraSPARCs running Linux have `uname -m` = "sparc64", but the # C compiler still generates 32-bit code. AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __sparcv9 || defined __arch64__ int ok; #else error fail #endif ]])], [gl_cv_host_cpu_c_abi_32bit=no], [gl_cv_host_cpu_c_abi_32bit=yes]) ;; *) gl_cv_host_cpu_c_abi_32bit=unknown ;; esac fi ]) HOST_CPU_C_ABI_32BIT="$gl_cv_host_cpu_c_abi_32bit" ]) # lib-ld.m4 serial 9 dnl Copyright (C) 1996-2003, 2009-2020 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Subroutines of libtool.m4, dnl with replacements s/_*LT_PATH/AC_LIB_PROG/ and s/lt_/acl_/ to avoid dnl collision with libtool.m4. dnl From libtool-2.4. Sets the variable with_gnu_ld to yes or no. AC_DEFUN([AC_LIB_PROG_LD_GNU], [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], [acl_cv_prog_gnu_ld], [# I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 </dev/null` in *GNU* | *'with BFD'*) acl_cv_prog_gnu_ld=yes ;; *) acl_cv_prog_gnu_ld=no ;; esac]) with_gnu_ld=$acl_cv_prog_gnu_ld ]) dnl From libtool-2.4. Sets the variable LD. AC_DEFUN([AC_LIB_PROG_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld [default=no]])], [test "$withval" = no || with_gnu_ld=yes], [with_gnu_ld=no])dnl # Prepare PATH_SEPARATOR. # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then # Determine PATH_SEPARATOR by trying to find /bin/sh in a PATH which # contains only /bin. Note that ksh looks also at the FPATH variable, # so we have to set that as well for the test. PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ || PATH_SEPARATOR=';' } fi if test -n "$LD"; then AC_MSG_CHECKING([for ld]) elif test "$GCC" = yes; then AC_MSG_CHECKING([for ld used by $CC]) elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi if test -n "$LD"; then # Let the user override the test with a path. : else AC_CACHE_VAL([acl_cv_path_LD], [ acl_cv_path_LD= # Final result of this test ac_prog=ld # Program to search in $PATH if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw acl_output=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) acl_output=`($CC -print-prog-name=ld) 2>&5` ;; esac case $acl_output in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld acl_output=`echo "$acl_output" | sed 's%\\\\%/%g'` while echo "$acl_output" | grep "$re_direlt" > /dev/null 2>&1; do acl_output=`echo $acl_output | sed "s%$re_direlt%/%"` done # Got the pathname. No search in PATH is needed. acl_cv_path_LD="$acl_output" ac_prog= ;; "") # If it fails, then pretend we aren't using GCC. ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac fi if test -n "$ac_prog"; then # Search for $ac_prog in $PATH. acl_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$acl_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then acl_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$acl_cv_path_LD" -v 2>&1 </dev/null` in *GNU* | *'with BFD'*) test "$with_gnu_ld" != no && break ;; *) test "$with_gnu_ld" != yes && break ;; esac fi done IFS="$acl_save_ifs" fi case $host in *-*-aix*) AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __powerpc64__ || defined _ARCH_PPC64 int ok; #else error fail #endif ]])], [# The compiler produces 64-bit code. Add option '-b64' so that the # linker groks 64-bit object files. case "$acl_cv_path_LD " in *" -b64 "*) ;; *) acl_cv_path_LD="$acl_cv_path_LD -b64" ;; esac ], []) ;; sparc64-*-netbsd*) AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[#if defined __sparcv9 || defined __arch64__ int ok; #else error fail #endif ]])], [], [# The compiler produces 32-bit code. Add option '-m elf32_sparc' # so that the linker groks 32-bit object files. case "$acl_cv_path_LD " in *" -m elf32_sparc "*) ;; *) acl_cv_path_LD="$acl_cv_path_LD -m elf32_sparc" ;; esac ]) ;; esac ]) LD="$acl_cv_path_LD" fi if test -n "$LD"; then AC_MSG_RESULT([$LD]) else AC_MSG_RESULT([no]) AC_MSG_ERROR([no acceptable ld found in \$PATH]) fi AC_LIB_PROG_LD_GNU ]) # lib-link.m4 serial 31 dnl Copyright (C) 2001-2020 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. AC_PREREQ([2.61]) dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and dnl augments the CPPFLAGS variable. dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) pushdef([Name],[m4_translit([$1],[./+-], [____])]) pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ AC_LIB_LINKFLAGS_BODY([$1], [$2]) ac_cv_lib[]Name[]_libs="$LIB[]NAME" ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" ac_cv_lib[]Name[]_cppflags="$INC[]NAME" ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX" ]) LIB[]NAME="$ac_cv_lib[]Name[]_libs" LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" INC[]NAME="$ac_cv_lib[]Name[]_cppflags" LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) AC_SUBST([LIB]NAME[_PREFIX]) dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the dnl results of this search when this library appears as a dependency. HAVE_LIB[]NAME=yes popdef([NAME]) popdef([Name]) ]) dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode, [missing-message]) dnl searches for libname and the libraries corresponding to explicit and dnl implicit dependencies, together with the specified include files and dnl the ability to compile and link the specified testcode. The missing-message dnl defaults to 'no' and may contain additional hints for the user. dnl If found, it sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} dnl and LTLIB${NAME} variables and augments the CPPFLAGS variable, and dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) pushdef([Name],[m4_translit([$1],[./+-], [____])]) pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME dnl accordingly. AC_LIB_LINKFLAGS_BODY([$1], [$2]) dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, dnl because if the user has installed lib[]Name and not disabled its use dnl via --without-lib[]Name-prefix, he wants to use it. ac_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ ac_save_LIBS="$LIBS" dnl If $LIB[]NAME contains some -l options, add it to the end of LIBS, dnl because these -l options might require -L options that are present in dnl LIBS. -l options benefit only from the -L options listed before it. dnl Otherwise, add it to the front of LIBS, because it may be a static dnl library that depends on another static library that is present in LIBS. dnl Static libraries benefit only from the static libraries listed after dnl it. case " $LIB[]NAME" in *" -l"*) LIBS="$LIBS $LIB[]NAME" ;; *) LIBS="$LIB[]NAME $LIBS" ;; esac AC_LINK_IFELSE( [AC_LANG_PROGRAM([[$3]], [[$4]])], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name='m4_if([$5], [], [no], [[$5]])']) LIBS="$ac_save_LIBS" ]) if test "$ac_cv_lib[]Name" = yes; then HAVE_LIB[]NAME=yes AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the lib][$1 library.]) AC_MSG_CHECKING([how to link with lib[]$1]) AC_MSG_RESULT([$LIB[]NAME]) else HAVE_LIB[]NAME=no dnl If $LIB[]NAME didn't lead to a usable library, we don't need dnl $INC[]NAME either. CPPFLAGS="$ac_save_CPPFLAGS" LIB[]NAME= LTLIB[]NAME= LIB[]NAME[]_PREFIX= fi AC_SUBST([HAVE_LIB]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) AC_SUBST([LIB]NAME[_PREFIX]) popdef([NAME]) popdef([Name]) ]) dnl Determine the platform dependent parameters needed to use rpath: dnl acl_libext, dnl acl_shlibext, dnl acl_libname_spec, dnl acl_library_names_spec, dnl acl_hardcode_libdir_flag_spec, dnl acl_hardcode_libdir_separator, dnl acl_hardcode_direct, dnl acl_hardcode_minus_L. AC_DEFUN([AC_LIB_RPATH], [ dnl Complain if config.rpath is missing. AC_REQUIRE_AUX_FILE([config.rpath]) AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done ]) wl="$acl_cv_wl" acl_libext="$acl_cv_libext" acl_shlibext="$acl_cv_shlibext" acl_libname_spec="$acl_cv_libname_spec" acl_library_names_spec="$acl_cv_library_names_spec" acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" acl_hardcode_direct="$acl_cv_hardcode_direct" acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" dnl Determine whether the user wants rpath handling at all. AC_ARG_ENABLE([rpath], [ --disable-rpath do not hardcode runtime library paths], :, enable_rpath=yes) ]) dnl AC_LIB_FROMPACKAGE(name, package) dnl declares that libname comes from the given package. The configure file dnl will then not have a --with-libname-prefix option but a dnl --with-package-prefix option. Several libraries can come from the same dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar dnl macro call that searches for libname. AC_DEFUN([AC_LIB_FROMPACKAGE], [ pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) define([acl_frompackage_]NAME, [$2]) popdef([NAME]) pushdef([PACK],[$2]) pushdef([PACKUP],[m4_translit(PACK,[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) define([acl_libsinpackage_]PACKUP, m4_ifdef([acl_libsinpackage_]PACKUP, [m4_defn([acl_libsinpackage_]PACKUP)[, ]],)[lib$1]) popdef([PACKUP]) popdef([PACK]) ]) dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_LINKFLAGS_BODY], [ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, lib[$1])]) pushdef([PACKUP],[m4_translit(PACK,[abcdefghijklmnopqrstuvwxyz./+-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, [acl_libsinpackage_]PACKUP, lib[$1])]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" eval additional_libdir2=\"$exec_prefix/$acl_libdirstem2\" eval additional_libdir3=\"$exec_prefix/$acl_libdirstem3\" ]) AC_ARG_WITH(PACK[-prefix], [[ --with-]]PACK[[-prefix[=DIR] search for ]PACKLIBS[ in DIR/include and DIR/lib --without-]]PACK[[-prefix don't search for ]PACKLIBS[ in includedir and libdir]], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" eval additional_libdir2=\"$exec_prefix/$acl_libdirstem2\" eval additional_libdir3=\"$exec_prefix/$acl_libdirstem3\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" additional_libdir2="$withval/$acl_libdirstem2" additional_libdir3="$withval/$acl_libdirstem3" fi fi ]) if test "X$additional_libdir2" = "X$additional_libdir"; then additional_libdir2= fi if test "X$additional_libdir3" = "X$additional_libdir"; then additional_libdir3= fi dnl Search the library and its dependencies in $additional_libdir and dnl $LDFLAGS. Using breadth-first-seach. LIB[]NAME= LTLIB[]NAME= INC[]NAME= LIB[]NAME[]_PREFIX= dnl HAVE_LIB${NAME} is an indicator that LIB${NAME}, LTLIB${NAME} have been dnl computed. So it has to be reset here. HAVE_LIB[]NAME= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='$1 $2' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" dnl See if it was already located by an earlier AC_LIB_LINKFLAGS dnl or AC_LIB_HAVE_LINKFLAGS call. uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./+-|ABCDEFGHIJKLMNOPQRSTUVWXYZ____|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" else dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined dnl that this library doesn't exist. So just drop it. : fi else dnl Search the library lib$name in $additional_libdir and $LDFLAGS dnl and the already constructed $LIBNAME/$LTLIBNAME. found_dir= found_la= found_so= found_a= eval libname=\"$acl_libname_spec\" # typically: libname=lib$name if test -n "$acl_shlibext"; then shrext=".$acl_shlibext" # typically: shrext=.so else shrext= fi if test $use_additional = yes; then for additional_libdir_variable in additional_libdir additional_libdir2 additional_libdir3; do if test "X$found_dir" = "X"; then eval dir=\$$additional_libdir_variable if test -n "$dir"; then dnl The same code as in the loop below: dnl First look for a shared library. if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext" && acl_is_expected_elfclass < "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver" && acl_is_expected_elfclass < "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f" && acl_is_expected_elfclass < "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi dnl Then look for a static library. if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext" && ${AR-ar} -p "$dir/$libname.$acl_libext" | acl_is_expected_elfclass; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi fi fi done fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` dnl First look for a shared library. if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext" && acl_is_expected_elfclass < "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver" && acl_is_expected_elfclass < "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f" && acl_is_expected_elfclass < "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi dnl Then look for a static library. if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext" && ${AR-ar} -p "$dir/$libname.$acl_libext" | acl_is_expected_elfclass; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then dnl Found the library. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then dnl Linking with a shared library. We attempt to hardcode its dnl directory into the executable's runpath, unless it's the dnl standard /usr/lib. if test "$enable_rpath" = no \ || test "X$found_dir" = "X/usr/$acl_libdirstem" \ || test "X$found_dir" = "X/usr/$acl_libdirstem2" \ || test "X$found_dir" = "X/usr/$acl_libdirstem3"; then dnl No hardcoding is needed. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl Use an explicit option to hardcode DIR into the resulting dnl binary. dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi dnl The hardcoding into $LIBNAME is system dependent. if test "$acl_hardcode_direct" = yes; then dnl Using DIR/libNAME.so during linking hardcodes DIR into the dnl resulting binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then dnl Use an explicit option to hardcode DIR into the resulting dnl binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else dnl Rely on "-L$found_dir". dnl But don't add it if it's already contained in the LDFLAGS dnl or the already constructed $LIBNAME haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" fi if test "$acl_hardcode_minus_L" != no; then dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH dnl here, because this doesn't fit in flags passed to the dnl compiler. So give up. No hardcoding. This affects only dnl very old systems. dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then dnl Linking with a static library. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" else dnl We shouldn't come here, but anyway it's good to have a dnl fallback. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" fi fi dnl Assume the include files are nearby. additional_includedir= case "$found_dir" in */$acl_libdirstem | */$acl_libdirstem/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; */$acl_libdirstem2 | */$acl_libdirstem2/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; */$acl_libdirstem3 | */$acl_libdirstem3/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem3/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then dnl Potentially add $additional_includedir to $INCNAME. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's /usr/local/include and we are using GCC on Linux, dnl 3. if it's already present in $CPPFLAGS or the already dnl constructed $INCNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INC[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $INCNAME. INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" fi fi fi fi fi dnl Look for dependencies. if test -n "$found_la"; then dnl Read the .la file. It defines the variables dnl dlname, library_names, old_library, dependency_libs, current, dnl age, revision, installed, dlopen, dlpreopen, libdir. save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" dnl We use only dependency_libs. for dep in $dependency_libs; do case "$dep" in -L*) dependency_libdir=`echo "X$dep" | sed -e 's/^X-L//'` dnl Potentially add $dependency_libdir to $LIBNAME and $LTLIBNAME. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's /usr/local/lib and we are using GCC on Linux, dnl 3. if it's already present in $LDFLAGS or the already dnl constructed $LIBNAME, dnl 4. if it doesn't exist as a directory. if test "X$dependency_libdir" != "X/usr/$acl_libdirstem" \ && test "X$dependency_libdir" != "X/usr/$acl_libdirstem2" \ && test "X$dependency_libdir" != "X/usr/$acl_libdirstem3"; then haveit= if test "X$dependency_libdir" = "X/usr/local/$acl_libdirstem" \ || test "X$dependency_libdir" = "X/usr/local/$acl_libdirstem2" \ || test "X$dependency_libdir" = "X/usr/local/$acl_libdirstem3"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$dependency_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$dependency_libdir"; then dnl Really add $dependency_libdir to $LIBNAME. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$dependency_libdir" fi fi haveit= for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$dependency_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$dependency_libdir"; then dnl Really add $dependency_libdir to $LTLIBNAME. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$dependency_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) dnl Handle this in the next round. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) dnl Handle this in the next round. Throw away the .la's dnl directory; it is already contained in a preceding -L dnl option. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) dnl Most likely an immediate library name. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" ;; esac done fi else dnl Didn't find the library; assume it is in the system directories dnl known to the linker and runtime loader. (All the system dnl directories known to the linker should also be known to the dnl runtime loader, otherwise the system is severely misconfigured.) LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$acl_hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user must dnl pass all path elements in one option. We can arrange that for a dnl single library, but not when more than one $LIBNAMEs are used. alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" done dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" else dnl The -rpath options are cumulative. for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then dnl When using libtool, the option that works for both libraries and dnl executables is -R. The -R options are cumulative. for found_dir in $ltrpathdirs; do LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" done fi popdef([PACKLIBS]) popdef([PACKUP]) popdef([PACK]) popdef([NAME]) ]) dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, dnl unless already present in VAR. dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes dnl contains two or three consecutive elements that belong together. AC_DEFUN([AC_LIB_APPENDTOVAR], [ for element in [$2]; do haveit= for x in $[$1]; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then [$1]="${[$1]}${[$1]:+ }$element" fi done ]) dnl For those cases where a variable contains several -L and -l options dnl referring to unknown libraries and directories, this macro determines the dnl necessary additional linker options for the runtime path. dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) dnl sets LDADDVAR to linker options needed together with LIBSVALUE. dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, dnl otherwise linking without libtool is assumed. AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], [ AC_REQUIRE([AC_LIB_RPATH]) AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) $1= if test "$enable_rpath" != no; then if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then dnl Use an explicit option to hardcode directories into the resulting dnl binary. rpathdirs= next= for opt in $2; do if test -n "$next"; then dir="$next" dnl No need to hardcode the standard /usr/lib. if test "X$dir" != "X/usr/$acl_libdirstem" \ && test "X$dir" != "X/usr/$acl_libdirstem2" \ && test "X$dir" != "X/usr/$acl_libdirstem3"; then rpathdirs="$rpathdirs $dir" fi next= else case $opt in -L) next=yes ;; -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` dnl No need to hardcode the standard /usr/lib. if test "X$dir" != "X/usr/$acl_libdirstem" \ && test "X$dir" != "X/usr/$acl_libdirstem2" \ && test "X$dir" != "X/usr/$acl_libdirstem3"; then rpathdirs="$rpathdirs $dir" fi next= ;; *) next= ;; esac fi done if test "X$rpathdirs" != "X"; then if test -n ""$3""; then dnl libtool is used for linking. Use -R options. for dir in $rpathdirs; do $1="${$1}${$1:+ }-R$dir" done else dnl The linker is used for linking directly. if test -n "$acl_hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user dnl must pass all path elements in one option. alldirs= for dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" done acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" $1="$flag" else dnl The -rpath options are cumulative. for dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" $1="${$1}${$1:+ }$flag" done fi fi fi fi fi AC_SUBST([$1]) ]) # lib-prefix.m4 serial 17 dnl Copyright (C) 2001-2005, 2008-2020 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed dnl to access previously installed libraries. The basic assumption is that dnl a user will want packages to use other packages he previously installed dnl with the same --prefix option. dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate dnl libraries, but is otherwise very convenient. AC_DEFUN([AC_LIB_PREFIX], [ AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_ARG_WITH([lib-prefix], [[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib --without-lib-prefix don't search for libraries in includedir and libdir]], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" fi fi ]) if test $use_additional = yes; then dnl Potentially add $additional_includedir to $CPPFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's already present in $CPPFLAGS, dnl 3. if it's /usr/local/include and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= for x in $CPPFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $CPPFLAGS. CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" fi fi fi fi dnl Potentially add $additional_libdir to $LDFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's already present in $LDFLAGS, dnl 3. if it's /usr/local/lib and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then haveit= for x in $LDFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LDFLAGS. LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" fi fi fi fi fi ]) dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, dnl acl_final_exec_prefix, containing the values to which $prefix and dnl $exec_prefix will expand at the end of the configure script. AC_DEFUN([AC_LIB_PREPARE_PREFIX], [ dnl Unfortunately, prefix and exec_prefix get only finally determined dnl at the end of configure. if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" ]) dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the dnl variables prefix and exec_prefix bound to the values they will have dnl at the end of the configure script. AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], [ acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" $1 exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" ]) dnl AC_LIB_PREPARE_MULTILIB creates dnl - a function acl_is_expected_elfclass, that tests whether standard input dn; has a 32-bit or 64-bit ELF header, depending on the host CPU ABI, dnl - 3 variables acl_libdirstem, acl_libdirstem2, acl_libdirstem3, containing dnl the basename of the libdir to try in turn, either "lib" or "lib64" or dnl "lib/64" or "lib32" or "lib/sparcv9" or "lib/amd64" or similar. AC_DEFUN([AC_LIB_PREPARE_MULTILIB], [ dnl There is no formal standard regarding lib, lib32, and lib64. dnl On most glibc systems, the current practice is that on a system supporting dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. However, on dnl Arch Linux based distributions, it's the opposite: 32-bit libraries go dnl under $prefix/lib32 and 64-bit libraries go under $prefix/lib. dnl We determine the compiler's default mode by looking at the compiler's dnl library search path. If at least one of its elements ends in /lib64 or dnl points to a directory whose absolute pathname ends in /lib64, we use that dnl for 64-bit ABIs. Similarly for 32-bit ABIs. Otherwise we use the default, dnl namely "lib". dnl On Solaris systems, the current practice is that on a system supporting dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib. AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([gl_HOST_CPU_C_ABI_32BIT]) AC_CACHE_CHECK([for ELF binary format], [gl_cv_elf], [AC_EGREP_CPP([Extensible Linking Format], [#ifdef __ELF__ Extensible Linking Format #endif ], [gl_cv_elf=yes], [gl_cv_elf=no]) ]) if test $gl_cv_elf; then # Extract the ELF class of a file (5th byte) in decimal. # Cf. https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header if od -A x < /dev/null >/dev/null 2>/dev/null; then # Use POSIX od. func_elfclass () { od -A n -t d1 -j 4 -N 1 } else # Use BSD hexdump. func_elfclass () { dd bs=1 count=1 skip=4 2>/dev/null | hexdump -e '1/1 "%3d "' echo } fi changequote(,)dnl case $HOST_CPU_C_ABI_32BIT in yes) # 32-bit ABI. acl_is_expected_elfclass () { test "`func_elfclass | sed -e 's/[ ]//g'`" = 1 } ;; no) # 64-bit ABI. acl_is_expected_elfclass () { test "`func_elfclass | sed -e 's/[ ]//g'`" = 2 } ;; *) # Unknown. acl_is_expected_elfclass () { : } ;; esac changequote([,])dnl else acl_is_expected_elfclass () { : } fi dnl Allow the user to override the result by setting acl_cv_libdirstems. AC_CACHE_CHECK([for the common suffixes of directories in the library search path], [acl_cv_libdirstems], [dnl Try 'lib' first, because that's the default for libdir in GNU, see dnl <https://www.gnu.org/prep/standards/html_node/Directory-Variables.html>. acl_libdirstem=lib acl_libdirstem2= acl_libdirstem3= case "$host_os" in solaris*) dnl See Solaris 10 Software Developer Collection > Solaris 64-bit Developer's Guide > The Development Environment dnl <https://docs.oracle.com/cd/E19253-01/816-5138/dev-env/index.html>. dnl "Portable Makefiles should refer to any library directories using the 64 symbolic link." dnl But we want to recognize the sparcv9 or amd64 subdirectory also if the dnl symlink is missing, so we set acl_libdirstem2 too. if test $HOST_CPU_C_ABI_32BIT = no; then acl_libdirstem2=lib/64 case "$host_cpu" in sparc*) acl_libdirstem3=lib/sparcv9 ;; i*86 | x86_64) acl_libdirstem3=lib/amd64 ;; esac fi ;; *) dnl If $CC generates code for a 32-bit ABI, the libraries are dnl surely under $prefix/lib or $prefix/lib32, not $prefix/lib64. dnl Similarly, if $CC generates code for a 64-bit ABI, the libraries dnl are surely under $prefix/lib or $prefix/lib64, not $prefix/lib32. dnl Find the compiler's search path. However, non-system compilers dnl sometimes have odd library search paths. But we can't simply invoke dnl '/usr/bin/gcc -print-search-dirs' because that would not take into dnl account the -m32/-m31 or -m64 options from the $CC or $CFLAGS. searchpath=`(LC_ALL=C $CC $CPPFLAGS $CFLAGS -print-search-dirs) 2>/dev/null \ | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` if test $HOST_CPU_C_ABI_32BIT != no; then # 32-bit or unknown ABI. if test -d /usr/lib32; then acl_libdirstem2=lib32 fi fi if test $HOST_CPU_C_ABI_32BIT != yes; then # 64-bit or unknown ABI. if test -d /usr/lib64; then acl_libdirstem3=lib64 fi fi if test -n "$searchpath"; then acl_save_IFS="${IFS= }"; IFS=":" for searchdir in $searchpath; do if test -d "$searchdir"; then case "$searchdir" in */lib32/ | */lib32 ) acl_libdirstem2=lib32 ;; */lib64/ | */lib64 ) acl_libdirstem3=lib64 ;; */../ | */.. ) # Better ignore directories of this form. They are misleading. ;; *) searchdir=`cd "$searchdir" && pwd` case "$searchdir" in */lib32 ) acl_libdirstem2=lib32 ;; */lib64 ) acl_libdirstem3=lib64 ;; esac ;; esac fi done IFS="$acl_save_IFS" if test $HOST_CPU_C_ABI_32BIT = yes; then # 32-bit ABI. acl_libdirstem3= fi if test $HOST_CPU_C_ABI_32BIT = no; then # 64-bit ABI. acl_libdirstem2= fi fi ;; esac test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" test -n "$acl_libdirstem3" || acl_libdirstem3="$acl_libdirstem" acl_cv_libdirstems="$acl_libdirstem,$acl_libdirstem2,$acl_libdirstem3" ]) dnl Decompose acl_cv_libdirstems into acl_libdirstem, acl_libdirstem2, and dnl acl_libdirstem3. changequote(,)dnl acl_libdirstem=`echo "$acl_cv_libdirstems" | sed -e 's/,.*//'` acl_libdirstem2=`echo "$acl_cv_libdirstems" | sed -e 's/^[^,]*,//' -e 's/,.*//'` acl_libdirstem3=`echo "$acl_cv_libdirstems" | sed -e 's/^[^,]*,[^,]*,//' -e 's/,.*//'` changequote([,])dnl ]) # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>. dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com> dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR # Copyright (C) 2002-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.5], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.5])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE="gmake" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl m4_ifdef([_$0_ALREADY_INIT], [m4_fatal([$0 expanded multiple times ]m4_defn([_$0_ALREADY_INIT]))], [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> # <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi AC_SUBST([CTAGS]) if test -z "$ETAGS"; then ETAGS=etags fi AC_SUBST([ETAGS]) if test -z "$CSCOPE"; then CSCOPE=cscope fi AC_SUBST([CSCOPE]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: <https://www.gnu.org/software/coreutils/>. If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar <conftest.tar]) AM_RUN_LOG([cat conftest.dir/file]) grep GrepMe conftest.dir/file >/dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/rclgrep/������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�14521161751�011524� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/rclgrep/rclgrep.cpp�������������������������������������������������������������������0000644�0001750�0001750�00000043562�14427373216�013626� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2004 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "autoconfig.h" #include <stdio.h> #include <signal.h> #include <errno.h> #include <fnmatch.h> #ifndef _WIN32 #include <sys/time.h> #include <sys/resource.h> #else #include <direct.h> #endif #include "safefcntl.h" #include "safeunistd.h" #include <getopt.h> #include <iostream> #include <sstream> #include <list> #include <string> #include <cstdlib> #include <deque> #include <memory> #include <algorithm> #include "log.h" #include "rclinit.h" #include "rclconfig.h" #include "smallut.h" #include "readfile.h" #include "pathut.h" #include "rclutil.h" #include "cancelcheck.h" #include "execmd.h" #include "internfile.h" #include "rcldoc.h" #include "fstreewalk.h" // Command line options static int op_flags; #define OPT_A 0x1 #define OPT_B 0x2 #define OPT_C 0x4 #define OPT_H 0x8 #define OPT_L 0x10 #define OPT_R 0x20 #define OPT_T 0x40 #define OPT_V 0x80 #define OPT_Z 0x100 #define OPT_a 0x200 #define OPT_c 0x400 #define OPT_e 0x800 #define OPT_f 0x1000 #define OPT_h 0x2000 #define OPT_i 0x4000 #define OPT_l 0x8000 #define OPT_n 0x10000 #define OPT_p 0x20000 #define OPT_q 0x40000 #define OPT_r 0x80000 #define OPT_s 0x100000 #define OPT_u 0x200000 #define OPT_v 0x400000 #define OPT_w 0x800000 #define OPT_x 0x1000000 enum OptVal {OPTVAL_RECOLL_CONFIG=1000, OPTVAL_HELP, OPTVAL_INCLUDE, OPTVAL_EXCLUDE, OPTVAL_EXCLUDEFROM}; static struct option long_options[] = { {"regexp", required_argument, 0, 'e'}, {"file", required_argument, 0, 'f'}, {"invert-match", required_argument, 0, 'v'}, {"word-regexp", 0, 0, 'w'}, {"line-regexp", 0, 0, 'x'}, {"config", required_argument, 0, OPTVAL_RECOLL_CONFIG}, {"count", 0, 0, 'c'}, {"files-without-match", 0, 0, 'L'}, {"files-with-match", 0, 0, 'l'}, {"with-filename", 0, 0, 'H'}, {"no-filename", 0, 0, 'h'}, {"line-number", 0, 0, 'n'}, {"help", 0, 0, OPTVAL_HELP}, {"after-context", required_argument, 0, 'A'}, {"before-context", required_argument, 0, 'B'}, {"context", required_argument, 0, 'C'}, {"recurse", 0, 0, 'r'}, {"include", required_argument, 0, OPTVAL_INCLUDE}, {"version", 0, 0, 'V'}, {"word-regexp", 0, 0, 'w'}, {"silent", 0, 0, 'q'}, {"quiet", 0, 0, 'q'}, {"no-messages", 0, 0, 's'}, {"dereference-recursive", 0, 0, 'R'}, {"text", 0, 0, 'a'}, //not implemented {"exclude", required_argument, 0, OPTVAL_EXCLUDE}, {"exclude-from", required_argument, 0, OPTVAL_EXCLUDEFROM}, {0, 0, 0, 0} }; std::vector<SimpleRegexp *> g_expressions; int g_reflags = SimpleRegexp::SRE_NOSUB; static RclConfig *config; namespace Rcl { // This is needed to satisfy a reference from one of the recoll files. std::string version_string() { return std::string("rclgrep ") + std::string(PACKAGE_VERSION); } } // Working directory before we change: it's simpler to change early // but some options need the original for computing absolute paths. static std::string orig_cwd; static std::string current_topdir; // Context management. We need to store the last seen lines in case we need to print them as // context. static int beforecontext; // Lines of context before match static int aftercontext; // Lines of context after match // Store line for before-context static void dequeshift(std::deque<std::string>& q, int n, const std::string& ln) { if (n <= 0) return; if (q.size() >= unsigned(n)) { q.pop_front(); } q.push_back(ln); } // Show context lines before the match static void dequeprint(std::deque<std::string>& q) { for (const auto& ln : q) { std::cout << ln; } } void grepit(std::istream& instream, const Rcl::Doc& doc) { std::string ppath; std::string indic; std::string nindic; if (op_flags & OPT_H) { ppath = fileurltolocalpath(doc.url); if (path_isabsolute(ppath) && ppath.size() > current_topdir.size()) { ppath = ppath.substr(current_topdir.size()); } ppath += ":"; ppath += doc.ipath; indic = "::"; nindic = "--"; } int matchcount = 0; std::deque<std::string> beflines; int lnum = 0; int idx; std::string ln; bool inmatch{false}; int aftercount = 0; for (;;) { std::string line; getline(instream, line, '\n'); if (!instream.good()) { break; } idx = lnum; ++lnum; if ((op_flags & OPT_n) && !(op_flags & OPT_c)) { ln = ulltodecstr(lnum) + ":"; } bool ismatch = false; for (const auto e_p : g_expressions) { auto match = e_p->simpleMatch(line); if (((op_flags & OPT_v) ^ match)) { ismatch = true; break; } } if (ismatch) { if (op_flags&OPT_q) { exit(0); } // We have a winner line. if (op_flags & OPT_c) { matchcount++; } else { // Stop printing after-context aftercount = 0; // If this is the beginning/first matching line, possibly output the before context if (!inmatch && beforecontext) { dequeprint(beflines); } inmatch=true; std::cout << ppath << indic << ln << line << "\n"; if (beforecontext && !beflines.empty()) { beflines.pop_front(); } } } else { if (op_flags&OPT_q) { continue; } // Non-matching line. if (inmatch && aftercontext && !(op_flags&OPT_c)) { aftercount = aftercontext; } inmatch = false; if (aftercount) { std::cout << ppath << nindic << ln << line << "\n"; aftercount--; if (aftercount == 0) std::cout << "--\n"; } else if (beforecontext) { dequeshift(beflines, beforecontext, ppath + nindic + ln + line + "\n"); } } } if (op_flags & OPT_L) { if (matchcount == 0) { std::cout << ppath << "\n"; } } else if (op_flags & OPT_l) { if (matchcount) { std::cout << ppath << "\n"; } } else if ((op_flags & OPT_c) && matchcount) { std::cout << ppath << "::" << matchcount << "\n"; } } bool processpath(RclConfig *config, const std::string& path) { // LOGINF("processpath: [" << path << "]\n"); // std::cerr << "processpath: [" << path << "]\n"; if (path.empty()) { // stdin Rcl::Doc doc; doc.url = std::string("file://") + "(standard input)"; grepit(std::cin, doc); } else { struct PathStat st; if (path_fileprops(path, &st, false) < 0) { std::cerr << path << " : "; perror("stat"); return false; } config->setKeyDir(path_getfather(path)); std::string mimetype; FileInterner interner(path, st, config, FileInterner::FIF_none); if (!interner.ok()) { return false; } mimetype = interner.getMimetype(); FileInterner::Status fis = FileInterner::FIAgain; bool hadNonNullIpath = false; Rcl::Doc doc; while (fis == FileInterner::FIAgain) { doc.erase(); try { fis = interner.internfile(doc); } catch (CancelExcept) { LOGERR("fsIndexer::processone: interrupted\n"); return false; } if (fis == FileInterner::FIError) { return false; } if (doc.url.empty()) doc.url = path_pathtofileurl(path); std::istringstream str(doc.text); grepit(str, doc); } } return true; } class WalkerCB : public FsTreeWalkerCB { public: WalkerCB(const std::vector<std::string>& selpats, RclConfig *config) : m_pats(selpats), m_config(config) {} virtual FsTreeWalker::Status processone( const std::string& fn, FsTreeWalker::CbFlag flg, const struct PathStat&) override { if (flg == FsTreeWalker::FtwRegular) { if (m_pats.empty()) { processpath(m_config, fn); } else { for (const auto& pat : m_pats) { if (fnmatch(pat.c_str(), fn.c_str(), 0) == 0) { processpath(m_config, fn); break; } } } } return FsTreeWalker::FtwOk; } const std::vector<std::string>& m_pats; RclConfig *m_config{nullptr}; }; bool recursive_grep(RclConfig *config, const std::string& top, const std::vector<std::string>& selpats,const std::vector<std::string>& exclpats) { // std::cerr << "recursive_grep: top : [" << top << "]\n"; WalkerCB cb(selpats, config); int opts = FsTreeWalker::FtwTravNatural; if (op_flags & OPT_R) { opts |= FsTreeWalker::FtwFollow; } FsTreeWalker walker(opts); walker.setSkippedNames(exclpats); current_topdir = top; if (path_isdir(top)) { path_catslash(current_topdir); } walker.walk(top, cb); return true; } bool processpaths(RclConfig *config, const std::vector<std::string> &_paths, const std::vector<std::string>& selpats, const std::vector<std::string>& exclpats) { if (_paths.empty()) return true; std::vector<std::string> paths; std::string origcwd = config->getOrigCwd(); for (const auto& path : _paths) { paths.push_back(path_canon(path, &origcwd)); } std::sort(paths.begin(), paths.end()); auto uit = std::unique(paths.begin(), paths.end()); paths.resize(uit - paths.begin()); for (const auto& path : paths) { LOGDEB("processpaths: " << path << "\n"); if (path_isdir(path)) { recursive_grep(config, path, selpats, exclpats); } else { if (!path_readable(path)) { if (!(op_flags & OPT_s)) { std::cerr << "Can't read: " << path << "\n"; } continue; } processpath(config, path); } } return true; } std::string make_config() { std::string confdir; const char *cp = nullptr; cp = getenv("XDG_CONFIG_HOME"); if (nullptr != cp) { confdir = cp; } else { confdir = path_cat(path_home(), ".config"); } confdir = path_cat(confdir, "rclgrep"); if (!path_makepath(confdir, 0755)) { std::cerr << "Could not create configuration directory " << confdir << " : "; perror(""); exit(1); } std::string mainconf = path_cat(confdir, "recoll.conf"); if (!path_exists(mainconf)) { FILE *fp = fopen(mainconf.c_str(), "w"); if (nullptr == fp) { std::cerr << "Could not create configuration file " << mainconf << " : "; perror(""); exit(1); } fprintf(fp, "# rclgrep default configuration. Will only be created by the program if it\n"); fprintf(fp, "# does not exist, you can add your own customisations in here\n"); fprintf(fp, "helperlogfilename = /dev/null\n"); fprintf(fp, "textunknownasplain = 1\n"); fprintf(fp, "loglevel = 1\n"); fclose(fp); } return confdir; } std::string thisprog; static const char usage [] = "\n" "rclgrep [--help] \n" " Print help\n" "rclgrep [-f] [<path [path ...]>]\n" " Search files.\n" " --config <configdir> : specify configuration directory (default ~/.config/rclgrep)\n" " -e PATTERNS, --regexp=PATTERNS patterns to search for. Can be given multiple times\n" ; static void Usage(FILE* fp = stdout) { fprintf(fp, "%s: Usage: %s", path_getsimple(thisprog).c_str(), usage); exit(1); } static void add_expressions(const std::string& exps) { std::vector<std::string> vexps; stringToTokens(exps, vexps, "\n"); for (const auto& pattern : vexps) { if (op_flags & OPT_x) { auto newpat = std::string("^(") + pattern + ")$"; g_expressions.push_back(new SimpleRegexp(newpat, g_reflags)); } else if (op_flags & OPT_w) { auto newpat = std::string("(^|[^a-zA-Z0-9_])(") + pattern + ")([^a-zA-Z0-9_]|$)"; g_expressions.push_back(new SimpleRegexp(newpat, g_reflags)); } else { g_expressions.push_back(new SimpleRegexp(pattern, g_reflags)); } } } std::vector<std::string> g_expstrings; static void buildexps() { for (const auto& s : g_expstrings) add_expressions(s); } static void exps_from_file(const std::string& fn) { std::string data; std::string reason; if (!file_to_string(fn, data, -1, -1, &reason)) { std::cerr << "Could not read " << fn << " : " << reason << "\n"; exit(1); } g_expstrings.push_back(data); } std::vector<std::string> g_excludestrings; static void excludes_from_file(const std::string& fn) { std::string data; std::string reason; if (!file_to_string(fn, data, -1, -1, &reason)) { std::cerr << "Could not read " << fn << " : " << reason << "\n"; exit(1); } g_excludestrings.push_back(data); } static std::vector<std::string> buildexcludes() { std::vector<std::string> ret; for (const auto& lst : g_excludestrings) { std::vector<std::string> v; stringToTokens(lst, v, "\n"); for (const auto& s : v) { ret.push_back(s); } } return ret; } int main(int argc, char *argv[]) { int ret; std::string a_config; std::vector<std::string> selpatterns; while ((ret = getopt_long(argc, argv, "A:B:C:HLRVce:f:hilnp:qrsvwx", long_options, NULL)) != -1) { switch (ret) { case 'A': op_flags |= OPT_A; aftercontext = atoi(optarg); break; case 'B': op_flags |= OPT_B; beforecontext = atoi(optarg); break; case 'C': op_flags |= OPT_C; aftercontext = beforecontext = atoi(optarg); break; case 'H': op_flags |= OPT_H; break; case 'L': op_flags |= OPT_L|OPT_c; break; case 'R': op_flags |= OPT_R; break; case 'V': std::cout << Rcl::version_string() << "\n"; return 0; case 'c': op_flags |= OPT_c; break; case 'e': op_flags |= OPT_e; g_expstrings.push_back(optarg); break; case 'f': op_flags |= OPT_f; exps_from_file(optarg);break; case 'h': op_flags |= OPT_h; break; case 'i': op_flags |= OPT_i; g_reflags |= SimpleRegexp::SRE_ICASE; break; case 'l': op_flags |= OPT_l|OPT_c; break; case 'n': op_flags |= OPT_n; break; case 'p': op_flags |= OPT_p; selpatterns.push_back(optarg); break; case 'q': op_flags |= OPT_q; break; case 'r': op_flags |= OPT_r|OPT_H; break; case 's': op_flags |= OPT_s; break; case 'v': op_flags |= OPT_v; break; case 'w': op_flags |= OPT_w; break; case 'x': op_flags |= OPT_x; break; case OPTVAL_RECOLL_CONFIG: a_config = optarg; break; case OPTVAL_HELP: Usage(stdout); break; case OPTVAL_INCLUDE: selpatterns.push_back(optarg); break; case OPTVAL_EXCLUDE: g_excludestrings.push_back(optarg); break; case OPTVAL_EXCLUDEFROM: excludes_from_file(optarg); break; default: Usage(); break; } } int aremain = argc - optind; if (!(op_flags & (OPT_e|OPT_f))) { if (aremain == 0) Usage(); std::string patterns = argv[optind++]; aremain--; g_expstrings.push_back(patterns); } buildexps(); // If there are more than 1 file args and -h was not used, we want to print file names. if ((aremain > 1 || (aremain == 1 && path_isdir(argv[optind]))) && !(op_flags & OPT_h)) { op_flags |= OPT_H; } if (a_config.empty()) { a_config = make_config(); } std::string reason; int flags = 0; config = recollinit(flags, nullptr, nullptr, reason, &a_config); if (config == 0 || !config->ok()) { std::cerr << "Configuration problem: " << reason << "\n"; exit(1); } // Get rid of log messages Logger::getTheLog()->setLogLevel(Logger::LLFAT); orig_cwd = path_cwd(); std::string rundir; config->getConfParam("idxrundir", rundir); if (!rundir.empty()) { if (!rundir.compare("tmp")) { rundir = tmplocation(); } LOGDEB2("rclgrep: changing current directory to [" << rundir << "]\n"); if (!path_chdir(rundir)) { LOGSYSERR("main", "chdir", rundir); } } std::vector<std::string> paths; if (aremain == 0 && !(op_flags & OPT_r)) { // Read from stdin processpath(config, std::string()); } else { if (aremain == 0) { paths.push_back("."); } else { while (aremain--) { paths.push_back(argv[optind++]); } } } auto excludes = buildexcludes(); bool status = processpaths(config, paths, selpatterns, excludes); if (op_flags&OPT_q) { exit(1); } return status ? 0 : 1; } ����������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/rclgrep/Makefile.am�������������������������������������������������������������������0000644�0001750�0001750�00000005143�14427373216�013511� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������CXXFLAGS ?= @CXXFLAGS@ XSLT_CFLAGS=@XSLT_CFLAGS@ XSLT_LINKADD=@XSLT_LINKADD@ LIBICONV=@LIBICONV@ INCICONV=@INCICONV@ DEFS=@DEFS@ COMMONCPPFLAGS = -I. \ -I$(top_srcdir)/bincimapmime \ -I$(top_srcdir)/common \ -I$(top_srcdir)/index \ -I$(top_srcdir)/internfile \ -I$(top_srcdir)/query \ -I$(top_srcdir)/rcldb \ -I$(top_srcdir)/unac \ -I$(top_srcdir)/utils \ -I$(top_srcdir)/xaposix \ -DBUILDING_RECOLL AM_CPPFLAGS = -Wall -Wno-unused -std=c++17 \ $(COMMONCPPFLAGS) \ $(INCICONV) \ $(XSLT_CFLAGS) \ -DRECOLL_DATADIR=\"${pkgdatadir}\" \ -DREADFILE_ENABLE_ZLIB -DREADFILE_ENABLE_MINIZ -DREADFILE_ENABLE_MD5 \ -D_GNU_SOURCE \ -DENABLE_LIBMAGIC \ $(DEFS) bin_PROGRAMS = rclgrep rclgrep_SOURCES = \ rclgrep.cpp \ ../bincimapmime/convert.cc \ ../bincimapmime/mime-parsefull.cc \ ../bincimapmime/mime-parseonlyheader.cc \ ../bincimapmime/mime-printbody.cc \ ../bincimapmime/mime.cc \ ../common/cstr.cpp \ ../common/rclconfig.cpp \ ../common/rclinit.cpp \ ../common/textsplit.cpp \ ../common/textsplitko.cpp \ ../common/webstore.cpp \ ../index/exefetcher.cpp \ ../index/fetcher.cpp \ ../index/fsfetcher.cpp \ ../index/idxdiags.cpp \ ../index/mimetype.cpp \ ../index/webqueuefetcher.cpp \ ../internfile/extrameta.cpp \ ../internfile/htmlparse.cpp \ ../internfile/internfile.cpp \ ../internfile/mh_exec.cpp \ ../internfile/mh_execm.cpp \ ../internfile/mh_html.cpp \ ../internfile/mh_mail.cpp \ ../internfile/mh_mbox.cpp \ ../internfile/mh_text.cpp \ ../internfile/mh_xslt.cpp \ ../internfile/mimehandler.cpp \ ../internfile/myhtmlparse.cpp \ ../internfile/txtdcode.cpp \ ../internfile/uncomp.cpp \ ../rcldb/rcldoc.cpp \ ../unac/unac.cpp \ ../utils/base64.cpp \ ../utils/cancelcheck.cpp \ ../utils/chrono.cpp \ ../utils/circache.cpp \ ../utils/closefrom.cpp \ ../utils/cmdtalk.cpp \ ../utils/conftree.cpp \ ../utils/copyfile.cpp \ ../utils/cpuconf.cpp \ ../utils/execmd.cpp \ ../utils/fileudi.cpp \ ../utils/fstreewalk.cpp \ ../utils/idfile.cpp \ ../utils/log.cpp \ ../utils/md5.cpp \ ../utils/md5ut.cpp \ ../utils/mimeparse.cpp \ ../utils/miniz.cpp \ ../utils/netcon.cpp \ ../utils/pathut.cpp \ ../utils/pxattr.cpp \ ../utils/rclutil.cpp \ ../utils/readfile.cpp \ ../utils/smallut.cpp \ ../utils/transcode.cpp \ ../utils/utf8iter.cpp \ ../utils/wipedir.cpp \ ../utils/zlibut.cpp rclgrep_LDADD = $(XSLT_LIBS) $(LIBICONV) -lmagic $(LIBTHREADS) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/README��������������������������������������������������������������������������������0000644�0001750�0001750�00001040041�14446761742�010702� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Recoll user manual Jean-Francois Dockes <jfd@recoll.org> Copyright © 2005-2023 Jean-Francois Dockes Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at the following location: GNU web site. This document introduces full text search notions and describes the installation and use of the Recoll application. This version describes Recoll 1.35. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Table of Contents 1. Introduction 1.1. Giving it a try 1.2. Full text search 1.3. Recoll overview 2. Indexing 2.1. Introduction 2.1.1. Indexing modes 2.1.2. Configurations, multiple indexes 2.1.3. Document types 2.1.4. Indexing failures 2.1.5. Recovery 2.2. Index storage 2.2.1. Xapian index formats 2.2.2. Security aspects 2.2.3. Special considerations for big indexes 2.3. Index configuration 2.3.1. The index configuration GUI 2.3.2. Multiple indexes 2.3.3. Index case and diacritics sensitivity 2.3.4. Indexing threads configuration (Unix-like systems) 2.4. Index update scheduling 2.4.1. Periodic indexing 2.4.2. Real time indexing 2.5. Miscellaneous indexing notes 2.5.1. The PDF input handler 2.5.2. Running OCR on image documents 2.5.3. Running a speech to text program on audio files 2.5.4. Removable volumes 2.5.5. Unix-like systems: indexing visited Web pages 2.5.6. Unix-like systems: using extended attributes 2.5.7. Unix-like systems: importing external tags 3. Searching 3.1. Introduction 3.2. Searching with the Qt graphical user interface 3.2.1. Simple search 3.2.2. The result list 3.2.3. The result table 3.2.4. The filters panel 3.2.5. Unix-like systems: running arbitrary commands on result files 3.2.6. Unix-like systems: displaying thumbnails 3.2.7. The preview window 3.2.8. The Query Fragments window 3.2.9. Assisted Complex Search (A.K.A. "Advanced Search") 3.2.10. The term explorer tool 3.2.11. Multiple indexes 3.2.12. Document history 3.2.13. Sorting search results and collapsing duplicates 3.2.14. Keyboard shortcuts 3.2.15. Search tips 3.2.16. Saving and restoring queries 3.2.17. Customizing the search interface 3.3. Searching with the KDE KIO slave 3.4. Searching on the command line 3.5. The query language 3.5.1. General syntax 3.5.2. Special field-like specifiers 3.5.3. Range clauses 3.5.4. Modifiers 3.6. Wildcards and anchored searches 3.6.1. Wildcards 3.6.2. Anchored searches 3.7. Using Synonyms (1.22) 3.8. Path translations 3.9. Search case and diacritics sensitivity 3.10. Desktop integration 3.10.1. Hotkeying recoll 3.10.2. The KDE Kicker Recoll applet 4. Programming interface 4.1. Writing a document input handler 4.1.1. Simple input handlers 4.1.2. "Multiple" handlers 4.1.3. Telling Recoll about the handler 4.1.4. Input handler output 4.1.5. Page numbers 4.2. Field data processing 4.3. Python API 4.3.1. Introduction 4.3.2. Interface elements 4.3.3. Log messages for Python scripts 4.3.4. Python search interface 4.3.5. Creating Python external indexers 5. Installation and configuration 5.1. Installing a binary copy 5.2. Supporting packages 5.3. Building from source 5.3.1. Prerequisites 5.3.2. Building 5.3.3. Installing 5.3.4. Python API package 5.4. Configuration overview 5.4.1. Environment variables 5.4.2. Recoll main configuration file, recoll.conf 5.4.3. The fields file 5.4.4. The mimemap file 5.4.5. The mimeconf file 5.4.6. The mimeview file 5.4.7. The ptrans file 5.4.8. Examples of configuration adjustments List of Tables 3.1. Keyboard shortcuts Chapter 1. Introduction This document introduces full text search notions and describes the installation and use of the Recoll application. It is updated for Recoll 1.35. Recoll was for a long time dedicated to Unix-like systems. It was only lately (2015) ported to MS-Windows. Many references in this manual, especially file locations, are specific to Unix, and not valid on Windows, where some described features are also not available. The manual will be progressively updated. Until this happens, on Windows, most references to shared files can be translated by looking under the Recoll installation directory (Typically C:/ Program Files (x86)/Recoll). Especially, anything referenced inside /usr/share in this document will be found in the Share subdirectory of the installation). The user configuration is stored by default under AppData/Local/Recoll inside the user directory, along with the index itself. 1.1. Giving it a try If you do not like reading manuals (who does?) but wish to give Recoll a try, just install the application and start the recoll graphical user interface (GUI), which will ask permission to index your home directory, allowing you to search immediately after indexing completes. Do not do this if your home directory contains a huge number of documents and you do not want to wait or are very short on disk space. In this case, you may first want to customize the configuration to restrict the indexed area. From the recoll GUI go to: Preferences → Indexing configuration, then adjust the Top directories section, which defines the directories from which the filesystem exploration starts. On Unix-like systems, you may need to install the appropriate supporting applications for document types that need them (for example antiword for Microsoft Word files). The Windows package is self-contained and includes most useful auxiliary programs. 1.2. Full text search Recoll is a full text search application, which means that it finds your data by content rather than by external attributes (like the file name). You specify words (terms) which should or should not appear in the text you are looking for, and receive in return a list of matching documents, ordered so that the most relevant documents will appear first. You do not need to remember in what file or email message you stored a given piece of information. You just ask for related terms, and the tool will return a list of documents where these terms are prominent, in a similar way to Internet search engines. Full text search applications try to determine which documents are most relevant to the search terms you provide. Computer algorithms for determining relevance can be very complex, and in general are inferior to the power of the human mind to rapidly determine relevance. The quality of relevance guessing is probably the most important aspect when evaluating a search application. Recoll relies on the Xapian probabilistic information retrieval library to determine relevance. In many cases, you are looking for all the forms of a word, including plurals, different tenses for a verb, or terms derived from the same root or stem (example: floor, floors, floored, flooring...). Queries are usually automatically expanded to all such related terms (words that reduce to the same stem). This can be prevented for searching for a specific form. Stemming, by itself, does not accommodate for misspellings or phonetic searches. A full text search application may also support this form of approximation. For example, a search for aliterattion returning no result might propose alliteration, alteration, alterations, or altercation as possible replacement terms. Recoll bases its suggestions on the actual index contents, so that suggestions may be made for words which would not appear in a standard dictionary. 1.3. Recoll overview Recoll uses the Xapian information retrieval library as its storage and retrieval engine. Xapian is a very mature package using a sophisticated probabilistic ranking model. The Xapian library manages an index database which describes where terms appear in your document files. It efficiently processes the complex queries which are produced by the Recoll query expansion mechanism, and is in charge of the all-important relevance computation task. Recoll provides the mechanisms and interface to get data into and out of the index. This includes translating the many possible document formats into pure text, handling term variations (using Xapian stemmers), and spelling approximations (using the aspell speller), interpreting user queries and presenting results. In a shorter way, Recoll does the dirty footwork, Xapian deals with the intelligent parts of the process. The Xapian index can be big (roughly the size of the original document set), but it is not a document archive. Recoll can only display documents that still exist at the place from which they were indexed. Recoll stores all internal data in Unicode UTF-8 format, and it can index many types of files with different character sets, encodings, and languages into the same index. It can process documents embedded inside other documents (for example a PDF document stored inside a Zip archive sent as an email attachment...), down to an arbitrary depth. Stemming is the process by which Recoll reduces words to their radicals so that searching does not depend, for example, on a word being singular or plural (floor, floors), or on a verb tense (flooring, floored). Because the mechanisms used for stemming depend on the specific grammatical rules for each language, there is a separate Xapian stemmer module for most common languages where stemming makes sense. Recoll stores the unstemmed versions of terms in the main index and uses auxiliary databases for term expansion (one for each stemming language), which means that you can switch stemming languages between searches, or add a language without needing a full reindex. Storing documents written in different languages in the same index is possible, and commonly done. In this situation, you can specify several stemming languages for the index. Recoll currently makes no attempt at automatic language recognition, which means that the stemmer will sometimes be applied to terms from other languages with potentially strange results. In practise, even if this introduces possibilities of confusion, this approach has been proven quite useful, and it is much less cumbersome than separating your documents according to what language they are written in. By default, Recoll strips most accents and diacritics from terms, and converts them to lower case before either storing them in the index or searching for them. As a consequence, it is impossible to search for a particular capitalization of a term (US / us), or to discriminate two terms based on diacritics (sake / saké, mate / maté). Recoll can optionally store the raw terms, without accent stripping or case conversion. In this configuration, default searches will behave as before, but it is possible to perform searches sensitive to case and diacritics. This is described in more detail in the section about index case and diacritics sensitivity. Recoll uses many parameters to define exactly what to index, and how to classify and decode the source documents. These are kept in configuration files . A default configuration is copied into a standard location (usually something like /usr/share/recoll/examples) during installation. The default values set by the configuration files in this directory may be overridden by values set inside your personal configuration. With the default configuration, Recoll will index your home directory with generic parameters. Most common parameters can be set by using configuration menus in the recoll GUI. Some less common parameters can only be set by editing the text files. The indexing process is started automatically (after asking permission), the first time you execute the recoll GUI. Indexing can also be performed by executing the recollindex command. Recoll indexing is multithreaded by default when appropriate hardware resources are available, and can perform multiple tasks in parallel for text extraction, segmentation and index updates. Searches are usually performed inside the recoll GUI, which has many options to help you find what you are looking for. However, there are other ways to query the index: • A command line interface. • A Recoll WebUI. • A Gnome Shell Search Provider . • A Python programming interface • A KDE KIO slave module. • A Ubuntu Unity Scope module. Chapter 2. Indexing 2.1. Introduction Indexing is the process by which the set of documents is analyzed and the data entered into the database. Recoll indexing is normally incremental: documents will only be processed if they have been modified since the last run. On the first execution, all documents will need processing. A full index build can be forced later by specifying an option to the indexing command (recollindex -z or -Z). recollindex skips files which caused an error during a previous pass. This is a performance optimization, and the command line option -k can be set to retry failed files, for example after updating an input handler. The following sections give an overview of different aspects of the indexing processes and configuration, with links to detailed sections. Depending on your data, temporary files may be needed during indexing, some of them possibly quite big. You can use the RECOLL_TMPDIR or TMPDIR environment variables to determine where they are created (the default is to use /tmp). Using TMPDIR has the nice property that it may also be taken into account by auxiliary commands executed by recollindex. 2.1.1. Indexing modes Recoll indexing can be performed along two main modes: • Periodic (or batch) indexing . recollindex is executed at discrete times. On Unix-like systems, the typical usage is to have a nightly run programmed into your cron file. On Windows, the Task Scheduler can be used to run indexing. In both cases, the Recoll GUI includes a simplified interface to configure the system scheduler. • Real time indexing . recollindex runs permanently as a daemon and uses a file system alteration monitor (e.g. inotify on Unix-like systems) to detect file changes. New or updated files are indexed at once. Monitoring a big file system tree can consume significant system resources. Choosing an indexing mode The choice between the two methods is mostly a matter of preference, and they can be combined by setting up multiple indexes (e.g.: use periodic indexing on a big documentation directory, and real time indexing on a small home directory), or by configuring the index so that only a subset of the tree will be monitored. The choice of method and the parameters used can be configured from the recoll GUI: Preferences → Indexing schedule dialog. 2.1.2. Configurations, multiple indexes Recoll supports defining multiple indexes, each defined by its own configuration directory. A configuration directory contains several files which describe what should be indexed and how. When recoll or recollindex is first executed, it creates a default configuration directory. This configuration is the one used for indexing and querying when no specific configuration is specified. It is located in $HOME /.recoll/ for Unix-like systems and %LOCALAPPDATA%/Recoll on Windows (typically C:/Users/[me]/Appdata/Local/Recoll). All configuration parameters have defaults, defined in system-wide files. Without further customisation, the default configuration will process your complete home directory, with a reasonable set of defaults. It can be adjusted to process a different area of the file system, select files in different ways, and many other things. In some cases, it may be useful to create additional configuration directories, for example, to separate personal and shared indexes, or to take advantage of the organization of your data to improve search precision. In order to do this, you would create an empty directory in a location of your choice, and then instruct recoll or recollindex to use it by setting either a command line option (-c /some/directory), or an environment variable (RECOLL_CONFDIR=/some/directory). Any modification performed by the commands (e.g. configuration customisation or searches by recoll or index creation by recollindex) would then apply to the new directory and not to the default one. Once multiple indexes are created, you can use each of them separately by setting the -c option or the RECOLL_CONFDIR environment variable when starting a command, to select the desired index. It is also possible to instruct one configuration to query one or several other indexes in addition to its own, by using the External index function in the recoll GUI, or some equivalent in the command line and programming tools. A plausible usage scenario for the multiple index feature would be for a system administrator to set up a central index for shared data, that you choose to search or not in addition to your personal data. Of course, there are other possibilities. for example, there are many cases where you know the subset of files that should be searched, and where narrowing the search can improve the results. You can achieve approximately the same effect by using a directory filter clause in a search, but multiple indexes may have better performance and may be worth the trouble in some cases. A more advanced use case would be to use multiple indexes to improve indexing performance, by updating several indexes in parallel (using multiple CPU cores and disks, or possibly several machines), and then merging them, or querying them in parallel. See the section about configuring multiple indexes for more detail 2.1.3. Document types Recoll knows about quite a few different document types. The parameters for document types recognition and processing are set in configuration files. Most file types, like HTML or word processing files, only hold one document. Some file types, like email folders or zip archives, can hold many individually indexed documents, which may themselves be compound ones. Such hierarchies can go quite deep, and Recoll can process, for example, a LibreOffice document stored as an attachment to an email message inside an email folder archived in a zip file... recollindex processes plain text, HTML, OpenDocument (Open/LibreOffice), email formats, and a few others internally. Other file types (e.g.: postscript, pdf, ms-word, rtf ...) need external applications for preprocessing. The list is in the installation section. After every indexing operation, Recoll updates a list of commands that would be needed for indexing existing files types. This list can be displayed by selecting the menu option File → Show Missing Helpers in the recoll GUI. It is stored in the missing text file inside the configuration directory. After installing a missing handler, you may need to tell recollindex to retry the failed files, by adding option -k to the command line, or by using the GUI File → Special indexing menu. This is because recollindex, in its default operation mode, will not retry files which caused an error during an earlier pass. In special cases, it may be useful to reset the data for a category of files before indexing. See the recollindex manual page. If your index is not too big, it may be simpler to just reset it. By default, Recoll will try to index any file type that it has a way to read. This is sometimes not desirable, and there are ways to either exclude some types, or on the contrary define a positive list of types to be indexed. In the latter case, any type not in the list will be ignored. Excluding files by name can be done by adding wildcard name patterns to the skippedNames list, which can be done from the GUI Index configuration menu. Excluding by type can be done by setting the excludedmimetypes list in the configuration file. This can be redefined for subdirectories. You can also define an exclusive list of MIME types to be indexed (no others will be indexed), by setting the indexedmimetypes configuration variable. Example: indexedmimetypes = text/html application/pdf It is possible to redefine this parameter for subdirectories. Example: [/path/to/my/dir] indexedmimetypes = application/pdf (When using sections like this, don't forget that they remain in effect until the end of the file or another section indicator). excludedmimetypes or indexedmimetypes, can be set either by editing the configuration file (recoll.conf) for the index, or by using the GUI index configuration tool. Note about MIME types When editing the indexedmimetypes or excludedmimetypes lists, you should use the MIME values listed in the mimemap file or in Recoll result lists in preference to file -i output: there are a number of differences. The file -i output should only be used for files without extensions, or for which the extension is not listed in mimemap 2.1.4. Indexing failures Indexing may fail for some documents, for a number of reasons: a helper program may be missing, the document may be corrupt, we may fail to uncompress a file because no file system space is available, etc. The Recoll indexer in versions 1.21 and later does not retry failed files by default, because some indexing failures can be quite costly (for example failing to uncompress a big file because of insufficient disk space). Retrying will only occur if an explicit option (-k) is set on the recollindex command line, or if a script executed when recollindex starts up says so. The script is defined by a configuration variable (checkneedretryindexscript), and makes a rather lame attempt at deciding if a helper command may have been installed, by checking if any of the common bin directories have changed. 2.1.5. Recovery In the rare case where the index becomes corrupted (which can signal itself by weird search results or crashes), the index files need to be erased before restarting a clean indexing pass. Just delete the xapiandb directory (see next section), or, alternatively, start the next recollindex with the -z option, which will reset the database before indexing. The difference between the two methods is that the second will not change the current index format, which may be undesirable if a newer format is supported by the Xapian version. 2.2. Index storage The default location for the index data is the xapiandb subdirectory of the Recoll configuration directory, typically $HOME/.recoll/xapiandb/ on Unix-like systems or C:/Users/[me]/Appdata/Local/Recoll/xapiandb on Windows. This can be changed via two different methods (with different purposes): 1. For a given configuration directory, you can specify a non-default storage location for the index by setting the dbdir parameter in the configuration file (see the configuration section). This method would mainly be of use if you wanted to keep the configuration directory in its default location, but desired another location for the index, typically out of disk occupation or performance concerns. 2. You can specify a different configuration directory by setting the RECOLL_CONFDIR environment variable, or using the -c option to the Recoll commands. This method would typically be used to index different areas of the file system to different indexes. For example, if you were to issue the following command: recoll -c ~/.indexes-email Then Recoll would use configuration files stored in ~/.indexes-email/ and, (unless specified otherwise in recoll.conf) would look for the index in ~ /.indexes-email/xapiandb/. Using multiple configuration directories and configuration options allows you to tailor multiple configurations and indexes to handle whatever subset of the available data you wish to make searchable. The size of the index is determined by the size of the set of documents, but the ratio can vary a lot. For a typical mixed set of documents, the index size will often be close to the data set size. In specific cases (a set of compressed mbox files for example), the index can become much bigger than the documents. It may also be much smaller if the documents contain a lot of images or other non-indexed data (an extreme example being a set of mp3 files where only the tags would be indexed). Of course, images, sound and video do not increase the index size, which means that in most cases, the space used by the index will be negligible compared to the total amount of data on the computer. The index data directory (xapiandb) only contains data that can be completely rebuilt by an index run (as long as the original documents exist), and it can always be destroyed safely. 2.2.1. Xapian index formats Xapian versions usually support several formats for index storage. A given major Xapian version will have a current format, used to create new indexes, and will also support the format from the previous major version. Xapian will not convert automatically an existing index from the older format to the newer one. If you want to upgrade to the new format, or if a very old index needs to be converted because its format is not supported any more, you will have to explicitly delete the old index (typically ~/.recoll/xapiandb), then run a normal indexing command. Using recollindex option -z would not work in this situation. 2.2.2. Security aspects The Recoll index does not hold complete copies of the indexed documents (it almost does after version 1.24). But it does hold enough data to allow for an almost complete reconstruction. If confidential data is indexed, access to the database directory should be restricted. Recoll will create the configuration directory with a mode of 0700 (access by owner only). As the index data directory is by default a sub-directory of the configuration directory, this should result in appropriate protection. If you use another setup, you should think of the kind of protection you need for your index, set the directory and files access modes appropriately, and also maybe adjust the umask used during index updates. 2.2.3. Special considerations for big indexes This only needs concern you if your index is going to be bigger than around 5 GBytes. Beyond 10 GBytes, it becomes a serious issue. Most people have much smaller indexes. For reference, 5 GBytes would be around 2000 bibles, a lot of text. If you have a huge text dataset (remember: images don't count, the text content of PDFs is typically less than 5% of the file size), read on. The amount of writing performed by Xapian during index creation is not linear with the index size (it is somewhere between linear and quadratic). For big indexes this becomes a performance issue, and may even be an SSD disk wear issue. The problem can be mitigated by observing the following rules: • Partition the data set and create several indexes of reasonable size rather than a huge one. These indexes can then be queried in parallel (using the Recoll external indexes facility), or merged using xapian-compact. • Have a lot of RAM available and set the idxflushmb Recoll configuration parameter as high as you can without swapping (experimentation will be needed). 200 would be a minimum in this context. • Use Xapian 1.4.10 or newer, as this version brought a significant improvement in the amount of writes. 2.3. Index configuration Variables stored inside the Recoll configuration files control which areas of the file system are indexed, and how files are processed. The values can be set by editing the text files. Most of the more commonly used ones can also be adjusted by using the dialogs in the recoll GUI. The first time you start recoll, you will be asked whether or not you would like it to build the index. If you want to adjust the configuration before indexing, just click Cancel at this point, which will get you into the configuration interface. If you exit at this point, recoll will have created a default configuration directory with empty configuration files, which you can then edit. The configuration is documented inside the installation chapter of this document, or in the recoll.conf(5) manual page. Both documents are automatically generated from the comments inside the configuration file. The most immediately useful variable is probably topdirs, which lists the subtrees and files to be indexed. The applications needed to index file types other than text, HTML or email (e.g.: pdf, postscript, ms-word...) are described in the external packages section. There are two incompatible types of Recoll indexes, depending on the treatment of character case and diacritics. A further section describes the two types in more detail. The default type is appropriate in most cases. 2.3.1. The index configuration GUI Most index configuration parameters can be set from the recoll GUI (set RECOLL_CONFDIR or use the -c option to affect a non-default index.) The interface is started from the Preferences → Index Configuration menu entry. It is divided in four tabs, Global parameters, Local parameters, Web history ( details) and Search parameters. The Global parameters tab allows setting global variables, like the lists of top/start directories, skipped paths, or stemming languages. The Local parameters tab allows setting variables that can be redefined for subdirectories. This second tab has an initially empty list of customisation directories, to which you can add. The variables are then set for the currently selected directory (or at the top level if the empty line is selected). The Search parameters section defines parameters which are used at query time, but are global to an index and affect all search tools, not only the GUI. The meaning for most entries in the interface is self-evident and documented by a ToolTip popup on the text label. For more detail, you may need to refer to the configuration section of this guide. The configuration tool normally respects the comments and most of the formatting inside the configuration file, so that it is quite possible to use it on hand-edited files, which you might nevertheless want to backup first... 2.3.2. Multiple indexes Multiple Recoll indexes can be created by using several configuration directories which would typically be set to index different areas of the file system. A specific configuration can be selected by setting the RECOLL_CONFDIR environment variable or giving the -c option to recoll and recollindex. The recollindex program, used for creating or updating indexes, always works on a single index. The different configurations are entirely independent (no parameters are ever shared between configurations when indexing). All the search interfaces (recoll, recollq, the Python API, etc.) operate with a main configuration, from which both configuration and index data are used, and can also query data from multiple additional indexes. Only the index data from additional indexes is used, their configuration parameters are ignored. This implies that some parameters should be consistent among index configurations which are to be used together. When searching, the current main index (defined by RECOLL_CONFDIR or -c) is always active. If this is undesirable, you can set up your base configuration to index an empty directory. Index configuration parameters can be set either by using a text editor on the files, or, for most parameters, by using the recoll index configuration GUI. In the latter case, the configuration directory for which parameters are modified is the one which was selected by RECOLL_CONFDIR or the -c parameter, and there is no way to switch configurations within the GUI. See the configuration section for a detailed description of the parameters Some configuration parameters must be consistent among a set of multiple indexes used together for searches. Most importantly, all indexes to be queried concurrently must have the same option concerning character case and diacritics stripping, but there are other constraints. Most of the relevant parameters affect the term generation. Using multiple configurations implies a small level of command line or file manager usage. The user must explicitly create additional configuration directories, the GUI will not do it. This is to avoid mistakenly creating additional directories when an argument is mistyped. Also, the GUI or the indexer must be launched with a specific option or environment to work on the right configuration. Creating and using an additional index: example The following applies to Unix-like systems Initially creating the configuration and index: mkdir /path/to/my/new/config Configuring the new index can be done from the recoll GUI, launched from the command line to pass the -c option (you could create a desktop file to do it for you), and then using the GUI index configuration tool to set up the index. recoll -c /path/to/my/new/config Alternatively, you can just start a text editor on the main configuration file: someEditor /path/to/my/new/config/recoll.conf Creating and updating the index can be done from the command line: recollindex -c /path/to/my/new/config or from the File menu of a GUI launched with the same option (recoll, see above). The same GUI would also let you set up batch indexing for the new index. Real time indexing can only be set up from the GUI for the default index (the menu entry will be inactive if the GUI was started with a non-default -c option). The new index can be queried alone with: recoll -c /path/to/my/new/config Or, in parallel with the default index, by starting recoll without a -c option, and using the External Indexes tab in the preferences dialog, which can be reached either trough: Preferences → GUI Configuration → External Index Dialog or Query → External index dialog. See the GUI external indexes section for more details. 2.3.3. Index case and diacritics sensitivity As of Recoll version 1.18 you have a choice of building an index with terms stripped of character case and diacritics, or one with raw terms. For a source term of Résumé, the former will store resume, the latter Résumé. Each type of index allows performing searches insensitive to case and diacritics: with a raw index, the user entry will be expanded to match all case and diacritics variations present in the index. With a stripped index, the search term will be stripped before searching. A raw index allows using case and diacritics to discriminate between terms, e.g., returning different results when searching for US and us or resume and résumé. Read the section about search case and diacritics sensitivity for more details. The type of index to be created is controlled by the indexStripChars configuration variable which can only be changed by editing the configuration file. Any change implies an index reset (not automated by Recoll), and all indexes in a search must be set in the same way (again, not checked by Recoll). Recoll creates a stripped index by default if indexStripChars is not set. As a cost for added capability, a raw index will be slightly bigger than a stripped one (around 10%). Also, searches will be more complex, so probably slightly slower, and the feature is relatively little used, so that a certain amount of weirdness cannot be excluded. One of the most adverse consequence of using a raw index is that some phrase and proximity searches may become impossible: because each term needs to be expanded, and all combinations searched for, the multiplicative expansion may become unmanageable. 2.3.4. Indexing threads configuration (Unix-like systems) Note: you don't probably don't need to read this. The default automatic configuration is fine is most cases. Only the part about disabling multithreading may be more commonly useful, so I'll prepend it here. In recoll.conf: thrQSizes = -1 -1 -1 The Recoll indexing process recollindex can use multiple threads to speed up indexing on multiprocessor systems. The work done to index files is divided in several stages and some of the stages can be executed by multiple threads. The stages are: 1. File system walking: this is always performed by the main thread. 2. File conversion and data extraction. 3. Text processing (splitting, stemming, etc.). 4. Xapian index update. You can also read a longer document about the transformation of Recoll indexing to multithreading. The threads configuration is controlled by two configuration file parameters. thrQSizes This variable defines the job input queues configuration. There are three possible queues for stages 2, 3 and 4, and this parameter should give the queue depth for each stage (three integer values). If a value of -1 is used for a given stage, no queue is used, and the thread will go on performing the next stage. In practise, deep queues have not been shown to increase performance. A value of 0 for the first queue tells Recoll to perform autoconfiguration (no need for anything else in this case, thrTCounts is not used) - this is the default configuration. thrTCounts This defines the number of threads used for each stage. If a value of -1 is used for one of the queue depths, the corresponding thread count is ignored. It makes no sense to use a value other than 1 for the last stage because updating the Xapian index is necessarily single-threaded (and protected by a mutex). Note If the first value in thrQSizes is 0, thrTCounts is ignored. The following example would use three queues (of depth 2), and 4 threads for converting source documents, 2 for processing their text, and one to update the index. This was tested to be the best configuration on the test system (quadri-processor with multiple disks). thrQSizes = 2 2 2 thrTCounts = 4 2 1 The following example would use a single queue, and the complete processing for each document would be performed by a single thread (several documents will still be processed in parallel in most cases). The threads will use mutual exclusion when entering the index update stage. In practise the performance would be close to the precedent case in general, but worse in certain cases (e.g. a Zip archive would be performed purely sequentially), so the previous approach is preferred. YMMV... The 2 last values for thrTCounts are ignored. thrQSizes = 2 -1 -1 thrTCounts = 6 1 1 The following example would disable multithreading. Indexing will be performed by a single thread. thrQSizes = -1 -1 -1 2.4. Index update scheduling 2.4.1. Periodic indexing Running the indexer The recollindex program performs index updates. You can start it either from the command line or from the File menu in the recoll GUI program. When started from the GUI, the indexing will run on the same configuration recoll was started on. When started from the command line, recollindex will use the RECOLL_CONFDIR variable or accept a -c confdir option to specify a non-default configuration directory. If the recoll program finds no index when it starts, it will automatically start indexing (except if canceled). The GUI File menu has entries to start or stop the current indexing operation. When indexing is not currently running, you have a choice between Update Index or Rebuild Index. The first choice only processes changed files, the second one erases the index before starting so that all files are processed. On Linux and Windows, the GUI can be used to manage the indexing operation. Stopping the indexer can be done from the recoll GUI File → Stop Indexing menu entry. On Linux, the recollindex indexing process can be interrupted by sending an interrupt (Ctrl-C, SIGINT) or terminate (SIGTERM) signal. When stopped, some time may elapse before recollindex exits, because it needs to properly flush and close the index. After an interruption, the index will be somewhat inconsistent because some operations which are normally performed at the end of the indexing pass will have been skipped (for example, the stemming and spelling databases will be inexistent or out of date). You just need to restart indexing at a later time to restore consistency. The indexing will restart at the interruption point (the full file tree will be traversed, but files that were indexed up to the interruption and for which the index is still up to date will not need to be reindexed). recollindex command line recollindex has many options which are listed in its manual page. Only a few will be described here. Option -z will reset the index when starting. This is almost the same as destroying the index files (the nuance is that the Xapian format version will not be changed). Option -Z will force the update of all documents without resetting the index first. This will not have the "clean start" aspect of -z, but the advantage is that the index will remain available for querying while it is rebuilt, which can be a significant advantage if it is very big (some installations need days for a full index rebuild). Option -k will force retrying files which previously failed to be indexed, for example because of a missing helper program. Of special interest also, maybe, are the -i and -f options. -i allows indexing an explicit list of files (given as command line parameters or read on stdin). -f tells recollindex to ignore file selection parameters from the configuration. Together, these options allow building a custom file selection process for some area of the file system, by adding the top directory to the skippedPaths list and using an appropriate file selection method to build the file list to be fed to recollindex -if. Trivial example: find . -name indexable.txt -print | recollindex -if recollindex -i will not descend into subdirectories specified as parameters, but just add them as index entries. It is up to the external file selection method to build the complete file list. Linux: using cron to automate indexing The most common way to set up indexing is to have a cron task execute it every night. For example the following crontab entry would do it every day at 3:30AM (supposing recollindex is in your PATH): 30 3 * * * recollindex > /some/tmp/dir/recolltrace 2>&1 Or, using anacron: 1 15 su mylogin -c "recollindex recollindex > /tmp/rcltraceme 2>&1" The Recoll GUI has dialogs to manage crontab entries for recollindex. You can reach them from the Preferences → Indexing Schedule menu. They only work with the good old cron, and do not give access to all features of cron scheduling. Entries created via the tool are marked with a RCLCRON_RCLINDEX= marker so that the tool knows which entries belong to it. As a side effect, this sets an environment variable for the process, but it's not actually used, this is just a marker. The usual command to edit your crontab is crontab -e (which will usually start the vi editor to edit the file). You may have more sophisticated tools available on your system. Please be aware that there may be differences between your usual interactive command line environment and the one seen by crontab commands. Especially the PATH variable may be of concern. Please check the crontab manual pages about possible issues. 2.4.2. Real time indexing Real time monitoring/indexing is performed by starting the recollindex -m command. With this option, recollindex will permanently monitor file changes and update the index. On Windows systems, the monitoring process is started from the recoll GUI File menu. On Unix-like systems, there are other possibilities, see the following sections. When this is in use, the recoll GUI File menu makes two operations available: Stop and Trigger incremental pass. Trigger incremental pass has the same effect as restarting the indexer, and will cause a complete walk of the indexed area, processing the changed files, then switch to monitoring. This is only marginally useful, maybe in cases where the indexer is configured to delay updates, or to force an immediate rebuild of the stemming and phonetic data, which are only processed at intervals by the real time indexer. While it is convenient that data is indexed in real time, repeated indexing can generate a significant load on the system when files such as email folders change. Also, monitoring large file trees by itself significantly taxes system resources. You probably do not want to enable it if your system is short on resources. Periodic indexing is adequate in most cases. As of Recoll 1.24, you can set the monitordirs configuration variable to specify that only a subset of your indexed files will be monitored for instant indexing. In this situation, an incremental pass on the full tree can be triggered by either restarting the indexer, or just running recollindex, which will notify the running process. The recoll GUI also has a menu entry for this. Unix-like systems: automatic daemon start with systemd The installation contains two example files (in share/recoll/examples) for starting the indexing daemon with systemd. recollindex.service would be used for starting recollindex as a user service. The indexer will start when the user logs in and run while there is a session open for them. recollindex@.service is a template service which would be used for starting the indexer at boot time, running as a specific user. It can be useful when running the text search as a shared service (e.g. when users access it through the WEB UI). If configured to do so, the unit files should have been installed in your system's default systemd paths (usually /usr/lib/systemd/system/ and /usr/lib/ systemd/user/). If not, you may need to copy the files there before starting the service. With the unit files installed in the proper location, the user unit can be started with the following commands: systemctl --user daemon-reload systemctl --user enable --now recollindex.service The system unit file can be enabled for a particular user by running, as root: systemctl daemon-reload systemctl enable --now recollindex@username.service (A valid user name should be substituted for username, of course.) Unix-like systems: automatic daemon start from the desktop session Under KDE, Gnome and some other desktop environments, the daemon can automatically started when you log in, by creating a desktop file inside the ~ /.config/autostart directory. This can be done for you by the Recoll GUI. Use the Preferences->Indexing Schedule menu. With older X11 setups, starting the daemon is normally performed as part of the user session script. The rclmon.sh script can be used to easily start and stop the daemon. It can be found in the examples directory (typically /usr/local/[share/]recoll/examples). For example, a good old xdm-based session could have a .xsession script with the following lines at the end: recollconf=$HOME/.recoll-home recolldata=/usr/local/share/recoll RECOLL_CONFDIR=$recollconf $recolldata/examples/rclmon.sh start fvwm The indexing daemon gets started, then the window manager, for which the session waits. By default the indexing daemon will monitor the state of the X11 session, and exit when it finishes, it is not necessary to kill it explicitly. (The X11 server monitoring can be disabled with option -x to recollindex). If you use the daemon completely out of an X11 session, you need to add option -x to disable X11 session monitoring (else the daemon will not start). Miscellaneous details Logging. By default, the messages from the indexing daemon will be sent to the same file as those from the interactive commands (logfilename). You may want to change this by setting the daemlogfilename and daemloglevel configuration parameters. Also the log file will only be truncated when the daemon starts. If the daemon runs permanently, the log file may grow quite big, depending on the log level. Unix-like systems: increasing resources for inotify. On Linux systems, monitoring a big tree may need increasing the resources available to inotify, which are normally defined in /etc/sysctl.conf. ### inotify # # cat /proc/sys/fs/inotify/max_queued_events - 16384 # cat /proc/sys/fs/inotify/max_user_instances - 128 # cat /proc/sys/fs/inotify/max_user_watches - 16384 # # -- Change to: # fs.inotify.max_queued_events=32768 fs.inotify.max_user_instances=256 fs.inotify.max_user_watches=32768 Especially, you will need to trim your tree or adjust the max_user_watches value if indexing exits with a message about errno ENOSPC (28) from inotify_add_watch. Slowing down the reindexing rate for fast changing files. When using the real time monitor, it may happen that some files need to be indexed, but change so often that they impose an excessive load for the system.Recoll provides a configuration option to specify the minimum time before which a file, specified by a wildcard pattern, cannot be reindexed. See the mondelaypatterns parameter in the configuration section. 2.5. Miscellaneous indexing notes 2.5.1. The PDF input handler The PDF format is very important for scientific and technical documentation, and document archival. It has extensive facilities for storing metadata along with the document, and these facilities are actually used in the real world. In consequence, the rclpdf.py PDF input handler has more complex capabilities than most others, and it is also more configurable. Specifically, rclpdf.py has the following features: • It can be configured to extract specific metadata tags from an XMP packet. • It can extract PDF attachments. • It can automatically perform OCR if the document text is empty. This is done by executing an external program and is now described in a separate section, because the OCR framework can also be used with non-PDF image files. XMP fields extraction The rclpdf.py script in Recoll version 1.23.2 and later can extract XMP metadata fields by executing the pdfinfo command (usually found with poppler-utils). This is controlled by the pdfextrameta configuration variable, which specifies which tags to extract and, possibly, how to rename them. The pdfextrametafix variable can be used to designate a file with Python code to edit the metadata fields (available for Recoll 1.23.3 and later. 1.23.2 has equivalent code inside the handler script). Example: import sys import re class MetaFixer(object): def __init__(self): pass def metafix(self, nm, txt): if nm == 'bibtex:pages': txt = re.sub(r'--', '-', txt) elif nm == 'someothername': # do something else pass elif nm == 'stillanother': # etc. pass return txt def wrapup(self, metaheaders): pass If the 'metafix()' method is defined, it is called for each metadata field. A new MetaFixer object is created for each PDF document (so the object can keep state for, for example, eliminating duplicate values). If the 'wrapup()' method is defined, it is called at the end of XMP fields processing with the whole metadata as parameter, as an array of '(nm, val)' pairs, allowing an alternate approach for editing or adding/deleting fields. See this page for a more detailed discussion about indexing PDF XMP properties. PDF attachment indexing If pdftk is installed, and if the the pdfattach configuration variable is set, the PDF input handler will try to extract PDF attachments for indexing as sub-documents of the PDF file. This is disabled by default, because it slows down PDF indexing a bit even if not one attachment is ever found (PDF attachments are uncommon in my experience). 2.5.2. Running OCR on image documents The Recoll PDF handler has the ability to call an external OCR program if the processed file has no text content. The OCR data is stored in a cache of separate files, avoiding any modification of the originals. It must be noted that, if modifying the files (or a copy) is acceptable, then running something like OCRmyPDF to add a text layer to the PDF itself is a better solution (e.g. allowing Recoll to position the PDF viewer on the search target when opening the document, and permitting secondary search in the native tool). To enable the Recoll OCR feature, you need to install one of the supported OCR applications (tesseract or ABBYY), enable OCR in the PDF handler (by setting pdfocr to 1 in the index configuration file), and tell Recoll how to run the OCR by setting configuration variables. All parameters can be localized in subdirectories through the usual main configuration mechanism (path sections). Example configuration fragment in recoll.conf: pdfocr = 1 ocrprogs = tesseract pdfocrlang = eng This facility got a major update in Recoll 1.26.5. Older versions had a more limited, non-caching capability to execute an external OCR program in the PDF handler. The new function has the following features: • The OCR output is cached, stored as separate files. The caching is ultimately based on a hash value of the original file contents, so that it is immune to file renames. A first path-based layer ensures fast operation for unchanged (unmoved files), and the data hash (which is still orders of magnitude faster than OCR) is only re-computed if the file has moved. OCR is only performed if the file was not previously processed or if it changed. • The support for a specific program is implemented in a simple Python module. It should be straightforward to add support for any OCR engine with a capability to run from the command line. • Modules initially exist for tesseract (Linux and Windows), and ABBYY FineReader (Linux, tested with version 11). ABBYY FineReader is a commercial closed source program, but it sometimes perform better than tesseract. • The OCR is currently only called from the PDF handler, but there should be no problem using it for other image types. 2.5.3. Running a speech to text program on audio files If the OpenAI Whisper program is available and the appropriate parameters set in the configuration files, the Recoll audio file handler will run speech to text recognition on audio files and the resulting text will be indexed. See the the FAQ entry for more details. The results of the speech recognition will be cached in the same manner as the results of image OCR. 2.5.4. Removable volumes Recoll used to have no support for indexing removable volumes (portable disks, USB keys, etc.). Recent versions have improved the situation and support indexing removable volumes in two different ways: • By indexing the volume in the main, fixed, index, and ensuring that the volume data is not purged if the indexing runs while the volume is mounted. (since Recoll 1.25.2). • By storing a volume index on the volume itself (since Recoll 1.24). Indexing removable volumes in the main index As of version 1.25.2, Recoll provides a simple way to ensure that the index data for an absent volume will not be purged. Two conditions must be met: • The volume mount point must be a member of the topdirs list. • The mount directory must be empty (when the volume is not mounted). If recollindex finds that one of the topdirs is empty when starting up, any existing data for the tree will be preserved by the indexing pass (no purge for this area). Self contained volumes As of Recoll 1.24, it has become possible to build self-contained datasets including a Recoll configuration directory and index together with the indexed documents, and to move such a dataset around (for example copying it to an USB drive), without having to adjust the configuration for querying the index. Note This is a query-time feature only. The index must only be updated in its original location. If an update is necessary in a different location, the index must be reset. The principle of operation is that the configuration stores the location of the original configuration directory, which must reside on the movable volume. If the volume is later mounted elsewhere, Recoll adjusts the paths stored inside the index by the difference between the original and current locations of the configuration directory. To make a long story short, here follows a script to create a Recoll configuration and index under a given directory (given as single parameter). The resulting data set (files + recoll directory) can later to be moved to a CDROM or thumb drive. Longer explanations come after the script. #!/bin/sh fatal() { echo $*;exit 1 } usage() { fatal "Usage: init-recoll-volume.sh <top-directory>" } test $# = 1 || usage topdir=$1 test -d "$topdir" || fatal $topdir should be a directory confdir="$topdir/recoll-config" test ! -d "$confdir" || fatal $confdir should not exist mkdir "$confdir" cd "$topdir" topdir=`pwd` cd "$confdir" confdir=`pwd` (echo topdirs = '"'$topdir'"'; \ echo orgidxconfdir = $topdir/recoll-config) > "$confdir/recoll.conf" recollindex -c "$confdir" The examples below will assume that you have a dataset under /home/me/mydata/, with the index configuration and data stored inside /home/me/mydata/ recoll-confdir. In order to be able to run queries after the dataset has been moved, you must ensure the following: • The main configuration file must define the orgidxconfdir variable to be the original location of the configuration directory (orgidxconfdir=/home/ me/mydata/recoll-confdir must be set inside /home/me/mydata/recoll-confdir/ recoll.conf in the example above). • The configuration directory must exist with the documents, somewhere under the directory which will be moved. E.g. if you are moving /home/me/mydata around, the configuration directory must exist somewhere below this point, for example /home/me/mydata/recoll-confdir, or /home/me/mydata/sub/ recoll-confdir. • You should keep the default locations for the index elements which are relative to the configuration directory by default (principally dbdir). Only the paths referring to the documents themselves (e.g. topdirs values) should be absolute (in general, they are only used when indexing anyway). Only the first point needs an explicit user action, the Recoll defaults are compatible with the third one, and the second is natural. If, after the move, the configuration directory needs to be copied out of the dataset (for example because the thumb drive is too slow), you can set the curidxconfdir, variable inside the copied configuration to define the location of the moved one. For example if /home/me/mydata is now mounted onto /media/me/ somelabel, but the configuration directory and index has been copied to /tmp/ tempconfig, you would set curidxconfdir to /media/me/somelabel/recoll-confdir inside /tmp/tempconfig/recoll.conf. orgidxconfdir would still be /home/me/ mydata/recoll-confdir in the original and the copy. If you are regularly copying the configuration out of the dataset, it will be useful to write a script to automate the procedure. This can't really be done inside Recoll because there are probably many possible variants. One example would be to copy the configuration to make it writable, but keep the index data on the medium because it is too big - in this case, the script would also need to set dbdir in the copied configuration. The same set of modifications (Recoll 1.24) has also made it possible to run queries from a readonly configuration directory (with slightly reduced function of course, such as not recording the query history). 2.5.5. Unix-like systems: indexing visited Web pages With the help of a Firefox extension, Recoll can index the Internet pages that you visit. The extension has a long history: it was initially designed for the Beagle indexer, then adapted to Recoll and the Firefox XUL API. The current version of the extension is located in the Mozilla add-ons repository uses the WebExtensions API, and works with current Firefox versions. The extension works by copying visited Web pages to an indexing queue directory, which Recoll then processes, storing the data into a local cache, then indexing it, then removing the file from the queue. The local cache is not an archive As mentioned above, a copy of the indexed Web pages is retained by Recoll in a local cache (from which data is fetched for previews, or when resetting the index). The cache is not changed by an index reset, just read for indexing. The cache has a maximum size, which can be adjusted from the Index configuration / Web history panel (webcachemaxmbs parameter in recoll.conf). Once the maximum size is reached, old pages are erased to make room for new ones. The pages which you want to keep indefinitely need to be explicitly archived elsewhere. Using a very high value for the cache size can avoid data erasure, but see the above 'Howto' page for more details and gotchas. The visited Web pages indexing feature can be enabled on the Recoll side from the GUI Index configuration panel, or by editing the configuration file (set processwebqueue to 1). The Recoll GUI has a tool to list and edit the contents of the Web cache. ( Tools → Webcache editor) The recollindex command has two options to help manage the Web cache: • --webcache-compact will recover the space from erased entries. It may need to use twice the disk space currently needed for the Web cache. • --webcache-burst destdir will extract all current entries into pairs of metadata and data files created inside destdir You can find more details on Web indexing, its usage and configuration in a Recoll 'Howto' entry. 2.5.6. Unix-like systems: using extended attributes User extended attributes are named pieces of information that most modern file systems can attach to any file. Recoll processes all extended attributes as document fields. Note that most fields are not indexed by default, you need to activate them by defining a prefix in the fields configuration file. A freedesktop standard defines a few special attributes, which are handled as such by Recoll: mime_type If set, this overrides any other determination of the file MIME type. charset If set, this defines the file character set (mostly useful for plain text files). By default, other attributes are handled as Recoll fields of the same name. On Linux, the user prefix is removed from the name. The name translation can be configured more precisely, also inside the fields configuration file. Setting the document modification/creation date Date fields are processed specially by Recoll. For obscure and uninteresting reasons, you should use modificationdate as extended attribute name for setting this value. Also, the date string should be an ASCII integer representing the Unix time (seconds since the epoch). An example Linux command line for setting this particular field follow. The substituted date prints the example date parameter in Unix time format (seconds since the epoch). setfattr -n user.modificationdate -v `date -d '2022-09-30 08:30:00' +%s` /some/file The date substitution will then be automatic, you do not need to customize the fields file. 2.5.7. Unix-like systems: importing external tags During indexing, it is possible to import metadata for each file by executing commands. This allows, for example, extracting tag data from an external application and storing it in a field for indexing. See the section about the metadatacmds field in the main configuration chapter for a description of the configuration syntax. For example, if you would want Recoll to use tags managed by tmsu in a field named tags, you would add the following to the configuration file: [/some/area/of/the/fs] metadatacmds = ; tags = tmsu tags %f Note Depending on the tmsu version, you may need/want to add options like --database =/some/db. You may want to restrict this processing to a subset of the directory tree, because it may slow down indexing a bit ([some/area/of/the/fs]). Note the initial semi-colon after the equal sign. In the example above, the output of tmsu is used to set a field named tags. The field name is arbitrary and could be tmsu or myfield just the same, but tags is an alias for the standard Recoll keywords field, and the tmsu output will just augment its contents. This will avoid the need to extend the field configuration. Once re-indexing is performed (you will need to force the file reindexing, Recoll will not detect the need by itself), you will be able to search from the query language, through any of its aliases: tags:some/alternate/values or tags:all,these,values. Tags changes will not be detected by the indexer if the file itself did not change. One possible workaround would be to update the file ctime when you modify the tags, which would be consistent with how extended attributes function. A pair of chmod commands could accomplish this, or a touch -a. Alternatively, just couple the tag update with a recollindex -e -i /path/to/the /file. Chapter 3. Searching 3.1. Introduction Getting answers to specific queries is of course the whole point of Recoll. The multiple provided interfaces always understand simple queries made of one or several words, and return appropriate results in most cases. In order to make the most of Recoll though, it may be worthwhile to understand how it processes your input. Five different modes exist: • In All Terms mode, Recoll looks for documents containing all your input terms. • The Query Language mode behaves like All Terms in the absence of special input, but it can also do much more. This is the best mode for getting the most of Recoll. It is usable from all possible interfaces (GUI, command line, WEB UI, ...), and is described here. • In Any Term mode, Recoll looks for documents containing any your input terms, preferring those which contain more. • In File Name mode, Recoll will only match file names, not content. Using a small subset of the index allows things like left-hand wildcards without performance issues, and may sometimes be useful. • The GUI Advanced Search mode is actually not more powerful than the query language, but it helps you build complex queries without having to remember the language, and avoids any interpretation ambiguity, as it bypasses the user input parser. These five input modes are supported by the different user interfaces which are described in the following sections. 3.2. Searching with the Qt graphical user interface The recoll program provides the main user interface for searching. It is based on the Qt library. recoll has two search interfaces: • Simple search (the default, on the main screen) has a single entry field where you can enter multiple words or a query language query. • Advanced search (a panel accessed through the Tools menu or the toolbox bar icon) has multiple entry fields, which you may use to build a logical condition, with additional filtering on file type, location in the file system, modification date, and size. The Advanced Search tool is easier to use, but not actually more powerful, than the Simple Search in query language mode. Its name is historical, but Assisted Search would probably have been a better designation. In most text areas, you can enter the terms as you think them, even if they contain embedded punctuation or other non-textual characters (e.g. Recoll can handle things like email addresses). The main case where you should enter text differently from how it is printed is for east-asian languages (Chinese, Japanese, Korean). Words composed of single or multiple characters should be entered separated by white space in this case (they would typically be printed without white space). Some searches can be quite complex, and you may want to re-use them later, perhaps with some tweaking. Recoll can save and restore searches. See Saving and restoring queries. 3.2.1. Simple search 1. Start the recoll program. 2. Possibly choose a search mode: Any term, All terms, File name or Query language. 3. Enter search term(s) in the text field at the top of the window. 4. Click the Search button or hit the Enter key to start the search. The initial default search mode is Query language. Without special directives, this will look for documents containing all of the search terms (the ones with more terms will get better scores), just like the All Terms mode. Any term will search for documents where at least one of the terms appear. File name will exclusively look for file names, not contents All search modes allow terms to be expanded with wildcards characters (*, ?, []). See the section about wildcards for more details. In all modes except File name, you can search for exact phrases (adjacent words in a given order) by enclosing the input inside double quotes. Ex: "virtual reality". The Query Language features are described in a separate section. When using a stripped index (the default), character case has no influence on search, except that you can disable stem expansion for any term by capitalizing it. E.g.: a search for floor will also normally look for flooring, floored, etc., but a search for Floor will only look for floor, in any character case. Stemming can also be disabled globally in the preferences. When using a raw index, the rules are a bit more complicated. Recoll remembers the last few searches that you performed. You can directly access the search history by clicking the clock button on the right of the search entry, while the latter is empty. Otherwise, the history is used for entry completion (see next). Only the search texts are remembered, not the mode (all/any/file name). While text is entered in the search area, recoll will display possible completions, filtered from the history and the index search terms. This can be disabled with a GUI Preferences option. Double-clicking on a word in the result list or a preview window will insert it into the simple search entry field. You can cut and paste any text into an All terms or Any term search field, punctuation, newlines and all - except for wildcard characters (single ? characters are ok). Recoll will process it and produce a meaningful search. This is what most differentiates this mode from the Query Language mode, where you have to care about the syntax. The File name search mode will specifically look for file names. The point of having a separate file name search is that wildcard expansion can be performed more efficiently on a small subset of the index (allowing wildcards on the left of terms without excessive cost). Things to know: • White space in the entry should match white space in the file name, and is not treated specially. • The search is insensitive to character case and accents, independently of the type of index. • An entry without any wildcard character and not capitalized will be prepended and appended with '*' (e.g.: etc -> *etc*, but Etc -> etc). • If you have a big index (many files), excessively generic fragments may result in inefficient searches. 3.2.2. The result list After starting a search, a list of results will instantly be displayed in the main window. By default, the document list is presented in order of relevance (how well the application estimates that the document matches the query). You can sort the results by ascending or descending date by using the vertical arrows in the toolbar. Each result is displayed as a structured text paragraph. The standard format is typically adequate, but the content and presentation are entirely customisable. Most results will contain Preview and Open clickable links. Clicking the Preview link will open an internal preview window for the document. Further Preview clicks for the same search will open tabs in the existing preview window. You can use Shift+Click to force the creation of another preview window, which may be useful to view the documents side by side. (You can also browse successive results in a single preview window by typing Shift+ArrowUp/Down in the window). Clicking the Open link will start an external viewer for the document. By default, Recoll lets the desktop choose the appropriate application for most document types. See further for customizing the applications. The Preview and Open links may not be present for all entries. They are only available, respectively, for documents with MIME types that Recoll can extract text from, and for documents that have a configured viewer. However, you can modify the configuration to adjust this behavior. In more detail: • The Preview link will appear for documents with a MIME type present in the [index] section of the mimeconf file, and, only if the textunknownasplain configuration variable is set, for all types identified as a subtype of text (text/*). • The Open link will appear for documents with a MIME type present in the [view] section of the mimeview configuration file. If textunknownasplain is set and no specific viewer is found for a subtype of text, the viewer for text/plain will be used. You can click on the Query details link at the top of the results page to see the actual Xapian query, after stem expansion and other processing. Double-clicking on any word inside the result list or a preview window will insert it into the simple search text. The result list is divided into pages. You can change the page size in the preferences. Use the arrow buttons in the toolbar or the links at the bottom of the page to browse the results. Customising the viewers By default Recoll lets the desktop choose what application should be used to open a given document, with exceptions. The details of this behaviour can be customized with the Preferences → GUI configuration → User interface → Choose editor applications dialog or by editing the mimeview configuration file. When Use desktop preferences, at the top of the dialog, is checked, the desktop default is generally used, but there is a small default list of exceptions, for MIME types where the Recoll choice should override the desktop one. These are applications which are well integrated with Recoll, for example, on Linux, evince for viewing PDF and Postscript files because of its support for opening the document at a specific page and passing a search string as an argument. You can add or remove document types to the exceptions by using the dialog. If you prefer to completely customize the choice of applications, you can uncheck Use desktop preferences, in which case the Recoll predefined applications will be used, and can be changed for each document type. This is probably not the most convenient approach in most cases. In all cases, the applications choice dialog accepts multiple selections of MIME types in the top section, and lets you define how they are processed in the bottom one. In most cases, you will be using %f as a place holder to be replaced by the file name in the application command line. You may also change the choice of applications by editing the mimeview configuration file if you find this more convenient. Under Unix-like systems, each result list entry also has a right-click menu with an Open With entry. This lets you choose an application from the list of those which registered with the desktop for the document MIME type, on a case by case basis. No results: the spelling suggestions When a search yields no result, and if the aspell dictionary is configured, Recoll will try to check for misspellings among the query terms, and will propose lists of replacements. Clicking on one of the suggestions will replace the word and restart the search. You can hold any of the modifier keys (Ctrl, Shift, etc.) while clicking if you would rather stay on the suggestion screen because several terms need replacement. The result list right-click menu Apart from the preview and edit links, you can display a pop-up menu by right-clicking over a paragraph in the result list. This menu has the following entries: • Preview • Open • Open With • Run Script • Copy File Name • Copy Url • Save to File • Find similar • Preview Parent document • Open Parent document • Open Snippets Window The Preview and Open entries do the same thing as the corresponding links. Open With (Unix-like systems) lets you open the document with one of the applications claiming to be able to handle its MIME type (the information comes from the .desktop files in /usr/share/applications). Run Script (Unix-like systems) allows starting an arbitrary command on the result file. It will only appear for results which are top-level files. See further for a more detailed description. The Copy File Name and Copy Url copy the relevant data to the clipboard, for later pasting. Save to File allows saving the contents of a result document to a chosen file. This entry will only appear if the document does not correspond to an existing file, but is a subdocument inside such a file (e.g.: an email attachment). It is especially useful to extract attachments with no associated editor. The Open/Preview Parent document entries allow working with the higher level document (e.g. the email message an attachment comes from). Recoll is sometimes not totally accurate as to what it can or can't do in this area. For example the Parent entry will also appear for an email which is part of an mbox folder file, but you can't actually visualize the mbox (there will be an error dialog if you try). If the document is a top-level file, Open Parent will start the default file manager on the enclosing filesystem directory. The Find similar entry will select a number of relevant term from the current document and enter them into the simple search field. You can then start a simple search, with a good chance of finding documents related to the current result. I can't remember a single instance where this function was actually useful to me... The Open Snippets Window entry will only appear for documents which support page breaks (typically PDF, Postscript, DVI). The snippets window lists extracts from the document, taken around search terms occurrences, along with the corresponding page number, as links which can be used to start the native viewer on the appropriate page. If the viewer supports it, its search function will also be primed with one of the search terms. 3.2.3. The result table As an alternative to the result list, the results can also be displayed in spreadsheet-like fashion. You can switch to this presentation by clicking the table-like icon in the toolbar (this is a toggle, click again to restore the list). Clicking on the column headers will allow sorting by the values in the column. You can click again to invert the order, and use the header right-click menu to reset sorting to the default relevance order (you can also use the sort-by-date arrows to do this). Both the list and the table display the same underlying results. The sort order set from the table is still active if you switch back to the list mode. You can click twice on a date sort arrow to reset it from there. The header right-click menu allows adding or deleting columns. The columns can be resized, and their order can be changed (by dragging). All the changes are recorded when you quit recoll Hovering over a table row will update the detail area at the bottom of the window with the corresponding values. You can click the row to freeze the display. The bottom area is equivalent to a result list paragraph, with links for starting a preview or a native application, and an equivalent right-click menu. Typing Esc (the Escape key) will unfreeze the display. Using Shift-click on a row will display the document extracted text (somewhat like a preview) instead of the document details. The functions of Click and Shift-Click can be reversed in the GUI preferences. 3.2.4. The filters panel By default, the GUI displays the filters panel on the left of the results area. This is new in version 1.32. You can adjust the width of the panel, and hide it by squeezing it completely. The width will be memorized for the next session. The panel currently has two areas, for filtering the results by dates, or by filesystem location. The panel is only active in Query Language search mode, and its effect is to add date: and dir: clauses to the actual search. The dates filter can be activated by clicking the checkbox. It has two assisted date entry widgets, for the minimum and maximum dates of the search period. The directory filter displays a subset of the filesystem directories, reduced to the indexed area, as defined by the topdirs list and the name exclusion parameters. You can independantly select and deselect directories by clicking them. Note that selecting a directory will activate the whole subtree for searching, there is no need to select the subdirectories, and no way to exclude some of them (use Query language dir: clauses if this is needed). 3.2.5. Unix-like systems: running arbitrary commands on result files Apart from the Open and Open With operations, which allow starting an application on a result document (or a temporary copy), based on its MIME type, it is also possible to run arbitrary commands on results which are top-level files, using the Run Script entry in the results pop-up menu. The commands which will appear in the Run Script submenu must be defined by .desktop files inside the scripts subdirectory of the current configuration directory. Here follows an example of a .desktop file, which could be named for example, ~ /.recoll/scripts/myscript.desktop (the exact file name inside the directory is irrelevant): [Desktop Entry] Type=Application Name=MyFirstScript Exec=/home/me/bin/tryscript %F MimeType=*/* The Name attribute defines the label which will appear inside the Run Script menu. The Exec attribute defines the program to be run, which does not need to actually be a script, of course. The MimeType attribute is not used, but needs to exist. The commands defined this way can also be used from links inside the result paragraph. As an example, it might make sense to write a script which would move the document to the trash and purge it from the Recoll index. 3.2.6. Unix-like systems: displaying thumbnails The default format for the result list entries and the detail area of the result table display an icon for each result document. The icon is either a generic one determined from the MIME type, or a thumbnail of the document appearance. Thumbnails are only displayed if found in the standard freedesktop location, where they would typically have been created by a file manager. Recoll has no capability to create thumbnails. A relatively simple trick is to use the Open parent document/folder entry in the result list popup menu. This should open a file manager window on the containing directory, which should in turn create the thumbnails (depending on your settings). Restarting the search should then display the thumbnails. There are also some pointers about thumbnail generation in the Recoll FAQ. 3.2.7. The preview window The preview window opens when you first click a Preview link inside the result list. Subsequent preview requests for a given search open new tabs in the existing window (except if you hold the Shift key while clicking which will open a new window for side by side viewing). Starting another search and requesting a preview will create a new preview window. The old one stays open until you close it. You can close a preview tab by typing Ctrl-W (Ctrl + W) in the window. Closing the last tab, or using the window manager button in the top of the frame will also close the window. You can display successive or previous documents from the result list inside a preview tab by typing Shift+Down or Shift+Up (Down and Up are the arrow keys). A right-click menu in the text area allows switching between displaying the main text or the contents of fields associated to the document (e.g.: author, abtract, etc.). This is especially useful in cases where the term match did not occur in the main text but in one of the fields. In the case of images, you can switch between three displays: the image itself, the image metadata as extracted by exiftool and the fields, which is the metadata stored in the index. You can print the current preview window contents by typing Ctrl-P (Ctrl + P) in the window text. Searching inside the preview The preview window has an internal search capability, mostly controlled by the panel at the bottom of the window, which works in two modes: as a classical editor incremental search, where we look for the text entered in the entry zone, or as a way to walk the matches between the document and the Recoll query that found it. Incremental text search The preview tabs have an internal incremental search function. You initiate the search either by typing a / (slash) or CTL-F inside the text area or by clicking into the Search for: text field and entering the search string. You can then use the Next and Previous buttons to find the next/previous occurrence. You can also type F3 inside the text area to get to the next occurrence. If you have a search string entered and you use Ctrl-Up/Ctrl-Down to browse the results, the search is initiated for each successive document. If the string is found, the cursor will be positioned at the first occurrence of the search string. Walking the match lists If the entry area is empty when you click the Next or Previous buttons, the editor will be scrolled to show the next match to any search term (the next highlighted zone). If you select a search group from the dropdown list and click Next or Previous, the match list for this group will be walked. This is not the same as a text search, because the occurrences will include non-exact matches (as caused by stemming or wildcards). The search will revert to the text mode as soon as you edit the entry area. 3.2.8. The Query Fragments window The Query Fragments window can be used to control filtering query language elements modifying the current query, simply by clicking a button. This can be useful to save typing, or avoid memorizing, simple clauses of common usage (e.g. selecting only standalone documents or attachments, or filtering out WEB results, selecting a file system subtree, a file type, etc.). Selecting the Tools → Query Fragments menu entry will open the dialog. The contents of the window are entirely customizable, and defined by the contents of a XML text file, named fragment-buttons.xml and which will be looked for in the current index configuration directory. The sample file distributed with Recoll contains a number of example filters. This will be automatically copied to the configuration directory if the file does not exist in there (e.g. ~/.recoll/fragment-buttons.xml under Linux and Mac OS, $HOME/ AppData/Local/Recoll/fragment-buttons.xml for Windows). Editing the copy will allow you to configure the tool for your needs . Note The fragment-buttons.xml file was named fragbuts.xml up to Recoll version 1.31.0. This was deemed too close to offensive for native English speakers, so that the file was renamed. An existing fragbuts.xml will still be used if fragment-buttons.xml does not exist. No automatic renaming will be performed. Here follows an example window: [frag-sampl] And the corresponding configuration file: <?xml version="1.0" encoding="UTF-8"?> <fragbuttons version="1.0"> <radiobuttons> <!-- Toggle WEB queue results inclusion --> <fragbutton> <label>Include Web Results</label> <frag></frag> </fragbutton> <fragbutton> <label>Exclude Web Results</label> <frag>-rclbes:BGL</frag> </fragbutton> <fragbutton> <label>Only Web Results</label> <frag>rclbes:BGL</frag> </fragbutton> </radiobuttons> <radiobuttons> <!-- Standalone vs embedded switch --> <fragbutton> <label>Include embedded documents</label> <frag></frag> </fragbutton> <fragbutton> <label>Only standalone documents</label> <frag>issub:0</frag> </fragbutton> <fragbutton> <label>Only embedded documents</label> <frag>issub:1</frag> </fragbutton> </radiobuttons> <buttons> <fragbutton> <label>Example: Year 2010</label> <frag>date:2010-01-01/2010-12-31</frag> </fragbutton> <fragbutton> <label>Example: c++ files</label> <frag>ext:cpp OR ext:cxx</frag> </fragbutton> <fragbutton> <label>Example: My Great Directory</label> <frag>dir:/my/great/directory</frag> </fragbutton> </buttons> </fragbuttons> There are two types of groupings radiobuttons and buttons, each defining a line of checkbuttons or radiobuttons inside the window. Any number of buttons can be selected, but the radiobuttons in a line are exclusive. Buttons are defined by a fragbutton section, which provides the label for a button, and the Query Language fragment which will be added (as an AND filter) before performing the query if the button is active. <fragbutton> <label>Example: My Great Directory</label> <frag>dir:/my/great/directory</frag> </fragbutton> It is also possible to add message elements inside the groups, for documenting the behaviour. message elements have a label but no frag element. Example: <buttons> <message> <label>This is a message</label> </message> </buttons> The label contents are interpreted as HTML. Take care to replace opening < characters with the < entity if you use tags. The only thing that you need to know about XML for editing this file is that any opening tag like <label> needs to be matched by a closing tag after the value: </label>. You will normally edit the file with a regular text editor, like, e.g. vi or notepad. Double-clicking the file in a file manager may not work, because this usually opens it in a WEB browser, which will not let you modify the contents. 3.2.9. Assisted Complex Search (A.K.A. "Advanced Search") The advanced search dialog helps you build more complex queries without memorizing the search language constructs. It can be opened through the Tools menu or through the main toolbar. Recoll keeps a history of searches. See Advanced search history. The dialog has two tabs: 1. The first tab lets you specify terms to search for, and permits specifying multiple clauses which are combined to build the search. 2. The second tab allows filtering the results according to file size, date of modification, MIME type, or location. Click on the Start Search button in the advanced search dialog, or type Enter in any text field to start the search. The button in the main window always performs a simple search. Click on the Show query details link at the top of the result page to see the query expansion. Advanced search: the "find" tab This part of the dialog lets you construct a query by combining multiple clauses of different types. Each entry field is configurable for the following modes: • All terms. • Any term. • None of the terms. • Phrase (exact terms in order within an adjustable window). • Proximity (terms in any order within an adjustable window). • Filename search. Additional entry fields can be created by clicking the Add clause button. When searching, the non-empty clauses will be combined either with an AND or an OR conjunction, depending on the choice made on the left (All clauses or Any clause). Entries of all types except "Phrase" and "Near" accept a mix of single words and phrases enclosed in double quotes. Stemming and wildcard expansion will be performed as for simple search. Phrase and Proximity searches These two clauses look for a group of terms in specified relative positions. They differ in the sense that the order of input terms is significant for phrase searches, but not for proximity searches. The latter do not impose an order on the words. In both cases, an adjustable number (slack) of non-matched words may be accepted between the searched ones. For phrase searches, the default count is zero (exact match). For proximity searches it is ten (meaning that two search terms, would be matched if found within a window of twelve words). Examples: a phrase search for quick fox with a slack of 0 will match quick fox but not quick brown fox. With a slack of 1 it will match the latter, but not fox quick. A proximity search for quick fox with the default slack will match the latter, and also a fox is a cunning and quick animal. The slack can be adjusted with the counter to the left of the input area Advanced search: the "filter" tab This part of the dialog has several sections which allow filtering the results of a search according to a number of criteria • The first section allows filtering by dates of last modification. You can specify both a minimum and a maximum date. The initial values are set according to the oldest and newest documents found in the index. • The next section allows filtering the results by file size. There are two entries for minimum and maximum size. Enter decimal numbers. You can use suffix multipliers: k/K, m/M, g/G, t/T for 10E3, 10E6, 10E9, 10E12 respectively. • The next section allows filtering the results by their MIME types, or MIME categories (e.g.: media/text/message/etc.). You can transfer the types between two boxes, to define which will be included or excluded by the search. The state of the file type selection can be saved as the default (the file type filter will not be activated at program start-up, but the lists will be in the restored state). • The bottom section allows restricting the search results to a sub-tree of the indexed area. You can use the Invert checkbox to search for files not in the sub-tree instead. If you use directory filtering often and on big subsets of the file system, you may think of setting up multiple indexes instead, as the performance may be better. You can use relative/partial paths for filtering. E.g., entering dirA/dirB would match either /dir1/dirA/dirB/myfile1 or /dir2/dirA/dirB/someother/ myfile2. Advanced search history The advanced search tool memorizes the last 100 searches performed. You can walk the saved searches by using the up and down arrow keys while the keyboard focus belongs to the advanced search dialog. The complex search history can be erased, along with the one for simple search, by selecting the File → Erase Search History menu entry. 3.2.10. The term explorer tool Recoll automatically manages the expansion of search terms to their derivatives (e.g.: plural/singular, verb inflections). But there are other cases where the exact search term is not known. For example, you may not remember the exact spelling, or only know the beginning of the name. The search will only propose replacement terms with spelling variations when no matching document were found. In some cases, both proper spellings and mispellings are present in the index, and it may be interesting to look for them explicitly. The term explorer tool (started from the toolbar icon or from the Term explorer entry of the Tools menu) can be used to search the full index terms list, or (later addition), display some statistics or other index information. It has several modes of operations: Wildcard In this mode of operation, you can enter a search string with shell-like wildcards (*, ?, []). e.g.: xapi* would display all index terms beginning with xapi. (More about wildcards here). Regular expression This mode will accept a regular expression as input. Example: word[0-9]+. The expression is implicitly anchored at the beginning. E.g.: press will match pression but not expression. You can use .*press to match the latter, but be aware that this will cause a full index term list scan, which can be quite long. Stem expansion This mode will perform the usual stem expansion normally done as part user input processing. As such it is probably mostly useful to demonstrate the process. Spelling/Phonetic In this mode, you enter the term as you think it is spelled, and Recoll will do its best to find index terms that sound like your entry. This mode uses the Aspell spelling application, which must be installed on your system for things to work (if your documents contain non-ascii characters, Recoll needs an aspell version newer than 0.60 for UTF-8 support). The language which is used to build the dictionary out of the index terms (which is done at the end of an indexing pass) is the one defined by your NLS environment. Weird things will probably happen if languages are mixed up. Show index statistics This will print a long list of boring numbers about the index List files which could not be indexed This will show the files which caused errors, usually because recollindex could not translate their format into text. Note that in cases where Recoll does not know the beginning of the string to search for (e.g. a wildcard expression like *coll), the expansion can take quite a long time because the full index term list will have to be processed. The expansion is currently limited at 10000 results for wildcards and regular expressions. It is possible to change the limit in the configuration file. Double-clicking on a term in the result list will insert it into the simple search entry field. You can also cut/paste between the result list and any entry field (the end of lines will be taken care of). 3.2.11. Multiple indexes See the section describing the use of multiple indexes for generalities. Only the aspects concerning the recoll GUI are described here. A recoll program instance is always associated with a main index, which is the one to be updated when requested from the File menu, but it can use any number of external Recoll indexes for searching. The external indexes can be selected through the External Indexes tab in the preferences dialog, which can be reached either trough: Preferences → GUI Configuration → External Index Dialog or Query → External index dialog. Index selection is performed in two phases. A set of all usable indexes must first be defined, and then the subset of indexes to be used for searching. These parameters are retained across program executions (there are kept separately for each Recoll configuration). The set of all indexes is usually quite stable, while the active ones might typically be adjusted quite frequently. The main index (defined by RECOLL_CONFDIR) is always active. If this is undesirable, you can set up your base configuration to index an empty directory. When adding a new index to the set, you can select either a Recoll configuration directory, or directly a Xapian index directory. In the first case, the Xapian index directory will be obtained from the selected configuration. If the external index is actually located on a volume mounted from another machine, and references remote files, there may be a need to adjust the result paths so that they match the locally mounted ones (for opening documents). This can be done by using the path translation facility. As building the set of all indexes can be a little tedious when done through the user interface, you can use the RECOLL_EXTRA_DBS environment variable to provide an initial set. This might typically be set up by a system administrator so that every user does not have to do it. The variable should define a colon-separated list of index directories, e.g.: export RECOLL_EXTRA_DBS=/some/place/xapiandb:/some/other/db On Windows, use semi-colons (;) as separators instead of colons. Another environment variable, RECOLL_ACTIVE_EXTRA_DBS allows adding to the active list of indexes. This variable was suggested and implemented by a Recoll user. It is mostly useful if you use scripts to mount external volumes with Recoll indexes. By using RECOLL_EXTRA_DBS and RECOLL_ACTIVE_EXTRA_DBS, you can add and activate the index for the mounted volume when starting recoll. Unreachable indexes will automatically be deactivated when starting up. 3.2.12. Document history Documents that you actually view (with the internal preview or an external tool) are entered into the document history, which is remembered. You can display the history list by using the Tools/Doc History menu entry. You can erase the document history by using the Erase document history entry in the File menu. 3.2.13. Sorting search results and collapsing duplicates The documents in a result list are normally sorted in order of relevance. It is possible to specify a different sort order, either by using the vertical arrows in the GUI toolbox to sort by date, or switching to the result table display and clicking on any header. The sort order chosen inside the result table remains active if you switch back to the result list, until you click one of the vertical arrows, until both are unchecked (you are back to sort by relevance). Sort parameters are remembered between program invocations, but result sorting is normally always inactive when the program starts. It is possible to keep the sorting activation state between program invocations by checking the Remember sort activation state option in the preferences. It is also possible to hide duplicate entries inside the result list (documents with the exact same contents as the displayed one). The test of identity is based on an MD5 hash of the document container, not only of the text contents (so that e.g., a text document with an image added will not be a duplicate of the text only). Duplicates hiding is controlled by an entry in the GUI configuration dialog, and is off by default. When a result document does have undisplayed duplicates, a Dups link will be shown with the result list entry. Clicking the link will display the paths (URLs + ipaths) for the duplicate entries. 3.2.14. Keyboard shortcuts A number of common actions within the graphical interface can be triggered through keyboard shortcuts. As of Recoll 1.29, many of the shortcut values can be customised from a screen in the GUI preferences. Most shortcuts are specific to a given context (e.g. within a preview window, within the result table). Most shortcuts can be changed to a preferred value by using the GUI shortcut editor: Preferences → GUI configuration → Shortcuts. In order to change a shortcut, just click the corresponding cell in the Shortcut column, and type the desired sequence. Table 3.1. Keyboard shortcuts ┌───────────────────────────────────────────────────────────┬─────────────────┐ │Description │Default value │ ├───────────────────────────────────────────────────────────┴─────────────────┤ │Context: almost everywhere │ ├───────────────────────────────────────────────────────────┬─────────────────┤ │Program exit │Ctrl+Q │ ├───────────────────────────────────────────────────────────┴─────────────────┤ │Context: advanced search │ ├───────────────────────────────────────────────────────────┬─────────────────┤ │Load the next entry from the search history │Up │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Load the previous entry from the search history │Down │ ├───────────────────────────────────────────────────────────┴─────────────────┤ │Context: main window │ ├───────────────────────────────────────────────────────────┬─────────────────┤ │Clear search. This will move the keyboard cursor to the │Ctrl+S │ │simple search entry and erase the current text │ │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Move the keyboard cursor to the search entry area without │Ctrl+L │ │erasing the current text │ │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Move the keyboard cursor to the search entry area without │Ctrl+Shift+S │ │erasing the current text │ │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Toggle displaying the current results as a table or as a │Ctrl+T │ │list │ │ ├───────────────────────────────────────────────────────────┴─────────────────┤ │Context: main window, when showing the results as a table │ ├───────────────────────────────────────────────────────────┬─────────────────┤ │Move the keyboard cursor to currently the selected row in │Ctrl+R │ │the table, or to the first one if none is selected │ │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Jump to row 0-9 or a-z in the table │Ctrl+[0-9] or │ │ │Ctrl+Shift+[a-z] │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Cancel the current selection │Esc │ ├───────────────────────────────────────────────────────────┴─────────────────┤ │Context: preview window │ ├───────────────────────────────────────────────────────────┬─────────────────┤ │Close the preview window │Esc │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Close the current tab │Ctrl+W │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Open a print dialog for the current tab contents │Ctrl+P │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Load the next result from the list to the current tab │Shift+Down │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Load the previous result from the list to the current tab │Shift+Up │ ├───────────────────────────────────────────────────────────┴─────────────────┤ │Context: result table │ ├───────────────────────────────────────────────────────────┬─────────────────┤ │Copy the text contained in the selected document to the │Ctrl+G │ │clipboard │ │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Copy the text contained in the selected document to the │Ctrl+Alt+Shift+G │ │clipboard, then exit recoll │ │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Open the current document │Ctrl+O │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Open the current document and exit Recoll │Ctrl+Alst+Shift+O│ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Show a full preview for the current document │Ctrl+D │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Toggle showing the column names │Ctrl+H │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Show a snippets (keyword in context) list for the current │Ctrl+E │ │document │ │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Toggle showing the row letters/numbers │Ctrl+V │ ├───────────────────────────────────────────────────────────┴─────────────────┤ │Context: snippets window │ ├───────────────────────────────────────────────────────────┬─────────────────┤ │Close the snippets window │Esc │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Find in the snippets list (method #1) │Ctrl+F │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Find in the snippets list (method #2) │/ │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Find the next instance of the search term │F3 │ ├───────────────────────────────────────────────────────────┼─────────────────┤ │Find the previous instance of the search term │Shift+F3 │ └───────────────────────────────────────────────────────────┴─────────────────┘ 3.2.15. Search tips Terms and search expansion Term completion. While typing into the simple search entry, a popup menu will appear and show completions for the current string. Values preceded by a clock icon come from the history, those preceded by a magnifier icon come from the index terms. This can be disabled in the preferences. Picking up new terms from result or preview text. Double-clicking on a word in the result list or in a preview window will copy it to the simple search entry field. Wildcards. Wildcards can be used inside search terms in all forms of searches. More about wildcards. Automatic suffixes. Words like odt or ods can be automatically turned into query language ext:xxx clauses. This can be enabled in the Search preferences panel in the GUI. Disabling stem expansion. Entering a capitalized word in any search field will prevent stem expansion (no search for gardening if you enter Garden instead of garden). This is the only case where character case should make a difference for a Recoll search. You can also disable stem expansion or change the stemming language in the preferences. Finding related documents. Selecting the Find similar documents entry in the result list paragraph right-click menu will select a set of "interesting" terms from the current result, and insert them into the simple search entry field. You can then possibly edit the list and start a search to find documents which may be apparented to the current result. File names. File names are added as terms during indexing, and you can specify them as ordinary terms in normal search fields (Recoll used to index all directories in the file path as terms. This has been abandoned as it did not seem really useful). Alternatively, you can use the specific file name search which will only look for file names, and may be faster than the generic search especially when using wildcards. Working with phrases and proximity Phrases searches. A phrase can be looked for by enclosing a number of terms in double quotes. Example: "user manual" will look only for occurrences of user immediately followed by manual. You can use the "Phrase" field of the advanced search dialog to the same effect. Phrases can be entered along simple terms in all simple or advanced search entry fields, except "Phrase". Proximity searches. A proximity search differs from a phrase search in that it does not impose an order on the terms. Proximity searches can be entered by specifying the "Proximity" type in the advanced search, or by postfixing a phrase search with a 'p'. Example: "user manual"p would also match "manual user". Also see the modifier section from the query language documentation. AutoPhrases. This option can be set in the preferences dialog. If it is set, a phrase will be automatically built and added to simple searches when looking for Any terms. This will not change radically the results, but will give a relevance boost to the results where the search terms appear as a phrase. E.g.: searching for virtual reality will still find all documents where either virtual or reality or both appear, but those which contain virtual reality should appear sooner in the list. Phrase searches can slow down a query if most of the terms in the phrase are common. If the autophrase option is on, very common terms will be removed from the automatically constructed phrase. The removal threshold can be adjusted from the search preferences. Phrases and abbreviations. Dotted abbreviations like I.B.M. are also automatically indexed as a word without the dots: IBM. Searching for the word inside a phrase (e.g.: "the IBM company") will only match the dotted abrreviation if you increase the phrase slack (using the advanced search panel control, or the o query language modifier). Literal occurrences of the word will be matched normally. Others Using fields. You can use the query language and field specifications to only search certain parts of documents. This can be especially helpful with email, for example only searching emails from a specific originator: search tips from:helpfulgui Adjusting the result table columns. When displaying results in table mode, you can use a right click on the table headers to activate a pop-up menu which will let you adjust what columns are displayed. You can drag the column headers to adjust their order. You can click them to sort by the field displayed in the column. You can also save the result list in CSV format. Changing the GUI geometry. It is possible to configure the GUI in wide form factor by dragging the toolbars to one of the sides (their location is remembered between sessions), and moving the category filters to a menu (can be set in the Preferences → GUI configuration → User interface panel). Query explanation. You can get an exact description of what the query looked for, including stem expansion, and Boolean operators used, by clicking on the result list header. Advanced search history. You can display any of the last 100 complex searches performed by using the up and down arrow keys while the advanced search panel is active. Forced opening of a preview window. You can use Shift+Click on a result list Preview link to force the creation of a preview window instead of a new tab in the existing one. 3.2.16. Saving and restoring queries Both simple and advanced query dialogs save recent history, but the amount is limited: old queries will eventually be forgotten. Also, important queries may be difficult to find among others. This is why both types of queries can also be explicitly saved to files, from the GUI menus: File → Save last query / Load last query The default location for saved queries is a subdirectory of the current configuration directory, but saved queries are ordinary files and can be written or moved anywhere. Some of the saved query parameters are part of the preferences (e.g. autophrase or the active external indexes), and may differ when the query is loaded from the time it was saved. In this case, Recoll will warn of the differences, but will not change the user preferences. 3.2.17. Customizing the search interface You can customize some aspects of the search interface by using the GUI configuration entry in the Preferences menu. There are several tabs in the dialog, dealing with the interface itself, the parameters used for searching and returning results, and what indexes are searched. User interface parameters: • Highlight color for query terms: Terms from the user query are highlighted in the result list samples and the preview window. The color can be chosen here. Any Qt color string should work (e.g. red, #ff0000). The default is blue. • Style sheet: The name of a Qt style sheet text file which is applied to the whole Recoll application on startup. The default value is empty, but there is a skeleton style sheet (recoll.qss) inside the /usr/share/recoll/ examples directory. Using a style sheet, you can change most recoll graphical parameters: colors, fonts, etc. See the sample file for a few simple examples. You should be aware that parameters (e.g.: the background color) set inside the Recoll GUI style sheet will override global system preferences, with possible strange side effects: for example if you set the foreground to a light color and the background to a dark one in the desktop preferences, but only the background is set inside the Recoll style sheet, and it is light too, then text will appear light-on-light inside the Recoll GUI. • Maximum text size highlighted for preview Inserting highlights on search term inside the text before inserting it in the preview window involves quite a lot of processing, and can be disabled over the given text size to speed up loading. • Prefer HTML to plain text for preview if set, Recoll will display HTML as such inside the preview window. If this causes problems with the Qt HTML display, you can uncheck it to display the plain text version instead. • Activate links in preview if set, Recoll will turn HTTP links found inside plain text into proper HTML anchors, and clicking a link inside a preview window will start the default browser on the link target. • Plain text to HTML line style: when displaying plain text inside the preview window, Recoll tries to preserve some of the original text line breaks and indentation. It can either use PRE HTML tags, which will well preserve the indentation but will force horizontal scrolling for long lines, or use BR tags to break at the original line breaks, which will let the editor introduce other line breaks according to the window width, but will lose some of the original indentation. The third option has been available in recent releases and is probably now the best one: use PRE tags with line wrapping. • Choose editor application: this opens a dialog which allows you to select the application to be used to open each MIME type. The default is to use the xdg-open utility, but you can use this dialog to override it, setting exceptions for MIME types that will still be opened according to Recoll preferences. This is useful for passing parameters like page numbers or search strings to applications that support them (e.g. evince). This cannot be done with xdg-open which only supports passing one parameter. • Disable Qt autocompletion in search entry: this will disable the completion popup. Il will only appear, and display the full history, either if you enter only white space in the search area, or if you click the clock button on the right of the area. • Document filter choice style: this will let you choose if the document categories are displayed as a list or a set of buttons, or a menu. • Start with simple search mode: this lets you choose the value of the simple search type on program startup. Either a fixed value (e.g. Query Language, or the value in use when the program last exited. • Start with advanced search dialog open : If you use this dialog frequently, checking the entries will get it to open when recoll starts. • Remember sort activation state if set, Recoll will remember the sort tool stat between invocations. It normally starts with sorting disabled. Result list parameters: • Number of results in a result page • Result list font: There is quite a lot of information shown in the result list, and you may want to customize the font and/or font size. The rest of the fonts used by Recoll are determined by your generic Qt config (try the qtconfig command). • Edit result list paragraph format string: allows you to change the presentation of each result list entry. See the result list customisation section. • Edit result page HTML header insert: allows you to define text inserted at the end of the result page HTML header. More detail in the result list customisation section. • Date format: allows specifying the format used for displaying dates inside the result list. This should be specified as an strftime() string (man strftime). • Abstract snippet separator: for synthetic abstracts built from index data, which are usually made of several snippets from different parts of the document, this defines the snippet separator, an ellipsis by default. Search parameters: • Hide duplicate results: decides if result list entries are shown for identical documents found in different places. • Stemming language: stemming obviously depends on the document's language. This listbox will let you chose among the stemming databases which were built during indexing (this is set in the main configuration file), or later added with recollindex -s (See the recollindex manual). Stemming languages which are dynamically added will be deleted at the next indexing pass unless they are also added in the configuration file. • Automatically add phrase to simple searches: a phrase will be automatically built and added to simple searches when looking for Any terms. This will give a relevance boost to the results where the search terms appear as a phrase (consecutive and in order). • Autophrase term frequency threshold percentage: very frequent terms should not be included in automatic phrase searches for performance reasons. The parameter defines the cutoff percentage (percentage of the documents where the term appears). • Replace abstracts from documents: this decides if we should synthesize and display an abstract in place of an explicit abstract found within the document itself. • Dynamically build abstracts: this decides if Recoll tries to build document abstracts (lists of snippets) when displaying the result list. Abstracts are constructed by taking context from the document information, around the search terms. • Synthetic abstract size: adjust to taste... • Synthetic abstract context words: how many words should be displayed around each term occurrence. • Query language magic file name suffixes: a list of words which automatically get turned into ext:xxx file name suffix clauses when starting a query language query (e.g.: doc xls xlsx...). This will save some typing for people who use file types a lot when querying. External indexes: This panel will let you browse for additional indexes that you may want to search. External indexes are designated by their database directory (e.g.: /home/someothergui/.recoll/xapiandb, /usr/local/recollglobal/ xapiandb). Once entered, the indexes will appear in the External indexes list, and you can chose which ones you want to use at any moment by checking or unchecking their entries. Your main database (the one the current configuration indexes to), is always implicitly active. If this is not desirable, you can set up your configuration so that it indexes, for example, an empty directory. An alternative indexer may also need to implement a way of purging the index from stale data, The result list format Recoll normally uses a full function HTML processor to display the result list and the snippets window. Depending on the version, this may be based on either Qt WebKit or Qt WebEngine. It is then possible to completely customise the result list with full support for CSS and Javascript. It is also possible to build Recoll to use a simpler Qt QTextBrowser widget to display the HTML, which may be necessary if the ones above are not ported on the system, or to reduce the application size and dependencies. There are limits to what you can do in this case, but it is still possible to decide what data each result will contain, and how it will be displayed. The result list presentation can be customized by adjusting two elements: • The paragraph format • HTML code inside the header section. This is also used for the snippets window. The paragraph format and the header fragment can be edited from the Result list tab of the GUI configuration. The header fragment is used both for the result list and the snippets window. The snippets list is a table and has a snippets class attribute. Each paragraph in the result list is a table, with class respar, but this can be changed by editing the paragraph format. There are a few examples on the page about customising the result list on the Recoll Web site. The paragraph format This is an arbitrary HTML string which will be transformed by printf-like % substitutions to show the results. Note Any literal % character in the input must be quoted as %%. E.g. <table style= "width: 100%;"> should be entered as <table style="width: 100%%;">. The following substitutions will be performed: • %A. Abstract • %D. Date • %I. Icon image name. This is normally determined from the MIME type. The associations are defined inside the mimeconf configuration file. If a thumbnail for the file is found at the standard Freedesktop location, this will be displayed instead. • %K. Keywords (if any) • %L. Precooked Preview, Edit, and possibly Snippets links • %M. MIME type • %N. result Number inside the result page • %P. Parent folder Url. In the case of an embedded document, this is the parent folder for the top level container file. • %R. Relevance percentage • %S. Size information • %T. Title or Filename if not set. • %t. Title or empty. • %(filename). File name. • %U. Url In addition to the predefined values above, all strings like %(fieldname) will be replaced by the value of the field named fieldname for this document. Only stored fields can be accessed in this way, the value of indexed but not stored fields is not known at this point in the search process (see field configuration). There are currently very few fields stored by default, apart from the values above (only author and filename), so this feature will need some custom local configuration to be useful. An example candidate would be the recipient field which is generated by the message input handlers. The format of the Preview, Edit, and Snippets links is <a href="P%N">, <a href= "E%N"> and <a href="A%N"> where docnum (%N) expands to the document number inside the result page). A link target defined as "F%N" will open the document corresponding to the %P parent folder expansion, usually creating a file manager window on the folder where the container file resides. E.g.: <a href="F%N">%P</a> A link target defined as R%N|scriptname will run the corresponding script on the result file (if the document is embedded, the script will be started on the top-level parent). See the section about defining scripts. The default value for the paragraph format string is: "<table class=\"respar\">\n" "<tr>\n" "<td><a href='%U'><img src='%I' width='64'></a></td>\n" "<td>%L  <i>%S</i>   <b>%T</b><br>\n" "<span style='white-space:nowrap'><i>%M</i> %D</span>    <i>%U</i> %i<br>\n" "%A %K</td>\n" "</tr></table>\n" You may, for example, try the following for a more web-like experience: <u><b><a href="P%N">%T</a></b></u><br> %A<font color=#008000>%U - %S</font> - %L Note that the P%N link in the above paragraph makes the title a preview link. Or the clean looking: <img src="%I" align="left">%L <font color="#900000">%R</font>   <b>%T&</b><br>%S  <font color="#808080"><i>%U</i></font> <table bgcolor="#e0e0e0"> <tr><td><div>%A</div></td></tr> </table>%K These samples, and some others are on the web site, with pictures to show how they look. It is also possible to define the value of the snippet separator inside the abstract section. 3.3. Searching with the KDE KIO slave The Recoll KIO slave allows performing a Recoll search by entering an appropriate URL in a KDE open dialog, or a Dolphin URL. The results are displayed as directory entries. The instructions for building this module are located in the source tree. See: kde/kio/recoll/00README.txt. Some Linux distributions do package the kio-recoll module, so check before diving into the build process, maybe it's already out there ready for one-click installation. 3.4. Searching on the command line There are several ways to obtain search results as a text stream, without a graphical interface: • By passing option -t to the recoll program, or by calling it as recollq (through a link). • By using the actual recollq program. • By writing a custom Python program, using the Recoll Python API. The first two methods work in the same way and accept/need the same arguments (except for the additional -t to recoll). The query to be executed is specified as command line arguments. recollq is not always built by default. You can use the Makefile in the query directory to build it. This is a very simple program, and if you can program a little c++, you may find it useful to taylor its output format to your needs. Apart from being easily customised, recollq is only really useful on systems where the Qt libraries are not available, else it is redundant with recoll -t. recollq has a man page. The Usage string follows: recollq: usage: -P: Show the date span for all the documents present in the index. [-o|-a|-f] [-q] <query string> Runs a recoll query and displays result lines. Default: will interpret the argument(s) as a xesam query string. Query elements: * Implicit AND, exclusion, field spec: t1 -t2 title:t3 * OR has priority: t1 OR t2 t3 OR t4 means (t1 OR t2) AND (t3 OR t4) * Phrase: "t1 t2" (needs additional quoting on cmd line) -o Emulate the GUI simple search in ANY TERM mode. -a Emulate the GUI simple search in ALL TERMS mode. -f Emulate the GUI simple search in filename mode. -q is just ignored (compatibility with the recoll GUI command line). Common options: -c <configdir> : specify config directory, overriding $RECOLL_CONFDIR. -C : collapse duplicates -d also dump file contents. -n [first-]<cnt> define the result slice. The default value for [first] is 0. Without the option, the default max count is 2000. Use n=0 for no limit. -b : basic. Just output urls, no mime types or titles. -Q : no result lines, just the processed query and result count. -m : dump the whole document meta[] array for each result. -A : output the document abstracts. -p <cnt> : show <cnt> snippets, with page numbers instead of the concatenated abstract. -g <cnt> : show <cnt> snippets, with line numbers instead of the concatenated abstract. -S fld : sort by field <fld>. -D : sort descending. -s stemlang : set stemming language to use (must exist in index...). Use -s "" to turn off stem expansion. -T <synonyms file>: use the parameter (Thesaurus) for word expansion. -i <dbdir> : additional index, several can be given. -e use url encoding (%xx) for urls. -E use exact result count instead of lower bound estimate. -F <field name list> : output exactly these fields for each result. The field values are encoded in base64, output in one line and separated by one space character. This is the recommended format for use by other programs. Use a normal query with option -m to see the field names. Use -F '' to output all fields, but you probably also want option -N in this case. -N : with -F, print the (plain text) field names before the field values. --extract_to <filepath> : extract the first result to filepath, which must not exist. Use a -n option with an offset to select the appropriate result. Sample execution: recollq 'ilur -nautique mime:text/html' Recoll query: ((((ilur:(wqf=11) OR ilurs) AND_NOT (nautique:(wqf=11) OR nautiques OR nautiqu OR nautiquement)) FILTER Ttext/html)) 4 results text/html [file:///Users/dockes/projets/bateaux/ilur/comptes.html] [comptes.html] 18593 bytes text/html [file:///Users/dockes/projets/nautique/webnautique/articles/ilur1/index.html] [Constructio... text/html [file:///Users/dockes/projets/pagepers/index.html] [psxtcl/writemime/recoll]... text/html [file:///Users/dockes/projets/bateaux/ilur/factEtCie/recu-chasse-maree.... 3.5. The query language The Recoll query language was based on the now defunct Xesam user search language specification. It allows defining general boolean searches within the main body text or specific fields, and has many additional features, broadly equivalent to those provided by complex search interface in the GUI. The query language processor is activated in the GUI simple search entry when the search mode selector is set to Query Language. It can also be used from the command line search, the KIO slave, or the WEB UI. If the results of a query language search puzzle you and you doubt what has been actually searched for, you can use the GUI Show Query link at the top of the result list to check the exact query which was finally executed by Xapian. 3.5.1. General syntax Here follows a sample request that we are going to explain: author:"john doe" Beatles OR Lennon Live OR Unplugged -potatoes This would search for all documents with John Doe appearing as a phrase in the author field (exactly what this is would depend on the document type, e.g.: the From: header, for an email message), and containing either beatles or lennon and either live or unplugged but not potatoes (in any part of the document). An element is composed of an optional field specification, and a value, separated by a colon (the field separator is the last colon in the element). Examples: • Eugenie • author:balzac • dc:title:grandet • dc:title:"eugenie grandet" The colon, if present, means "contains". Xesam defines other relations, which are mostly unsupported for now (except in special cases, described further down). All elements in the search entry are normally combined with an implicit AND. It is possible to specify that elements be OR'ed instead, as in Beatles OR Lennon. The OR must be entered literally (capitals), and it has priority over the AND associations: word1 word2 OR word3 means word1 AND (word2 OR word3) not (word1 AND word2) OR word3. You can use parentheses to group elements (from version 1.21), which will sometimes make things clearer, and may allow expressing combinations which would have been difficult otherwise. An element preceded by a - specifies a term that should not appear. By default, words inside double-quotes define a phrase search (the order of words is significant), so that title:"prejudice pride" is not the same as title:prejudice title:pride, and is unlikely to find a result. This can be changed by using modifiers. Words inside phrases and capitalized words are not stem-expanded. Wildcards may be used anywhere inside a term. Specifying a wildcard on the left of a term can produce a very slow search (or even an incorrect one if the expansion is truncated because of excessive size). Also see More about wildcards. To save you some typing, Recoll versions 1.20 and later interpret a field value given as a comma-separated list of terms as an AND list and a slash-separated list as an OR list. No white space is allowed. So author:john,lennon will search for documents with john and lennon inside the author field (in any order), and author:john/ringo would search for john or ringo. This behaviour is only triggered by a field prefix: without it, comma- or slash- separated input will produce a phrase search. However, you can use a text field name to search the main text this way, as an alternate to using an explicit OR, e.g. text:napoleon/bonaparte would generate a search for napoleon or bonaparte in the main text body. Modifiers can be set on a double-quote value, for example to specify a proximity search (unordered). See the modifier section. No space must separate the final double-quote and the modifiers value, e.g. "two one"po10 Recoll currently manages the following default fields: • title, subject or caption are synonyms which specify data to be searched for in the document title or subject. • author or from for searching the documents originators. • recipient or to for searching the documents recipients. • keyword for searching the document-specified keywords (few documents actually have any). • filename for the document's file name. You can use the shorter fn alias. This value is not set for all documents: internal documents contained inside a compound one (for example an EPUB section) do not inherit the container file name any more, this was replaced by an explicit field (see next). Sub-documents can still have a filename, if it is implied by the document format, for example the attachment file name for an email attachment. • containerfilename, aliased as cfn. This is set for all documents, both top-level and contained sub-documents, and is always the name of the filesystem file which contains the data. The terms from this field can only be matched by an explicit field specification (as opposed to terms from filename which are also indexed as general document content). This avoids getting matches for all the sub-documents when searching for the container file name. • ext specifies the file name extension (Ex: ext:html). • rclmd5 the MD5 checksum for the document. This is used for displaying the duplicates of a search result (when querying with the option to collapse duplicate results). Incidentally, this could be used to find the duplicates of any given file by computing its MD5 checksum and executing a query with just the rclmd5 value. You can define aliases for field names, in order to use your preferred denomination or to save typing (e.g. the predefined fn and cfn aliases defined for filename and containerfilename). See the section about the fields file. The document input handlers have the possibility to create other fields with arbitrary names, and aliases may be defined in the configuration, so that the exact field search possibilities may be different for you if someone took care of the customisation. 3.5.2. Special field-like specifiers The field syntax also supports a few field-like, but special, criteria, for which the values are interpreted differently. Regular processing does not apply (for example the slash- or comma- separated lists don't work). A list follows. • dir for filtering the results on file location. For example, dir:/home/me/ somedir will restrict the search to results found anywhere under the /home/ me/somedir directory (including subdirectories). Tilde expansion will be performed as usual. Wildcards will be expanded, but please have a look at an important limitation of wildcards in path filters. You can also use relative paths. For example, dir:share/doc would match either /usr/share/doc or /usr/local/share/doc. -dir will find results not in the specified location. Several dir clauses can be specified, both positive and negative. For example the following makes sense: dir:recoll dir:src -dir:utils -dir:common This would select results which have both recoll and src in the path (in any order), and which have not either utils or common. You can also use OR conjunctions with dir: clauses. On Unix-like systems, a special aspect of dir clauses is that the values in the index are not transcoded to UTF-8, and never lower-cased or unaccented, but stored as binary. This means that you need to enter the values in the exact lower or upper case, and that searches for names with diacritics may sometimes be impossible because of character set conversion issues. Non-ASCII UNIX file paths are an unending source of trouble and are best avoided. You need to use double-quotes around the path value if it contains space characters. The shortcut syntax to define OR or AND lists within fields with commas or slash characters is not available. • size for filtering the results on file size. Example: size<10000. You can use <, > or = as operators. You can specify a range like the following: size>100 size<1000. The usual k/K, m/M, g/G, t/T can be used as (decimal) multipliers. Ex: size>1k to search for files bigger than 1000 bytes. • date for searching or filtering on dates. The syntax for the argument is based on the ISO8601 standard for dates and time intervals. Only dates are supported, no times. The general syntax is 2 elements separated by a / character. Each element can be a date or a period of time. Periods are specified as PnYnMnD. The n numbers are the respective numbers of years, months or days, any of which may be missing. Dates are specified as YYYY-MM -DD. The days and months parts may be missing. If the / is present but an element is missing, the missing element is interpreted as the lowest or highest date in the index. Examples: □ 2001-03-01/2002-05-01 the basic syntax for an interval of dates. □ 2001-03-01/P1Y2M the same specified with a period. □ 2001/ from the beginning of 2001 to the latest date in the index. □ 2001 the whole year of 2001 □ P2D/ means 2 days ago up to now if there are no documents with dates in the future. □ /2003 all documents from 2003 or older. Periods can also be specified with small letters (e.g.: p2y). • mime or format for specifying the MIME type. These clauses are processed apart from the normal Boolean logic of the search: multiple values will be OR'ed (instead of the normal AND). You can specify types to be excluded, with the usual -, and use wildcards. Example: mime:text/* -mime:text/plain. Specifying an explicit boolean operator before a mime specification is not supported and will produce strange results. • type or rclcat for specifying the category (as in text/media/presentation/ etc.). The classification of MIME types in categories is defined in the Recoll configuration (mimeconf), and can be modified or extended. The default category names are those which permit filtering results in the main GUI screen. Categories are OR'ed like MIME types above, and can be negated with -. • issub for specifying that only standalone (issub:0) or only embedded (issub:1) documents should be returned as results. Note mime, rclcat, size, issub and date criteria always affect the whole query (they are applied as a final filter), even if set with other terms inside a parenthese. Note mime (or the equivalent rclcat) is the only field with an OR default. You do need to use OR with ext terms for example. 3.5.3. Range clauses Recoll 1.24 and later support range clauses on fields which have been configured to support it. No default field uses them currently, so this paragraph is only interesting if you modified the fields configuration and possibly use a custom input handler. A range clause looks like one of the following: myfield:small..big myfield:small.. myfield:..big The nature of the clause is indicated by the two dots .., and the effect is to filter the results for which the myfield value is in the possibly open-ended interval. See the section about the fields configuration file for the details of configuring a field for range searches (list them in the [values] section). 3.5.4. Modifiers Some characters are recognized as search modifiers when found immediately after the closing double quote of a phrase, as in "some term"modifierchars. The actual "phrase" can be a single term of course. Supported modifiers: • l can be used to turn off stemming (mostly makes sense with p because stemming is off by default for phrases, but see also x further down). • o can be used to specify a "slack" for both phrase and proximity searches: the number of additional terms that may be found between the specified ones. If o is followed by an integer number, this is the slack, else the default is 10. The default slack (with no o) is 0 for phrase searches and 10 for proximity searches. • p can be used to turn an ordered phrase search into an unordered proximity one. Example: "order any in"p. You can find a little more detail about phrase and proximity searches here. • s (1.22) can be used to turn off synonym expansion, if a synonyms file is in place. • x (1.33.2) will enable the expansion of terms inside a phrase search (the default is for phrases to be searched verbatim). Also see the stemexpandphrases in the configuration section, for changing the default behaviour. • A weight can be specified for a query element by specifying a decimal value at the start of the modifiers. Example: "Important"2.5. The following only make sense on indexes which are capable of case and diacritics sensitivity (not the default): • C will turn on case sensitivity. • D will turn on diacritics sensitivity (if the index supports it). • e (explicit) will turn on diacritics sensitivity and case sensitivity, and prevent stem expansion. 3.6. Wildcards and anchored searches Some special characters are interpreted by Recoll in search strings to expand or specialize the search. Wildcards expand a root term in controlled ways. Anchor characters can restrict a search to succeed only if the match is found at or near the beginning of the document or one of its fields. 3.6.1. Wildcards All words entered in Recoll search fields will be processed for wildcard expansion before the request is finally executed. The wildcard characters are: • * which matches 0 or more characters. • ? which matches a single character. • [] which allow defining sets of characters to be matched (ex: [abc] matches a single character which may be 'a' or 'b' or 'c', [0-9] matches any number. You should be aware of a few things when using wildcards. • Using a wildcard character at the beginning of a word can make for a slow search because Recoll will have to scan the whole index term list to find the matches. However, this is much less a problem for field searches, and queries like author:*@domain.com can sometimes be very useful. • For Recoll version 18 only, when working with a raw index (preserving character case and diacritics), the literal part of a wildcard expression will be matched exactly for case and diacritics. This is not true any more for versions 19 and later. • Using a * at the end of a word can produce more matches than you would think, and strange search results. You can use the term explorer tool to check what completions exist for a given term. You can also see exactly what search was performed by clicking on the link at the top of the result list. In general, for natural language terms, stem expansion will produce better results than an ending * (stem expansion is turned off when any wildcard character appears in the term). Wildcards and path filtering Due to the way that Recoll processes wildcards inside dir path filtering clauses, they will have a multiplicative effect on the query size. A clause containing wildcards in several paths elements, like, for example, dir:/home/me /*/*/docdir, will almost certainly fail if your indexed tree is of any realistic size. Depending on the case, you may be able to work around the issue by specifying the paths elements more narrowly, with a constant prefix, or by using 2 separate dir: clauses instead of multiple wildcards, as in dir:/home/me dir: docdir. The latter query is not equivalent to the initial one because it does not specify a number of directory levels, but that's the best we can do (and it may be actually more useful in some cases). 3.6.2. Anchored searches Two characters are used to specify that a search hit should occur at the beginning or at the end of the text. ^ at the beginning of a term or phrase constrains the search to happen at the start, $ at the end force it to happen at the end. As this function is implemented as a phrase search it is possible to specify a maximum distance at which the hit should occur, either through the controls of the advanced search panel, or using the query language, for example, as in: "^someterm"o10 which would force someterm to be found within 10 terms of the start of the text. This can be combined with a field search as in somefield:"^someterm"o10 or somefield:someterm$. This feature can also be used with an actual phrase search, but in this case, the distance applies to the whole phrase and anchor, so that, for example, bla bla my unexpected term at the beginning of the text would be a match for "^my term"o5. Anchored searches can be very useful for searches inside somewhat structured documents like scientific articles, in case explicit metadata has not been supplied, for example for looking for matches inside the abstract or the list of authors (which occur at the top of the document). 3.7. Using Synonyms (1.22) Term synonyms and text search: in general, there are two main ways to use term synonyms for searching text: • At index creation time, they can be used to alter the indexed terms, either increasing or decreasing their number, by expanding the original terms to all synonyms, or by reducing all synonym terms to a canonical one. • At query time, they can be used to match texts containing terms which are synonyms of the ones specified by the user, either by expanding the query for all synonyms, or by reducing the user entry to canonical terms (the latter only works if the corresponding processing has been performed while creating the index). Recoll only uses synonyms at query time. A user query term which part of a synonym group will be optionally expanded into an OR query for all terms in the group. Synonym groups are defined inside ordinary text files. Each line in the file defines a group. Example: hi hello "good morning" # not sure about "au revoir" though. Is this english ? bye goodbye "see you" \ "au revoir" As usual, lines beginning with a # are comments, empty lines are ignored, and lines can be continued by ending them with a backslash. Multi-word synonyms are supported, but be aware that these will generate phrase queries, which may degrade performance and will disable stemming expansion for the phrase terms. The contents of the synonyms file must be casefolded (not only lowercased), because this is what expected at the point in the query processing where it is used. There are a few cases where this makes a difference, for example, German sharp s should be expressed as ss, Greek final sigma as sigma. For reference, Python3 has an easy way to casefold words (str.casefold()). The synonyms file can be specified in the Search parameters tab of the GUI configuration Preferences menu entry, or as an option for command-line searches. Once the file is defined, the use of synonyms can be enabled or disabled directly from the Preferences menu. The synonyms are searched for matches with user terms after the latter are stem-expanded, but the contents of the synonyms file itself is not subjected to stem expansion. This means that a match will not be found if the form present in the synonyms file is not present anywhere in the document set (same with accents when using a raw index). The synonyms function is probably not going to help you find your letters to Mr. Smith. It is best used for domain-specific searches. For example, it was initially suggested by a user performing searches among historical documents: the synonyms file would contains nicknames and aliases for each of the persons of interest. 3.8. Path translations In some cases, the document paths stored inside the index do not match the actual ones, so that document previews and accesses will fail. This can occur in a number of circumstances: • When using multiple indexes it is a relatively common occurrence that some will actually reside on a remote volume, for example mounted via NFS. In this case, the paths used to access the documents on the local machine are not necessarily the same than the ones used while indexing on the remote machine. For example, /home/me may have been used as a topdirs elements while indexing, but the directory might be mounted as /net/server/home/me on the local machine. • The case may also occur with removable disks. It is perfectly possible to configure an index to live with the documents on the removable disk, but it may happen that the disk is not mounted at the same place so that the documents paths from the index are invalid. In some case, the path adjustments can be automated. • As a last example, one could imagine that a big directory has been moved, but that it is currently inconvenient to run the indexer. Recoll has a facility for rewriting access paths when extracting the data from the index. The translations can be defined for the main index and for any additional query index. In the above NFS example, Recoll could be instructed to rewrite any file:/// home/me URL from the index to file:///net/server/home/me, allowing accesses from the client. The translations are defined in the ptrans configuration file, which can be edited by hand or from the GUI external indexes configuration dialog: Preferences → External index dialog, then click the Paths translations button on the right below the index list: translations will be set for the main index if no external index is currently selected in the list, or else for the currently selected index. Example entry from a ptrans file: [/path/to/external/xapiandb] /some/index/path = /some/local/path This would decide that, for the index stored in /path/to/external/xapiandb, any occurence of /some/index/path should be replaced with /some/local/path when presenting a result. Windows note At the moment, the path comparisons done for path translation under MS Windows are case sensitive (this will be fixed at some point). Use the natural character case as displayed in the file explorer. Example: [Z:/some/mounted/xapiandb] C: = Z: 3.9. Search case and diacritics sensitivity For Recoll versions 1.18 and later, and when working with a raw index (not the default), searches can be sensitive to character case and diacritics. How this happens is controlled by configuration variables and what search data is entered. The general default is that searches entered without upper-case or accented characters are insensitive to case and diacritics. An entry of resume will match any of Resume, RESUME, résumé, Résumé etc. Two configuration variables can automate switching on sensitivity (they were documented but actually did nothing until Recoll 1.22): autodiacsens If this is set, search sensitivity to diacritics will be turned on as soon as an accented character exists in a search term. When the variable is set to true, resume will start a diacritics-unsensitive search, but résumé will be matched exactly. The default value is false. autocasesens If this is set, search sensitivity to character case will be turned on as soon as an upper-case character exists in a search term except for the first one. When the variable is set to true, us or Us will start a diacritics-unsensitive search, but US will be matched exactly. The default value is true (contrary to autodiacsens). As in the past, capitalizing the first letter of a word will turn off its stem expansion and have no effect on case-sensitivity. You can also explicitly activate case and diacritics sensitivity by using modifiers with the query language. C will make the term case-sensitive, and D will make it diacritics-sensitive. Examples: "us"C will search for the term us exactly (Us will not be a match). "resume"D will search for the term resume exactly (résumé will not be a match). When either case or diacritics sensitivity is activated, stem expansion is turned off. Having both does not make much sense. 3.10. Desktop integration Being independent of the desktop type has its drawbacks: Recoll desktop integration is minimal. However there are a few tools available: • Users of recent Ubuntu-derived distributions, or any other Gnome desktop systems (e.g. Fedora) can install the Recoll GSSP (Gnome Shell Search Provider). • The KDE KIO Slave was described in a previous section. It can provide search results inside Dolphin. • If you use an oldish version of Ubuntu Linux, you may find the Ubuntu Unity Lens module useful. • There is also an independently developed Krunner plugin. Here follow a few other things that may help. 3.10.1. Hotkeying recoll It is surprisingly convenient to be able to show or hide the Recoll GUI with a single keystroke. Recoll comes with a small Python script, based on the libwnck window manager interface library, which will allow you to do just this. The detailed instructions are on this wiki page. 3.10.2. The KDE Kicker Recoll applet This is probably obsolete now. Anyway: The Recoll source tree contains the source code to the recoll_applet, a small application derived from the find_applet. This can be used to add a small Recoll launcher to the KDE panel. The applet is not automatically built with the main Recoll programs, nor is it included with the main source distribution (because the KDE build boilerplate makes it relatively big). You can download its source from the recoll.org download page. Use the omnipotent configure;make;make install incantation to build and install. You can then add the applet to the panel by right-clicking the panel and choosing the Add applet entry. The recoll_applet has a small text window where you can type a Recoll query (in query language form), and an icon which can be used to restrict the search to certain types of files. It is quite primitive, and launches a new recoll GUI instance every time (even if it is already running). You may find it useful anyway. Chapter 4. Programming interface Recoll has an Application Programming Interface, usable both for indexing and searching, currently accessible from the Python language. Another less radical way to extend the application is to write input handlers for new types of documents. The processing of metadata attributes for documents (fields) is highly configurable. 4.1. Writing a document input handler Terminology The small programs or pieces of code which handle the processing of the different document types for Recoll used to be called filters, which is still reflected in the name of the directory which holds them and many configuration variables. They were named this way because one of their primary functions is to filter out the formatting directives and keep the text content. However these modules may have other behaviours, and the term input handler is now progressively substituted in the documentation. filter is still used in many places though. Recoll input handlers cooperate to translate from the multitude of input document formats, simple ones as opendocument, acrobat, or compound ones such as Zip or Email, into the final Recoll indexing input format, which is plain text (in many cases the processing pipeline has an intermediary HTML step, which may be used for better previewing presentation). Most input handlers are executable programs or scripts. A few handlers are coded in C++ and live inside recollindex. This latter kind will not be described here. There are two kinds of external executable input handlers: • Simple exec handlers run once and exit. They can be bare programs like antiword, or scripts using other programs. They are very simple to write, because they just need to print the converted document to the standard output. Their output can be plain text or HTML. HTML is usually preferred because it can store metadata fields and it allows preserving some of the formatting for the GUI preview. However, these handlers have limitations: □ They can only process one document per file. □ The output MIME type must be known and fixed. □ The character encoding, if relevant, must be known and fixed (or possibly just depending on location). • Multiple execm handlers can process multiple files (sparing the process startup time which can be very significant), or multiple documents per file (e.g.: for archives or multi-chapter publications). They communicate with the indexer through a simple protocol, but are nevertheless a bit more complicated than the older kind. Most of the new handlers are written in Python (exception: rclimg which is written in Perl because exiftool has no real Python equivalent). The Python handlers use common modules to factor out the boilerplate, which can make them very simple in favorable cases. The subdocuments output by these handlers can be directly indexable (text or HTML), or they can be other simple or compound documents that will need to be processed by another handler. In both cases, handlers deal with regular file system files, and can process either a single document, or a linear list of documents in each file. Recoll is responsible for performing up to date checks, deal with more complex embedding and other upper level issues. A simple handler returning a document in text/plain format, can transfer no metadata to the indexer. Generic metadata, like document size or modification date, will be gathered and stored by the indexer. Handlers that produce text/html format can return an arbitrary amount of metadata inside HTML meta tags. These will be processed according to the directives found in the fields configuration file. The handlers that can handle multiple documents per file return a single piece of data to identify each document inside the file. This piece of data, called an ipath will be sent back by Recoll to extract the document at query time, for previewing, or for creating a temporary file to be opened by a viewer. These handlers can also return metadata either as HTML meta tags, or as named data through the communication protocol. The following section describes the simple handlers, and the next one gives a few explanations about the execm ones. You could conceivably write a simple handler with only the elements in the manual. This will not be the case for the other ones, for which you will have to look at the code. 4.1.1. Simple input handlers Recoll simple handlers are usually shell-scripts, but this is in no way necessary. Extracting the text from the native format is the difficult part. Outputting the format expected by Recoll is trivial. Happily enough, most document formats have translators or text extractors which can be called from the handler. In some cases the output of the translating program is completely appropriate, and no intermediate shell-script is needed. Input handlers are called with a single argument which is the source file name. They should output the result to stdout. When writing a handler, you should decide if it will output plain text or HTML. Plain text is simpler, but you will not be able to add metadata or vary the output character encoding (this will be defined in a configuration file). Additionally, some formatting may be easier to preserve when previewing HTML. Actually the deciding factor is metadata: Recoll has a way to extract metadata from the HTML header and use it for field searches.. The RECOLL_FILTER_FORPREVIEW environment variable (values yes, no) tells the handler if the operation is for indexing or previewing. Some handlers use this to output a slightly different format, for example stripping uninteresting repeated keywords (e.g.: Subject: for email) when indexing. This is not essential. You should look at one of the simple handlers, for example rclps for a starting point. Don't forget to make your handler executable before testing ! 4.1.2. "Multiple" handlers If you can program and want to write an execm handler, it should not be too difficult to make sense of one of the existing handlers. The existing handlers differ in the amount of helper code which they are using: • rclimg is written in Perl and handles the execm protocol all by itself (showing how trivial it is). • All the Python handlers share at least the rclexecm.py module, which handles the communication. Have a look at, for example, rclzip.py for a handler which uses rclexecm.py directly. • Most Python handlers which process single-document files by executing another command are further abstracted by using the rclexec1.py module. See for example rclrtf.py for a simple one, or rcldoc.py for a slightly more complicated one (possibly executing several commands). • Handlers which extract text from an XML document by using an XSLT style sheet are now executed inside recollindex, with only the style sheet stored in the filters/ directory. These can use a single style sheet (e.g. abiword.xsl), or two sheets for the data and metadata (e.g. opendoc-body.xsl and opendoc-meta.xsl). The mimeconf configuration file defines how the sheets are used, have a look. Before the C++ import, the xsl-based handlers used a common module rclgenxslt.py, it is still around but unused at the moment. The handler for OpenXML presentations is still the Python version because the format did not fit with what the C++ code does. It would be a good base for another similar issue. There is a sample trivial handler based on rclexecm.py, with many comments, not actually used by Recoll. It would index a text file as one document per line. Look for rcltxtlines.py in the src/filters directory in the online Recoll Git repository (the sample not in the distributed release at the moment). You can also have a look at the slightly more complex rclzip.py which uses Zip file paths as identifiers (ipath). execm handlers sometimes need to make a choice for the nature of the ipath elements that they use in communication with the indexer. Here are a few guidelines: • Use ASCII or UTF-8 (if the identifier is an integer print it, for example, like printf %d would do). • If at all possible, the data should make some kind of sense when printed to a log file to help with debugging. • Recoll uses a colon (:) as a separator to store a complex path internally (for deeper embedding). Colons inside the ipath elements output by a handler will be escaped, but would be a bad choice as a handler-specific separator (mostly, again, for debugging issues). In any case, the main goal is that it should be easy for the handler to extract the target document, given the file name and the ipath element. execm handlers will also produce a document with a null ipath element. Depending on the type of document, this may have some associated data (e.g. the body of an email message), or none (typical for an archive file). If it is empty, this document will be useful anyway for some operations, as the parent of the actual data documents. 4.1.3. Telling Recoll about the handler There are two elements that link a file to the handler which should process it: the association of file to MIME type and the association of a MIME type with a handler. The association of files to MIME types is mostly based on name suffixes. The types are defined inside the mimemap file. Example: .doc = application/msword If no suffix association is found for the file name, Recoll will try to execute a system command (typically file -i or xdg-mime) to determine a MIME type. The second element is the association of MIME types to handlers in the mimeconf file. A sample will probably be better than a long explanation: [index] application/msword = exec antiword -t -i 1 -m UTF-8;\ mimetype = text/plain ; charset=utf-8 application/ogg = exec rclogg text/rtf = exec unrtf --nopict --html; charset=iso-8859-1; mimetype=text/html application/x-chm = execm rclchm.py The fragment specifies that: • application/msword files are processed by executing the antiword program, which outputs text/plain encoded in utf-8. • application/ogg files are processed by the rclogg script, with default output type (text/html, with encoding specified in the header, or utf-8 by default). • text/rtf is processed by unrtf, which outputs text/html. The iso-8859-1 encoding is specified because it is not the utf-8 default, and not output by unrtf in the HTML header section. • application/x-chm is processed by a persistent handler. This is determined by the execm keyword. 4.1.4. Input handler output Both the simple and persistent input handlers can return any MIME type to Recoll, which will further process the data according to the MIME configuration. Most input filters filters produce either text/plain or text/html data. There are exceptions, for example, filters which process archive file (zip, tar, etc.) will usually return the documents as they are found, without processing them further. There is nothing to say about text/plain output, except that its character encoding should be consistent with what is specified in the mimeconf file. For filters producing HTML, the output could be very minimal like the following example: <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> </head> <body> Some text content </body> </html> You should take care to escape some characters inside the text by transforming them into appropriate entities. At the very minimum, "&" should be transformed into "&", "<" should be transformed into "<". This is not always properly done by external helper programs which output HTML, and of course never by those which output plain text. When encapsulating plain text in an HTML body, the display of a preview may be improved by enclosing the text inside <pre> tags. The character set needs to be specified in the header. It does not need to be UTF-8 (Recoll will take care of translating it), but it must be accurate for good results. Recoll will process meta tags inside the header as possible document fields candidates. Documents fields can be processed by the indexer in different ways, for searching or displaying inside query results. This is described in a following section. By default, the indexer will process the standard header fields if they are present: title, meta/description, and meta/keywords are both indexed and stored for query-time display. A predefined non-standard meta tag will also be processed by Recoll without further configuration: if a date tag is present and has the right format, it will be used as the document date (for display and sorting), in preference to the file modification date. The date format should be as follows: <meta name="date" content="YYYY-mm-dd HH:MM:SS"> or <meta name="date" content="YYYY-mm-ddTHH:MM:SS"> Example: <meta name="date" content="2013-02-24 17:50:00"> Input handlers also have the possibility to "invent" field names. This should also be output as meta tags: <meta name="somefield" content="Some textual data" /> You can embed HTML markup inside the content of custom fields, for improving the display inside result lists. In this case, add a (wildly non-standard) markup attribute to tell Recoll that the value is HTML and should not be escaped for display. <meta name="somefield" markup="html" content="Some <i>textual</i> data" /> As written above, the processing of fields is described in a further section. Persistent filters can use another, probably simpler, method to produce metadata, by calling the setfield() helper method. This avoids the necessity to produce HTML, and any issue with HTML quoting. See, for example, rclaudio.py in Recoll 1.23 and later for an example of handler which outputs text/plain and uses setfield() to produce metadata. 4.1.5. Page numbers The indexer will interpret ^L characters in the handler output as indicating page breaks, and will record them. At query time, this allows starting a viewer on the right page for a hit or a snippet. Currently, only the PDF, Postscript and DVI handlers generate page breaks. 4.2. Field data processing Fields are named pieces of information in or about documents, like title, author, abstract. The field values for documents can appear in several ways during indexing: either output by input handlers as meta fields in the HTML header section, or extracted from file extended attributes, or added as attributes of the Doc object when using the API, or again synthetized internally by Recoll. The Recoll query language allows searching for text in a specific field. Recoll defines a number of default fields. Additional ones can be output by handlers, and described in the fields configuration file. Fields can be: • indexed, meaning that their terms are separately stored in inverted lists (with a specific prefix), and that a field-specific search is possible. • stored, meaning that their value is recorded in the index data record for the document, and can be returned and displayed with search results. A field can be either or both indexed and stored. This and other aspects of fields handling is defined inside the fields configuration file. Some fields may also designated as supporting range queries, meaning that the results may be selected for an interval of its values. See the configuration section for more details. The sequence of events for field processing is as follows: • During indexing, recollindex scans all meta fields in HTML documents (most document types are transformed into HTML at some point). It compares the name for each element to the configuration defining what should be done with fields (the fields file) • If the name for the meta element matches one for a field that should be indexed, the contents are processed and the terms are entered into the index with the prefix defined in the fields file. • If the name for the meta element matches one for a field that should be stored, the content of the element is stored with the document data record, from which it can be extracted and displayed at query time. • At query time, if a field search is performed, the index prefix is computed and the match is only performed against appropriately prefixed terms in the index. • At query time, the field can be displayed inside the result list by using the appropriate directive in the definition of the result list paragraph format. All fields are displayed on the fields screen of the preview window (which you can reach through the right-click menu). This is independent of the fact that the search which produced the results used the field or not. You can find more information in the section about the fields file, or in comments inside the file. You can also have a look at the example in the FAQs area, detailing how one could add a page count field to pdf documents for displaying inside result lists. 4.3. Python API 4.3.1. Introduction The Recoll Python programming interface can be used both for searching and for creating/updating an index. Bindings exist for Python2 and Python3 (Jan 2021: python2 support will be dropped soon). The search interface is used in a number of active projects: the Recoll Gnome Shell Search Provider , the Recoll Web UI, and the upmpdcli UPnP Media Server, in addition to many small scripts. The index update section of the API may be used to create and update Recoll indexes on specific configurations (separate from the ones created by recollindex). The resulting databases can be queried alone, or in conjunction with regular ones, through the GUI or any of the query interfaces. The search API is modeled along the Python database API version 2.0 specification (early versions used the version 1.0 spec). The recoll package contains two modules: • The recoll module contains functions and classes used to query (or update) the index. • The rclextract module contains functions and classes used at query time to access document data. The recoll module must be imported before rclextract There is a good chance that your system repository has packages for the Recoll Python API, sometimes in a package separate from the main one (maybe named something like python-recoll). Else refer to the Building from source chapter. As an introduction, the following small sample will run a query and list the title and url for each of the results. The python/samples source directory contains several examples of Python programming with Recoll, exercising the extension more completely, and especially its data extraction features. #!/usr/bin/python3 from recoll import recoll db = recoll.connect() query = db.query() nres = query.execute("some query") results = query.fetchmany(20) for doc in results: print("%s %s" % (doc.url, doc.title)) You can also take a look at the source for the Recoll WebUI, the upmpdcli local media server, or the Gnome Shell Search Provider. 4.3.2. Interface elements A few elements in the interface are specific and and need an explanation. ipath This data value (set as a field in the Doc object) is stored, along with the URL, but not indexed by Recoll. Its contents are not interpreted by the index layer, and its use is up to the application. For example, the Recoll file system indexer uses the ipath to store the part of the document access path internal to (possibly imbricated) container documents. ipath in this case is a vector of access elements (e.g, the first part could be a path inside a zip file to an archive member which happens to be an mbox file, the second element would be the message sequential number inside the mbox etc.). url and ipath are returned in every search result and define the access to the original document. ipath is empty for top-level document/ files (e.g. a PDF document which is a filesystem file). The Recoll GUI knows about the structure of the ipath values used by the filesystem indexer, and uses it for such functions as opening the parent of a given document. udi An udi (unique document identifier) identifies a document. Because of limitations inside the index engine, it is restricted in length (to 200 bytes), which is why a regular URI cannot be used. The structure and contents of the udi is defined by the application and opaque to the index engine. For example, the internal file system indexer uses the complete document path (file path + internal path), truncated to length, the suppressed part being replaced by a hash value. The udi is not explicit in the query interface (it is used "under the hood" by the rclextract module), but it is an explicit element of the update interface. parent_udi If this attribute is set on a document when entering it in the index, it designates its physical container document. In a multilevel hierarchy, this may not be the immediate parent. parent_udi is optional, but its use by an indexer may simplify index maintenance, as Recoll will automatically delete all children defined by parent_udi == udi when the document designated by udi is destroyed. e.g. if a Zip archive contains entries which are themselves containers, like mbox files, all the subdocuments inside the Zip file (mbox, messages, message attachments, etc.) would have the same parent_udi, matching the udi for the Zip file, and all would be destroyed when the Zip file (identified by its udi) is removed from the index. The standard filesystem indexer uses parent_udi. Stored and indexed fields The fields file inside the Recoll configuration defines which document fields are either indexed (searchable), stored (retrievable with search results), or both. Apart from a few standard/internal fields, only the stored fields are retrievable through the Python search interface. 4.3.3. Log messages for Python scripts Two specific configuration variables: pyloglevel and pylogfilename allow overriding the generic values for Python programs. Set pyloglevel to 2 to suppress default startup messages (printed at level 3). 4.3.4. Python search interface The recoll module connect(confdir=None, extra_dbs=None, writable = False) The connect() function connects to one or several Recoll index(es) and returns a Db object. This call initializes the recoll module, and it should always be performed before any other call or object creation. • confdir may specify a configuration directory. The usual defaults apply. • extra_dbs is a list of additional indexes (Xapian directories). • writable decides if we can index new data through this connection. Examples: from recoll import recoll # Opening the default db db = recoll.connect() # Opening the default db and a pair of additional indexes db = recoll.connect(extra_dbs=["/home/me/.someconfdir/xapiandb", "/data/otherconf/xapiandb"]) The Db class A Db object is created by a connect() call and holds a connection to a Recoll index. Db.query(), Db.cursor() These (synonym) methods return a blank Query object for this index. Db.termMatch(match_type, expr, field='', maxlen=-1, casesens=False, diacsens= False, lang='english') Expand an expression against the index term list. Performs the basic function from the GUI term explorer tool. match_type can be either of wildcard, regexp or stem. Returns a list of terms expanded from the input expression. Db.setAbstractParams(maxchars, contextwords) Set the parameters used to build snippets (sets of keywords in context text fragments). maxchars defines the maximum total size of the abstract. contextwords defines how many terms are shown around the keyword. Db.close() Closes the connection. You can't do anything with the Db object after this. The Query class A Query object (equivalent to a cursor in the Python DB API) is created by a Db.query() call. It is used to execute index searches. Query.sortby(fieldname, ascending=True) Sets the sorting order for future searches for using fieldname, in ascending or descending order. Must be called before executing the search. Query.execute(query_string, stemming=1, stemlang="english", fetchtext=False, collapseduplicates=False) Starts a search for query_string, a Recoll search language string. If the index stores the document texts and fetchtext is True, store the document extracted text in doc.text. Query.executesd(SearchData, fetchtext=False, collapseduplicates=False) Starts a search for the query defined by the SearchData object. If the index stores the document texts and fetchtext is True, store the document extracted text in doc.text. Query.fetchmany(size=query.arraysize) Fetches the next Doc objects in the current search results, and returns them as an array of the required size, which is by default the value of the arraysize data member. Query.fetchone() Fetches the next Doc object from the current search results. Generates a StopIteration exception if there are no results left. Query.__iter__() and Query.next() So that things like for doc in query: will work. Example: from recoll import recoll db = recoll.connect() q = db.query() nres = q.execute("some query") for doc in q: print("%s" % doc.title) Query.close() Closes the query. The object is unusable after the call. Query.scroll(value, mode='relative') Adjusts the position in the current result set. mode can be relative or absolute. Query.getgroups() Retrieves the expanded query terms as a list of pairs. Meaningful only after executexx In each pair, the first entry is a list of user terms (of size one for simple terms, or more for group and phrase clauses), the second a list of query terms as derived from the user terms and used in the Xapian Query. Query.getxquery() Return the Xapian query description as a Unicode string. Meaningful only after executexx. Query.highlight(text, ishtml = 0, methods = object) Will insert <span "class=rclmatch">, </span> tags around the match areas in the input text and return the modified text. ishtml can be set to indicate that the input text is HTML and that HTML special characters should not be escaped. methods if set should be an object with methods startMatch(i) and endMatch() which will be called for each match and should return a begin and end tag Query.makedocabstract(doc, methods = object)) Create a snippets abstract for doc (a Doc object) by selecting text around the match terms. If methods is set, will also perform highlighting. See the highlight method. Query.getsnippets(doc, maxoccs = -1, ctxwords = -1, sortbypage=False, methods = object) Will return a list of extracts from the result document by selecting text around the match terms. Each entry in the result list is a triple: page number, term, text. By default, the most relevants snippets appear first in the list. Set sortbypage to sort by page number instead. If methods is set, the fragments will be highlighted (see the highlight method). If maxoccs is set, it defines the maximum result list length. ctxwords allows adjusting the individual snippet context size. Query.arraysize Default number of records processed by fetchmany (r/w). Query.rowcount Number of records returned by the last execute. Query.rownumber Next index to be fetched from results. Normally increments after each fetchone() call, but can be set/reset before the call to effect seeking (equivalent to using scroll()). Starts at 0. The Doc class A Doc object contains index data for a given document. The data is extracted from the index when searching, or set by the indexer program when updating. The Doc object has many attributes to be read or set by its user. It mostly matches the Rcl::Doc C++ object. Some of the attributes are predefined, but, especially when indexing, others can be set, the name of which will be processed as field names by the indexing configuration. Inputs can be specified as Unicode or strings. Outputs are Unicode objects. All dates are specified as Unix timestamps, printed as strings. Please refer to the rcldb/rcldoc.cpp C++ file for a full description of the predefined attributes. Here follows a short list. • url the document URL but see also getbinurl() • ipath the document ipath for embedded documents. • fbytes, dbytes the document file and text sizes. • fmtime, dmtime the document file and document times. • xdocid the document Xapian document ID. This is useful if you want to access the document through a direct Xapian operation. • mtype the document MIME type. • Fields stored by default: author, filename, keywords, recipient At query time, only the fields that are defined as stored either by default or in the fields configuration file will be meaningful in the Doc object. The document processed text may be present or not, depending if the index stores the text at all, and if it does, on the fetchtext query execute option. See also the rclextract module for accessing document contents. get(key), [] operator Retrieve the named document attribute. You can also use getattr(doc, key) or doc.key. doc.key = value Set the the named document attribute. You can also use setattr(doc, key, value). getbinurl() Retrieve the URL in byte array format (no transcoding), for use as parameter to a system call. setbinurl(url) Set the URL in byte array format (no transcoding). items() Return a dictionary of doc object keys/values keys() list of doc object keys (attribute names). The SearchData class A SearchData object allows building a query by combining clauses, for execution by Query.executesd(). It can be used in replacement of the query language approach. The interface is going to change a little, so no detailed doc for now... addclause(type='and'|'or'|'excl'|'phrase'|'near'|'sub', qstring=string, slack= 0, field='', stemming=1, subSearch=SearchData) The rclextract module Prior to Recoll 1.25, index queries could not provide document content because it was never stored. Recoll 1.25 and later usually store the document text, which can be optionally retrieved when running a query (see query.execute() above - the result is always plain text). Independantly, the rclextract module can give access to the original document and to the document text content, possibly as an HTML version. Accessing the original document is particularly useful if it is embedded (e.g. an email attachment). You need to import the recoll module before the rclextract module. The Extractor class Extractor(doc) An Extractor object is built from a Doc object, output from a query. Extractor.textextract(ipath) Extract document defined by ipath and return a Doc object. The doc.text field has the document text converted to either text/plain or text/html according to doc.mimetype. The typical use would be as follows: from recoll import recoll, rclextract qdoc = query.fetchone() extractor = rclextract.Extractor(qdoc) doc = extractor.textextract(qdoc.ipath) # use doc.text, e.g. for previewing Passing qdoc.ipath to textextract() is redundant, but reflects the fact that the Extractor object actually has the capability to access the other entries in a compound document. Extractor.idoctofile(ipath, targetmtype, outfile='') Extracts document into an output file, which can be given explicitly or will be created as a temporary file to be deleted by the caller. Typical use: from recoll import recoll, rclextract qdoc = query.fetchone() extractor = rclextract.Extractor(qdoc) filename = extractor.idoctofile(qdoc.ipath, qdoc.mimetype) In all cases the output is a copy, even if the requested document is a regular system file, which may be wasteful in some cases. If you want to avoid this, you can test for a simple file document as follows: not doc.ipath and (not "rclbes" in doc.keys() or doc["rclbes"] == "FS") Search API usage example The following sample would query the index with a user language string. See the python/samples directory inside the Recoll source for other examples. The recollgui subdirectory has a very embryonic GUI which demonstrates the highlighting and data extraction functions. #!/usr/bin/python3 from recoll import recoll db = recoll.connect() db.setAbstractParams(maxchars=80, contextwords=4) query = db.query() nres = query.execute("some user question") print("Result count: %d" % nres) if nres > 5: nres = 5 for i in range(nres): doc = query.fetchone() print("Result #%d" % (query.rownumber)) for k in ("title", "size"): print("%s : %s" % (k, getattr(doc, k))) print("%s\n" % db.makeDocAbstract(doc, query)) 4.3.5. Creating Python external indexers The update API can be used to create an index from data which is not accessible to the regular Recoll indexer, or structured in a way which presents difficulties to the Recoll input handlers. An indexer created using this API will have to do equivalent work as the the Recoll file system indexer: look for modified documents, extract their text, call the API for indexing it, take care of purging the index of documents which do not exist any more. The index data from such an external indexer should be stored in an index separate from any used by the Recoll internal file system indexer. The reason is that the main document indexer purge pass (removal of deleted documents) would also remove all the documents belonging to the external indexer, as they were not seen during the filesystem walk (and conversely, the external indexer purge pass would delete all the regular document entries). While there would be ways to enable multiple foreign indexers to cooperate on a single index, it is just simpler to use separate ones, and use the multiple index access capabilities of the query interface, if needed. There are two parts in the update interface: • Methods inside the recoll module allow the foreign indexer to insert data into the index, to make it accessible by the normal query interface. • An interface based on scripts execution is defined to allow either the GUI or the rclextract module to access original document data for previewing or editing. Python update interface The update methods are part of the recoll module described above. The connect() method is used with a writable=true parameter to obtain a writable Db object. The following Db object methods are then available. addOrUpdate(udi, doc, parent_udi=None) Add or update index data for a given document The udi string must define a unique id for the document. It is an opaque interface element and not interpreted inside Recoll. doc is a Doc object, created from the data to be indexed (the main text should be in doc.text). If parent_udi is set, this is a unique identifier for the top-level container (e.g. for the filesystem indexer, this would be the one which is an actual file). The doc url and possibly ipath fields should also be set to allow access to the actual document after a query. Other fields should also be set to allow further access to the data, see the description further down: rclbes, sig, mimetype. Of course, any standard or custom Recoll field can also be added. delete(udi) Purge index from all data for udi, and all documents (if any) which have a matching parent_udi. needUpdate(udi, sig) Test if the index needs to be updated for the document identified by udi. If this call is to be used, the doc.sig field should contain a signature value when calling addOrUpdate(). The needUpdate() call then compares its parameter value with the stored sig for udi. sig is an opaque value, compared as a string. The filesystem indexer uses a concatenation of the decimal string values for file size and update time, but a hash of the contents could also be used. As a side effect, if the return value is false (the index is up to date), the call will set the existence flag for the document (and any subdocument defined by its parent_udi), so that a later purge() call will preserve them). The use of needUpdate() and purge() is optional, and the indexer may use another method for checking the need to reindex or to delete stale entries. purge() Delete all documents that were not touched during the just finished indexing pass (since open-for-write). These are the documents for the needUpdate() call was not performed, indicating that they no longer exist in the primary storage system. createStemDbs(lang|sequence of langs) Create stemming dictionaries for query stemming expansion. Should be called when done updating the index. Available only after Recoll 1.34.3. As an alternative, you can close the index and execute: recollindex -c <confdir> -s <lang(s)> The Python module currently has no interface to the Aspell speller functions, so the same approach can be used for creating the spelling dictionary (with option -S). Query data access for external indexers Recoll has internal methods to access document data for its internal (filesystem) indexer. An external indexer needs to provide data access methods if it needs integration with the GUI (e.g. preview function), or support for the rclextract module. The index data and the access method are linked by the rclbes (recoll backend storage) Doc field. You should set this to a short string value identifying your indexer (e.g. the filesystem indexer uses either "FS" or an empty value, the Web history indexer uses "BGL"). The link is actually performed inside a backends configuration file (stored in the configuration directory). This defines commands to execute to access data from the specified indexer. Example, for the mbox indexing sample found in the Recoll source (which sets rclbes="MBOX"): [MBOX] fetch = /path/to/recoll/src/python/samples/rclmbox.py fetch makesig = path/to/recoll/src/python/samples/rclmbox.py makesig fetch and makesig define two commands to execute to respectively retrieve the document text and compute the document signature (the example implementation uses the same rclmbox.py script with different first parameters to perform both operations, but this is in no way mandatory). The scripts are called with three additional arguments: udi, url, ipath. These were set by the indexer and stored with the document by the addOrUpdate() call described above. Not all arguments are needed in all cases, the script will use what it needs to perform the requested operation. The caller expects the result data on stdout. External indexer samples The Recoll source tree has two samples of external indexers in the src/python/ samples directory. • rclmbox.py indexes a directory containing mbox folder files. Of course it is not really useful because Recoll can do this by itself, but it exercises most features in the update interface, and has a data access interface. Also it generates compound documents with actual ipath values. • rcljoplin.py indexes a Joplin application main notes SQL table. Joplin sets an an update date attribute for each record in the table, so each note record can be processed as a standalone document (no ipath necessary). The sample has full preview and open support (the latter using a Joplin callback URL which allows displaying the result note inside the native app), so it could actually be useful to perform a unified search of the Joplin data and the regular Recoll data. See the comments inside the scripts for more information. Using an external indexer index in conjunction with a regular one When adding an external indexer to a regular one for unified querying, some elements of the foreign index configuration should be copied or merged into the main index configuration. At the very least, the backends file needs to be copied or merged, and also possibly data from the mimeconf and mimeview files. See the rcljoplin.py sample for an example. Chapter 5. Installation and configuration 5.1. Installing a binary copy Recoll binary copies are always distributed as regular packages for your system. They can be obtained either through the system's normal software distribution framework (e.g. Debian/Ubuntu apt, FreeBSD ports, etc.), or from some type of "backports" repository providing versions newer than the standard ones, or found on the Recoll Web site in some cases. The most up-to-date information about Recoll packages can usually be found on the Recoll Web site downloads page The Windows version of Recoll comes in a self-contained setup file, there is nothing else to install. On Unix-like systems, the package management tools will automatically install hard dependencies for packages obtained from a proper package repository. You will have to deal with them by hand for downloaded packages (for example, when dpkg complains about missing dependencies). In all cases, you will have to check or install supporting applications for the file types that you want to index beyond those that are natively processed by Recoll (text, HTML, email files, and a few others). You should also maybe have a look at the configuration section (but this may not be necessary for a quick test with default parameters). Most parameters can be more conveniently set from the GUI interface. 5.2. Supporting packages Note The Windows installation of Recoll is self-contained. Windows users can skip this section. Recoll uses external applications to index some file types. You need to install them for the file types that you wish to have indexed (these are run-time optional dependencies. None is needed for building or running Recoll except for indexing their specific file type). After an indexing pass, the commands that were found missing can be displayed from the recoll File menu. The list is stored in the missing text file inside the configuration directory. The past has proven that I was unable to maintain an up to date application list in this manual. Please check http://www.recoll.org/pages/features.html for a complete list along with links to the home pages or best source/patches pages, and misc tips. What follows is only a very short extract of the stable essentials. • PDF files need pdftotext which is part of Poppler (usually comes with the poppler-utils package). Avoid the original one from Xpdf. • MS Word documents need antiword. It is also useful to have wvWare installed as it may be be used as a fallback for some files which antiword does not handle. • RTF files need unrtf, which, in its older versions, has much trouble with non-western character sets. Many Linux distributions carry outdated unrtf versions. Check http://www.recoll.org/pages/features.html for details. • Pictures: Recoll uses the Exiftool Perl package to extract tag information. Most image file formats are supported. • Up to Recoll 1.24, many XML-based formats need the xsltproc command, which usually comes with libxslt. These are: abiword, fb2 ebooks, kword, openoffice, opendocument svg. Recoll 1.25 and later process them internally (using libxslt). 5.3. Building from source 5.3.1. Prerequisites The following prerequisites are described in broad terms and Debian package names. The dependencies should be available as packages on most common Unix derivatives, and it should be quite uncommon that you would have to build one of them. Finding the right package name for other systems is left to the sagacity of the reader. If you do not need the GUI, you can avoid all GUI dependencies by disabling its build. (See the configure section further down: --disable-qtgui). The shopping list follows: • If you start from git code, you will need the git command (git), and the autoconf, automake and libtool triad (autoconf, automake, libtool). These are not needed for building from tar distributions. • The pkg-config command and gettext package are needed for configuring the build (pkg-config, gettext). • The make command (make which will actually install GNU make on Linux). If you get strange error messages about dependency tracking from configure, you probably forgot to install make. • A C++ compiler with at least C++14 compatibility (g++ or clang). Versions 1.33.4 and older only require c++11. • The bison command is not generally needed, but might be if some file modification times are not right. • If you want to process CHM files, you will need libchm (libchm-dev), else you can set the --disable-python-chm option to the configure command. • For building the documentation: the xsltproc command, and the Docbook XML and style sheet files. You can avoid this dependency by disabling documentation building with the --disable-userdoc configure option. • Development files for Xapian core (libxapian-dev). Important If you are building Xapian for an older CPU (before Pentium 4 or Athlon 64), you need to add the --disable-sse flag to the configure command. Else all Xapian applications will crash with an illegal instruction error. • Development files for libxslt (libxslt1-dev). • Development files for zlib (zlib1g-dev). • Development files for libaspell (libaspell-dev). Can be avoided with the --disable-aspell option to configure. • If you want to build the GUI: development files for Qt 5. Else give the --disable-qtgui option to the configure command. You will probably need qtbase5-dev and qttools5-dev-tools I can never remember what other packages to install, but installing the Qt5 Webkit, (or Qt5 Webengine if you set --enable-webengine) will usually bring them as dependencies (libqt5webkit5-dev or qtwebengine5dev). The Recoll GUI can use either Webkit or Webengine for displaying the results. • Development files for Python3 (python3-all-dev, python3-setuptools). You can use the --disable-python-module option for disabling the build of the Python extension. On older systems still supporting Python2, you can also install python2-dev and python-setuptools. The build will test for the presence of the python2 and/or python3 commands and behave accordingly. • You may also need libiconv. On Linux systems, the iconv interface is part of libc and you should not need to do anything special. Check the Recoll download page for up to date version information. 5.3.2. Building Recoll has been built on Linux, FreeBSD, MacOS, and Solaris, most versions after 2005 should be ok, maybe some older ones too (Solaris 8 used to be ok). Current Recoll versions (1.34 and later) need a c++14 compiler and Qt5, so they will not build on old systems, but if really needed, you can probably find an older version which will work for you. If you build on another system, and need to modify things, I would very much welcome patches. Configure options: --without-aspell will disable the code for phonetic matching of search terms. --with-fam or --with-inotify will enable the code for real time indexing. Inotify support is enabled by default on Linux systems. --with-qzeitgeist will enable sending Zeitgeist events about the visited search results, and needs the qzeitgeist package. --disable-qtgui will disable the Qt graphical interface, which allows building the indexer and the command line search program in absence of a Qt environment. --disable-webkit will implement the result list with a Qt QTextBrowser instead of a WebKit widget if you do not or can't depend on the latter. --enable-webengine will enable the use of Qt Webengine (only meaningful if the Qt GUI is enabled), in place or Qt Webkit. --enable-guidebug will build the recoll GUI program with debug symbols. This makes it very big (~50MB), which is why it is stripped by default. --disable-idxthreads is available from version 1.19 to suppress multithreading inside the indexing process. You can also use the run-time configuration to restrict recollindex to using a single thread, but the compile-time option may disable a few more unused locks. This only applies to the use of multithreading for the core index processing (data input). The Recoll monitor mode always uses at least two threads of execution. --disable-python-module will avoid building the Python module. --disable-python-chm will avoid building the Python libchm interface used to index CHM files. --enable-camelcase will enable splitting camelCase words. This is not enabled by default as it has the unfortunate side-effect of making some phrase searches quite confusing: ie, "MySQL manual" would be matched by "MySQL manual" and "my sql manual" but not "mysql manual" (only inside phrase searches). --with-file-command Specify the version of the 'file' command to use (e.g.: --with-file-command=/usr/local/bin/file). Can be useful to enable the gnu version on systems where the native one is bad. --disable-x11mon Disable X11 connection monitoring inside recollindex. Together with --disable-qtgui, this allows building recoll without Qt and X11. --disable-userdoc will avoid building the user manual. This avoids having to install the Docbook XML/XSL files and the TeX toolchain used for translating the manual to PDF. --enable-recollq Enable building the recollq command line query tool (recoll -t without need for Qt). This is done by default if --disable-qtgui is set but this option enables forcing it. --disable-pic (Recoll versions up to 1.21 only) will compile Recoll with position-dependant code. This is incompatible with building the KIO or the Python or PHP extensions, but might yield very marginally faster code. --without-systemd Disable the automatic installation of systemd unit files. Normally unit files are installed if the install path can be detected. --with-system-unit-dir=DIR Provide an install path for the systemd system unit template file. --with-user-unit-dir=DIR Provide an install path for the systemd user unit file. Of course the usual autoconf configure options, like --prefix apply. Normal procedure, for source extracted from a tar distribution) cd recoll-xxx ./configure [options] make (practices usual hardship-repelling invocations) Building from git code When building from source cloned from the git repository, you also need to install autoconf, automake, and libtool and you must execute sh autogen.sh in the top source directory before running configure. 5.3.3. Installing Use make install in the root of the source tree. This will copy the commands to prefix/bin and the sample configuration files, scripts and other shared data to prefix/share/recoll. 5.3.4. Python API package The Python interface can be found in the source tree, under the python/recoll directory. The normal Recoll build procedure (see above) installs the API package for both Python2 and Python3. The Python2 version will probably go away in the near future. The python/recoll/ directory contains the usual setup.py. After configuring and building the main Recoll code, you can use the script to build and install the Python module for a non-standard Python version. cd recoll-xxx/python/recoll pythonX setup.py build sudo pythonX setup.py install 5.4. Configuration overview Most of the parameters specific to the recoll GUI are set through the Preferences menu and stored in the standard Qt place ($HOME/.config/Recoll.org/ recoll.conf). You probably do not want to edit this by hand. Recoll indexing options are set inside text configuration files located in a configuration directory. There can be several such directories, each of which defines the parameters for one index. The configuration files can be edited by hand or through the Index configuration dialog (Preferences menu). The GUI tool will try to respect your formatting and comments as much as possible, so it is quite possible to use both approaches on the same configuration. For each index, there are at least two sets of configuration files. System-wide configuration files are kept in a directory named like /usr/share/recoll/ examples, and define default values, shared by all indexes (the values in these files are often commented out, and just present to indicate the default coded in the program). For each index, a parallel set of files defines the customized parameters. On Unix-like systems, the default location of the customized configuration is the .recoll directory in your home. On Windows it is C:/Users/[you]/AppData/ Local/Recoll. Most people will only use this directory. The default location for the configuration directory can be changed, or others can be added for separate indexes with the RECOLL_CONFDIR environment variable or the -c option parameter to recoll and recollindex. In addition (as of Recoll version 1.19.7), it is possible to specify two additional configuration directories which will be stacked before and after the user configuration directory. These are defined by the RECOLL_CONFTOP and RECOLL_CONFMID environment variables. Values from configuration files inside the top directory will override user ones, values from configuration files inside the middle directory will override system ones and be overridden by user ones. These two variables may be of use to applications which augment Recoll functionality, and need to add configuration data without disturbing the user's files. Please note that the two, currently single, values will probably be interpreted as colon-separated lists in the future: do not use colon characters inside the directory paths. If the default configuration directory does not exist when recoll or recollindex are started, it will be created with a set of empty configuration files. recoll will give you a chance to edit the configuration file before starting indexing. recollindex will proceed immediately. To avoid mistakes, the automatic directory creation will only occur for the default location, not if -c or RECOLL_CONFDIR were used (in the latter cases, you will have to create the directory). All configuration files share the same format. For example, a short extract of the main configuration file might look as follows: # Space-separated list of files and directories to index. topdirs = ~/docs /usr/share/doc [~/somedirectory-with-utf8-txt-files] defaultcharset = utf-8 There are three kinds of lines: • Comment (starts with #) or empty. • Parameter affectation (name = value). • Section definition ([somedirname]). Long lines can be broken by ending each incomplete part with a backslash (\). Depending on the type of configuration file, section definitions either separate groups of parameters or allow redefining some parameters for a directory sub-tree. They stay in effect until another section definition, or the end of file, is encountered. Some of the parameters used for indexing are looked up hierarchically from the current directory location upwards. Not all parameters can be meaningfully redefined, this is specified for each in the next section. Important Global parameters must not be defined in a directory subsection, else they will not be found at all by the Recoll code, which looks for them at the top level (e.g. skippedPaths). When found at the beginning of a file path, the tilde character (~) is expanded to the name of the user's home directory, as a shell would do. Some parameters are lists of strings. White space is used for separation. List elements with embedded spaces can be quoted using double-quotes. Double quotes inside these elements can be escaped with a backslash. No value inside a configuration file can contain a newline character. Long lines can be continued by escaping the physical newline with backslash, even inside quoted strings. astringlist = "some string \ with spaces" thesame = "some string with spaces" Parameters which are not part of string lists can't be quoted, and leading and trailing space characters are stripped before the value is used. Important Quotes processing is ONLY applied to parameter values which are lists. Double quoting a single value like, e.g. dbdir will result in an incorrect value, with quotes included. This is quite confusing, and may have been a design mistake but it is much too late to fix. Encoding issues. Most of the configuration parameters are plain ASCII. Two particular sets of values may cause encoding issues: • File path parameters may contain non-ascii characters and should use the exact same byte values as found in the file system directory. Usually, this means that the configuration file should use the system default locale encoding. • The unac_except_trans parameter should be encoded in UTF-8. If your system locale is not UTF-8, and you need to also specify non-ascii file paths, this poses a difficulty because common text editors cannot handle multiple encodings in a single file. In this relatively unlikely case, you can edit the configuration file as two separate text files with appropriate encodings, and concatenate them to create the complete configuration. 5.4.1. Environment variables RECOLL_CONFDIR Defines the main configuration directory. RECOLL_TMPDIR, TMPDIR Locations for temporary files, in this order of priority. The default if none of these is set is to use /tmp. Big temporary files may be created during indexing, mostly for decompressing, and also for processing, e.g. email attachments. RECOLL_CONFTOP, RECOLL_CONFMID Allow adding configuration directories with priorities below and above the user directory (see above the Configuration overview section for details). RECOLL_EXTRA_DBS, RECOLL_ACTIVE_EXTRA_DBS Help for setting up external indexes. See this paragraph for explanations. RECOLL_DATADIR Defines replacement for the default location of Recoll data files, normally found in, e.g., /usr/share/recoll). RECOLL_FILTERSDIR Defines replacement for the default location of Recoll filters, normally found in, e.g., /usr/share/recoll/filters). ASPELL_PROG aspell program to use for creating the spelling dictionary. The result has to be compatible with the libaspell which Recoll is using. 5.4.2. Recoll main configuration file, recoll.conf Parameters affecting what documents we index topdirs Space-separated list of files or directories to recursively index. Default to ~ (indexes $HOME). You can use symbolic links in the list, they will be followed, independently of the value of the followLinks variable. monitordirs Space-separated list of files or directories to monitor for updates. When running the real-time indexer, this allows monitoring only a subset of the whole indexed area. The elements must be included in the tree defined by the 'topdirs' members. skippedNames Files and directories which should be ignored. White space separated list of wildcard patterns (simple ones, not paths, must contain no '/' characters), which will be tested against file and directory names. Have a look at the default configuration for the initial value, some entries may not suit your situation. The easiest way to see it is through the GUI Index configuration "local parameters" panel. The list in the default configuration does not exclude hidden directories (names beginning with a dot), which means that it may index quite a few things that you do not want. On the other hand, email user agents like Thunderbird usually store messages in hidden directories, and you probably want this indexed. One possible solution is to have ".*" in "skippedNames", and add things like "~/.thunderbird" "~/.evolution" to "topdirs". Not even the file names are indexed for patterns in this list, see the "noContentSuffixes" variable for an alternative approach which indexes the file names. Can be redefined for any subtree. skippedNames- List of name endings to remove from the default skippedNames list. skippedNames+ List of name endings to add to the default skippedNames list. onlyNames Regular file name filter patterns If this is set, only the file names not in skippedNames and matching one of the patterns will be considered for indexing. Can be redefined per subtree. Does not apply to directories. noContentSuffixes List of name endings (not necessarily dot-separated suffixes) for which we don't try MIME type identification, and don't uncompress or index content. Only the names will be indexed. This complements the now obsoleted recoll_noindex list from the mimemap file, which will go away in a future release (the move from mimemap to recoll.conf allows editing the list through the GUI). This is different from skippedNames because these are name ending matches only (not wildcard patterns), and the file name itself gets indexed normally. This can be redefined for subdirectories. noContentSuffixes- List of name endings to remove from the default noContentSuffixes list. noContentSuffixes+ List of name endings to add to the default noContentSuffixes list. skippedPaths Absolute paths we should not go into. Space-separated list of wildcard expressions for absolute filesystem paths (for files or directories). The variable must be defined at the top level of the configuration file, not in a subsection. Any value in the list must be textually consistent with the values in topdirs, no attempts are made to resolve symbolic links. In practise, if, as is frequently the case, /home is a link to /usr/home, your default topdirs will have a single entry '~' which will be translated to '/home/ yourlogin'. In this case, any skippedPaths entry should start with '/home/ yourlogin' *not* with '/usr/home/yourlogin'. The index and configuration directories will automatically be added to the list. The expressions are matched using 'fnmatch(3)' with the FNM_PATHNAME flag set by default. This means that '/' characters must be matched explicitly. You can set 'skippedPathsFnmPathname' to 0 to disable the use of FNM_PATHNAME (meaning that '/*/dir3' will match '/dir1/dir2/dir3'). The default value contains the usual mount point for removable media to remind you that it is in most cases a bad idea to have Recoll work on these Explicitly adding '/media/xxx' to the 'topdirs' variable will override this. skippedPathsFnmPathname Set to 0 to override use of FNM_PATHNAME for matching skipped paths. nowalkfn File name which will cause its parent directory to be skipped. Any directory containing a file with this name will be skipped as if it was part of the skippedPaths list. Ex: .recoll-noindex daemSkippedPaths skippedPaths equivalent specific to real time indexing. This enables having parts of the tree which are initially indexed but not monitored. If daemSkippedPaths is not set, the daemon uses skippedPaths. zipUseSkippedNames Use skippedNames inside Zip archives. Fetched directly by the rclzip.py handler. Skip the patterns defined by skippedNames inside Zip archives. Can be redefined for subdirectories. See https://www.lesbonscomptes.com/recoll/ faqsandhowtos/FilteringOutZipArchiveMembers.html zipSkippedNames Space-separated list of wildcard expressions for names that should be ignored inside zip archives. This is used directly by the zip handler. If zipUseSkippedNames is not set, zipSkippedNames defines the patterns to be skipped inside archives. If zipUseSkippedNames is set, the two lists are concatenated and used. Can be redefined for subdirectories. See https:// www.lesbonscomptes.com/recoll/faqsandhowtos/ FilteringOutZipArchiveMembers.html followLinks Follow symbolic links during indexing. The default is to ignore symbolic links to avoid multiple indexing of linked files. No effort is made to avoid duplication when this option is set to true. This option can be set individually for each of the 'topdirs' members by using sections. It can not be changed below the 'topdirs' level. Links in the 'topdirs' list itself are always followed. indexedmimetypes Restrictive list of indexed mime types. Normally not set (in which case all supported types are indexed). If it is set, only the types from the list will have their contents indexed. The names will be indexed anyway if indexallfilenames is set (default). MIME type names should be taken from the mimemap file (the values may be different from xdg-mime or file -i output in some cases). Can be redefined for subtrees. excludedmimetypes List of excluded MIME types. Lets you exclude some types from indexing. MIME type names should be taken from the mimemap file (the values may be different from xdg-mime or file -i output in some cases) Can be redefined for subtrees. nomd5types Don't compute md5 for these types. md5 checksums are used only for deduplicating results, and can be very expensive to compute on multimedia or other big files. This list lets you turn off md5 computation for selected types. It is global (no redefinition for subtrees). At the moment, it only has an effect for external handlers (exec and execm). The file types can be specified by listing either MIME types (e.g. audio/mpeg) or handler names (e.g. rclaudio.py). compressedfilemaxkbs Size limit for compressed files. We need to decompress these in a temporary directory for identification, which can be wasteful in some cases. Limit the waste. Negative means no limit. 0 results in no processing of any compressed file. Default 100 MB. textfilemaxmbs Size limit for text files. Mostly for skipping monster logs. Default 20 MB. Use a value of -1 to disable. textunknownasplain Process unknown text/xxx files as text/plain Allows indexing misc. text files identified as text/whatever by 'file' or 'xdg-mime' without having to explicitely set config entries for them. This works fine for indexing (but will cause processing of a lot of garbage though), but the documents indexed this way will be opened by the desktop viewer, even if text/plain has a specific editor. indexallfilenames Index the file names of unprocessed files Index the names of files the contents of which we don't index because of an excluded or unsupported MIME type. usesystemfilecommand Use a system command for file MIME type guessing as a final step in file type identification This is generally useful, but will usually cause the indexing of many bogus 'text' files. See 'systemfilecommand' for the command used. systemfilecommand Command used to guess MIME types if the internal methods fails This should be a "file -i" workalike. The file path will be added as a last parameter to the command line. "xdg-mime" works better than the traditional "file" command, and is now the configured default (with a hard-coded fallback to "file") processwebqueue Decide if we process the Web queue. The queue is a directory where the Recoll Web browser plugins create the copies of visited pages. textfilepagekbs Page size for text files. If this is set, text/plain files will be divided into documents of approximately this size. Will reduce memory usage at index time and help with loading data in the preview window at query time. Particularly useful with very big files, such as application or system logs. Also see textfilemaxmbs and compressedfilemaxkbs. membermaxkbs Size limit for archive members. This is passed to the filters in the environment as RECOLL_FILTER_MAXMEMBERKB. Parameters affecting how we generate terms and organize the index indexStripChars Decide if we store character case and diacritics in the index. If we do, searches sensitive to case and diacritics can be performed, but the index will be bigger, and some marginal weirdness may sometimes occur. The default is a stripped index. When using multiple indexes for a search, this parameter must be defined identically for all. Changing the value implies an index reset. indexStoreDocText Decide if we store the documents' text content in the index. Storing the text allows extracting snippets from it at query time, instead of building them from index position data. Newer Xapian index formats have rendered our use of positions list unacceptably slow in some cases. The last Xapian index format with good performance for the old method is Chert, which is default for 1.2, still supported but not default in 1.4 and will be dropped in 1.6. The stored document text is translated from its original format to UTF-8 plain text, but not stripped of upper-case, diacritics, or punctuation signs. Storing it increases the index size by 10-20% typically, but also allows for nicer snippets, so it may be worth enabling it even if not strictly needed for performance if you can afford the space. The variable only has an effect when creating an index, meaning that the xapiandb directory must not exist yet. Its exact effect depends on the Xapian version. For Xapian 1.4, if the variable is set to 0, the Chert format will be used, and the text will not be stored. If the variable is 1, Glass will be used, and the text stored. For Xapian 1.2, and for versions after 1.5 and newer, the index format is always the default, but the variable controls if the text is stored or not, and the abstract generation method. With Xapian 1.5 and later, and the variable set to 0, abstract generation may be very slow, but this setting may still be useful to save space if you do not use abstract generation at all. nonumbers Decides if terms will be generated for numbers. For example "123", "1.5e6", 192.168.1.4, would not be indexed if nonumbers is set ("value123" would still be). Numbers are often quite interesting to search for, and this should probably not be set except for special situations, ie, scientific documents with huge amounts of numbers in them, where setting nonumbers will reduce the index size. This can only be set for a whole index, not for a subtree. dehyphenate Determines if we index 'coworker' also when the input is 'co-worker'. This is new in version 1.22, and on by default. Setting the variable to off allows restoring the previous behaviour. backslashasletter Process backslash as normal letter. This may make sense for people wanting to index TeX commands as such but is not of much general use. underscoreasletter Process underscore as normal letter. This makes sense in so many cases that one wonders if it should not be the default. maxtermlength Maximum term length. Words longer than this will be discarded. The default is 40 and used to be hard-coded, but it can now be adjusted. You need an index reset if you change the value. nocjk Decides if specific East Asian (Chinese Korean Japanese) characters/word splitting is turned off. This will save a small amount of CPU if you have no CJK documents. If your document base does include such text but you are not interested in searching it, setting nocjk may be a significant time and space saver. cjkngramlen This lets you adjust the size of n-grams used for indexing CJK text. The default value of 2 is probably appropriate in most cases. A value of 3 would allow more precision and efficiency on longer words, but the index will be approximately twice as large. indexstemminglanguages Languages for which to create stemming expansion data. Stemmer names can be found by executing 'recollindex -l', or this can also be set from a list in the GUI. The values are full language names, e.g. english, french... defaultcharset Default character set. This is used for files which do not contain a character set definition (e.g.: text/plain). Values found inside files, e.g. a 'charset' tag in HTML documents, will override it. If this is not set, the default character set is the one defined by the NLS environment ($LC_ALL, $LC_CTYPE, $LANG), or ultimately iso-8859-1 (cp-1252 in fact). If for some reason you want a general default which does not match your LANG and is not 8859-1, use this variable. This can be redefined for any sub-directory. unac_except_trans A list of characters, encoded in UTF-8, which should be handled specially when converting text to unaccented lowercase. For example, in Swedish, the letter a with diaeresis has full alphabet citizenship and should not be turned into an a. Each element in the space-separated list has the special character as first element and the translation following. The handling of both the lowercase and upper-case versions of a character should be specified, as appartenance to the list will turn-off both standard accent and case processing. The value is global and affects both indexing and querying. We also convert a few confusing Unicode characters (quotes, hyphen) to their ASCII equivalent to avoid "invisible" search failures. Examples: Swedish: unac_except_trans = ää Ää öö Öö üü Üü ßss œoe Œoe æae Æae ffff fifi flfl åå Åå ’' ❜' ʼ' ‐- . German: unac_except_trans = ää Ää öö Öö üü Üü ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐- . French: you probably want to decompose oe and ae and nobody would type a German ß unac_except_trans = ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐- . The default for all until someone protests follows. These decompositions are not performed by unac, but it is unlikely that someone would type the composed forms in a search. unac_except_trans = ßss œoe Œoe æae Æae ffff fifi flfl ’' ❜' ʼ' ‐- maildefcharset Overrides the default character set for email messages which don't specify one. This is mainly useful for readpst (libpst) dumps, which are utf-8 but do not say so. localfields Set fields on all files (usually of a specific fs area). Syntax is the usual: name = value ; attr1 = val1 ; [...] value is empty so this needs an initial semi-colon. This is useful, e.g., for setting the rclaptg field for application selection inside mimeview. testmodifusemtime Use mtime instead of ctime to test if a file has been modified. The time is used in addition to the size, which is always used. Setting this can reduce re-indexing on systems where extended attributes are used (by some other application), but not indexed, because changing extended attributes only affects ctime. Notes: - This may prevent detection of change in some marginal file rename cases (the target would need to have the same size and mtime). - You should probably also set noxattrfields to 1 in this case, except if you still prefer to perform xattr indexing, for example if the local file update pattern makes it of value (as in general, there is a risk for pure extended attributes updates without file modification to go undetected). Perform a full index reset after changing this. noxattrfields Disable extended attributes conversion to metadata fields. This probably needs to be set if testmodifusemtime is set. metadatacmds Define commands to gather external metadata, e.g. tmsu tags. There can be several entries, separated by semi-colons, each defining which field name the data goes into and the command to use. Don't forget the initial semi-colon. All the field names must be different. You can use aliases in the "field" file if necessary. As a not too pretty hack conceded to convenience, any field name beginning with "rclmulti" will be taken as an indication that the command returns multiple field values inside a text blob formatted as a recoll configuration file ("fieldname = fieldvalue" lines). The rclmultixx name will be ignored, and field names and values will be parsed from the data. Example: metadatacmds = ; tags = tmsu tags %f; rclmulti1 = cmdOutputsConf %f Parameters affecting where and how we store things cachedir Top directory for Recoll data. Recoll data directories are normally located relative to the configuration directory (e.g. ~/.recoll/xapiandb, ~/.recoll /mboxcache). If 'cachedir' is set, the directories are stored under the specified value instead (e.g. if cachedir is ~/.cache/recoll, the default dbdir would be ~/.cache/recoll/xapiandb). This affects dbdir, webcachedir, mboxcachedir, aspellDicDir, which can still be individually specified to override cachedir. Note that if you have multiple configurations, each must have a different cachedir, there is no automatic computation of a subpath under cachedir. maxfsoccuppc Maximum file system occupation over which we stop indexing. The value is a percentage, corresponding to what the "Capacity" df output column shows. The default value is 0, meaning no checking. dbdir Xapian database directory location. This will be created on first indexing. If the value is not an absolute path, it will be interpreted as relative to cachedir if set, or the configuration directory (-c argument or $RECOLL_CONFDIR). If nothing is specified, the default is then ~/.recoll/ xapiandb/ idxstatusfile Name of the scratch file where the indexer process updates its status. Default: idxstatus.txt inside the configuration directory. mboxcachedir Directory location for storing mbox message offsets cache files. This is normally 'mboxcache' under cachedir if set, or else under the configuration directory, but it may be useful to share a directory between different configurations. mboxcacheminmbs Minimum mbox file size over which we cache the offsets. There is really no sense in caching offsets for small files. The default is 5 MB. mboxmaxmsgmbs Maximum mbox member message size in megabytes. Size over which we assume that the mbox format is bad or we misinterpreted it, at which point we just stop processing the file. webcachedir Directory where we store the archived web pages. This is only used by the web history indexing code Default: cachedir/webcache if cachedir is set, else $RECOLL_CONFDIR/webcache webcachemaxmbs Maximum size in MB of the Web archive. This is only used by the web history indexing code. Default: 40 MB. Reducing the size will not physically truncate the file. webqueuedir The path to the Web indexing queue. This used to be hard-coded in the old plugin as ~/.recollweb/ToIndex so there would be no need or possibility to change it, but the WebExtensions plugin now downloads the files to the user Downloads directory, and a script moves them to webqueuedir. The script reads this value from the config so it has become possible to change it. webdownloadsdir The path to browser downloads directory. This is where the new browser add-on extension has to create the files. They are then moved by a script to webqueuedir. webcachekeepinterval Page recycle interval By default, only one instance of an URL is kept in the cache. This can be changed by setting this to a value determining at what frequency we keep multiple instances ('day', 'week', 'month', 'year'). Note that increasing the interval will not erase existing entries. aspellDicDir Aspell dictionary storage directory location. The aspell dictionary (aspdict.(lang).rws) is normally stored in the directory specified by cachedir if set, or under the configuration directory. filtersdir Directory location for executable input handlers. If RECOLL_FILTERSDIR is set in the environment, we use it instead. Defaults to $prefix/share/recoll /filters. Can be redefined for subdirectories. iconsdir Directory location for icons. The only reason to change this would be if you want to change the icons displayed in the result list. Defaults to $prefix/share/recoll/images Parameters affecting indexing performance and resource usage idxflushmb Threshold (megabytes of new data) where we flush from memory to disk index. Setting this allows some control over memory usage by the indexer process. A value of 0 means no explicit flushing, which lets Xapian perform its own thing, meaning flushing every $XAPIAN_FLUSH_THRESHOLD documents created, modified or deleted: as memory usage depends on average document size, not only document count, the Xapian approach is is not very useful, and you should let Recoll manage the flushes. The program compiled value is 0. The configured default value (from this file) is now 50 MB, and should be ok in many cases. You can set it as low as 10 to conserve memory, but if you are looking for maximum speed, you may want to experiment with values between 20 and 200. In my experience, values beyond this are always counterproductive. If you find otherwise, please drop me a note. filtermaxseconds Maximum external filter execution time in seconds. Default 1200 (20mn). Set to 0 for no limit. This is mainly to avoid infinite loops in postscript files (loop.ps) filtermaxmbytes Maximum virtual memory space for filter processes (setrlimit(RLIMIT_AS)), in megabytes. Note that this includes any mapped libs (there is no reliable Linux way to limit the data space only), so we need to be a bit generous here. Anything over 2000 will be ignored on 32 bits machines. The high default value is needed because of java-based handlers (pdftk) which need a lot of VM (most of it text), esp. pdftk when executed from Python rclpdf.py. You can use a much lower value if you don't need Java. thrQSizes Stage input queues configuration. There are three internal queues in the indexing pipeline stages (file data extraction, terms generation, index update). This parameter defines the queue depths for each stage (three integer values). If a value of -1 is given for a given stage, no queue is used, and the thread will go on performing the next stage. In practise, deep queues have not been shown to increase performance. Default: a value of 0 for the first queue tells Recoll to perform autoconfiguration based on the detected number of CPUs (no need for the two other values in this case). Use thrQSizes = -1 -1 -1 to disable multithreading entirely. thrTCounts Number of threads used for each indexing stage. The three stages are: file data extraction, terms generation, index update). The use of the counts is also controlled by some special values in thrQSizes: if the first queue depth is 0, all counts are ignored (autoconfigured); if a value of -1 is used for a queue depth, the corresponding thread count is ignored. It makes no sense to use a value other than 1 for the last stage because updating the Xapian index is necessarily single-threaded (and protected by a mutex). Miscellaneous parameters loglevel Log file verbosity 1-6. A value of 2 will print only errors and warnings. 3 will print information like document updates, 4 is quite verbose and 6 very verbose. logfilename Log file destination. Use 'stderr' (default) to write to the console. idxloglevel Override loglevel for the indexer. idxlogfilename Override logfilename for the indexer. helperlogfilename Destination file for external helpers standard error output. The external program error output is left alone by default, e.g. going to the terminal when the recoll[index] program is executed from the command line. Use /dev/ null or a file inside a non-existent directory to completely suppress the output. daemloglevel Override loglevel for the indexer in real time mode. The default is to use the idx... values if set, else the log... values. daemlogfilename Override logfilename for the indexer in real time mode. The default is to use the idx... values if set, else the log... values. pyloglevel Override loglevel for the python module. pylogfilename Override logfilename for the python module. orgidxconfdir Original location of the configuration directory. This is used exclusively for movable datasets. Locating the configuration directory inside the directory tree makes it possible to provide automatic query time path translations once the data set has moved (for example, because it has been mounted on another location). curidxconfdir Current location of the configuration directory. Complement orgidxconfdir for movable datasets. This should be used if the configuration directory has been copied from the dataset to another location, either because the dataset is readonly and an r/w copy is desired, or for performance reasons. This records the original moved location before copy, to allow path translation computations. For example if a dataset originally indexed as '/ home/me/mydata/config' has been mounted to '/media/me/mydata', and the GUI is running from a copied configuration, orgidxconfdir would be '/home/me/ mydata/config', and curidxconfdir (as set in the copied configuration) would be '/media/me/mydata/config'. idxrundir Indexing process current directory. The input handlers sometimes leave temporary files in the current directory, so it makes sense to have recollindex chdir to some temporary directory. If the value is empty, the current directory is not changed. If the value is (literal) tmp, we use the temporary directory as set by the environment (RECOLL_TMPDIR else TMPDIR else /tmp). If the value is an absolute path to a directory, we go there. checkneedretryindexscript Script used to heuristically check if we need to retry indexing files which previously failed. The default script checks the modified dates on /usr/bin and /usr/local/bin. A relative path will be looked up in the filters dirs, then in the path. Use an absolute path to do otherwise. recollhelperpath Additional places to search for helper executables. This is used, e.g., on Windows by the Python code, and on Mac OS by the bundled recoll.app (because I could find no reliable way to tell launchd to set the PATH). The example below is for Windows. Use ':' as entry separator for Mac and Ux-like systems, ';' is for Windows only. idxabsmlen Length of abstracts we store while indexing. Recoll stores an abstract for each indexed file. The text can come from an actual 'abstract' section in the document or will just be the beginning of the document. It is stored in the index so that it can be displayed inside the result lists without decoding the original file. The idxabsmlen parameter defines the size of the stored abstract. The default value is 250 bytes. The search interface gives you the choice to display this stored text or a synthetic abstract built by extracting text around the search terms. If you always prefer the synthetic abstract, you can reduce this value and save a little space. idxmetastoredlen Truncation length of stored metadata fields. This does not affect indexing (the whole field is processed anyway), just the amount of data stored in the index for the purpose of displaying fields inside result lists or previews. The default value is 150 bytes which may be too low if you have custom fields. idxtexttruncatelen Truncation length for all document texts. Only index the beginning of documents. This is not recommended except if you are sure that the interesting keywords are at the top and have severe disk space issues. idxsynonyms Name of the index-time synonyms file. This is used for indexing multiword synonyms as single terms, which in turn is only useful if you want to perform proximity searches with such terms. stemexpandphrases Default to applying stem expansion to phrase terms. Recoll normally does not apply stem expansion to terms inside phrase searches. Setting this parameter will change the default behaviour to expanding terms inside phrases. If set, you can use a 'l' modifier to disable expansion for a specific instance. noaspell Disable aspell use. The aspell dictionary generation takes time, and some combinations of aspell version, language, and local terms, result in aspell crashing, so it sometimes makes sense to just disable the thing. aspellLanguage Language definitions to use when creating the aspell dictionary. The value must match a set of aspell language definition files. You can type "aspell dicts" to see a list The default if this is not set is to use the NLS environment to guess the value. The values are the 2-letter language codes (e.g. 'en', 'fr'...) aspellAddCreateParam Additional option and parameter to aspell dictionary creation command. Some aspell packages may need an additional option (e.g. on Debian Jessie: --local-data-dir=/usr/lib/aspell). See Debian bug 772415. aspellKeepStderr Set this to have a look at aspell dictionary creation errors. There are always many, so this is mostly for debugging. autoSpellRarityThreshold Inverse of the ratio of term occurrence to total db terms over which we look for spell neighbours for automatic query expansion When a term is very uncommon, we may (depending on user choice) look for spelling variations which would be more common and possibly add them to the query. autoSpellSelectionThreshold Ratio of spell neighbour frequency over user input term frequency beyond which we include the neighbour in the query. When a term has been selected for spelling expansion because of its rarity, we only include spelling neighbours which are more common by this ratio. monauxinterval Auxiliary database update interval. The real time indexer only updates the auxiliary databases (stemdb, aspell) periodically, because it would be too costly to do it for every document change. The default period is one hour. monixinterval Minimum interval (seconds) between processings of the indexing queue. The real time indexer does not process each event when it comes in, but lets the queue accumulate, to diminish overhead and to aggregate multiple events affecting the same file. Default 30 S. mondelaypatterns Timing parameters for the real time indexing. Definitions for files which get a longer delay before reindexing is allowed. This is for fast-changing files, that should only be reindexed once in a while. A list of wildcardPattern:seconds pairs. The patterns are matched with fnmatch (pattern, path, 0) You can quote entries containing white space with double quotes (quote the whole entry, not the pattern). The default is empty. Example: mondelaypatterns = *.log:20 "*with spaces.*:30" idxniceprio "nice" process priority for the indexing processes. Default: 19 (lowest) Appeared with 1.26.5. Prior versions were fixed at 19. monioniceclass ionice class for the indexing process. Despite the misleading name, and on platforms where this is supported, this affects all indexing processes, not only the real time/monitoring ones. The default value is 3 (use lowest "Idle" priority). monioniceclassdata ionice class level parameter if the class supports it. The default is empty, as the default "Idle" class has no levels. Query-time parameters (no impact on the index) autodiacsens auto-trigger diacritics sensitivity (raw index only). IF the index is not stripped, decide if we automatically trigger diacritics sensitivity if the search term has accented characters (not in unac_except_trans). Else you need to use the query language and the "D" modifier to specify diacritics sensitivity. Default is no. autocasesens auto-trigger case sensitivity (raw index only). IF the index is not stripped (see indexStripChars), decide if we automatically trigger character case sensitivity if the search term has upper-case characters in any but the first position. Else you need to use the query language and the "C" modifier to specify character-case sensitivity. Default is yes. maxTermExpand Maximum query expansion count for a single term (e.g.: when using wildcards). This only affects queries, not indexing. We used to not limit this at all (except for filenames where the limit was too low at 1000), but it is unreasonable with a big index. Default 10000. maxXapianClauses Maximum number of clauses we add to a single Xapian query. This only affects queries, not indexing. In some cases, the result of term expansion can be multiplicative, and we want to avoid eating all the memory. Default 50000. snippetMaxPosWalk Maximum number of positions we walk while populating a snippet for the result list. The default of 1,000,000 may be insufficient for very big documents, the consequence would be snippets with possibly meaning-altering missing words. thumbnailercmd Command to use for generating thumbnails. If set, this should be a path to a command or script followed by its constant arguments. Four arguments will be appended before execution: the document URL, MIME type, target icon SIZE (e.g. 128), and output file PATH. The command should generate a thumbnail from these values. E.g. if the MIME is video, a script could use: ffmpegthumbnailer -iURL -oPATH -sSIZE. Parameters for the PDF input script pdfocr Attempt OCR of PDF files with no text content. This can be defined in subdirectories. The default is off because OCR is so very slow. pdfattach Enable PDF attachment extraction by executing pdftk (if available). This is normally disabled, because it does slow down PDF indexing a bit even if not one attachment is ever found. pdfextrameta Extract text from selected XMP metadata tags. This is a space-separated list of qualified XMP tag names. Each element can also include a translation to a Recoll field name, separated by a '|' character. If the second element is absent, the tag name is used as the Recoll field names. You will also need to add specifications to the "fields" file to direct processing of the extracted data. pdfextrametafix Define name of XMP field editing script. This defines the name of a script to be loaded for editing XMP field values. The script should define a 'MetaFixer' class with a metafix() method which will be called with the qualified tag name and value of each selected field, for editing or erasing. A new instance is created for each document, so that the object can keep state for, e.g. eliminating duplicate values. Parameters for OCR processing ocrprogs OCR modules to try. The top OCR script will try to load the corresponding modules in order and use the first which reports being capable of performing OCR on the input file. Modules for tesseract (tesseract) and ABBYY FineReader (abbyy) are present in the standard distribution. For compatibility with the previous version, if this is not defined at all, the default value is "tesseract". Use an explicit empty value if needed. A value of "abbyy tesseract" will try everything. ocrcachedir Location for caching OCR data. The default if this is empty or undefined is to store the cached OCR data under $RECOLL_CONFDIR/ocrcache. tesseractlang Language to assume for tesseract OCR. Important for improving the OCR accuracy. This can also be set through the contents of a file in the currently processed directory. See the rclocrtesseract.py script. Example values: eng, fra... See the tesseract documentation. tesseractcmd Path for the tesseract command. Do not quote. This is mostly useful on Windows, or for specifying a non-default tesseract command. E.g. on Windows. tesseractcmd = C:/ProgramFiles(x86)/Tesseract-OCR/tesseract.exe abbyylang Language to assume for abbyy OCR. Important for improving the OCR accuracy. This can also be set through the contents of a file in the currently processed directory. See the rclocrabbyy.py script. Typical values: English, French... See the ABBYY documentation. abbyyocrcmd Path for the abbyy command The ABBY directory is usually not in the path, so you should set this. Parameters for running speech to text conversion speechtotext Activate speech to text conversion The only possible value at the moment is "whisper" for using the OpenAI whisper program. sttmodel Name of the whisper model sttdevice Name of the device to be used by for whisper Parameters for specific handlers orgmodesubdocs Index org-mode level 1 sections as separate sub-documents This is the default. If set to false, org-mode files will be indexed as plain text kioshowsubdocs Show embedded document results in KDE dolphin/kio and krunner Embedded documents may clutter the results and are not always easily usable from the kio or krunner environment. Setting this variable will restrict the results to standalone documents. Parameters set for specific locations mhmboxquirks Enable thunderbird/mozilla-seamonkey mbox format quirks Set this for the directory where the email mbox files are stored. 5.4.3. The fields file This file contains information about dynamic fields handling in Recoll. Some very basic fields have hard-wired behaviour, and, mostly, you should not change the original data inside the fields file. But you can create custom fields fitting your data and handle them just like they were native ones. The fields file has several sections, which each define an aspect of fields processing. Quite often, you'll have to modify several sections to obtain the desired behaviour. We will only give a short description here, you should refer to the comments inside the default file for more detailed information. Field names should be lowercase alphabetic ASCII. [prefixes] A field becomes indexed (searchable) by having a prefix defined in this section. There is a more complete explanation of what prefixes are in used by a standard recoll installation. In a nutshell: extension prefixes should be all caps, begin with XY, and short. E.g. XYMFLD. [values] Fields listed in this section will be stored as Xapian values inside the index. This makes them available for range queries, allowing to filter results according to the field value. This feature currently supports string and integer data. See the comments in the file for more detail [stored] A field becomes stored (displayable inside results) by having its name listed in this section (typically with an empty value). [aliases] This section defines lists of synonyms for the canonical names used inside the [prefixes] and [stored] sections [queryaliases] This section also defines aliases for the canonic field names, with the difference that the substitution will only be used at query time, avoiding any possibility that the value would pick-up random metadata from documents. handler-specific sections Some input handlers may need specific configuration for handling fields. Only the email message handler currently has such a section (named [mail]). It allows indexing arbitrary email headers in addition to the ones indexed by default. Other such sections may appear in the future. Here follows a small example of a personal fields file. This would extract a specific email header and use it as a searchable field, with data displayable inside result lists. (Side note: as the email handler does no decoding on the values, only plain ascii headers can be indexed, and only the first occurrence will be used for headers that occur several times). [prefixes] # Index mailmytag contents (with the given prefix) mailmytag = XMTAG [stored] # Store mailmytag inside the document data record (so that it can be # displayed - as %(mailmytag) - in result lists). mailmytag = [queryaliases] filename = fn containerfilename = cfn [mail] # Extract the X-My-Tag mail header, and use it internally with the # mailmytag field name x-my-tag = mailmytag Extended attributes in the fields file Recoll versions 1.19 and later process user extended file attributes as documents fields by default. Attributes are processed as fields of the same name, after removing the user prefix on Linux. The [xattrtofields] section of the fields file allows specifying translations from extended attributes names to Recoll field names. An empty translation disables use of the corresponding attribute data. 5.4.4. The mimemap file mimemap specifies the file name extension to MIME type mappings. For file names without an extension, or with an unknown one, a system command ( file -i, or xdg-mime) will be executed to determine the MIME type (this can be switched off, or the command changed inside the main configuration file). All extension values in mimemap must be entered in lower case. File names extensions are lower-cased for comparison during indexing, meaning that an upper case mimemap entry will never be matched. The mappings can be specified on a per-subtree basis, which may be useful in some cases. Example: okular notes have a .xml extension but should be handled specially, which is possible because they are usually all located in one place. Example: [~/.kde/share/apps/okular/docdata] .xml = application/x-okular-notes The recoll_noindex mimemap variable has been moved to recoll.conf and renamed to noContentSuffixes, while keeping the same function, as of Recoll version 1.21. For older Recoll versions, see the documentation for noContentSuffixes but use recoll_noindex in mimemap. 5.4.5. The mimeconf file The main purpose of the mimeconf file is to specify how the different MIME types are handled for indexing. This is done in the [index] section, which should not be modified casually. See the comments in the file. The file also contains other definitions which affect the query language and the GUI, and which, in retrospect, should have been stored elsewhere. The [icons] section allows you to change the icons which are displayed by the recoll GUI in the result lists (the values are the basenames of the png images inside the iconsdir directory (which is itself defined in recoll.conf). The [categories] section defines the groupings of MIME types into categories as used when adding an rclcat clause to a query language query. rclcat clauses are also used by the default guifilters buttons in the GUI (see next). The filter controls appear at the top of the recoll GUI, either as checkboxes just above the result list, or as a dropbox in the tool area. By default, they are labeled: media, message, other, presentation, spreadsheet and text, and each maps to a document category. This is determined in the [guifilters] section, where each control is defined by a variable naming a query language fragment. A simple example will hopefully make things clearer. [guifilters] Big Books = dir:"~/My Books" size>10K My Docs = dir:"~/My Documents" Small Books = dir:"~/My Books" size<10K System Docs = dir:/usr/share/doc The above definition would create four filter checkboxes, labelled Big Books, My Docs, etc. The text after the equal sign must be a valid query language fragment, and, when the button is checked, it will be combined with the rest of the query with an AND conjunction. Any name text before a colon character will be erased in the display, but used for sorting. You can use this to display the checkboxes in any order you like. For example, the following would do exactly the same as above, but ordering the checkboxes in the reverse order. [guifilters] d:Big Books = dir:"~/My Books" size>10K c:My Docs = dir:"~/My Documents" b:Small Books = dir:"~/My Books" size<10K a:System Docs = dir:/usr/share/doc As you may have guessed, The default [guifilters] section looks like: [guifilters] text = rclcat:text spreadsheet = rclcat:spreadsheet presentation = rclcat:presentation media = rclcat:media message = rclcat:message other = rclcat:other 5.4.6. The mimeview file mimeview specifies which programs are started when you click on an Open link in a result list. E.g.: HTML is normally displayed using firefox, but you may prefer Konqueror, your openoffice.org program might be named oofice instead of openoffice etc. Changes to this file can be done by direct editing, or through the recoll GUI preferences dialog. If Use desktop preferences to choose document editor is checked in the Recoll GUI preferences, all mimeview entries will be ignored except the one labelled application/x-all (which is set to use xdg-open by default). In this case, the xallexcepts top level variable defines a list of MIME type exceptions which will be processed according to the local entries instead of being passed to the desktop. This is so that specific Recoll options such as a page number or a search string can be passed to applications that support them, such as the evince viewer. As for the other configuration files, the normal usage is to have a mimeview inside your own configuration directory, with just the non-default entries, which will override those from the central configuration file. All viewer definition entries must be placed under a [view] section. The keys in the file are normally MIME types. You can add an application tag to specialize the choice for an area of the filesystem (using a localfields specification in mimeconf). The syntax for the key is mimetype|tag The nouncompforviewmts entry, (placed at the top level, outside of the [view] section), holds a list of MIME types that should not be uncompressed before starting the viewer (if they are found compressed, e.g.: mydoc.doc.gz). The right side of each assignment holds a command to be executed for opening the file. The following substitutions are performed: • %D. Document date • %f. File name. This may be the name of a temporary file if it was necessary to create one (e.g.: to extract a subdocument from a container). • %i. Internal path, for subdocuments of containers. The format depends on the container type. If this appears in the command line, Recoll will not create a temporary file to extract the subdocument, expecting the called application (possibly a script) to be able to handle it. • %M. MIME type • %p. Page index. Only significant for a subset of document types, currently only PDF, Postscript and DVI files. If it is set, a significant term will be chosen in the query, and %p will be substituted with the first page where the term appears. Can be used to start the editor at the right page for a match or snippet. • %l. Line number. Only significant for document types with relevant line breaks, mostly text/plain and analogs. If it is set, a significant term will be chosen in the query, and %p will be substituted with the first line where the term appears. • %s. Search term. The value will only be set for documents with indexed page or line numbers and if %p or %l is also used. The value will be one of the matched search terms. It would allow pre-setting the value in the "Find" entry inside Evince for example, for easy highlighting of the term. • %u. Url. In addition to the predefined values above, all strings like %(fieldname) will be replaced by the value of the field named fieldname for the document. This could be used in combination with field customisation to help with opening the document. 5.4.7. The ptrans file ptrans specifies query-time path translations. These can be useful in multiple cases. The file has a section for any index which needs translations, either the main one or additional query indexes. The sections are named with the Xapian index directory names. No slash character should exist at the end of the paths (all comparisons are textual). An example should make things sufficiently clear [/home/me/.recoll/xapiandb] /this/directory/moved = /to/this/place [/path/to/additional/xapiandb] /server/volume1/docdir = /net/server/volume1/docdir /server/volume2/docdir = /net/server/volume2/docdir 5.4.8. Examples of configuration adjustments Adding an external viewer for an non-indexed type Imagine that you have some kind of file which does not have indexable content, but for which you would like to have a functional Open link in the result list (when found by file name). The file names end in .blob and can be displayed by application blobviewer. You need two entries in the configuration files for this to work: • In $RECOLL_CONFDIR/mimemap (typically ~/.recoll/mimemap), add the following line: .blob = application/x-blobapp Note that the MIME type is made up here, and you could call it diesel/oil just the same. • In $RECOLL_CONFDIR/mimeview under the [view] section, add: application/x-blobapp = blobviewer %f We are supposing that blobviewer wants a file name parameter here, you would use %u if it liked URLs better. If you just wanted to change the application used by Recoll to display a MIME type which it already knows, you would just need to edit mimeview. The entries you add in your personal file override those in the central configuration, which you do not need to alter. mimeview can also be modified from the Gui. Adding indexing support for a new file type Let us now imagine that the above .blob files actually contain indexable text and that you know how to extract it with a command line program. Getting Recoll to index the files is easy. You need to perform the above alteration, and also to add data to the mimeconf file (typically in ~/.recoll/mimeconf): • Under the [index] section, add the following line (more about the rclblob indexing script later): application/x-blobapp = exec rclblob Or if the files are mostly text and you don't need to process them for indexing: application/x-blobapp = internal text/plain • Under the [icons] section, you should choose an icon to be displayed for the files inside the result lists. Icons are normally 64x64 pixels PNG files which live in /usr/share/recoll/images. • Under the [categories] section, you should add the MIME type where it makes sense (you can also create a category). Categories may be used for filtering in advanced search. The rclblob handler should be an executable program or script which exists inside /usr/share/recoll/filters. It will be given a file name as argument and should output the text or html contents on the standard output. The filter programming section describes in more detail how to write an input handler. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/filters/������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�14521161751�011536� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/filters/rclopxml.py�������������������������������������������������������������������0000755�0001750�0001750�00000020026�14410615043�013666� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python3 # Copyright (C) 2015-2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ###################################### # Note that .docx and .xlsx are now normally processed by the C++ mh_xslt.cpp # module. See the openxml-xxx.xsl files for the style sheets used by the C++. # # .pptx and .vsdx are processed by this Python module because the C++ module # can't process their multiple document structure (pages) at the moment. import sys from zipfile import ZipFile import rclexecm from rclbasehandler import RclBaseHandler import rclxslt import re # # Common style sheet for the openxml metadata # meta_stylesheet = '''<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <!-- <xsl:output method="text"/> --> <xsl:output omit-xml-declaration="yes"/> <xsl:template match="cp:coreProperties"> <xsl:text> </xsl:text> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <xsl:text> </xsl:text> <xsl:apply-templates/> </xsl:template> <xsl:template match="dc:creator"> <meta> <xsl:attribute name="name"> <!-- <xsl:value-of select="name()"/> pour sortir tous les meta avec le meme nom que dans le xml (si on devenait dc-natif) --> <xsl:text>author</xsl:text> </xsl:attribute> <xsl:attribute name="content"> <xsl:value-of select="."/> </xsl:attribute> </meta> <xsl:text> </xsl:text> </xsl:template> <xsl:template match="dcterms:modified"> <meta> <xsl:attribute name="name"> <xsl:text>date</xsl:text> </xsl:attribute> <xsl:attribute name="content"> <xsl:value-of select="."/> </xsl:attribute> </meta> <xsl:text> </xsl:text> </xsl:template> <xsl:template match="*"> </xsl:template> </xsl:stylesheet> ''' ##################################### # .docx definitions. Not used any more by Recoll in its default config word_tagmatch = 'w:p' word_xmlns_decls = '''xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" ''' word_moretemplates = '' ##################################### # .xlsx definitions. Not used any more by Recoll in its default config xl_tagmatch = 'x:t' xl_xmlns_decls='''xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ''' xl_moretemplates = '' ##################### # .pptx definitions pp_tagmatch = 'a:t' pp_xmlns_decls = '''xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" ''' # I want to suppress text output for all except a:t, don't know how to do it # help ! At least get rid of these: pp_moretemplates = '''<xsl:template match="p:attrName"> </xsl:template> ''' ##################### # .vsdx definitions vs_tagmatch = 'Text' vs_xmlns_decls = '''xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" ''' vs_moretemplates = '' ############################## # Common style sheet (with replaceable parts) for .pptx and .vsdx (also .docx # and .xlsx, but not used by default). content_stylesheet = '''<?xml version="1.0"?> <xsl:stylesheet @XMLNS_DECLS@ > <xsl:output omit-xml-declaration="yes"/> <xsl:template match="/"> <div> <xsl:apply-templates/> </div> </xsl:template> <xsl:template match="@TAGMATCH@"> <p> <xsl:value-of select="."/> </p> </xsl:template> @MORETEMPLATES@ </xsl:stylesheet> ''' class OXExtractor(RclBaseHandler): def __init__(self, em): super(OXExtractor, self).__init__(em) # Replace values inside data style sheet, depending on type of doc def computestylesheet(self, nm): decls = globals()[nm + '_xmlns_decls'] stylesheet = content_stylesheet.replace('@XMLNS_DECLS@', decls) tagmatch = globals()[nm + '_tagmatch'] stylesheet = stylesheet.replace('@TAGMATCH@', tagmatch) moretmpl = globals()[nm + '_moretemplates'] stylesheet = stylesheet.replace('@MORETEMPLATES@', moretmpl) return stylesheet def html_text(self, fn): f = open(fn, 'rb') zip = ZipFile(f) docdata = b'<html><head>' try: metadata = zip.read("docProps/core.xml") if metadata: res = rclxslt.apply_sheet_data(meta_stylesheet, metadata) docdata += res except Exception as err: pass docdata += b'</head><body>' try: content= zip.read('word/document.xml') stl = self.computestylesheet('word') docdata += rclxslt.apply_sheet_data(stl, content) except: pass try: content = zip.read('xl/sharedStrings.xml') stl = self.computestylesheet('xl') docdata += rclxslt.apply_sheet_data(stl, content) except: pass try: stl = None # Extract number suffix for numeric sort prefix = "ppt/slides/slide" exp = prefix + '[0-9]+' + '.xml' names = [fn for fn in zip.namelist() if re.match(exp, fn)] for fn in sorted( names, key=lambda e,prefix=prefix: int(e[len(prefix):len(e)-4])): if stl is None: stl = self.computestylesheet('pp') content = zip.read(fn) docdata += rclxslt.apply_sheet_data(stl, content) except Exception as ex: #self.em.rclog("PPT Exception: %s" % ex) pass try: stl = None # Extract number suffix for numeric sort prefix = 'visio/pages/page' exp = prefix + '[0-9]+' + '.xml' names = [fn for fn in zip.namelist() if re.match(exp, fn)] for fn in sorted( names, key=lambda e,prefix=prefix: int(e[len(prefix):len(e)-4])): if stl is None: stl = self.computestylesheet('vs') content = zip.read(fn) docdata += rclxslt.apply_sheet_data(stl, content) except Exception as ex: #self.em.rclog("VISIO Exception: %s" % ex) pass docdata += b'</body></html>' return docdata if __name__ == '__main__': proto = rclexecm.RclExecM() extract = OXExtractor(proto) rclexecm.main(proto, extract) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/filters/rclocr.py���������������������������������������������������������������������0000755�0001750�0001750�00000007610�14410615043�013316� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python3 ################################# # Copyright (C) 2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################################## # Running OCR programs for Recoll. This is executed from, # e.g. rclpdf.py if pdftotext returns no data. # # The script tries to retrieve the data from the ocr cache, else it # runs the configured OCR program and updates the cache. In both cases it writes # the resulting text to stdout. import os import sys import atexit import signal import importlib.util import rclconfig import rclocrcache import rclexecm def _deb(s): rclexecm.logmsg("rclocr: %s" % s) ocrcleanupmodule = None @atexit.register def finalcleanup(): if ocrcleanupmodule: ocrcleanupmodule.cleanocr() def signal_handler(sig, frame): sys.exit(1) # Not all signals necessary exist on all systems, use catch try: signal.signal(signal.SIGHUP, signal_handler) except: pass try: signal.signal(signal.SIGINT, signal_handler) except: pass try: signal.signal(signal.SIGQUIT, signal_handler) except: pass try: signal.signal(signal.SIGTERM, signal_handler) except: pass def Usage(): _deb("Usage: rclocr.py <imagefilename>") sys.exit(1) def breakwrite(f, data): # On Windows, writing big chunks can fail with a "not enough space" # error. Seems a combined windows/python bug, depending on versions. # See https://bugs.python.org/issue11395 # In any case, just break it up total = len(data) bs = 4*1024 offset = 0 while total > 0: if total < bs: tow = total else: tow = bs f.write(data[offset:offset+tow]) offset += tow total -= tow if len(sys.argv) != 2: Usage() path = sys.argv[1] config = rclconfig.RclConfig() config.setKeyDir(os.path.dirname(path)) cache = rclocrcache.OCRCache(config) incache, data = cache.get(path) if incache: try: breakwrite(sys.stdout.buffer, data) except Exception as e: _deb("error writing: %s" % e) sys.exit(1) sys.exit(0) #### Data not in cache # Retrieve configured OCR program names and try to load the # corresponding module ocrprogs = config.getConfParam("ocrprogs") if ocrprogs is None: # Compat: the previous version has no ocrprogs variable, but would do # tesseract by default. Use "ocrprogs = " for a really empty list ocrprogs = "tesseract" if not ocrprogs: _deb("No ocrprogs variable in recoll configuration") sys.exit(0) #_deb("ocrprogs: %s" % ocrprogs) proglist = ocrprogs.split(" ") ok = False for ocrprog in proglist: try: modulename = "rclocr" + ocrprog ocr = importlib.import_module(modulename) if ocr.ocrpossible(config, path): ok = True break except Exception as err: _deb("While loading %s: got: %s" % (modulename, err)) pass if not ok: _deb("No OCR module could be loaded") sys.exit(1) #_deb("Using ocr module %s" % modulename) # The OCR module will retrieve its specific parameters from the # configuration ocrcleanupmodule = ocr status, data = ocr.runocr(config, path) if not status: _deb("runocr failed") sys.exit(1) cache.store(path, data) sys.stdout.buffer.write(data) sys.exit(0) ������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/filters/gnumeric.xsl������������������������������������������������������������������0000755�0001750�0001750�00000004312�14410615043�014015� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:gnm="http://www.gnumeric.org/v10.dtd" exclude-result-prefixes="office xlink meta ooo dc" > <xsl:output method="html" encoding="UTF-8"/> <xsl:template match="/"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <xsl:apply-templates select="//office:document-meta/office:meta"/> </head> <body> <xsl:apply-templates select="//gnm:Cells"/> <xsl:apply-templates select="//gnm:Objects"/> </body> </html> </xsl:template> <xsl:template match="//dc:date"> <meta> <xsl:attribute name="name">date</xsl:attribute> <xsl:attribute name="content"><xsl:value-of select="."/></xsl:attribute> </meta> </xsl:template> <xsl:template match="//dc:description"> <meta> <xsl:attribute name="name">abstract</xsl:attribute> <xsl:attribute name="content"><xsl:value-of select="."/></xsl:attribute> </meta> </xsl:template> <xsl:template match="//meta:keyword"> <meta> <xsl:attribute name="name">keywords</xsl:attribute> <xsl:attribute name="content"><xsl:value-of select="."/></xsl:attribute> </meta> </xsl:template> <xsl:template match="//dc:subject"> <meta> <xsl:attribute name="name">keywords</xsl:attribute> <xsl:attribute name="content"><xsl:value-of select="."/></xsl:attribute> </meta> </xsl:template> <xsl:template match="//dc:title"> <title> <xsl:value-of select="."/> author

recoll-1.36.1/filters/recfiltcommon0000755000175000017500000000277614410615043014254 00000000000000 #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE recoll-1.36.1/filters/rclocrcache.py0000755000175000017500000004610014444307651014311 00000000000000#!/usr/bin/env python3 ########################################################################## # Copyright (C) 2020-2023 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ########################################################################### """Caching the results of expensive processing of file documents Overview: ========= This module was initially created for caching the result of running OCR on image files, hence its name, but it was then reused for the output of speech recognition (STT, Speech To Text) on audio files. More generally, it could be used for caching the results of any expensive transformation performed on a document file. The storage is organised so that retrieving the cached data for an unchanged file is very fast, and retrieving it for a file which has been moved while retaining the same contents is much faster than the OCR or STT treatment, only needing a, possibly partial, contents hash computation. The cache is made of two separate directory trees: the paths tree holds files named by a hash of the document file path, and contain its path, size, recorded modification time and data hash. The data tree holds files named by a hash of the document file data, and containing the results of the OCR or STT operation. Interface: ========== API: ---- The main programming interface is provided by the methods in the OCRCache class: store(), erase(), get(), along with some auxiliary methods for purging stale data. Script interface: ----------------- This file can be run as a script with for performing operations on the cache, with different options. In all cases the cache affected is the one designated by the Recoll configuration determined by the RECOLL_CONFDIR environment variable (the -c option is not supported at the moment). rclocrcache.py --purge [--purgedata] ++++++++++++++++++++++++++++++++++++ Purge the cache of obsolete data. * --purge: purge the paths tree. Walk the tree, read each path file, and check the existence of the recorded path. If the recorded path does not exist any more, delete the path file. * --purgedata: purge the data tree. Make a list of all data files referenced by at least one path file, then walk the data tree, deleting unreferenced files. This means that data files from temporary document copies (see further below) will be deleted, which is quite unsatisfying. This would be difficult to change: - There is no way to detect the affected files because the data files store no origin information. - Even if we wanted to store an indication that the data file comes from a temporary document, we'd have no way to access the original document because the full ipath is not available. Changing this would be close to impossible for Recoll internals reasons (internfile...). In consequence the --purgedata option must be explicitly added for a data purge to be performed. Only set it if re-OCRing all embedded documents is reasonable. rclocrcache.py --store ++++++++++++++++++++++++++++++++++++++++++++++++++ Store data: create a cache entry for for the source file , and the processed data . rclocrcache.py --get ++++++++++++++++++++++++++++++++++ Retrieve data stored for . The data is written to stdout. rclocrcache.py --erase ++++++++++++++++++++++++++++++++++++ Erase the cache contents for . Cache organisation details: ============================ The cache stores 2 kinds of objects: - Path files are named from the hash of the document file path and contain the document data hash, the modification time and size of the document file at the time it was processed, and the document file path itself (the last is for purging only). - Data files are named with the hash of the document data and contain the processed data (z-lib compressed). - The cache path and data files are stored under top subdirectories: objects/ and paths/ - In both cases, the the first two characters of the path or data sha1 hash are used as a subdirectory name, the rest as a file name, so a typical path would look like *objects/xx/xxxxxxxxxx* or *paths/xx/xxxxxxxxxxx*. When retrieving data from the cache: - We first use the document file path, size, and modification time: if an entry exists for the imagepath/mtime/size triplet, and is up to date, the data file is obtained from the hash contained in the path file, and its contents are returned. - Else we then use the document data: if an entry exists for the computed hashed value of the data, it is returned. This allows moving files around without needing to process them again, but of course, it is more expensive than the first step. If we need to use the second step, as a side effect, a path file is created or updated so that the data will be found with the first step next time around. When processing embedded documents like email attachments, recoll uses temporary copies in TMPDIR (which defaults to /tmp) or RECOLL_TMPDIR. Of course the paths for the temporary files changes when re-processing a given document. We do not store the Path file for data stored in TMPDIR or RECOLL_TMPDIR, because doing so would cause an indefinite accumulation of unusable Path files. This means that access to the OCR data for these documents always causes the computation of the data hash, and is slower. With recent Recoll versions which cache the text content in the index, this only occurs when reindexing (with older versions, this could also occur for Preview). """ import sys import os import hashlib import urllib.parse import zlib import glob from rclexecm import logmsg as _deb def _catslash(p): if p and p[-1] != "/": p += "/" return p _tmpdir = os.environ["TMPDIR"] if "TMPDIR" in os.environ else "/tmp" _tmpdir = _catslash(_tmpdir) _recoll_tmpdir = os.environ["RECOLL_TMPDIR"] if "RECOLL_TMPDIR" in os.environ else None _recoll_tmpdir = _catslash(_recoll_tmpdir) class OCRCache(object): def __init__(self, conf, hashmb=0): """Initialize the cache object using parameters from conf, a RclConfig object. Keyword arguments: hashmb -- if non zero we only hash the amount of data specified by the value (mb). Data slices will be taken from the start, middle, and end of the file. """ self.config = conf self.hashmb = hashmb self.cachedir = conf.getConfParam("ocrcachedir") if not self.cachedir: self.cachedir = os.path.join(self.config.getConfDir(), "ocrcache") self.objdir = os.path.join(self.cachedir, "objects") self.pathdir = os.path.join(self.cachedir, "paths") for dir in (self.objdir, self.pathdir): os.makedirs(dir, exist_ok=True) # Compute sha1 of path, as two parts of 2 and 38 chars def _hashpath(self, path): if type(path) != type(b""): path = path.encode('utf-8') m = hashlib.sha1() m.update(path) h = m.hexdigest() return h[0:2], h[2:] def _hashslice(self, hobj, f, offs, slicesz): """Update hash with data slice from file""" #_deb(f"Hashing {slicesz} at {offs}") f.seek(int(offs)) cnt = 0 while cnt < slicesz: rcnt = min(slicesz - cnt, 8192) d = f.read(int(rcnt)) if not d: break hobj.update(d) cnt += len(d) def _hashdata(self, path): """Compute sha1 of path data contents, as two parts of 2 and 38 chars. If hashmb was set on init, we compute a partial hash in three slices at the start, middle and end of the file, with a total size of hashmb * MBs. """ #_deb("Hashing DATA") sz = os.path.getsize(path) hobj = hashlib.sha1() hashbytes = self.hashmb * 1000000 with open(path, "rb", buffering=8192) as f: if not self.hashmb or sz <= hashbytes: self._hashslice(hobj, f, 0, sz) else: slicesz = hashbytes/3 for offs in (0, sz/2 - slicesz/2, sz - slicesz): self._hashslice(hobj, f, offs, slicesz) h = hobj.hexdigest() #_deb(f"DATA hash {h[0:2]} {h[2:]}") return h[0:2], h[2:] def _readpathfile(self, ppf): """Read path file and return values. We do not decode the image path as this is only used for purging""" with open(ppf, 'r') as f: line = f.read() dd, df, tm, sz, pth = line.split() tm = int(tm) sz = int(sz) return dd, df, tm, sz, pth # Try to read the stored attributes for a given path: data hash, # modification time and size. If this fails, the path itself is # not cached (but the data still might be, maybe the file was moved) def _cachedpathattrs(self, path): pd, pf = self._hashpath(path) pathfilepath = os.path.join(self.pathdir, pd, pf) if not os.path.exists(pathfilepath): return False, None, None, None, None try: dd, df, tm, sz, pth = self._readpathfile(pathfilepath) return True, dd, df, tm, sz except Exception as ex: _deb(f"Error while trying to access pathfile {pathfilepath}: {ex}") return False, None, None, None, None # Compute the path hash, and get the mtime and size for given # path, for updating the cache path file def _newpathattrs(self, path): pd, pf = self._hashpath(path) tm = int(os.path.getmtime(path)) sz = int(os.path.getsize(path)) return pd, pf, tm, sz # Check if the cache appears up to date for a given path, only # using the modification time and size. Return the data file path # elements if we get a hit. def _pathincache(self, path): ret, od, of, otm, osz = self._cachedpathattrs(path) if not ret: return False, None, None pd, pf, ntm, nsz = self._newpathattrs(path) # _deb(" tm %d sz %d" % (ntm, nsz)) # _deb("otm %d osz %d" % (otm, osz)) if otm != ntm or osz != nsz: return False, None, None return True, od, of # Compute the data file name for path. Expensive: we compute the data hash. # Return both the data file path and path elements (for storage in path file) def _datafilename(self, path): d, f = self._hashdata(path) return os.path.join(self.objdir, d, f), d, f # Create path file with given elements. def _updatepathfile(self, pd, pf, dd, df, tm, sz, path): global _tmpdir, _recoll_tmpdir if (_tmpdir and path.startswith(_tmpdir)) or \ (_recoll_tmpdir and path.startswith(_recoll_tmpdir)): _deb(f"ocrcache: not storing path data for temporary file {path}") return dir = os.path.join(self.pathdir, pd) if not os.path.exists(dir): os.makedirs(dir) pfile = os.path.join(dir, pf) codedpath = urllib.parse.quote(path) with open(pfile, "w") as f: f.write("%s %s %d %d %s\n" % (dd, df, tm, sz, codedpath)) # Store data for path. Only rewrite an existing data file if told # to do so: this is only useful if we are forcing an OCR re-run. def store(self, path, datatostore, force=False): dd, df = self._hashdata(path) pd, pf, tm, sz = self._newpathattrs(path) self._updatepathfile(pd, pf, dd, df, tm, sz, path) dir = os.path.join(self.objdir, dd) if not os.path.exists(dir): os.makedirs(dir) dfile = os.path.join(dir, df) if force or not os.path.exists(dfile): # _deb("Storing data") cpressed = zlib.compress(datatostore) with open(dfile, "wb") as f: f.write(cpressed) return True def erase(self, path): """Unconditionally erase cached data for input data file path""" ok=True pd, pf = self._hashpath(path) pfn = os.path.join(self.pathdir, pd, pf) try: dd, df, tm, sz, orgpath = self._readpathfile(pfn) except Exception as ex: _deb(f"Ocrcache: failed reading path file: {ex}") return False dfn = os.path.join(self.objdir, dd, df) try: os.unlink(pfn) except Exception as ex: ok = False _deb(f"Ocrcache: failed deleting {pfn}: {ex}") try: os.unlink(dfn) except Exception as ex: ok = False _deb(f"Ocrcache: failed deleting {dpfn}: {ex}") return ok # Retrieve cached OCR'd data for image path. Possibly update the # path file as a side effect (case where the image has moved, but # the data has not changed). def get(self, path): pincache, dd, df = self._pathincache(path) if pincache: dfn = os.path.join(self.objdir, dd, df) else: dfn, dd, df = self._datafilename(path) if not os.path.exists(dfn): _deb(f"ocrcache: no cached data for {path}") return False, b"" if not pincache: # File may have moved. Create/Update path file for next time _deb(f"ocrcache::get: data ok but path file for {path} does not exist: creating it") pd, pf, tm, sz = self._newpathattrs(path) self._updatepathfile(pd, pf, dd, df, tm, sz, path) with open(dfn, "rb") as f: cpressed = f.read() data = zlib.decompress(cpressed) return True, data def _pathstale(self, origpath, otm, osz): """Return True if the input path has been removed or modified""" if not os.path.exists(origpath): return True ntm = int(os.path.getmtime(origpath)) nsz = int(os.path.getsize(origpath)) if ntm != otm or nsz != osz: # _deb("Purgepaths otm %d ntm %d osz %d nsz %d"%(otm, ntm, osz, nsz)) return True return False def purgepaths(self): """Remove all stale pathfiles: source image does not exist or has been changed. Mostly useful for removed files, modified ones would be processed by recollindex.""" allpathfiles = glob.glob(os.path.join(self.pathdir, "*", "*")) for pathfile in allpathfiles: dd, df, tm, sz, orgpath = self._readpathfile(pathfile) needpurge = self._pathstale(orgpath, tm, sz) if needpurge: _deb("purgepaths: removing %s (%s)" % (pathfile, orgpath)) os.remove(pathfile) def _walk(self, topdir, cb): """Specific fs walk: we know that our tree has 2 levels. Call cb with the file path as parameter for each file""" dlist = glob.glob(os.path.join(topdir, "*")) for dir in dlist: files = glob.glob(os.path.join(dir, "*")) for f in files: cb(f) def _pgdt_pathcb(self, f): """Get a pathfile name, read it, and record datafile identifier (concatenate data file subdir and file name)""" # _deb("_pgdt_pathcb: %s" % f) dd, df, tm, sz, orgpath = self._readpathfile(f) self._pgdt_alldatafns.add(dd+df) def _pgdt_datacb(self, datafn): """Get a datafile name and check that it is referenced by a previously seen pathfile""" p1, fn = os.path.split(datafn) p2, dn = os.path.split(p1) tst = dn+fn if tst in self._pgdt_alldatafns: _deb("purgedata: ok : %s" % datafn) pass else: _deb("purgedata: removing : %s" % datafn) os.remove(datafn) def purgedata(self): """Remove all data files which do not match any from the input list, based on data contents hash. We make a list of all data files referenced by the path files, then walk the data tree, removing all unreferenced files. This should only be run after an indexing pass, so that the path files are up to date. It's a relatively onerous operation as we have to read all the path files, and walk both sets of files.""" self._pgdt_alldatafns = set() self._walk(self.pathdir, self._pgdt_pathcb) self._walk(self.objdir, self._pgdt_datacb) if __name__ == "__main__": import rclconfig import getopt def Usage(f=sys.stderr): print("Usage: rclocrcache.py --purge [--purgedata]", file=f) print("Usage: rclocrcache.py --store ", file=f) print("Usage: rclocrcache.py --get ", file=f) print("Usage: rclocrcache.py --erase ", file=f) sys.exit(1) opts, args = getopt.getopt(sys.argv[1:], "h", ["help", "purge", "purgedata", "store", "get", "erase", "hashmb="]) purgedata = False action = "" hashmb = 0 for opt, arg in opts: if opt in ['-h', '--help']: Usage(sys.stdout) elif opt in ['--purgedata']: purgedata = True elif opt in ['--hashmb']: hashmb = int(arg) elif opt in ['--purge']: if len(args) != 0: Usage() action = "purge" elif opt in ['--store']: if len(args) != 2: Usage() imgdatapath = args[0] ocrdatapath = args[1] ocrdata = open(ocrdatapath, "rb").read() action = "store" elif opt in ['--get']: if len(args) != 1: Usage() imgdatapath = args[0] action = "get" elif opt in ['--erase']: if len(args) != 1: Usage() imgdatapath = args[0] action = "erase" else: print(f"Unknown option {opt}", file=sys.stderr) Usage() conf = rclconfig.RclConfig() cache = OCRCache(conf, hashmb=hashmb) if action == "purge": cache.purgepaths() if purgedata: cache.purgedata() elif action == "store": cache.store(imgdatapath, ocrdata, force=False) elif action == "get": incache, data = cache.get(imgdatapath) if incache: print(f"OCR data from cache {data}") sys.exit(0) else: print("OCR Data was not found in cache", file=sys.stderr) sys.exit(1) elif action == "erase": if cache.erase(imgdatapath): print(f"Erased data for {imgdatapath}") sys.exit(0) else: print(f"Failed erasing data for {imgdatapath}") sys.exit(1) else: Usage() recoll-1.36.1/filters/rcltext.py0000755000175000017500000000270114410615043013513 00000000000000#!/usr/bin/env python3 # Copyright (C) 2016 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Wrapping a text file. Recoll does it internally in most cases, but # this is for use by another filter. import rclexecm import sys from rclbasehandler import RclBaseHandler class TxtDump(RclBaseHandler): def __init__(self, em): super(TxtDump, self).__init__(em) def html_text(self, fn): # No charset, so recoll will have to use its config to guess it html = b'
'
        with open(fn, "rb") as f:
            html += rclexecm.htmlescape(f.read())
        html += b'
' return html if __name__ == '__main__': proto = rclexecm.RclExecM() extract = TxtDump(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclshowinfo0000755000175000017500000000154714410615043013743 00000000000000#!/bin/sh # A small script to help recoll start info on the node corresponding to # the result document. The title is the node path, but it needs to be # somewhat processed fatal() { echo $* exit 1 } Usage() { fatal "Usage: rclshowinfo filename top node / sub node [/ ...]" } test $# -ge 2 || Usage filename=`echo $1 | sed -e 's!^file://!!'` shift # The title set by recoll while indexing is like: # infofilename / first node path element / second node path element ... IFS=/ set $* while test $# -gt 1;do shift # node=`echo $1 | sed -e 's/^ *//' -e 's/ *$//'` node=`eval "echo $1"` nodepath="$nodepath '$node'" done for t in x-terminal-emulator gnome-terminal konsole xfce4-terminal xterm ; do tp=`which $t` echo "termpath $tp" if test X"$tp" != X ; then set -x exec "$tp" -e "info -f $filename $nodepath" fi done recoll-1.36.1/filters/rcldvi0000755000175000017500000001002314410615043012656 00000000000000#!/bin/sh # @(#$Id: rcldvi,v 1.7 2008-10-08 16:15:22 dockes Exp $ (C) 2006 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Extract text from a dvi file by either executing dvitops and # pstotext or using catdvi. # # pstotext does not work any more with gs 9.22-25, so catdvi is always # needed now. # Initially dvitops had given better results during tests, and was # chosen first if available, but the dvitops/pstotext combination # is much slower than catdvi set variables. In any case, the # program is not too good with special characters (e.g. ligatures) LANG=C ; export LANG LC_ALL=C ; export LC_ALL progname="rcldvi" filetype=dvi #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE decoderdvips() { dvips -f $1 2> /dev/null | pstotext | iconv -f cp1252 -t utf-8 -c -s } decodercatdvi() { catdvi --output-encoding=UTF-8 $1 } decoder="" # pstotext does not work any more # iscmd dvips # isdvips=$? # iscmd pstotext # ispstotext=$? # if test $isdvips -eq 0 -a $ispstotext -eq 0; then # decoder=decoderdvips # elif iscmd catdvi ; then # decoder=decodercatdvi # fi if iscmd catdvi ; then decoder=decodercatdvi fi if test X$decoder = X ; then senderror HELPERNOTFOUND catdvi fi # The strange 'BEGIN' setup is to prevent 'file' from thinking this file # is an awk program $decoder "$infile" | awk 'BEGIN'\ ' { printf("\n") printf("\n") printf("\n

"); cont = "" } { $0 = cont $0 cont = "" if ($0 == "\f") { print "

\n
\n\f

" next } else if ($0 ~ /-$/) { match($0, "[ \t][^ \t]+$") line = substr($0, 0, RSTART) cont = substr($0, RSTART, RLENGTH) $0 = line gsub("-", "", cont) } gsub(/&/, "\\&", $0) gsub(//, "\\>", $0) print $0 "
" } END { print "

" }' recoll-1.36.1/filters/rclbibtex.sh0000755000175000017500000000153014410615043013765 00000000000000#!/bin/sh # Remove unwanted data from bibtex file by deleting "bad" lines and # cleaning "good" ones. if test "X$RECOLL_FILTER_FORPREVIEW" = "Xyes" ; then sed \ -e '/^$/N;/^\n$/D' \ -e '/(bibdsk.*\|month\|bibsource\|crossref\|ee\|groups\|owner\|pages\|timestamp\|url\|file\|price\|citeulike.*\|markedentry\|posted-at)[[:space:]=]*/I d' \ -e '/@[^{]*{/ d' \ \ -e 's/{//g' \ -e 's/},\?$//g' \ -e 's/^[ ]*//' \ -e 's/({|}|=)//g' \ < $1 else sed -e '/\(StaticGroup\|bibdsk.*\|month\|edition\|address\|query\|bibsource\|crossref\|ee\|groups\|owner\|pages\|timestamp\|url\|file\|price\|citeulike.*\|markedentry\|posted-at\)[[:space:]=]*/I d' \ -e '/@[^{]*{/ d' \ \ -e 's/^[^{]*{//g' \ -e 's/},\?$//g' \ -e 's/({|}|=)//g' \ < $1 fi recoll-1.36.1/filters/rcluncomp.py0000644000175000017500000000277614410615043014041 00000000000000# No shebang: this is only used on Windows. We use a shell script on Linux import rclexecm import sys import os import shutil import platform import subprocess import glob def _msg(s): rclexecm.logmsg(s) sysplat = platform.system() if sysplat != "Windows": _msg("rcluncomp.py: only for Windows") sys.exit(1) try: import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) except Exception as err: _msg("setmode binary failed: %s" % str(err)) sevenz = rclexecm.which("7z") if not sevenz: _msg("rcluncomp.py: can't find 7z exe. Maybe set recollhelperpath " \ "in recoll.conf ?") sys.exit(2) # Params: uncompression program, input file name, temp directory. # We ignore the uncomp program, and always use 7z on Windows infile = sys.argv[2] outdir = sys.argv[3] # _msg("rcluncomp.py infile [%s], outdir [%s]" % (infile, outdir)) # There is apparently no way to suppress 7z output. Hopefully the # possible deadlock described by the subprocess module doc can't occur # here because there is little data printed. AFAIK nothing goes to stderr anyway try: cmd = [sevenz, "e", "-bd", "-y", "-o" + outdir, infile] subprocess.check_output(cmd, stderr = subprocess.PIPE) # Don't use os.path.join, we always want to use '/' outputname = glob.glob(outdir + "/*") # There should be only one file in there.. print(outputname[0]) except Exception as err: _msg("%s" % (str(err),)) sys.exit(4) sys.exit(0) recoll-1.36.1/filters/rclepub.py0000755000175000017500000001257514463114123013475 00000000000000#!/usr/bin/env python3 """Extract Html content from an EPUB file (.epub)""" rclepub_html_mtype = "text/html" import sys import os import re import subprocess import rclexecm import rclconfig sys.path.insert(0, os.path.join(os.path.dirname(__file__), "recollepub.zip")) try: import epub except: print("RECFILTERROR HELPERNOTFOUND python3:epub") sys.exit(1); class rclEPUB: """RclExecM slave worker for extracting all text from an EPUB file. We first extract the list of internal nodes, and them return them one by one. The ipath is the internal href""" def __init__(self, em): self.currentindex = 0 self.em = em self.em.setmimetype(rclepub_html_mtype) cf = rclconfig.RclConfig() self.catenate = cf.getConfParam("epubcatenate") self.catenate = int(self.catenate) if self.catenate else False def _docheader(self): meta = self.book.opf.metadata title = "" for tt, lang in meta.titles: title += tt + " " author = "" for name, role, fileas in meta.creators: author += name + " " data = "\n\n" if title: data += "" + rclexecm.htmlescape(title) + "\n" if author: data += '\n' if meta.description: data += '\n' for value in meta.subjects: data += '\n' data += "" return data.encode('UTF-8') def _catbodies(self): data = b'' ids = [] if self.book.opf.spine: for id, linear in self.book.opf.spine.itemrefs: ids.append(id) else: for id, item in self.book.opf.manifest.items(): ids.append(id) for id in ids: item = self.book.get_item(id) if item is None or item.media_type != 'application/xhtml+xml': continue doc = self.book.read_item(item) doc = re.sub(b'''<\?.*\?>''', b'', doc) doc = re.sub(b''']*>''', b'', doc, 1, flags=re.DOTALL|re.I) doc = re.sub(b'''''', b'', doc, flags=re.I) doc = re.sub(b'''''', b'', doc, flags=re.I) data += doc data += b'' return data def _selfdoc(self): data = self._docheader() self.em.setmimetype('text/html') if len(self.contents) == 0: self.closefile() eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, data, "", eof) def extractone(self, id): """Extract one path-named internal file from the EPUB file""" #self.em.rclog("extractone: [%s]"%(path)) iseof = rclexecm.RclExecM.noteof if self.currentindex >= len(self.contents) -1: iseof = rclexecm.RclExecM.eofnext try: item = self.book.get_item(id) if item is None: raise Exception("Item not found for id %s" % (id,)) doc = self.book.read_item(item) doc = re.sub(b'''''', b'''''', doc) self.em.setmimetype(rclepub_html_mtype) return (True, doc, id, iseof) except Exception as err: self.em.rclog("extractone: failed: [%s]" % err) return (False, "", id, iseof) def dumpall(self): data = self._docheader() data += self._catbodies() return data def closefile(self): self.book.close() def openfile(self, params): """Open the EPUB file, create a contents array""" self.currentindex = -1 self.contents = [] try: self.book = epub.open_epub(params["filename"].decode('UTF-8')) except Exception as err: self.em.rclog("openfile: epub.open failed: [%s]" % err) return False for id, item in self.book.opf.manifest.items(): if item.media_type == 'application/xhtml+xml': self.contents.append(id) return True def getipath(self, params): return self.extractone(params["ipath"].decode('UTF-8')) def getnext(self, params): if self.catenate: alltxt = self.dumpall() self.closefile() if alltxt: return (True, alltxt, "", rclexecm.RclExecM.eofnext) else: return (False, "", "", rclexecm.RclExecM.eofnow) if self.currentindex == -1: self.currentindex = 0 return self._selfdoc() if self.currentindex >= len(self.contents): self.closefile() return (False, "", "", rclexecm.RclExecM.eofnow) else: ret = self.extractone(self.contents[self.currentindex]) if ret[3] == rclexecm.RclExecM.eofnext or \ ret[3] == rclexecm.RclExecM.eofnow: self.closefile() self.currentindex += 1 return ret proto = rclexecm.RclExecM() extract = rclEPUB(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rcllyx0000755000175000017500000001546514410615043012727 00000000000000#!/bin/sh # @(#$Id: rcllyx,v 1.6 2007-06-08 13:51:09 dockes Exp $ (C) 2004 J.F.Dockes # There may still be code from Estraier in here: #================================================================ # Estraier: a personal full-text search system # Copyright (C) 2003-2004 Mikio Hirabayashi #================================================================ #================================================================ # Convert a lyx file to recoll HTML. # # We use lyx --export. It was suggested to use untex, but it doesn't give # good results on raw lyx (of course, this is not TeX), and exporting to # LaTex then using untex doesn't look nice when we can use the native lyx # text export. # The character encoding of the exported text is defined by the # \inputencoding directive in the lyx file header and, in quite an obscure # way, by the \language parameter. We use a heuristic to divine the output # text encoding and it is guaranteed not to work in all cases. Trials using # an intermediary dvi, pdf or ps file gave worse results. This needs # improvement. It doesn't even take into account the fact that the language # can change inside the doc (does this change the encoding or not ?). To be # frank, this is not entirely my fault, the lyx format is a joke. # # As there is unfortunately no way to define the output file name, we have # to use a temporary directory and link the input file in there. # set variables #LANG=C ; export LANG #LC_ALL=C ; export LC_ALL progname="rcllyx" filetype=lyx #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE checkcmds lyx iconv # We need a temporary directory if test z"$RECOLL_TMPDIR" != z; then ttdir=$RECOLL_TMPDIR elif test z"$TMPDIR" != z ; then ttdir=$TMPDIR else ttdir=/tmp fi tmpdir=$ttdir/rcllyx_tmp$$ mkdir $tmpdir || exit 1 mkdir $tmpdir/rcllyxtmp || exit 1 cleanup() { # Note that we're using a constant part (rcllyxtmp), that hopefully # guarantees that we can't do big mistakes here. rm -rf $tmpdir/rcllyxtmp rmdir $tmpdir } trap cleanup EXIT HUP QUIT INT TERM workdir=$tmpdir/rcllyxtmp case "$infile" in */*) ;; *) infile=`pwd`/"$infile";; esac ln -s "$infile" "$workdir/input.lyx" || exit 1 lyxfile=$workdir/input.lyx textfile=$workdir/input.txt #echo lyxfile: $lyxfile ; ls -l $lyxfile; echo textfile: $textfile # Run lyx --export lyx --export text "$lyxfile" || senderror "lyx --export text "$lyxfile" failed" # Need the lyx version. After some point -export prints utf-8, # whatever the input version LYXOUTPUTUTF=No vline=`lyx --version 2>&1 | head -1 | tr '.' ' '` set $vline for i in $2 $3 $4;do expr "$i" ':' '[0-9][0-9]*' > /dev/null || \ senderror "Bad lyx version string $vline" done maj=`expr $2 '*' 10000` med=`expr $3 '*' 100` min=`expr $4 '*' 1` version=`expr $maj + $med + $min` || senderror "Bad lyx version string $vline" if test $version -ge 10607 ; then LYXOUTPUTUTF=Yes fi if test X$LYXOUTPUTUTF = XNo ; then echo "OLD VERSION" # Charset and language formatline=`egrep '^\\\lyxformat ' "$lyxfile"` if test -n "$formatline" ; then set $formatline format=$2 fi charsetline=`egrep '^\\\inputencoding ' "$lyxfile"` if test -n "$charsetline" ; then set $charsetline charset=$2 fi langline=`egrep '^\\\language ' "$lyxfile"` if test -n "$langline" ; then set $langline lang=$2 fi #echo format: [$format] charset: [$charset] lang [$lang] if test "$format" -ge 249 ; then charset=utf-8 else # try to guess the charset from the language: this is in no way guaranteed # to work, the logic has built-in inconsistencies even beyond the numerous # external ones (what if the ukrainian writer prefers koi8-r ?). This is a # joke. if test -z "$charset" -o "$charset" = default -o "$charset" = auto ; then case "$lang" in american|afrikaans|basque|catalan|danish|dutch|english|faeroese|finnish|french|galician|german|icelandic|irish|italian|norwegian|portuguese|spanish|swedish) charset=iso-8859-1;; czech|german|hungarian|polish|romanian|croatian|slovak|slovene) charset=iso-8859-2;; esperanto|galician|maltese|Turkish) charset=iso-8859-3;; estonian|latvian|lithuanian) charset=iso-8859-4;; bulgarian|byelorussian|macedonian|russian|serbian|ukrainian) charset=iso-8859-5;; arabic) charset=iso-8859-6;; greek) charset=iso-8859-7;; hebrew) charset=iso-8859-8;; #ISO-8859-9 - Latin 5 Same as 8859-1 except for Turkish instead of #Icelandic. ? What is one to do :) #ISO-8859-10 - Latin 6 lappish|nordic|eskimo|inuit|sami) charset=iso-8859-10;; albanian|german|english|basque|breton|catalan|danish|spanish|estonian|esthonian|faeroese|faroese|finnish|french|frisian|friesian|scottish|goidelic|irish|gaelic|galician|welsh|greenlandic|inuit|icelandic|italian|latin|dutch|norvegian|portuguese|romansch|romansh|friulian|ladin|swedish) charset=iso-8859-15;; *) charset=iso-8859-1;; esac fi fi # End Old lyx needing output tweaking fi if test -n "$charset" ; then inputcmd="iconv -f $charset -t UTF-8 -c -s" else inputcmd=cat fi #echo inputcmd: [$inputcmd] cat < $title
EOF

$inputcmd < "$textfile"

cat <


EOF
recoll-1.36.1/filters/rclmidi.py0000644000175000017500000006524014463114123013456 00000000000000# The MIT License (MIT)
# Copyright (c) 2013 Giles F. Hall
# https://github.com/vishnubob/python-midi/blob/master/LICENSE
# Modifications: Copyright (c) 2012-2018 J.F. Dockes
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# 

import sys
from struct import unpack, pack

def next_byte_as_int(data):
    return next(data)
def next_byte_as_char(data):
    return bytes([next(data)])

##
## Constants
##

OCTAVE_MAX_VALUE = 12
OCTAVE_VALUES = list(range( OCTAVE_MAX_VALUE ))

NOTE_NAMES = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']
WHITE_KEYS = [0, 2, 4, 5, 7, 9, 11]
BLACK_KEYS = [1, 3, 6, 8, 10]
NOTE_PER_OCTAVE = len( NOTE_NAMES )
NOTE_VALUES = list(range( OCTAVE_MAX_VALUE * NOTE_PER_OCTAVE ))
NOTE_NAME_MAP_FLAT = {}
NOTE_VALUE_MAP_FLAT = []
NOTE_NAME_MAP_SHARP = {}
NOTE_VALUE_MAP_SHARP = []

for value in list(range( 128 )):
    noteidx = value % NOTE_PER_OCTAVE
    octidx = value / OCTAVE_MAX_VALUE
    name = NOTE_NAMES[noteidx]
    if len( name ) == 2:
        # sharp note
        flat = NOTE_NAMES[noteidx+1] + 'b'
        NOTE_NAME_MAP_FLAT['%s-%d' % (flat, octidx)] = value
        NOTE_NAME_MAP_SHARP['%s-%d' % (name, octidx)] = value
        NOTE_VALUE_MAP_FLAT.append( '%s-%d' % (flat, octidx) )
        NOTE_VALUE_MAP_SHARP.append( '%s-%d' % (name, octidx) )
        globals()['%s_%d' % (name[0] + 's', octidx)] = value
        globals()['%s_%d' % (flat, octidx)] = value
    else:
        NOTE_NAME_MAP_FLAT['%s-%d' % (name, octidx)] = value
        NOTE_NAME_MAP_SHARP['%s-%d' % (name, octidx)] = value
        NOTE_VALUE_MAP_FLAT.append( '%s-%d' % (name, octidx) )
        NOTE_VALUE_MAP_SHARP.append( '%s-%d' % (name, octidx) )
        globals()['%s_%d' % (name, octidx)] = value

BEATNAMES = ['whole', 'half', 'quarter', 'eighth', 'sixteenth', 'thiry-second', 'sixty-fourth']
BEATVALUES = [4, 2, 1, .5, .25, .125, .0625]
WHOLE = 0
HALF = 1
QUARTER = 2
EIGHTH = 3
SIXTEENTH = 4
THIRTYSECOND = 5
SIXTYFOURTH = 6

DEFAULT_MIDI_HEADER_SIZE = 14


"""
EventMIDI : Concrete class used to describe MIDI Events.
Inherits from Event.
"""

class EventMeta(type):
    def __init__(cls, name, bases, dict):
        if name not in ['Event', 'MetaEvent', 'NoteEvent']:
            EventFactory.register_event(cls, bases)

class Event(object, metaclass=EventMeta):
    length = 0
    name = "Generic MIDI Event"
    statusmsg = 0x0

    class __metaclass__(type):
        def __init__(cls, name, bases, dict):
            if name not in ['Event', 'MetaEvent', 'NoteEvent']:
                EventFactory.register_event(cls, bases)

    def __init__(self):
        """ event type derived from __class__ """
        self.type       = self.__class__.__name__
        """ midi channel """
        self.channel    = 0
        """ midi tick """
        self.tick       = 0
        """ delay in ms """
        self.msdelay    = 0
        """ data after statusmsg """
        self.data       = b''
        """ track number """
        self.track      = 0
        """ sort order """
        self.order      = None

    def is_event(cls, statusmsg):
        return (cls.statusmsg == (statusmsg & 0xF0))
    is_event = classmethod(is_event)

    def __str__(self):
        return "%s @%d %dms C%d T%d" % (self.name, 
                            self.tick,
                            self.msdelay,
                            self.channel,
                            self.track)

    def __cmp__(self, other):
        if self.tick < other.tick: return -1
        elif self.tick > other.tick: return 1
        return 0
    def __lt__(self, other):
        return self.tick < other.tick
    def __eq__(self, other):
        return self.tick == other.tick

    def adjust_msdelay(self, tempo):
        rtick = self.tick - tempo.tick
        self.msdelay = int((rtick * tempo.mpt) + tempo.msdelay)
     
    def decode(self, tick, statusmsg, track, runningstatus=b''):
        assert(self.is_event(statusmsg))
        self.tick = tick
        self.channel = statusmsg & 0x0F
        self.data = b''
        if runningstatus:
            self.data += runningstatus
        remainder = self.length - len(self.data)
        if remainder:
            self.data += bytes.join(b'', [next_byte_as_char(track)
                                         for x in range(remainder)])
        self.decode_data()

    def decode_data(self):
        pass

    
"""
MetaEvent is a special subclass of Event that is not meant to
be used as a concrete class.  It defines a subset of Events known
as the Meta  events.
"""
    
class MetaEvent(Event):
    statusmsg = 0xFF
    metacommand = 0x0
    name = 'Meta Event'

    def is_event(cls, statusmsg):
        return (cls.statusmsg == statusmsg)
    is_event = classmethod(is_event)

    def is_meta_event(cls, metacmd):
        return (cls.metacommand == metacmd)
    is_meta_event = classmethod(is_meta_event)

    def decode(self, tick, command, track):
        assert(self.is_meta_event(command))
        self.tick = tick
        self.channel = 0
        if not hasattr(self, 'order'):
            self.order = None
        len = read_varlen(track)
        self.data = bytes.join(b'', [next_byte_as_char(track)
                                     for x in range(len)])
        self.decode_data()


"""
EventFactory is a singleton that you should not instantiate.  It is
a helper class that assists you in building MIDI event objects.
"""

class EventFactory(object):
    EventRegistry = []
    MetaEventRegistry = []
    
    def __init__(self):
        self.RunningStatus = None
        self.RunningTick = 0

    def register_event(cls, event, bases):
        if MetaEvent in bases:
            cls.MetaEventRegistry.append(event)
        elif (Event in bases) or (NoteEvent in bases):
            cls.EventRegistry.append(event)
        else:
            raise ValueError("Unknown bases class in event type: "+event.name)
    register_event = classmethod(register_event)

    def parse_midi_event(self, track):
        # first datum is varlen representing delta-time
        tick = read_varlen(track)
        self.RunningTick += tick
        # next byte is status message
        stsmsg = next_byte_as_int(track)
        # is the event a MetaEvent?
        if MetaEvent.is_event(stsmsg):
            # yes, figure out which one
            cmd = next_byte_as_int(track)
            for etype in self.MetaEventRegistry:
                if etype.is_meta_event(cmd):
                    evi = etype()
                    evi.decode(self.RunningTick, cmd, track)
                    return evi
            else:
                raise Warning("Unknown Meta MIDI Event: " + repr(cmd))
        # not a Meta MIDI event, must be a general message
        else:
            for etype in self.EventRegistry:
                if etype.is_event(stsmsg):
                    self.RunningStatus = (stsmsg, etype)
                    evi = etype()
                    evi.decode(self.RunningTick, stsmsg, track)
                    return evi
            else:
                if self.RunningStatus:
                    cached_stsmsg, etype = self.RunningStatus
                    evi = etype()
                    evi.decode(self.RunningTick, 
                            cached_stsmsg, track, bytes([stsmsg]))
                    return evi
                else:
                    raise Warning("Unknown MIDI Event: " + repr(stsmsg))

class NoteEvent(Event):
    length = 2
    fields = ['pitch', 'velocity']

    def __str__(self):
        return "%s [ %s(%s) %d ]" % \
                            (super(NoteEvent, self).__str__(),
                                NOTE_VALUE_MAP_SHARP[self.pitch],
                                self.pitch,
                                self.velocity)

    def decode_data(self):
        self.pitch = self.data[0]
        self.velocity = self.data[1]


class NoteOnEvent(NoteEvent):
    statusmsg = 0x90
    name = 'Note On'

class NoteOffEvent(NoteEvent):
    statusmsg = 0x80
    name = 'Note Off'

class AfterTouchEvent(Event):
    statusmsg = 0xA0
    length = 2
    name = 'After Touch'

    def __str__(self):
        return "%s [ %s %s ]" % \
                            (super(AfterTouchEvent, self).__str__(),
                                hex(ord(self.data[0])),
                                hex(ord(self.data[1])))

class ControlChangeEvent(Event):
    statusmsg = 0xB0
    length = 2
    name = 'Control Change'
    
    def __str__(self):
        return "%s [ %s %s ]" % \
                            (super(ControlChangeEvent, self).__str__(),
                                hex(ord(self.data[0])),
                                hex(ord(self.data[1])))

    def decode_data(self):
        self.control = self.data[0]
        self.value = self.data[1]


class ProgramChangeEvent(Event):
    statusmsg = 0xC0
    length = 1
    name = 'Program Change'

    def __str__(self):
        return "%s [ %s ]" % \
                            (super(ProgramChangeEvent, self).__str__(),
                                hex(ord(self.data[0])))

    def decode_data(self):
        self.value = self.data[0]


class ChannelAfterTouchEvent(Event):
    statusmsg = 0xD0
    length = 1
    name = 'Channel After Touch'

    def __str__(self):
        return "%s [ %s ]" % \
                            (super(ChannelAfterTouchEvent,self).__str__(),
                                hex(ord(self.data[0])))

class PitchWheelEvent(Event):
    statusmsg = 0xE0
    length = 2
    name = 'Pitch Wheel'

    def __str__(self):
        return "%s [ %s %s ]" % \
                            (super(PitchWheelEvent, self).__str__(),
                                hex(ord(self.data[0])),
                                hex(ord(self.data[1])))

    def decode_data(self):
        first = self.data[0]
        second = self.data[1]
        self.value = ((second << 7) | first) - 0x2000


class SysExEvent(Event):
    statusmsg = 0xF0
    name = 'SysEx'

    def is_event(cls, statusmsg):
        return (cls.statusmsg == statusmsg)
    is_event = classmethod(is_event)

    def decode(self, tick, statusmsg, track):
        self.tick = tick
        self.channel = statusmsg & 0x0F
        len = read_varlen(track)
        self.data = bytes.join(b'', [next_byte_as_char(track)
                                     for x in range(len)])

class SequenceNumberMetaEvent(MetaEvent):
    name = 'Sequence Number'
    metacommand = 0x00

class TextMetaEvent(MetaEvent):
    name = 'Text'
    metacommand = 0x01

    def __str__(self):
        return "%s [ %s ]" % \
                            (super(TextMetaEvent, self).__str__(),
                            self.data)

class CopyrightMetaEvent(MetaEvent):
    name = 'Copyright Notice'
    metacommand = 0x02

class TrackNameEvent(MetaEvent):
    name = 'Track Name'
    metacommand = 0x03
    order = 3

    def __str__(self):
        return "%s [ %s ]" % \
                            (super(TrackNameEvent, self).__str__(),
                            self.data)

class InstrumentNameEvent(MetaEvent):
    name = 'Instrument Name'
    metacommand = 0x04
    order = 4

    def __str__(self):
        return "%s [ %s ]" % \
                            (super(InstrumentNameEvent, self).__str__(),
                            self.data)


class LryricsEvent(MetaEvent):
    name = 'Lyrics'
    metacommand = 0x05

    def __str__(self):
        return "%s [ %s ]" % \
                            (super(LryricsEvent, self).__str__(),
                            self.data)


class MarkerEvent(MetaEvent):
    name = 'Marker'
    metacommand = 0x06

class CuePointEvent(MetaEvent):
    name = 'Cue Point'
    metacommand = 0x07

class UnknownEvent(MetaEvent):
    name = 'whoknows?'
    metacommand = 0x09

class ChannelPrefixEvent(MetaEvent):
    name = 'Cue Point'
    metacommand = 0x20

class ChannelPrefixEvent(MetaEvent):
    name = 'Cue Point'
    metacommand = 0x20

class PortEvent(MetaEvent):
    fields = ['port']
    name = 'MIDI Port/Cable'
    metacommand = 0x21
    order = 5

    def __str__(self):
        return "%s [ port: %d ]" % \
                            (super(PortEvent, self).__str__(),
                            self.port)

    def decode_data(self):
        assert(len(self.data) == 1)
        self.port = self.data[0]

class TrackLoopEvent(MetaEvent):
    name = 'Track Loop'
    metacommand = 0x2E

class EndOfTrackEvent(MetaEvent):
    name = 'End of Track'
    metacommand = 0x2F
    order = 2

class SetTempoEvent(MetaEvent):
    fields = ['mpqn', 'tempo']
    name = 'Set Tempo'
    metacommand = 0x51
    order = 1

    def __str__(self):
        return "%s [ mpqn: %d tempo: %d ]" % \
                            (super(SetTempoEvent, self).__str__(),
                            self.mpqn, self.tempo)

    def __setattr__(self, item, value):
        if item == 'mpqn':
            self.__dict__['mpqn'] = value
            self.__dict__['tempo'] = float(6e7) / value 
        elif item == 'tempo':
            self.__dict__['tempo'] = value
            self.__dict__['mpqn'] = int(float(6e7) / value)
        else:
            self.__dict__[item] = value

    def decode_data(self):
        assert(len(self.data) == 3)
        self.mpqn = (self.data[0] << 16) + (self.data[1] << 8) + self.data[2]
        self.tempo = float(6e7) / self.mpqn


class SmpteOffsetEvent(MetaEvent):
    name = 'SMPTE Offset'
    metacommand = 0x54

class TimeSignatureEvent(MetaEvent):
    fields = ['numerator', 'denominator', 'metronome', 'thirtyseconds']
    name = 'Time Signature'
    metacommand = 0x58
    order = 0

    def __str__(self):
        return "%s [ %d/%d  metro: %d  32nds: %d ]" % \
                            (super(TimeSignatureEvent, self).__str__(),
                                self.numerator, self.denominator,
                                self.metronome, self.thirtyseconds)
    def decode_data(self):
        assert(len(self.data) == 4)
        self.numerator = self.data[0]
        # Weird: the denominator is two to the power of the data variable
        self.denominator = 2 ** self.data[1]
        self.metronome = self.data[2]
        self.thirtyseconds = self.data[3]


class KeySignatureEvent(MetaEvent):
    name = 'Key Signature'
    metacommand = 0x59

class BeatMarkerEvent(MetaEvent):
    name = 'Beat Marker'
    metacommand = 0x7F

class SequencerSpecificEvent(MetaEvent):
    name = 'Sequencer Specific'
    metacommand = 0x7F


class TempoMap(list):
    def __init__(self, stream):
        self.stream = stream

    def add_and_update(self, event):
        self.add(event)
        self.update()

    def add(self, event):
        # get tempo in microseconds per beat
        tempo = event.mpqn
        # convert into milliseconds per beat
        tempo = tempo / 1000.0
        # generate ms per tick
        event.mpt = tempo / self.stream.resolution
        self.append(event)

    def update(self):
        self.sort()
        # adjust running time
        last = None
        for event in self:
            if last:
                event.msdelay = last.msdelay + \
                    int(last.mpt * (event.tick - last.tick))
            last = event

    def get_tempo(self, offset=0):
        last = self[0]
        for tm in self[1:]:
            if tm.tick > offset:
                return last
            last = tm
        return last

class EventStreamIterator(object):
    def __init__(self, stream, window):
        self.stream = stream
        self.trackpool = stream.trackpool
        self.window_length = window
        self.window_edge = 0
        self.leftover = None
        self.events = self.stream.iterevents()
        # First, need to look ahead to see when the
        # tempo markers end
        self.ttpts = []
        for tempo in stream.tempomap[1:]:
            self.ttpts.append(tempo.tick)
        # Finally, add the end of track tick.
        self.ttpts.append(stream.endoftrack.tick)
        self.ttpts = iter(self.ttpts)
        # Setup next tempo timepoint
        self.ttp = self.ttpts.next()
        self.tempomap = iter(self.stream.tempomap)
        self.tempo = self.tempomap.next()
        self.endoftrack = False

    def __iter__(self):
        return self

    def __next_edge(self):
        if self.endoftrack:
            raise StopIteration()
        lastedge = self.window_edge
        self.window_edge += int(self.window_length / self.tempo.mpt)
        if self.window_edge > self.ttp:
            # We're past the tempo-marker.
            oldttp = self.ttp
            try:
                self.ttp = self.ttpts.next()
            except StopIteration:
                # End of Track!
                self.window_edge = self.ttp
                self.endoftrack = True
                return
            # Calculate the next window edge, taking into
            # account the tempo change.
            msused = (oldttp - lastedge) * self.tempo.mpt
            msleft = self.window_length - msused
            self.tempo = self.tempomap.next()
            ticksleft = msleft / self.tempo.mpt
            self.window_edge = ticksleft + self.tempo.tick

    def next(self):
        ret = []
        self.__next_edge()
        if self.leftover:
            if self.leftover.tick > self.window_edge:
                return ret
            ret.append(self.leftover)
            self.leftover = None
        for event in self.events:
            if event.tick > self.window_edge:
                self.leftover = event
                return ret
            ret.append(event)
        return ret



"""
EventStream : Class used to describe a collection of MIDI Events.
"""
class EventStream(object):
    def __init__(self):
        self.format = 1
        self.trackcount = 0
        self.tempomap = TempoMap(self)
        self.curtrack = None
        self.trackpool = []
        self.tracklist = {}
        self.timemap = []
        self.endoftrack = None
        self.beatmap = []
        self.resolution = 220
        self.tracknames = {}

    def __set_resolution(self, resolution):
        # XXX: Add code to rescale notes
        assert(not self.trackpool)
        self.__resolution = resolution
        self.beatmap = []
        for value in BEATVALUES:
            self.beatmap.append(int(value * resolution))

    def __get_resolution(self):
        return self.__resolution
    resolution = property(__get_resolution, __set_resolution, None,
                                "Ticks per quarter note")

    def add_track(self):
        if self.curtrack == None:
            self.curtrack = 0
        else:
            self.curtrack += 1
        self.tracklist[self.curtrack] = []
        # Don't: when reading from a file trackcount comes from the header
        #self.trackcount += 1

    def get_current_track_number(self):
        return self.curtrack

    def get_track_by_number(self, tracknum):
        return self.tracklist[tracknum]

    def get_current_track(self):
        return self.tracklist[self.curtrack]

    def get_track_by_name(self, trackname):
        tracknum = self.tracknames[trackname]
        return self.get_track_by_number(tracknum)

    def replace_current_track(self, track):
        self.tracklist[self.curtrack] = track
        self.__refresh()

    def replace_track_by_number(self, tracknum, track):
        self.tracklist[tracknumber] = track
        self.__refresh()

    def replace_track_by_name(self, trackname, track):
        tracknum = self.tracklist[tracknum]
        self.repdeletelace_track_by_number(tracknum, track)

    def delete_current_track(self, track):
        del self.tracklist[self.curtrack]
        self.trackcount -= 1
        self.__refresh()

    def delete_track_by_number(self, tracknum):
        del self.tracklist[tracknum]
        self.trackcount -= 1
        self.__refresh()

    def delete_track_by_name(self, trackname, track):
        tracknum = self.tracklist[trackname]
        self.delete_track_by_number(tracknum, track)

    def add_event(self, event):
        self.__adjust_endoftrack(event)
        if not isinstance(event, EndOfTrackEvent):
            event.track = self.curtrack
            self.trackpool.append(event)
            self.tracklist[self.curtrack].append(event)
        if isinstance(event, TrackNameEvent):
            self.__refresh_tracknames()
        if isinstance(event, SetTempoEvent):
            self.tempomap.add_and_update(event)
            self.__refresh_timemap()
        else:
            if self.tempomap:
                tempo = self.tempomap.get_tempo(event.tick)
                event.adjust_msdelay(tempo)

    def get_tempo(self, offset=0):
        return self.tempomap.get_tempo(offset)

    def timesort(self):
        self.trackpool.sort()
        for track in self.tracklist.values():
            track.sort()
    
    def textdump(self):
        for event in self.trackpool:
            print("%s" % event)

    def __iter__(self):
        return iter(self.tracklist.values())

    def iterevents(self, mswindow=0):
        self.timesort()
        if mswindow:
            return EventStreamIterator(self, mswindow)
        return iter(self.trackpool)

    def __len__(self):
        print("LEN: len(self.tracklist): %d trackcount: %d" % \
              (len(self.tracklist), self.trackcount))
        
        assert(len(self.tracklist) == self.trackcount)
        return self.trackcount

    def __getitem__(self, intkey):
        return self.tracklist[intkey]

    def __refresh(self):
        self.__refresh_trackpool()
        self.__refresh_tempomap()
        self.__refresh_timemap()
        self.__refresh_tracknames()

    def __refresh_tracknames(self):
        self.tracknames = {}
        for tracknum in self.tracklist:
            track = self.tracklist[tracknum]
            for event in track:
                if isinstance(event, TrackNameEvent):
                    self.tracknames[event.data] = tracknum
                    break

    def __refresh_trackpool(self):
        self.trackpool = []
        for track in self.tracklist:
            track = self.tracklist[tracknum]
            for event in track:
                self.trackpool.append(event)
        self.trackpool.sort()

    def __refresh_tempomap(self):
        self.endoftrack = None
        self.tempomap = TempoMap(self)
        for event in self.trackpool:
            if isinstance(event, SetTempoEvent):
                self.tempomap.add(event)
            elif isinstance(event, EndOfTrackEvent):
                self.__adjust_endoftrack(event)
            self.tempomap.update()

    def __refresh_timemap(self):
        for event in self.trackpool:
            if not isinstance(event, SetTempoEvent):
                tempo = self.tempomap.get_tempo(event.tick)
                event.adjust_msdelay(tempo)

    def __adjust_endoftrack(self, event):
        if not self.endoftrack:
            if not event or not isinstance(event, EndOfTrackEvent):
                ev = EndOfTrackEvent()
                ev.tick = event.tick
                ev.track = self.curtrack
                self.endoftrack = ev
            else:
                self.endoftrack = event
            self.trackpool.append(self.endoftrack)
            self.tracklist[self.curtrack].append(self.endoftrack)
        else:
            self.endoftrack.tick = max(event.tick + 1, self.endoftrack.tick)
        if self.tempomap:
            tempo = self.tempomap.get_tempo(self.endoftrack.tick)
            self.endoftrack.adjust_msdelay(tempo)

class EventStreamReader(object):
    def __init__(self, instream, outstream):
        self.eventfactory = None
        self.parse(instream, outstream)

    def read(cls, instream, outstream=None):
        if not outstream:
            outstream = EventStream()
        cls(instream, outstream)
        return outstream
    read = classmethod(read)
    
    def parse(self, instream, outstream):
        self.midistream = outstream
        self.instream = instream
        if type(instream) in (type(b''), type(u'')):
            self.instream = open(instream, 'rb')
        self.parse_file_header()
        for track in range(self.midistream.trackcount):  
            trksz = self.parse_track_header()
            self.eventfactory = EventFactory()
            self.midistream.add_track()
            self.parse_track(trksz)
        
    def parse_file_header(self):
        # First four bytes are MIDI header
        magic = self.instream.read(4)
        if magic != b'MThd':
            raise TypeError("Bad header in MIDI file.")
        # next four bytes are header size
        # next two bytes specify the format version
        # next two bytes specify the number of tracks
        # next two bytes specify the resolution/PPQ/Parts Per Quarter
        # (in other words, how many ticks per quarter note)
        data = unpack(">LHHH", self.instream.read(10))
        hdrsz = data[0]
        self.midistream.format = data[1]
        self.midistream.trackcount = data[2]
        self.midistream.resolution = data[3]
        # XXX: the assumption is that any remaining bytes
        # in the header are padding
        if hdrsz > DEFAULT_MIDI_HEADER_SIZE:
            self.instream.read(hdrsz - DEFAULT_MIDI_HEADER_SIZE)
            
    def parse_track_header(self):
        # First four bytes are Track header
        magic = self.instream.read(4)
        if magic != b'MTrk':
            raise TypeError("Bad track header in MIDI file: " + magic)
        # next four bytes are header size
        trksz = unpack(">L", self.instream.read(4))[0]
        return trksz

    def parse_track(self, trksz):
        track = iter(self.instream.read(trksz))
        while True:
            try:
                event = self.eventfactory.parse_midi_event(track)
                self.midistream.add_event(event)
            except StopIteration:
                break
                
def read_varlen(data):
    NEXTBYTE = 1
    value = 0
    while NEXTBYTE:
        chr = next_byte_as_int(data)
        # is the hi-bit set?
        if not (chr & 0x80):
            # no next BYTE
            NEXTBYTE = 0
        # mask out the 8th bit
        chr = chr & 0x7f
        # shift last value up 7 bits
        value = value << 7
        # add new value
        value += chr
    return value

read_midifile = EventStreamReader.read
recoll-1.36.1/filters/xlsxmltocsv.py0000755000175000017500000000555214410615043014443 00000000000000#!/usr/bin/env python3
# Copyright (C) 2015 J.F.Dockes
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the
#   Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

# Transform XML output from xls-dump.py into csv format.
#
# Note: this would be difficult to make compatible with python 3 <=
# 3.4 because of the use of % interpolation on what should be bytes.
# # % terpolation for bytes is available as of python 3.5, which is
# the minimum version supported.


import sys
import xml.sax

dtt = True

if dtt:
    sepstring = b"\t"
    dquote = b""
else:
    sepstring = b","
    dquote = b'"'

class XlsXmlHandler(xml.sax.handler.ContentHandler):
    def __init__(self):
        self.output = []
        
    def startElement(self, name, attrs):
        if name == "worksheet":
            if "name" in attrs:
                self.output.append(b"%s\n" % attrs["name"].encode("UTF-8"))
        elif name == "row":
            self.cells = dict()
        elif name == "label-cell" or name == "number-cell":
            if "value" in attrs:
                value = attrs["value"].encode("UTF-8")
            else:
                value = b''
            if "col" in attrs:
                self.cells[int(attrs["col"])] = value
            else:
                #??
                self.output.append(b"%s%s" % (value.encode("UTF-8"), sepstring))
        elif name == "formula-cell":
            if "formula-result" in attrs and "col" in attrs:
                self.cells[int(attrs["col"])] = \
                             attrs["formula-result"].encode("UTF-8")
            
    def endElement(self, name, ):
        if name == "row":
            curidx = 0
            line = []
            for idx, value in self.cells.items():
                line.append(sepstring * (idx - curidx))
                line.append(b"%s%s%s" % (dquote, value, dquote))
                curidx = idx
            self.output.append(b''.join(line))
        elif name == "worksheet":
            self.output.append(b'')


if __name__ == '__main__':
    try:
        handler = XlsXmlHandler()
        xml.sax.parse(sys.stdin, handler)
        print(b'\n'.join(handler.output))
    except BaseException as err:
        print("xml-parse: %s\n" % (str(sys.exc_info()[:2]),), file=sys.stderr)
        sys.exit(1)

    sys.exit(0)
recoll-1.36.1/filters/rcltar.py0000755000175000017500000000643014444307651013332 00000000000000#!/usr/bin/env python3

# Tar-file filter for Recoll
# Thanks to Recoll user Martin Ziegler
# This is a modified version of /usr/share/recoll/filters/rclzip.py
# It works not only for tar-files, but automatically for gzipped and
# bzipped tar-files at well.

import os
import rclexecm

try:
    import tarfile
except:
    print("RECFILTERROR HELPERNOTFOUND python3:tarfile")
    sys.exit(1);

class TarExtractor:
    def __init__(self, em):
        self.currentindex = 0
        self.em = em
        self.namen = []

    def extractone(self, ipath):
        docdata = b''
        try:
            info = self.tar.getmember(ipath)
            if info.size > self.em.maxmembersize:
                # skip
                docdata = b''
                self.em.rclog("extractone: entry %s size %d too big" % (ipath, info.size))
                docdata = b'' # raise TarError("Member too big")
            else:
                docdata = self.tar.extractfile(ipath).read()
            ok = True
        except Exception as err:
            ok = False
        iseof = rclexecm.RclExecM.noteof
        if self.currentindex >= len(self.namen) -1:
            iseof = rclexecm.RclExecM.eofnext
        # We use fsencode, not makebytes, to convert to bytes. The latter would fail if the ipath
        # was actually binary, because it tries to encode to utf-8 but python3 had used fsdecode for
        # converting to str, and the result is not encodable to utf-8 (get: "surrogates
        # not allowed). We use fsdecode in getipath to revert the process.
        return (ok, docdata, os.fsencode(ipath), iseof)

    def closefile(self):
        self.tar = None

    def openfile(self, params):
        self.currentindex = -1
        try:
            self.tar = tarfile.open(name=params["filename"], mode='r')
            #self.namen = [ y.name for y in filter(lambda z:z.isfile(),self.tar.getmembers())]
            self.namen = [ y.name for y in [z for z in self.tar.getmembers() if z.isfile()]]

            return True
        except:
            return False

    def getipath(self, params):
        ipath = os.fsdecode(params["ipath"])
        ok, data, ipath, eof = self.extractone(ipath)
        if ok:
            return (ok, data, ipath, eof)
        try:
            ipath = ipath.decode("utf-8")
            return self.extractone(ipath)
        except Exception as err:
            return (ok, data, ipath, eof)

    def getnext(self, params):

        if self.currentindex == -1:
            # Return "self" doc
            self.currentindex = 0
            self.em.setmimetype('text/plain')
            if len(self.namen) == 0:
                self.closefile()
                eof = rclexecm.RclExecM.eofnext
            else:
                eof = rclexecm.RclExecM.noteof
            return (True, "", "", eof)

        if self.currentindex >= len(self.namen):
            self.namen=[]
            self.closefile()
            return (False, "", "", rclexecm.RclExecM.eofnow)
        else:
            ret = self.extractone(self.namen[self.currentindex])
            self.currentindex += 1
            if ret[3] == rclexecm.RclExecM.eofnext or \
               ret[3] == rclexecm.RclExecM.eofnow:
                self.closefile()
            return ret


proto = rclexecm.RclExecM()
extract = TarExtractor(proto)
rclexecm.main(proto, extract)
recoll-1.36.1/filters/openxml-word-body.xsl0000644000175000017500000000223714410615043015573 00000000000000


  

  
    

recoll-1.36.1/filters/kosplitter.py0000755000175000017500000000764014410615043014235 00000000000000#!/usr/bin/env python3 ################################# # Copyright (C) 2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################################## # # Interface to the konlpy Korean text analyser: we receive text from # our parent process and have it segmented by the analyser, then # return the results. The analyser startup is very expensive (several # seconds), which is why we can't just execute it from the main # process. # import sys import cmdtalk # We can either use konlpy, which supports different analysers, or use # the python-mecab-ko, a direct interface to mecab, with the same # interface as konlpy https://pypi.org/project/python-mecab-ko/ try: import mecab usingkonlpy = False except: import konlpy.tag usingkonlpy = True class Processor(object): def __init__(self, proto): self.proto = proto self.tagsOkt = False self.tagsMecab = False self.tagsKomoran = False def _init_tagger(self, taggername): global usingkonlpy if not usingkonlpy and taggername != "Mecab": from konlpy.tag import Okt,Mecab,Komoran usingkonlpy = True if taggername == "Okt": self.tagger = konlpy.tag.Okt() self.tagsOkt = True elif taggername == "Mecab": if usingkonlpy: # Use Mecab(dicpath="c:/some/path/mecab-ko-dic") for a # non-default location. (?? mecab uses rcfile and dicdir not # dicpath) self.tagger = konlpy.tag.Mecab() else: self.tagger = mecab.MeCab() self.tagsMecab = True elif taggername == "Komoran": self.tagger = konlpy.tag.Komoran() self.tagsKomoran = True else: raise Exception("Bad tagger name " + taggername) def process(self, params): if 'data' not in params: return {'error':'No data field in parameters'} if not (self.tagsOkt or self.tagsMecab or self.tagsKomoran): if 'tagger' not in params: return {'error':'No "tagger" field in parameters'} self._init_tagger(params['tagger']); spliteojeol = False if spliteojeol: data = params['data'].split() pos = [] for d in data: pos += self.tagger.pos(d) else: pos = self.tagger.pos(params['data']) #proto.log("POS: %s" % pos) text = "" tags = "" for e in pos: word = e[0] word = word.replace('\t', ' ') text += word + "\t" tag = e[1] if self.tagsOkt: pass elif self.tagsMecab or self.tagsKomoran: tb = tag[0:2] if tb[0] == "N": tag = "Noun" elif tb == "VV": tag = "Verb" elif tb == "VA": tag = "Adjective" elif tag == "MAG": tag = "Adverb" else: pass tags += tag + "\t" return {'text': text, 'tags': tags} proto = cmdtalk.CmdTalk() processor = Processor(proto) cmdtalk.main(proto, processor) recoll-1.36.1/filters/injectcommon.sh0000755000175000017500000000053014410615043014473 00000000000000#!/bin/sh fatal() { echo $* exit 1 } commoncode=recfiltcommon test -f recfiltcommon || fatal must be executed inside the filters directory for filter in rcl* ; do sed -e '/^#RECFILTCOMMONCODE/r recfiltcommon /^#RECFILTCOMMONCODE/,/^#ENDRECFILTCOMMONCODE/d ' < $filter > filtertmp && mv -f filtertmp $filter && chmod a+x $filter done recoll-1.36.1/filters/rcldoc.py0000755000175000017500000001271714410615043013304 00000000000000#!/usr/bin/env python3 import rclexecm import rclexec1 import re import sys import os # Processing the output from antiword: create html header and tail, process # continuation lines escape, HTML special characters, accumulate the data. class WordProcessData: def __init__(self, em): self.em = em self.out = [] self.cont = b'' self.gotdata = False # Line with continued word (ending in -) # we strip the - which is not nice for actually hyphenated word. # What to do ? self.patcont = re.compile(b'''[\w][-]$''') # Pattern for breaking continuation at last word start self.patws = re.compile(b'''([\s])([\w]+)(-)$''') def takeLine(self, line): if not self.gotdata: if line == b'': return self.out.append(b'' + \ b'' + \ b'

') self.gotdata = True if self.cont: line = self.cont + line self.cont = "" if line == b'\f': self.out.append('


') return if self.patcont.search(line): # Break at last whitespace match = self.patws.search(line) if match: self.cont = line[match.start(2):match.end(2)] line = line[0:match.start(1)] else: self.cont = line line = b'' if line: self.out.append(rclexecm.htmlescape(line) + b'
') else: self.out.append(b'
') def wrapData(self): if self.gotdata: self.out.append(b'

') self.em.setmimetype("text/html") return b'\n'.join(self.out) # Null data accumulator. We use this when antiword has failed, and the # data actually comes from rclrtf, rcltext or vwWare, which all # output HTML class WordPassData: def __init__(self, em): self.out = [] self.em = em def takeLine(self, line): self.out.append(line) def wrapData(self): self.em.setmimetype("text/html") return b'\n'.join(self.out) # Filter for msword docs. Try antiword, and if this fails, check for # an rtf or text document (.doc are sometimes like this...). Also try # vwWare if the doc is actually a word doc class WordFilter: def __init__(self, em, td): self.em = em self.ntry = 0 self.execdir = td self.rtfprolog = b'{\\rtf1' self.docprolog = b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1' def reset(self): self.ntry = 0 def hasControlChars(self, data): for c in data: if c < b' '[0] and c != b'\n'[0] and c != b'\t'[0] and \ c != b'\f'[0] and c != b'\r'[0]: return True return False def mimetype(self, fn): try: f = open(fn, "rb") except: return "" data = f.read(100) if data[0:6] == self.rtfprolog: return "text/rtf" elif data[0:8] == self.docprolog: return "application/msword" elif self.hasControlChars(data): return "application/octet-stream" else: return "text/plain" def getCmd(self, fn): '''Return command to execute, and postprocessor, according to our state: first try antiword, then others depending on mime identification. Do 2 tries at most''' if self.ntry == 0: self.ntry = 1 cmd = rclexecm.which("antiword") if cmd: return ([cmd, "-t", "-i", "1", "-m", "UTF-8"], WordProcessData(self.em)) else: return ([],None) elif self.ntry == 1: self.ntry = 2 # antiword failed. Check for an rtf file, or text and # process accordingly. It the doc is actually msword, try # wvWare. mt = self.mimetype(fn) self.em.rclog("rcldoc.py: actual MIME type %s" % mt) if mt == "text/plain": return ([sys.executable, os.path.join(self.execdir, "rcltext.py")], WordPassData(self.em)) elif mt == "text/rtf": cmd = [sys.executable, os.path.join(self.execdir, "rclrtf.py"), "-s"] self.em.rclog("rcldoc.py: returning cmd %s" % cmd) return (cmd, WordPassData(self.em)) elif mt == "application/msword": cmd = rclexecm.which("wvWare") if cmd: return ([cmd, "--nographics", "--charset=utf-8"], WordPassData(self.em)) else: return ([],None) else: return ([],None) else: return ([],None) if __name__ == '__main__': # Remember where we execute filters from, in case we need to exec another execdir = os.path.dirname(sys.argv[0]) # Check that we have antiword. We could fallback to wvWare, but # this is not what the old filter did. if not rclexecm.which("antiword"): print("RECFILTERROR HELPERNOTFOUND antiword") sys.exit(1) proto = rclexecm.RclExecM() filter = WordFilter(proto, execdir) extract = rclexec1.Executor(proto, filter) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclimg.py0000755000175000017500000000572414410615043013313 00000000000000#!/usr/bin/env python3 # Python-based Image Tag extractor for Recoll. This is less thorough # than the Perl-based rclimg script, but useful if you don't want to # have to install Perl (e.g. on Windows). # # Uses pyexiv2. Also tried Pillow, found it useless for tags. # import sys import os import rclexecm import re from rclbasehandler import RclBaseHandler try: import pyexiv2 except: print("RECFILTERROR HELPERNOTFOUND python3:pyexiv2") sys.exit(1); khexre = re.compile('.*\.0[xX][0-9a-fA-F]+$') pyexiv2_titles = { 'Xmp.dc.subject', 'Xmp.lr.hierarchicalSubject', 'Xmp.MicrosoftPhoto.LastKeywordXMP', } # Keys for which we set meta tags meta_pyexiv2_keys = { 'Xmp.dc.subject', 'Xmp.lr.hierarchicalSubject', 'Xmp.MicrosoftPhoto.LastKeywordXMP', 'Xmp.digiKam.TagsList', 'Exif.Photo.DateTimeDigitized', 'Exif.Photo.DateTimeOriginal', 'Exif.Image.DateTime', } exiv2_dates = ['Exif.Photo.DateTimeOriginal', 'Exif.Image.DateTime', 'Exif.Photo.DateTimeDigitized'] class ImgTagExtractor(RclBaseHandler): def __init__(self, em): super(ImgTagExtractor, self).__init__(em) def html_text(self, filename): ok = False metadata = pyexiv2.ImageMetadata(filename) metadata.read() keys = metadata.exif_keys + metadata.iptc_keys + metadata.xmp_keys mdic = {} for k in keys: # we skip numeric keys and undecoded makernote data if k != 'Exif.Photo.MakerNote' and not khexre.match(k): mdic[k] = str(metadata[k].raw_value) docdata = b'\n' ttdata = set() for k in pyexiv2_titles: if k in mdic: ttdata.add(rclexecm.htmlescape(mdic[k])) if ttdata: title = "" for v in ttdata: v = v.replace('[', '').replace(']', '').replace("'", "") title += v + " " docdata += rclexecm.makebytes("" + title + "\n") for k in exiv2_dates: if k in mdic: # Recoll wants: %Y-%m-%d %H:%M:%S. # We get 2014:06:27 14:58:47 dt = mdic[k].replace(":", "-", 2) docdata += b'\n' break for k,v in mdic.items(): if k == 'Xmp.digiKam.TagsList': docdata += b'\n' docdata += b'\n' for k,v in mdic.items(): docdata += rclexecm.makebytes(k + " : " + \ rclexecm.htmlescape(mdic[k]) + "
\n") docdata += b'' return docdata if __name__ == '__main__': proto = rclexecm.RclExecM() extract = ImgTagExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/opendoc-flat.xsl0000755000175000017500000000607714410615043014571 00000000000000 <xsl:value-of select="."/> abstract keywords author keywords


recoll-1.36.1/filters/rclepub1.py0000755000175000017500000000522214410615043013544 00000000000000#!/usr/bin/env python3 """Extract Html content from an EPUB file (.chm), concatenating all sections""" import sys import os import re import rclexecm from rclbasehandler import RclBaseHandler sys.path.append(sys.path[0]+"/recollepub.zip") try: import epub except: print("RECFILTERROR HELPERNOTFOUND python3:epub") sys.exit(1); class EPUBConcatExtractor(RclBaseHandler): """RclExecM slave worker for extracting all text from an EPUB file. This version concatenates all nodes.""" def __init__(self, em): super(EPUBConcatExtractor, self).__init__(em) def _docheader(self): meta = self.book.opf.metadata title = "" for tt, lang in meta.titles: title += tt + " " author = "" for name, role, fileas in meta.creators: author += name + " " data = "\n\n" if title: data += "" + rclexecm.htmlescape(title) + "\n" if author: data += '\n' if meta.description: data += '\n' for value in meta.subjects: data += '\n' data += "" return data.encode('UTF-8') def _catbodies(self): data = b'' ids = [] if self.book.opf.spine: for id, linear in self.book.opf.spine.itemrefs: ids.append(id) else: for id, item in self.book.opf.manifest.items(): ids.append(id) for id in ids: item = self.book.get_item(id) if item is None or item.media_type != 'application/xhtml+xml': continue doc = self.book.read_item(item) doc = re.sub(b'''<\?.*\?>''', b'', doc) doc = re.sub(b''']*>''', b'', doc, 1, flags=re.DOTALL|re.I) doc = re.sub(b'''''', b'', doc, flags=re.I) doc = re.sub(b'''''', b'', doc, flags=re.I) data += doc data += b'' return data def html_text(self, fn): """Extract EPUB data as concatenated HTML""" f = open(fn, 'rb') self.book = epub.open_epub(f) data = self._docheader() data += self._catbodies() self.book.close() return data proto = rclexecm.RclExecM() extract = EPUBConcatExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclaspell-sugg.py0000755000175000017500000000402514444307651014765 00000000000000#!/usr/bin/python3 import sys import getopt try: from aspell import Speller except: try: from recollaspell import Speller except: print("RECFILTERROR HELPERNOTFOUND aspell/recollaspell") sys.exit(1); def deb(s): print(s, file=sys.stderr) def Usage(f=sys.stderr): print("Usage: rclaspell-sugg.py --lang=lang --encoding=encoding " "--master=pathtodict --sug-mode=mode --data-dir=dir --local-data-dir=dir pipe", file=f) sys.exit(1) options=[] opts, args = getopt.getopt(sys.argv[1:], "h", ["help", "lang=", "encoding=", "master=", "sug-mode=", "mode=", "data-dir=", "local-data-dir="]) for opt, arg in opts: if opt in ['-h', '--help']: Usage(sys.stdout) elif opt in ['--lang']: options.append(("lang", arg)) elif opt in ['--encoding']: options.append(("encoding", arg)) elif opt in ['--master']: options.append(("master", arg)) elif opt in ['--sug-mode']: options.append(("sug-mode", arg)) elif opt in ['--mode']: options.append(("mode", arg)) elif opt in ['--data-dir']: options.append(("data-dir", arg)) elif opt in ['--local-data-dir']: options.append(("local-data-dir", arg)) else: print(f"Unknown option {opt}", file=sys.stderr) Usage() if len(args) != 1 or args[0] != "pipe": Usage() #[("lang","en"), ("encoding","utf-8"), ("master","/home/dockes/.recoll-prod/aspdict.en.rws"), # ("sug-mode", "fast")] try: speller = Speller(*options) except Exception as ex: deb(f"Aspell speller creation failed: {ex}") sys.exit(1) print("@(#) International Ispell Version 3.1.20 (but really rclaspell-sugg 0.60.8)") sys.stdout.flush() while True: line = sys.stdin.readline() if not line: break term = line.strip(" \t\n\r") words = speller.suggest(term) if words: print(f"& {term} {len(words)} 0: " + ", ".join(words)) else: print("*") print() sys.stdout.flush() recoll-1.36.1/filters/opendoc-meta.xsl0000644000175000017500000000353614410615043014563 00000000000000 <xsl:value-of select="."/> abstract keywords author keywords recoll-1.36.1/filters/rclwar.py0000755000175000017500000000402214410615043013316 00000000000000#!/usr/bin/env python3 # WAR web archive filter for recoll. War file are gzipped tar files import rclexecm import tarfile class WarExtractor: def __init__(self, em): self.em = em def extractone(self, tarinfo): docdata = "" iseof = rclexecm.RclExecM.noteof try: member = self.tar.extractfile(tarinfo) docdata = member.read() ok = True except Exception as err: self.em.rclog("extractone: failed: [%s]" % err) iseof = rclexecm.RclExecM.eofnow ok = False return (ok, docdata, tarinfo.name, iseof) def closefile(self): self.tar = None ###### File type handler api, used by rclexecm ----------> def openfile(self, params): self.currentindex = -1 try: self.tar = tarfile.open(params["filename"]) return True except Exception as err: self.em.rclog(str(err)) return False def getipath(self, params): ipath = params["ipath"] try: tarinfo = self.tar.getmember(ipath) except Exception as err: self.em.rclog(str(err)) return (False, "", ipath, rclexecm.RclExecM.noteof) return self.extractone(tarinfo) def getnext(self, params): if self.currentindex == -1: # Return "self" doc self.currentindex = 0 return (True, "", "", rclexecm.RclExecM.noteof) tarinfo = self.tar.next() if tarinfo is None: #self.em.rclog("getnext: EOF hit") self.closefile() return (False, "", "", rclexecm.RclExecM.eofnow) else: ret = self.extractone(tarinfo) if ret[3] == rclexecm.RclExecM.eofnext or \ ret[3] == rclexecm.RclExecM.eofnow: self.closefile() return ret # Main program: create protocol handler and extractor and run them proto = rclexecm.RclExecM() extract = WarExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclscribus0000755000175000017500000001215714444307651013572 00000000000000#!/bin/sh # @(#$Id: rclscribus,v 1.4 2007-06-08 13:51:09 dockes Exp $ (C) 2004 J.F.Dockes # There may still be code from Estraier in here: #================================================================ # Estraier: a personal full-text search system # Copyright (C) 2003-2004 Mikio Hirabayashi #================================================================ #================================================================ # Convert a scribus file to recoll HTML. This only handles the newer .sla # files until I can have a look at an older .scd. # # We just hack into the scribus XML, taking advantage that the tag of # interest is apparently always output on a single line. # The text seems to be found in attribute CH of tag ITEXT, it is utf-8 # # Tried to convert this to xsltproc but it seems that quite a few # Scribus document are not actually proper xml # set variables LANG=C ; export LANG LC_ALL=C ; export LC_ALL progname="rclscribus" filetype=Scribus #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE checkcmds grep awk sed # A small sed program to join lines where they are broken inside an # attribute value. The idea is that all scribus tag are apparently on one # line except when there are embedded new lines in an attribute lie # 'comments'. The first version of the sed script joins line which does not # end with > with the next. It doesn't guard against an embedded '>'. The # second joins line not beginning with '<' with the previous. It is much # slower for some reason. sedjoinprog=':a /[^>] *$/N; s/\n/ /; ta' #sedjoinprog1=':a #$!N;/^ *[^<]/s/\n/ /;ta #P;D' # Extract description title author and keywords description=`sed -e "$sedjoinprog" < "$infile" | \ awk ' /" } } '` title=`sed -e "$sedjoinprog" < "$infile" | \ awk ' /" } } '` author=`sed -e "$sedjoinprog" < "$infile" | \ awk ' /" } } '` keywords=`sed -e "$sedjoinprog" < "$infile" | \ awk ' /" } } '` #echo description: [$description];echo title: [$title]; #echo author: [$author];echo keywords: [$keywords] cat < $title

EOF sed -e ':a' -e '/[^>] *$/N; s/\n/ /; ta' < "$infile" | \ awk ' /" # but it might be good to have one in some instances } else if (match($0, "") # New paragraph so a
} else if (match($0, "") # End of something so a
} } END { print "

" } ' | \ sed -e 's//
/g' -e 's//
/g' -e 's/ /
/g' -e 's//
/g' -e 's// /g' recoll-1.36.1/filters/rclexec1.py0000644000175000017500000001074014410615043013533 00000000000000################################# # Copyright (C) 2014 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################################## # Common code for replacing the old shell scripts with Python execm # ones: this implements the basic functions for a filter which # executes a command to translate a simple file (like rclword with # antiword). # # This was motivated by the Windows port: to replace shell and Unix # utility (awk , etc usage). We can't just execute python scripts, # this would be to slow. So this helps implementing a permanent script # to repeatedly execute single commands. import subprocess import rclexecm from rclbasehandler import RclBaseHandler # This class has the code to execute the subprocess and call a # data-specific post-processor. Command and processor are supplied by # the object which we receive as a parameter, which in turn is defined # in the actual executable filter (e.g. rcldoc.py) class Executor(RclBaseHandler): opt_ignxval = 1 def __init__(self, em, flt): super(Executor, self).__init__(em) self.flt = flt def runCmd(self, cmd, filename, postproc, opt): ''' Substitute parameters and execute command, process output with the specific postprocessor and return the complete text. We expect cmd as a list of command name + arguments, except that, for the special value "cat", we just read the file''' if cmd == "cat": try: data = open(filename, 'rb').read() ok = True except Exception as err: self.em.rclog("runCmd: error reading %s: %s"%(filename, err)) return(False, "") for line in data.split(b'\n'): postproc.takeLine(line) return True, postproc.wrapData() else: try: fullcmd = cmd + [rclexecm.subprocfile(filename)] proc = subprocess.Popen(fullcmd, stdout = subprocess.PIPE) stdout = proc.stdout except subprocess.CalledProcessError as err: self.em.rclog("extractone: Popen(%s) error: %s" % (fullcmd, err)) return (False, "") except OSError as err: self.em.rclog("extractone: Popen(%s) OS error: %s" % (fullcmd, err)) return (False, "") try: for line in stdout: postproc.takeLine(line.strip()) except: return (False, "") proc.wait() try: data = postproc.wrapData() except: return (False, "") if (opt & self.opt_ignxval) == 0 and proc.returncode: self.em.rclog("extractone: [%s] returncode %d" % \ (filename, proc.returncode)) return False, data else: return True, data def extractone(self, params): #self.em.rclog("extractone %s %s" % (params["filename"], \ # params["mimetype"])) self.flt.reset() ok = False if not "filename" in params: self.em.rclog("extractone: no file name") return (ok, "", "", rclexecm.RclExecM.eofnow) fn = params["filename"] while True: cmdseq = self.flt.getCmd(fn) cmd = cmdseq[0] postproc = cmdseq[1] opt = cmdseq[2] if len(cmdseq) == 3 else 0 if cmd: ok, data = self.runCmd(cmd, fn, postproc, opt) if ok: break else: break if ok: return (ok, data, "", rclexecm.RclExecM.eofnext) else: return (ok, "", "", rclexecm.RclExecM.eofnow) recoll-1.36.1/filters/rclaudio.py0000755000175000017500000005706414465627655013672 00000000000000#!/usr/bin/env python3 """Audio extractor for Recoll, using mutagen for metadata and optionally using whisper for speech to text.""" import sys import os import gc import time import datetime import re import rclexecm from rclbasehandler import RclBaseHandler import rclconfig try: import mutagen from mutagen import File from mutagen.id3 import ID3 except ImportError: print("RECFILTERROR HELPERNOTFOUND python3:mutagen") sys.exit(1) re_pairnum = re.compile(r"[([]*([0-9]+),\s*([0-9]+)") _htmlprefix = b'''
'''
_htmlsuffix = b'''
''' # The 'Easy' mutagen tags conversions are incomplete. We do it ourselves. # TPA,TPOS,disc DISCNUMBER/TOTALDISCS # TRCK,TRK,trkn TRACKNUMBER/TOTALTRACKS # The conversions here are consistent with the ones in MinimServer (2019-03), # including the rating stuff and TXXX. Lacking: Itunes '----' handling ? # The 'GROUP' tag is a specific minimserver tag used to create # sub-containers inside a folder. We used to use 'CONTENTGROUP' for # this, which was wrong, the latter is a vaguely defined "music # category" thing. tagdict = { 'ALBUM ARTIST': 'ALBUMARTIST', 'ALBUM': 'ALBUM', 'ALBUMARTIST': 'ALBUMARTIST', 'ALBUMARTISTSORT': 'ALBUMARTISTSORT', 'ALBUMSORT': 'ALBUMSORT', 'ARTIST': 'ARTIST', 'ARTISTSORT': 'ARTISTSORT', 'BPM': 'BPM', 'COM': 'COMMENT', 'COMM': 'COMMENT', 'COMMENT': 'COMMENT', 'COMPILATION': 'COMPILATION', 'COMPOSER': 'COMPOSER', 'COMPOSERSORT': 'COMPOSERSORT', 'CONDUCTOR': 'CONDUCTOR', 'CONTENTGROUP': 'CONTENTGROUP', 'COPYRIGHT': 'COPYRIGHT', 'DATE': 'DATE', 'DISCNUMBER': 'DISCNUMBER', 'DISCSUBTITLE': 'DISCSUBTITLE', 'DISCTOTAL': 'TOTALDISCS', 'ENCODEDBY': 'ENCODEDBY', 'ENSEMBLE': 'ORCHESTRA', 'GENRE': 'GENRE', 'GROUP': 'GROUP', 'ISRC': 'ISRC', 'LABEL': 'LABEL', 'LANGUAGE': 'LANGUAGE', 'LYRICIST': 'LYRICIST', 'LYRICS': 'LYRICS', 'MOOD': 'MOOD', 'ORCHESTRA': 'ORCHESTRA', 'PERFORMER': 'PERFORMER', 'POP': 'RATING1', 'POPM': 'RATING1', 'ORIGINALARTIST': 'ORIGINALARTIST', 'ORIGINALDATE': 'ORIGINALDATE', 'RELEASEDATE': 'RELEASEDATE', 'REMIXER': 'REMIXER', 'SUBTITLE': 'SUBTITLE', 'TAL': 'ALBUM', 'TALB': 'ALBUM', 'TBP': 'BPM', 'TBPM': 'BPM', 'TCM': 'COMPOSER', 'TCMP': 'COMPILATION', 'TCO': 'GENRE', 'TCOM': 'COMPOSER', 'TCON': 'GENRE', 'TCOP': 'COPYRIGHT', 'TCP': 'COMPILATION', 'TCR': 'COPYRIGHT', 'TDA': 'DATE', 'TDAT': 'DATE', 'TDOR': 'ORIGINALDATE', 'TDRC': 'DATE', 'TDRL': 'RELEASEDATE', 'TEN': 'ENCODEDBY', 'TENC': 'ENCODEDBY', 'TEXT': 'LYRICIST', 'TIT1': 'CONTENTGROUP', 'TIT2': 'TITLE', 'TIT3': 'SUBTITLE', 'TITLE': 'TITLE', 'TITLESORT': 'TITLESORT', 'TLA': 'LANGUAGE', 'TLAN': 'LANGUAGE', 'TMOO': 'MOOD', 'TOA': 'ORIGINALARTIST', 'TOPE': 'ORIGINALARTIST', 'TOR': 'ORIGINALDATE', 'TORY': 'ORIGINALDATE', 'TOTALDISCS': 'TOTALDISCS', 'TOTALTRACKS': 'TOTALTRACKS', 'TP1': 'ARTIST', 'TP2': 'ALBUMARTIST', 'TP3': 'CONDUCTOR', 'TP4': 'REMIXER', 'TPA': 'DISCNUMBER', 'TPB': 'LABEL', 'TPE1': 'ARTIST', 'TPE2': 'ALBUMARTIST', 'TPE3': 'CONDUCTOR', 'TPE4': 'REMIXER', 'TPOS': 'DISCNUMBER', 'TPUB': 'LABEL', 'TRACK': 'TRACKNUMBER', 'TRACKNUM': 'TRACKNUMBER', 'TRACKNUMBER': 'TRACKNUMBER', 'TRACKTOTAL': 'TOTALTRACKS', 'TRC': 'ISRC', 'TRCK': 'TRACKNUMBER', 'TRDA': 'DATE', 'TRK': 'TRACKNUMBER', 'TS2': 'ALBUMARTISTSORT', 'TSA': 'ALBUMSORT', 'TSC': 'COMPOSERSORT', 'TSO2': 'ALBUMARTISTSORT', 'TSOA': 'ALBUMSORT', 'TSOC': 'COMPOSERSORT', 'TSOP': 'ARTISTSORT', 'TSOT': 'TITLESORT', 'TSP': 'ARTISTSORT', 'TSRC': 'ISRC', 'TSST': 'DISCSUBTITLE', 'TST': 'TITLESORT', 'TT1': 'CONTENTGROUP', 'TT2': 'TITLE', 'TT3': 'SUBTITLE', 'TXT': 'LYRICIST', 'TXXX:ORCHESTRA': 'ORCHESTRA', 'TXX:ORCHESTRA': 'ORCHESTRA', 'TYE': 'DATE', 'TYER': 'DATE',# wikipedia id3: YEAR 'ULT': 'LYRICS', 'USLT': 'LYRICS', 'SLT': 'LYRICS', 'SYLT': 'LYRICS', 'YEAR': 'DATE', 'aART': 'ALBUMARTIST', 'cond': 'CONDUCTOR', 'cpil': 'COMPILATION', 'cprt': 'COPYRIGHT', 'disk': 'DISCNUMBER', 'gnre': 'GENRE', 'labl': 'LABEL', 'soaa': 'ALBUMARTISTSORT', 'soal': 'ALBUMSORT', 'soar': 'ARTISTSORT', 'soco': 'COMPOSERSORT', 'sonm': 'TITLESORT', 'tmpo': 'BPM', 'trkn': 'TRACKNUMBER', '\xa9ART': 'ARTIST', '\xa9alb': 'ALBUM', '\xa9cmt': 'COMMENT', '\xa9con': 'CONDUCTOR', '\xa9day': 'DATE', '\xa9gen': 'GENRE', '\xa9grp': 'CONTENTGROUP', '\xa9lyr': 'LYRICS', '\xa9nam': 'TITLE', '\xa9ope': 'ORIGINALARTIST', '\xa9too': 'ENCODEDBY', '\xa9wrt': 'COMPOSER', } def tobytes(s): if type(s) == type(b''): return s if type(s) != type(''): s = str(s) return s.encode('utf-8', errors='replace') # mp3: album, title, artist, genre, date, tracknumber # flac: album, title, artist, genre, xxx, tracknumber # oggvorbis:album, title, artist, genre, date, tracknumber class AudioTagExtractor(RclBaseHandler): def __init__(self, em): """Set tag fixing up.""" super(AudioTagExtractor, self).__init__(em) self.config = rclconfig.RclConfig() self.recoll_confdir = self.config.confdir self.process_stt = False self.preview_mode = os.environ.get("RECOLL_FILTER_FORPREVIEW", "no") if self.config.getConfParam("speechtotext") == "whisper": self.process_stt = True tagfixerfn = self.config.getConfParam("audiotagfixerscript") self.tagfix = None if tagfixerfn: import runpy try: d = runpy.run_path(tagfixerfn) self.tagfix = d['tagfix'] self.tagfix() except Exception as ex: self.em.rclog("tagfix script import failed: %s" % ex) pass def _showMutaInfo(self, mutf): self.em.rclog("%s" % mutf.info.pprint()) for prop in dir(mutf.info): self.em.rclog(f"mutinfo: {prop} -> {getattr( mutf.info, prop)}") def _fixrating(self, minf): if 'RATING1' in minf: if not 'RATING' in minf: val = int(minf['RATING1']) // 51 + 1 if val > 5: val = 5 if val < 1: val = 1 minf['RATING'] = str(val) del minf['RATING1'] def _embeddedImageFormat(self, mutf): # self.em.rclog("_embeddedImage: MIME: %s"%mutf.mime) try: # This fails if we're passed a mutagen.ID3 instead of File mime = mutf.mime except: mime = [] # First pretend that this is an ID3. These can come inside multiple file formats, so don't # try to select on mime. for tagname in mutf.keys(): if tagname.startswith('APIC:'): # self.em.rclog("mp3 img: %s" % mutf[tagname].mime) return 'jpg' if mutf[tagname].mime == 'image/jpeg' else 'png' if 'audio/flac' in mime: if mutf.pictures: return 'jpg' if mutf.pictures[0].mime == 'image/jpeg' else 'png' elif 'audio/mp4' in mime: if 'covr' in mutf.keys(): format = mutf['covr'][0].imageformat if format == mutagen.mp4.AtomDataType.JPEG: return 'jpg' else: return 'png' return '' def parsedate(self, dt): """Try to make sense of data found in the date fields. Date formats found in actual files (any date field): [1961] [1967-01-01] [1996-11-04T08:00:00Z] [] [0000] [1994-08-08 07:00] We don't try to process the time part. The method translates the date into a Unix timestamp which means possible trouble for pre-1970 recordings (negative time). Oldest possible date with 32 bits time stamp is 1901, which is ok though. Previous recoll versions had an alias from date to dmtime, which was wrong, because dmtime is the unix integer time. We have removed the alias, and set dmtime from the parsed date value. """ try: dt = dt.decode('utf-8', errors='ignore') if len(dt) > 10: dt = dt[0:10] date_parts = dt.split('-') if len(date_parts) > 3 or len(date_parts) == 2 or len(date_parts[0]) != 4 or \ date_parts[0] == '0000': return '' if len(date_parts) == 1: pdt = datetime.datetime.strptime(dt, "%Y") elif len(date_parts) == 3: pdt = datetime.datetime.strptime(dt, "%Y-%m-%d") val = time.mktime(pdt.timetuple()) return b"%d" % val except: return b"0" def _extractaudioparams(self, filename, minf, mutf): """Extract audio parameters from mutagen output. Not all file types supply all or even use the same property names. Translate to consistent str keys and encoded values into our fields dict. """ for prop, dflt in [('sample_rate', 44100), ('channels', 2), ('length', 0), ('bitrate', 0)]: try: minf[prop] = getattr(mutf.info, prop) except Exception as e: # self.em.rclog("NO %s prop: %s" % (prop, e)) minf[prop] = dflt if minf['bitrate'] == 0 and minf['length'] > 0: br = int(os.path.getsize(filename) * 8 / minf['length']) minf['bitrate'] = br minf['duration'] = minf['length'] del minf['length'] # Bits/samp is named sample_size or bits_per_sample (depend on file tp) try: minf['bits_per_sample'] = getattr(mutf.info, 'bits_per_sample') except: try: minf['bits_per_sample'] = getattr(mutf.info, 'sample_size') except: # self.em.rclog("using default bits_per_sample") minf['bits_per_sample'] = 16 for tag, val in minf.items(): minf[tag] = tobytes(val) def _fixtagname(self, tag): """Translate native tag name to our filetype-independant ones. Mostly uses the big tagdict dictionary, with other adjustments. """ # Variations on the COMM tag: # - "COMM::eng" We don't know what to do with the language (or possible other attributes), # so get rid of it for now. # - Also possible: "COMM:ID3v1 Comment:eng" "COMM:iTunNORM:eng" "COMM:Performers:eng" if tag.find("COMM:") == 0: tag = "COMM" # TXXX:TOTALTRACKS TXXX:ORCHESTRA elif tag.find('TXXX:') == 0: tag = tag[5:] if tag.startswith("QuodLibet::"): tag = tag[11:] elif tag.find('TXX:') == 0: tag = tag[4:] if tag.upper() in tagdict: tag = tag.upper() if tag in tagdict: # self.em.rclog("Original tag: <%s>, type0 %s val <%s>" % (tag, type(val), val)) ntag = tagdict[tag].lower() # self.em.rclog("New tag: %s" % ntag) else: if not tag.isalnum(): return None ntag = tag.lower() # self.em.rclog(f"Processing unexpected tag: {tag}, value {val}") return ntag def _processdiscortracknumber(self, minf, ntag, val): """Disc and track numbers are special, and output in varying ways.""" # TPA,TPOS,disc DISCNUMBER/TOTALDISCS # TRCK,TRK,trkn TRACKNUMBER/TOTALTRACKS for what in ("disc", "track"): k = what + "number" if ntag == k: if isinstance(val, list): val = val[0] if not isinstance(val, tuple): val = str(val) mo = re_pairnum.match(val) if mo: val = (mo.group(1), mo.group(2)) else: val = val.split('/') else: # self.em.rclog(f"{k} : tuple: {val} tp1 {type(val[0])} tp2 {type(val[1])}") pass minf[k] = tobytes(val[0]) if len(val) == 2 and val[1] != 0: k1 = "total" + what + "s" # self.em.rclog(f"Setting {k1} : {val[1]}") minf[k1] = tobytes(val[1]) # self.em.rclog(f"{k} finally: {minf[k]}") return True return False def _processtags(self, mutf, minf, filename): """Extract and process metadata tags.""" for tag, val in mutf.items(): # self.em.rclog(f"TAG <{tag}> VALUE <{val}>") # The names vary depending on the file type. Make them consistent ntag = self._fixtagname(tag) if not ntag: continue # self.em.rclog(f"Translated tag: <{ntag}>") if self._processdiscortracknumber(minf, ntag, val): continue try: # id3v2.4 can send multiple values as 0-separated strings. Change this to a # list. mutagen should probably do this for us... Some file types do return lists # of values, e.g. FLAC. if not isinstance(val, (list, tuple)): val = str(val) val = val.split("\x00") # Change list to string for sending up to recoll. try: if isinstance(val, (list, tuple,)): if isinstance(val[0], str): val = " | ".join(val) else: # Usually mp4.MP4Cover: ignore # self.em.rclog(f"Got value for {ntag} which is list of " # f"non-strings: {type(val[0])}") continue except Exception as err: self.em.rclog(f"Trying list join: {err} for {filename}") # self.em.rclog(f"VAL NOW <{val}>") val = tobytes(val) if ntag in minf: # Note that it would be nicer to use proper CSV quoting minf[ntag] += b" | " + val else: minf[ntag] = val # self.em.rclog(f"Tag <{ntag}> -> <{val}>") except Exception as err: self.em.rclog(f"tag: {tag} {minf[ntag]}: {err}. fn {filename}") self._fixrating(minf) if 'orchestra' in minf: val = minf['orchestra'] if val.startswith(b'orchestra='): minf['orchestra'] = val[10:] # self.em.rclog(f"minf after tags {minf}") def transcribe_via_whisper(self, filename: str): """Manage whisper stt access via a lock file. This is necessary to keep from exhausting memory and from running longer then needed. The speech to text engine is fully concurrent and asynchronous, so the indexing behavior of starting multiple indexing processes at once works against us here. We will queue up and wait until the resource is available. """ # A valid whisper openai model file, such as "small.en" or "small" sttdataset = self.config.getConfParam("sttmodel") device_name = self.config.getConfParam("sttdevice") lock_file_name = os.path.join(self.recoll_confdir, "stt.lock") raw_result = {"segments": {}} # os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:1024" # os.environ["CUDA_LAUNCH_BLOCKING"] = "1" try: from filelock import FileLock except ImportError: print("RECFILTERROR HELPERNOTFOUND python3:filelock") sys.exit(1) try: with FileLock(lock_file_name): # self.em.rclog(f"Acquired stt file lock: {lock_file_name} to process {filename}.") try: import whisper import torch except ImportError: print("RECFILTERROR HELPERNOTFOUND python3:openai-whisper") sys.exit(1) if sttdataset not in whisper.available_models(): self.em.rclog(f"Invalid stt model specified, skipping speech transcription " "for {filename}.") else: if device_name: stt_model = whisper.load_model(name=sttdataset, device=device_name) else: stt_model = whisper.load_model(name=sttdataset) try: torch.cuda.empty_cache() raw_result = stt_model.transcribe(filename) del stt_model except Exception as ex: self.em.rclog(f"Whisper speech to text transcription error: {ex}, " "skipping transcription of {filename}.") finally: torch.cuda.empty_cache() gc.collect() # self.em.rclog(torch.cuda.memory_summary()) # self.em.rclog(f"Released stt file lock for: {filename}.") except Exception as ex: self.em.rclog(f"Whisper speech to text lock error: {ex}, skipping transcription " "of {filename}.") return raw_result def speech_to_text(self, filename: str): """Output html content containing the speech to text content found in the audio track. This reuses recolls page numbering mechanism, wherein a slash f designates a new page, i.e. a form feed. In the case of an audio track, we are using a page to represent a second in time. We fill the output with empty page form feeds unless we have text that starts during that second/page, then we output the content and append the form feed to it. When the audio/video player is called from opened from recoll using a snippet of indexed speech to text, it should open to the correct second where the snippet was played. Not all audio players and formats do this accurately. """ output_array = [] result_dict = {} import rclocrcache import json # The cache can find data either based on file metadata, or, in case, e.g. the file has been # renamed, based on a data hash. We limit the hash size to 3mb (which will be taken as 3 1mb # slices at the beginning, middle and end of the file cache = rclocrcache.OCRCache(self.config, hashmb=3) incache, json_result = cache.get(filename) if incache: raw_result = json.loads(json_result.decode("UTF-8")) else: raw_result = self.transcribe_via_whisper(filename) cache.store(filename, json.dumps(raw_result).encode("UTF-8")) for segment in raw_result["segments"]: if "start" in segment: segment_start = int(segment["start"]) if segment_start in result_dict: result_dict[segment_start] += " " + segment["text"] else: result_dict[segment_start] = segment["text"] max_seconds = 0 if result_dict: max_seconds = max(result_dict.keys()) + 1 for i in range(max_seconds): if i in result_dict: output_array.append(result_dict.get(i)) else: output_array.append("") if self.preview_mode == "yes": return '\n'.join([e for e in output_array if e]) else: return '\f\n'.join(output_array) def html_text(self, filename): # self.em.rclog(f"processing {filename}") if not self.inputmimetype: raise Exception("html_text: input MIME type not set") self.config.setKeyDir(os.path.dirname(filename)) if self.config.getConfParam("speechtotext") == "whisper": self.process_stt = True else: self.process_stt = False # The field storage dictionary minf = {} # We open the file here in order to specify the buffering parameter. For some reason it # makes a huge difference in performance (e.g. 6x) at least in some cases with an # NFS-mounted volume. mutagen itself does not specify a buffering parameter (as of # 1.45.1). No idea if this is a Python, or Linux kernel (or ?) issue. with open(filename, "rb", buffering=4096) as fileobj: strex = "" mutf = None try: mutf = File(fileobj) except Exception as ex: strex = str(ex) try: # Note: this would work only in the off chance that the file format is not # recognized, but the file does begin with an ID3 tag. fileobj.seek(0) mutf = ID3(fileobj) except Exception as ex: strex += " " + str(ex) if mutf is None: # Note: mutagen will fail the open (and raise) for a valid file with no tags. Maybe # we should just return an empty text in this case? We seem to get an empty str(ex) # in this case, and a non empty one for, e.g. permission denied, but I am not sure # that the emptiness will be consistent for all file types. The point of detecting # this would be to avoid error messages and useless retries. if not strex: return b'' else: raise Exception(f"Open failed: {strex}") # self._showMutaInfo(mutf) self._extractaudioparams(filename, minf, mutf) self._processtags(mutf, minf, filename) # Check for embedded image. We just set a flag. embdimg = self._embeddedImageFormat(mutf) if embdimg: #self.em.rclog("Embedded image format: %s" % embdimg) minf['embdimg'] = tobytes(embdimg) self.em.setfield("charset", b'utf-8') if self.tagfix: self.tagfix(minf) if 'date' in minf: uxtime = self.parsedate(minf['date']) if uxtime: minf['dmtime'] = uxtime for tag, val in minf.items(): # self.em.rclog("%s -> %s" % (tag, val)) self.em.setfield(tag, val) # Compat with old version if tag == 'artist': self.em.setfield('author', val) html_output = _htmlprefix ################# # Document text: use the mutagen pprint function. The values may be somewhat # different from what is in the metadata above because of the corrections we apply or # different format choices. try: docdata = tobytes(mutf.pprint()) except Exception as err: docdata = "" self.em.rclog(f"Doc pprint error: {err}") stt_results = b"" if self.process_stt and "LYRICS" not in mutf: stt_results = self.speech_to_text(filename.decode('utf-8')).encode('utf-8') html_output += docdata html_output += "\n".encode('utf-8') + stt_results html_output += _htmlsuffix # self.em.rclog(f"Results: {html_output}") return html_output if __name__ == '__main__': proto = rclexecm.RclExecM() extract = AudioTagExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclpython.py0000755000175000017500000001533314410615043014055 00000000000000#!/usr/bin/env python3 # Rclpython is based on "colorize.py" from: # http://chrisarndt.de/en/software/python/colorize.html # # Based on the code from Jürgen Herman, the following changes where made: # # Mike Brown : # - make script callable as a CGI and a Apache handler for .py files. # # Christopher Arndt : # - make script usable as a module # - use class tags and style sheet instead of """ class Parser: """ Send colored python source. """ stylesheet = _STYLESHEET def __init__(self, raw, out=sys.stdout): """ Store the source text. """ self.raw = raw.expandtabs().strip() self.out = out def format(self): """ Parse and send the colored source. """ # store line offsets in self.lines self.lines = [0, 0] pos = 0 while 1: pos = self.raw.find(b'\n', pos) + 1 if not pos: break self.lines.append(pos) self.lines.append(len(self.raw)) # parse the source and write it self.pos = 0 text = io.BytesIO(self.raw) self.out.write(rclexecm.makebytes(self.stylesheet)) self.out.write(b'
\n')
        try:
            for a,b,c,d,e in tokenize.tokenize(text.readline):
                self(a,b,c,d,e)
        except Exception as ex:
            # There are other possible exceptions, for example for a
            # bad encoding line (->SyntaxError). e.g. # -*- coding: lala -*-
            self.out.write(("

ERROR: %s

\n" % ex).encode('utf-8')) self.out.write(b'\n
') def __call__(self, toktype, toktext, startpos, endpos, line): """ Token handler. """ srow, scol = startpos erow, ecol = endpos if 0: print("type %s %s text %s start %s %s end %s %s
\n" % \ (toktype, token.tok_name[toktype], toktext, \ srow, scol,erow,ecol), file=sys.stderr) # calculate new positions oldpos = self.pos newpos = self.lines[srow] + scol self.pos = newpos + len(toktext) if toktype == token_encoding_type: return # handle newlines if toktype in [token.NEWLINE, tokenize.NL]: self.out.write(b'\n') return # send the original whitespace, if needed if newpos > oldpos: self.out.write(self.raw[oldpos:newpos]) # skip indenting tokens if toktype in [token.INDENT, token.DEDENT]: self.pos = newpos return # map token type to a color group if token.LPAR <= toktype and toktype <= token.OP: toktype = token.OP elif toktype == token.NAME and keyword.iskeyword(toktext): toktype = _KEYWORD css_class = _css_classes.get(toktype, 'text') # send text self.out.write(rclexecm.makebytes('' % (css_class,))) self.out.write(rclexecm.makebytes(html.escape(toktext))) self.out.write(b'') def colorize_file(file=None, outstream=sys.stdout, standalone=True): """Convert a python source file into colorized HTML. Reads file and writes to outstream (default sys.stdout). file can be a filename or a file-like object (only the read method is used). If file is None, act as a filter and read from sys.stdin. If standalone is True (default), send a complete HTML document with header and footer. Otherwise only a stylesheet and a
 section are written.
    """

    from os.path import basename
    if hasattr(file, 'read'):
        sourcefile = file
        file = None
        try:
            filename = basename(file.name)
        except:
            filename = 'STREAM'
    elif file is not None:
        try:
            sourcefile = open(file, 'rb')
            filename = basename(file)
        except IOError:
            raise SystemExit("File %s unknown." % file)
    else:
        sourcefile = sys.stdin
        filename = 'STDIN'
    source = sourcefile.read()

    if standalone:
        outstream.write(rclexecm.makebytes(_HTML_HEADER % {'title': filename}))
    Parser(source, out=outstream).format()
    if standalone:
        outstream.write(rclexecm.makebytes(_HTML_FOOTER))

    if file:
        sourcefile.close()


class PythonDump(RclBaseHandler):
    def __init__(self, em):
        super(PythonDump, self).__init__(em)

    def html_text(self, fn):
        preview = os.environ.get("RECOLL_FILTER_FORPREVIEW", "no")
        if preview == "yes":
            out = io.BytesIO()
            colorize_file(fn, out)
            return out.getvalue()
        else:
            self.outputmimetype = "text/plain"
            return open(fn, 'rb').read()

if __name__ == '__main__':
    proto = rclexecm.RclExecM()
    extract = PythonDump(proto)
    rclexecm.main(proto, extract)
recoll-1.36.1/filters/rclxml.py0000755000175000017500000000336514410615043013336 00000000000000#!/usr/bin/env python3
# Copyright (C) 2014 J.F.Dockes
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the
#   Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
######################################

import sys
import rclexecm
import rclgenxslt

stylesheet_all = '''


  

  
    
      
	
	  
	    <xsl:value-of select="//*[local-name() = 'title'][1]"/>
	  
	
      
      
	
      
    
  

  
    
      

''' if __name__ == '__main__': proto = rclexecm.RclExecM() extract = rclgenxslt.XSLTExtractor(proto, stylesheet_all) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclpst.py0000755000175000017500000003750514410615043013347 00000000000000#!/usr/bin/env python3 ################################# # Copyright (C) 2019 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################################## # # Process the stream produced by a modified pffexport: # https://github.com/libyal/libpff # The modification allows producing a data stream instead of a file tree # import sys import os import pathlib import email.parser import email.policy import email.message import mailbox import subprocess import rclexecm import rclconfig import conftree import base64 import traceback _mswindows = (sys.platform == "win32" or sys.platform == "msys") if _mswindows: import ntpath met_basename = ntpath.basename met_dirname = ntpath.dirname met_splitext = ntpath.splitext met_join = ntpath.join def _backslashize(s): if type(s) == type(""): return s.replace("/", "\\") else: return s.replace(b"/", b"\\") else: met_basename = os.path.basename met_dirname = os.path.dirname met_splitext = os.path.splitext met_join = os.path.join def _backslashize(s): return s # The pffexport stream yields the email in several pieces, with some # data missing (e.g. attachment MIME types). We rebuild a complete # message for parsing by the Recoll email handler class EmailBuilder(object): def __init__(self, logger, mimemap): self.log = logger self.reset() self.mimemap = mimemap self.parser = email.parser.Parser(policy = email.policy.default) def reset(self): self.headers = '' self.body = '' self.bodymimemain = '' self.bodymimesub = '' self.attachments = [] def setheaders(self, h): #self.log("EmailBuilder: headers") self.headers = h def setbody(self, body, main, sub): #self.log("EmailBuilder: body") self.body = body self.bodymimemain = main self.bodymimesub = sub def addattachment(self, att, filename): #self.log("EmailBuilder: attachment") self.attachments.append((att, filename)) def flush(self): if not (self.headers and (self.body or self.attachments)): #self.log("Not flushing because no headers or no body/attach") self.reset() return None newmsg = email.message.EmailMessage(policy=email.policy.default) headerstr = self.headers.decode("UTF-8", errors='replace') # print("%s" % headerstr) try: headers = self.parser.parsestr(headerstr, headersonly=True) except: # This sometimes fails, for example with 'day is out of range for month'. Try to go on # without headers headers = email.message.EmailMessage() #self.log("EmailBuilder: content-type %s" % headers['content-type']) for nm in ('from', 'subject', 'date'): if nm in headers: try: newmsg.add_header(nm, headers[nm]) except: pass for h in ('to', 'cc'): try: tolist = headers.get_all(h) except: tolist = [] if not tolist: continue alldests = "" for toheader in tolist: for dest in toheader.addresses: sd = str(dest).replace('\n', '').replace('\r','') #self.log("EmailBuilder: dest %s" % sd) alldests += sd + ", " if alldests: alldests = alldests.rstrip(", ") try: newmsg.add_header(h, alldests) except: pass # Decoding the body: the .pst contains the text value decoded from qp # or base64 (at least that's what libpff sends). Unfortunately, it # appears that the charset value for subparts (e.g. the html part of a # multipart/related) is not saved (or not transmitted). # # This information is both necessary and unavailable, so we apply an heuristic # which works in 'most' cases: if we have a charset in the message # header, hopefully, this is a simple body and the charset # applies. Else try to decode from utf-8, and use charset=utf-8 if it # succeeds. Else, send binary and hope for the best (the HTML handler # still has a chance to get the charset from the HTML header). # # There are cases of an HTML UTF-8 text having charset=iso in the # head. Don't know if the original HTML was borked or if outlook or # libpff decoded to utf-8 without changing the head charset. if self.body: if self.bodymimemain == 'text': charset = headers.get_content_charset() body = '' if charset: if charset == 'unicode': charset = 'utf-16' try: body = self.body.decode(charset, errors='replace') #self.log("DECODE FROM HEADER CHARSET %s SUCCEEDED"% charset) except: pass else: try: body = self.body.decode('utf-8') #self.log("DECODE FROM GUESSED UTF-8 SUCCEEDED") except: pass if body: #self.log("Unicode body: %s" % body) newmsg.set_content(body, subtype = self.bodymimesub) else: newmsg.set_content(self.body, maintype = self.bodymimemain, subtype = self.bodymimesub) else: newmsg.set_content(self.body, maintype = self.bodymimemain, subtype = self.bodymimesub) for att in self.attachments: fn = att[1] ext = met_splitext(fn)[1] mime = self.mimemap.get(ext) if not mime: mime = 'application/octet-stream' #self.log("Attachment: filename %s MIME %s" % (fn, mime)) mt,st = mime.split('/') newmsg.add_attachment(att[0], maintype=mt, subtype=st, filename=fn) ret = newmsg.as_string(maxheaderlen=100) #newmsg.set_unixfrom("From some@place.org Sun Jan 01 00:00:00 2000") #print("%s\n" % newmsg.as_string(unixfrom=True, maxheaderlen=80)) #self.log("MESSAGE: %s" % ret) self.reset() return ret class PFFReader(object): def __init__(self, logger, infile=sys.stdin): self.log = logger config = rclconfig.RclConfig() dir1 = os.path.join(config.getConfDir(), "examples") dir2 = os.path.join(config.datadir, "examples") self.mimemap = conftree.ConfStack('mimemap', [dir1, dir2]) self.infile = infile self.fields = {} self.msg = EmailBuilder(self.log, self.mimemap) # Read single parameter from process input: line with param name and size # followed by data. The param name is returned as str/unicode, the data # as bytes def readparam(self): inf = self.infile s = inf.readline() if s == b'': return ('', b'') s = s.rstrip(b'\n') if s == b'': return ('', b'') l = s.split() if len(l) != 2: self.log(b'bad line: [' + s + b']', 1, 1) return ('', b'') paramname = l[0].decode('ASCII').rstrip(':') paramsize = int(l[1]) if paramsize > 0: paramdata = inf.read(paramsize) if len(paramdata) != paramsize: self.log("Bad read: wanted %d, got %d" % (paramsize, len(paramdata)), 1, 1) return('', b'') else: paramdata = b'' return (paramname, paramdata) def mainloop(self): basename = '' fullpath = '' ipath = '' while 1: name, data = self.readparam() if name == "": break try: paramstr = data.decode("UTF-8") except: paramstr = '' if name == 'filename': #self.log("filename: %s" % paramstr) fullpath = paramstr basename = met_basename(fullpath) parentdir = met_basename(met_dirname(fullpath)) #self.log("basename [%s] parentdir [%s]" % (basename, parentdir)) elif name == 'data': if parentdir == 'Attachments': #self.log("Attachment: %s" % basename) self.msg.addattachment(data, basename) else: if basename == 'OutlookHeaders.txt': doc = self.msg.flush() if doc: yield((doc, ipath)) elif basename == 'InternetHeaders.txt': #self.log("name: [%s] data: %s" % (name, paramstr[:20])) # This part is the indispensable one. Record # the ipath at this point: if _mswindows: p = pathlib.PureWindowsPath(fullpath) else: p = pathlib.Path(fullpath) # Strip the top dir (/nonexistent.export/) p = p.relative_to(*p.parts[:2]) # We use the parent directory as ipath: all # the message parts are in there ipath = str(p.parents[0]) self.msg.setheaders(data) elif met_splitext(basename)[0] == 'Message': ext = met_splitext(basename)[1] if ext == '.txt': self.msg.setbody(data, 'text', 'plain') elif ext == '.html': self.msg.setbody(data, 'text', 'html') elif ext == '.rtf': self.msg.setbody(data, 'text', 'rtf') else: # Note: I don't know what happens with a # message body of type, e.g. image/jpg. # This is probably not a big issue, # because there is nothing to index # We raised during dev to see if we would find one, # now just pass # raise Exception("PST: Unknown body type %s"%ext) pass elif basename == 'ConversationIndex.txt': pass elif basename == 'Recipients.txt': pass else: raise Exception("Unknown param name: %s" % name) #self.log("Out of loop") doc = self.msg.flush() if doc: yield((doc, ipath)) return class PstExtractor(object): def __init__(self, em): self.generator = None self.em = em if _mswindows: self.target = "\\\\?\\c:\\nonexistent" else: self.target = "/nonexistent" self.pffexport = rclexecm.which("pffinstall/mingw32/bin/pffexport") if not self.pffexport: self.pffexport = rclexecm.which("pffexport") if not self.pffexport: # No need for anything else. openfile() will return an # error at once return self.cmd = [self.pffexport, "-q", "-t", self.target, "-s"] def startCmd(self, filename, ipath=None): fullcmd = list(self.cmd) if ipath: # There is no way to pass an utf-8 string on the command # line on Windows. Use base64 encoding bip = base64.b64encode(ipath.encode("UTF-8")) fullcmd += ["-p", bip.decode("UTF-8")] fn = _backslashize(rclexecm.subprocfile(filename)) fullcmd += [fn,] #self.em.rclog("PstExtractor: command: [%s]" % fullcmd) try: self.proc = subprocess.Popen(fullcmd, stdout=subprocess.PIPE) except subprocess.CalledProcessError as err: self.em.rclog("Pst: Popen(%s) error: %s" % (fullcmd, err)) return False except OSError as err: self.em.rclog("Pst: Popen(%s) OS error: %s" % (fullcmd, err)) return (False, "") except Exception as err: self.em.rclog("Pst: Popen(%s) Exception: %s" % (fullcmd, err)) return (False, "") self.filein = self.proc.stdout return True ###### File type handler api, used by rclexecm ----------> def openfile(self, params): if not self.pffexport: print("RECFILTERROR HELPERNOTFOUND pffexport") sys.exit(1); self.filename = params["filename"] self.generator = None return True def getipath(self, params): ipath = met_join(self.target + ".export", params["ipath"].decode("UTF-8")) self.em.rclog("getipath: [%s]" % ipath) if not self.startCmd(self.filename, ipath=ipath): return (False, "", "", rclexecm.RclExecM.eofnow) reader = PFFReader(self.em.rclog, infile=self.filein) self.generator = reader.mainloop() try: doc, ipath = next(self.generator) self.em.setmimetype("message/rfc822") self.em.rclog("getipath doc len %d [%s] ipath %s" % (len(doc), doc[:20], ipath)) except StopIteration: self.em.rclog("getipath: StopIteration") return(False, "", "", rclexecm.RclExecM.eofnow) return (True, doc, ipath, False) def getnext(self, params): #self.em.rclog("getnext:") if not self.generator: #self.em.rclog("starting generator") if not self.startCmd(self.filename): return False reader = PFFReader(self.em.rclog, infile=self.filein) self.generator = reader.mainloop() ipath = "" try: doc, ipath = next(self.generator) self.em.setmimetype("message/rfc822") #self.em.rclog("getnext: ipath %s\ndoc\n%s" % (ipath, doc)) except StopIteration: #self.em.rclog("getnext: end of iteration") self.proc.wait(3) if self.proc.returncode == 0: return(True, "", "", rclexecm.RclExecM.eofnext) else: self.em.rclog("getnext: subprocess returned code %d" % self.proc.returncode) return(False, "", "", rclexecm.RclExecM.eofnow) except Exception as ex: self.em.rclog("getnext: exception: %s" % ex) traceback.print_exc() return(False, "", "", rclexecm.RclExecM.eofnow) return (True, doc, ipath, rclexecm.RclExecM.noteof) if True: # Main program: create protocol handler and extractor and run them proto = rclexecm.RclExecM() extract = PstExtractor(proto) rclexecm.main(proto, extract) else: reader = PFFReader(_deb, infile=sys.stdin.buffer) generator = reader.mainloop() for doc, ipath in generator: _deb("Got %s data len %d" % (ipath, len(doc))) recoll-1.36.1/filters/rcl7z.py0000755000175000017500000001053114507456214013101 00000000000000#!/usr/bin/env python3 # 7-Zip file filter for Recoll # Thanks to Recoll user Martin Ziegler # This is a modified version of rclzip.py, with some help from rcltar.py # # Normally using py7zr https://github.com/miurahr/py7zr # # Else, but it does not work on all archives, may use: # Python pylzma library required. See http://www.joachim-bauch.de/projects/pylzma/ import sys import os import fnmatch import rclexecm usingpy7zr = False try: from py7zr import SevenZipFile as Archive7z usingpy7zr = True except: try: from py7zlib import Archive7z except: print("RECFILTERROR HELPERNOTFOUND python3:py7zr or python3:pylzma") sys.exit(1); import rclconfig class SevenZipExtractor: def __init__(self, em): self.currentindex = 0 self.fp = None self.em = em def extractone(self, ipath): #self.em.rclog("extractone: [%s]" % ipath) docdata = b'' ok = False try: if usingpy7zr: docdata = self.sevenzdic[ipath].read() else: docdata = self.sevenzip.getmember(ipath).read() ok = True except Exception as err: self.em.rclog("extractone: failed: [%s]" % err) iseof = rclexecm.RclExecM.noteof if self.currentindex >= len(self.names) -1: iseof = rclexecm.RclExecM.eofnext return (ok, docdata, rclexecm.makebytes(ipath), iseof) def closefile(self): if self.fp: self.fp.close() ###### File type handler api, used by rclexecm ----------> def openfile(self, params): filename = params["filename"] self.currentindex = -1 self.skiplist = [] config = rclconfig.RclConfig() config.setKeyDir(os.path.dirname(filename)) skipped = config.getConfParam("zipSkippedNames") if skipped is not None: self.skiplist = skipped.split(" ") try: self.fp = open(filename, 'rb') self.sevenzip = Archive7z(self.fp) if usingpy7zr: self.sevenzdic = self.sevenzip.readall() self.names = [k[0] for k in self.sevenzdic.items()] else: self.names = self.sevenzip.getnames() return True except Exception as err: self.em.rclog("openfile: failed: [%s]" % err) return False def getipath(self, params): ipath = params["ipath"] ok, data, ipath, eof = self.extractone(ipath) if ok: return (ok, data, ipath, eof) # Not found. Maybe we need to decode the path? try: ipath = ipath.decode("utf-8") return self.extractone(ipath) except Exception as err: return (ok, data, ipath, eof) def getnext(self, params): if self.currentindex == -1: # Return "self" doc self.currentindex = 0 self.em.setmimetype('text/plain') if len(self.names) == 0: self.closefile() eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, "", "", eof) if self.currentindex >= len(self.names): #self.em.rclog("getnext: EOF hit") self.closefile() return (False, "", "", rclexecm.RclExecM.eofnow) entryname = self.names[self.currentindex] if len(self.skiplist) != 0: while self.currentindex < len(self.names): entryname = self.names[self.currentindex] for pat in self.skiplist: if fnmatch.fnmatch(entryname, pat): entryname = None break if entryname is not None: break self.currentindex += 1 if entryname is None: self.closefile() return (False, "", "", rclexecm.RclExecM.eofnow) ret = self.extractone(entryname) if ret[3] == rclexecm.RclExecM.eofnext or \ ret[3] == rclexecm.RclExecM.eofnow: self.closefile() self.currentindex += 1 return ret # Main program: create protocol handler and extractor and run them proto = rclexecm.RclExecM() extract = SevenZipExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclocrabbyy.py0000755000175000017500000001024614410615043014344 00000000000000#!/usr/bin/env python3 ################################# # Copyright (C) 2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################################## # Running abbyyocr for Recoll OCR (see rclocr.py) import os import sys import atexit import tempfile import subprocess import glob import rclexecm _mswindows = (sys.platform == "win32") if _mswindows: ocrlangfile = "rclocrlang.txt" else: ocrlangfile = ".rclocrlang" _okexts = ('.pdf', '.tif', '.tiff', '.jpg', '.png', '.jpeg') abbyyocrcmd = "" abbyocrdir = "" def _deb(s): rclexecm.logmsg(s) def cleanocr(): pass # Return true if abbyy appears to be available def ocrpossible(config, path): global abbyyocrcmd if not abbyyocrcmd: config.setKeyDir(os.path.dirname(path)) abbyyocrcmd = config.getConfParam("abbyyocrcmd") if not abbyyocrcmd: abbyyocrcmd = rclexecm.which("abbyyocr11") if not abbyyocrcmd: return False global abbyyocrdir abbyyocrdir = os.path.dirname(abbyyocrcmd) # Check input format base,ext = os.path.splitext(path) ext = ext.lower() if ext in _okexts: return True return False # Try to guess tesseract language. This should depend on the input # file, but we have no general way to determine it. So use the # environment and hope for the best. def _guessocrlang(config, path): ocrlang = "" dirname = os.path.dirname(path) # First look for a language def file in the file's directory langfile = os.path.join(dirname, ocrlangfile) if os.path.isfile(langfile): ocrlang = open(langfile, "r").read().strip() if ocrlang: _deb("OCR lang from file: %s" % ocrlang) return ocrlang # Then look for a config file option. config.setKeyDir(dirname) ocrlang = config.getConfParam("abbyylang") if ocrlang: _deb("OCR lang from config: %s" % ocrlang) return ocrlang # Half-assed trial to guess from LANG then default to english try: localelang = os.environ.get("LANG", "").split("_")[0] if localelang == "en": ocrlang = "English" elif localelang == "de": ocrlang = "German" elif localelang == "fr": ocrlang = "French" except: pass if not ocrlang: ocrlang = "English" _deb("OCR lang (guessed): %s" % ocrlang) return ocrlang # run ocr on the input path and output the result data. def runocr(config, path): ocrlang = _guessocrlang(config, path) my_env = os.environ.copy() eldpn = "LD_LIBRARY_PATH" if eldpn in my_env: oldpath = ":" + my_env[eldpn] else: oldpath = "" my_env[eldpn] = abbyyocrdir + oldpath try: out = subprocess.check_output( [abbyyocrcmd, "-lpp", "BookArchiving_Accuracy", "-rl", ocrlang, "-tet", "UTF8", "-f", "TextUnicodeDefaults", "-if", path, "-c"], env = my_env, stderr=subprocess.DEVNULL) except Exception as e: _deb("%s failed: %s" % (abbyyocrcmd,e)) return False, "" return True, out if __name__ == '__main__': import rclconfig config = rclconfig.RclConfig() path = sys.argv[1] if ocrpossible(config, path): ok, data = runocr(config, sys.argv[1]) else: _deb("ocrpossible returned false") sys.exit(1) if ok: sys.stdout.buffer.write(data) else: _deb("OCR program failed") recoll-1.36.1/filters/rclinfo.py0000755000175000017500000001571314410615043013471 00000000000000#!/usr/bin/env python3 # Read a file in GNU info format and output its nodes as subdocs, # interfacing with recoll execm import rclexecm import sys import os import subprocess # Prototype for the html document we're returning. Info files are # normally ascii. Set no charset, and let it be provided by the # environment if necessary # # Some info source docs contain charset info like: # @documentencoding ISO-2022-JP # But this seems to be absent from outputs. # RclExecm interface class InfoExtractor: def __init__(self, em): self.file = "" self.contents = [] self.em = em def extractone(self, index): if index >= len(self.contents): return(False, "", "", True) nodename, docdata = self.contents[index] nodename = rclexecm.htmlescape(nodename) docdata = rclexecm.htmlescape(docdata) # strange whitespace to avoid changing the module tests (same as old) docdata = b'\n\n \n ' + \ nodename + \ b'\n' + \ b' \n' + \ b' \n \n' + \ b'
\n   ' + \
                  docdata + \
                  b'\n   
\n\n' iseof = rclexecm.RclExecM.noteof if self.currentindex >= len(self.contents) -1: iseof = rclexecm.RclExecM.eofnext self.em.setmimetype("text/html") return (True, docdata, str(index), iseof) ###### File type handler api, used by rclexecm ----------> def openfile(self, params): self.file = params["filename"] if not os.path.isfile(self.file): self.em.rclog("Openfile: %s is not a file" % self.file) return False cmd = b'info --subnodes -o - -f ' + self.file nullstream = open(os.devnull, 'w') try: infostream = subprocess.Popen(cmd, shell=True, bufsize=1, stderr=nullstream, stdout=subprocess.PIPE).stdout except Exception as e: # Consider this as permanently fatal. self.em.rclog("Openfile: exec info: %s" % str(e)) print("RECFILTERROR HELPERNOTFOUND info") sys.exit(1); self.currentindex = -1 self.contents = InfoSimpleSplitter().splitinfo(self.file, infostream) #self.em.rclog("openfile: Entry count: %d"%(len(self.contents))) return True # Extract specific node def getipath(self, params): try: index = int(params["ipath"]) except: return (False, "", "", True) return self.extractone(index) # Extract next in list def getnext(self, params): if self.currentindex == -1: # Return "self" doc self.currentindex = 0 self.em.setmimetype('text/plain') if len(self.contents) == 0: eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, "", "", eof) if self.currentindex >= len(self.contents): self.em.rclog("getnext: EOF hit") return (False, "", "", rclexecm.RclExecM.eofnow) else: ret= self.extractone(self.currentindex) self.currentindex += 1 return ret # Info file splitter class InfoSimpleSplitter: def splitinfo(self, filename, fin): gotblankline = 1 index = 0 listout = [] node_dict = {} node = b'' infofile = os.path.basename(filename) nodename = b'Unknown' for line in fin: # Top of node ? # It sometimes happens that info --subnodes produces a Node line # beginning with spaces (it's a bug probably, only seen it once) # Maybe we'd actually be better off directly interpreting the # info files if gotblankline and line.lstrip(b' ').startswith(b'File: '): prevnodename = nodename line = line.rstrip(b'\n\r') pairs = line.split(b',') up = b'Top' nodename = str(index) try: for pair in pairs: name, value = pair.split(b':') name = name.strip(b' ') value = value.strip(b' ') if name == b'Node': nodename = value if name == b'Up': up = value if name == b'File': infofile = value except Exception as err: print("rclinfo.py: bad line in %s: [%s] %s\n" % \ (infofile, line, err), file = sys.stderr) nodename = prevnodename node += line continue if nodename in node_dict: print("Info file %s Dup node: %s" % (filename, nodename), \ file=sys.stderr) node_dict[nodename] = up if index != 0: listout.append((prevnodename, node)) node = b'' index += 1 if line.rstrip(b'\n\r') == b'': gotblankline = 1 else: gotblankline = 0 node += line # File done, add last dangling node if node != b'': listout.append((nodename, node)) # Compute node paths (concatenate "Up" values), to be used # as page titles. It's unfortunate that this will crash if # the info file tree is bad listout1 = [] for nodename, node in listout: title = b'' loop = 0 error = 0 while nodename != b'Top': title = nodename + b' / ' + title if nodename in node_dict: nodename = node_dict[nodename] else: print( "Infofile: node's Up does not exist: file %s, path %s, up [%s]" % \ (infofile, title, nodename), sys.stderr) error = 1 break loop += 1 if loop > 50: print("Infofile: bad tree (looping) %s" % infofile, \ file = sys.stderr) error = 1 break if error: continue if title == b'': title = infofile else: title = infofile + b' / ' + title title = title.rstrip(b' / ') listout1.append((title, node)) return listout1 ##### Main program: either talk to the parent or execute test loop proto = rclexecm.RclExecM() extract = InfoExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclipynb.py0000755000175000017500000000414614426501047013662 00000000000000#!/usr/bin/env python3 # Copyright (C) 2021 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ Recoll handler for iPython / Jupyter notebook files.""" # Rem 02-2023. This is not used and I'm not sure why it exists. We use jupyter nbconvert directly # set in mimeconf instead. Kept around just in case. import os import sys import json import rclexecm from rclbasehandler import RclBaseHandler class IPYNBextractor(RclBaseHandler): def __init__(self, em): super(IPYNBextractor, self).__init__(em) def html_text(self, fn): text = open(fn, 'rb').read() data = json.loads(text) mdtext = "" if "worksheets" in data: cells = data["worksheets"][0]["cells"] else: cells = data["cells"] for cell in cells: if cell["cell_type"] == "markdown": mdtext += "\n" for line in cell["source"]: mdtext += "# " + line + "\n" elif cell["cell_type"] == "code": mdtext += "\n\n" key = "source" if "source" in cell else "input" for line in cell[key]: mdtext += line mdtext += "\n" #print("%s"%mdtext, file=sys.stderr) self.outputmimetype = 'text/plain' return mdtext # Main program: create protocol handler and extractor and run them proto = rclexecm.RclExecM() extract = IPYNBextractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclpdf.py0000755000175000017500000006470714501255536013326 00000000000000#!/usr/bin/env python3 # Copyright (C) 2014-2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Recoll PDF extractor, with support for attachments # # pdftotext sometimes outputs unescaped text inside HTML text sections. # We try to correct. # # If pdftotext produces no text and the configuration allows it, we may try to # perform OCR. import os import sys import re import urllib.request import subprocess import tempfile import glob import traceback import atexit import signal import time import rclexecm import rclconfig _mswindows = (sys.platform == "win32") if _mswindows: import platform if platform.machine().endswith('64'): # See comments in ../windows/mkinstdir.sh about the setup of the poppler installation popplerdir = "poppler/Library/bin" else: popplerdir = "poppler32" # Test access to the poppler-glib python3 bindings ? This allows # extracting text from annotations. # - On Ubuntu, this comes with package gir1.2-poppler-0.18 # - On opensuse the package is named typelib-1_0-Poppler-0_18 # (actual versions may differ of course). # # NOTE: we are using the glib introspection bindings for the Poppler C # API, which is not the same as the Python bindings of the Poppler C++ # API (poppler-cpp). The interface is quite different # (e.g. attachments are named "embeddedfiles" in the C++ interface. havepopplerglib = False try: import gi gi.require_version('Poppler', '0.18') from gi.repository import Poppler havepopplerglib = True except: pass tmpdir = None _htmlprefix =b'''
'''
_htmlsuffix = b'''
''' def finalcleanup(): global tmpdir if tmpdir: del tmpdir tmpdir = None ocrproc = None def signal_handler(signal, frame): global ocrproc if ocrproc: ocrproc.wait() ocrproc = None sys.exit(1) atexit.register(finalcleanup) # Not all signals necessary exist on all systems, use catch try: signal.signal(signal.SIGHUP, signal_handler) except: pass try: signal.signal(signal.SIGINT, signal_handler) except: pass try: signal.signal(signal.SIGQUIT, signal_handler) except: pass try: signal.signal(signal.SIGTERM, signal_handler) except: pass class PDFExtractor: def __init__(self, em): self.currentindex = 0 self.pdftotext = None self.pdftotextversion = 0 self.pdfinfo = None self.pdfinfoversion = 0 self.pdftk = None self.em = em self.tesseract = None self.extrameta = None self.re_head = None self.config = rclconfig.RclConfig() self.confdir = self.config.getConfDir() # Avoid picking up a default version on Windows, we want ours if not _mswindows: self.pdftotext = rclexecm.which("pdftotext") if _mswindows and not self.pdftotext: self.pdftotext = rclexecm.which(popplerdir + "/pdftotext") if not self.pdftotext: # No need for anything else. openfile() will return an # error at once return self.pdftotextversion = self._popplerutilversion(self.pdftotext) # Check if we need to escape portions of text: old versions of pdftotext output raw # HTML special characters. Don't know exactly when this changed but it's fixed in 0.26.5 if self.pdftotextversion >= 2605: self.needescape = False else: self.needescape = True # pdfinfo may be used to extract XML metadata and custom PDF properties if not _mswindows: self.pdfinfo = rclexecm.which("pdfinfo") if _mswindows and not self.pdfinfo: self.pdfinfo = rclexecm.which(popplerdir + "/pdfinfo") if self.pdfinfo: self.pdfinfoversion = self._popplerutilversion(self.pdfinfo) # The user can set a list of meta tags to be extracted from the XMP metadata # packet. These are specified as (xmltag,rcltag) pairs self.extrameta = self.config.getConfParam("pdfextrameta") if self.extrameta: self.extrametafix = self.config.getConfParam("pdfextrametafix") self._initextrameta() #self.em.rclog(f"PDFINFOVERSION {self.pdfinfoversion}") # Extracting the outline / bookmarks needs still another poppler command... self.dooutline = self.config.getConfParam("pdfoutline") if self.dooutline: if not _mswindows: self.pdftohtml = rclexecm.which("pdftohtml") if _mswindows and not self.pdftohtml: self.pdftohtml = rclexecm.which(popplerdir + "/pdftohtml") if not self.pdftohtml: self.dooutline = False # Pdftk is optionally used to extract attachments. This takes # a hit on performance even in the absence of any attachments, # so it can be disabled in the configuration. self.attextractdone = False self.attachlist = [] cf_attach = self.config.getConfParam("pdfattach") cf_attach = rclexecm.configparamtrue(cf_attach) if cf_attach: self.pdftk = rclexecm.which("pdftk") if self.pdftk: self.maybemaketmpdir() def _popplerutilversion(self, tool): try: output = subprocess.check_output([tool, "-v"], stderr=subprocess.STDOUT) l1 = output.split(b"\n")[0] v = l1.split()[2] M,m,r = v.split(b".") version = int(M) * 10000 + int(m) * 100 + int(r) except: version = 0 return version def _initextrameta(self): if not self.pdfinfo: return # extrameta is like "metanm|rclnm ...", where |rclnm maybe absent (keep # original name). Parse into a list of pairs. l = self.extrameta.split() self.extrameta = [] for e in l: l1 = e.split('|') if len(l1) == 1: l1.append(l1[0]) self.extrameta.append(l1) # Using lxml because it is better with # namespaces. With xml, we'd have to walk the XML tree # first, extracting all xmlns attributes and # constructing a tree (I tried and did not succeed in # doing this actually). lxml does it partially for # us. See http://stackoverflow.com/questions/14853243/ # parsing-xml-with-namespace-in-python-via-elementtree global ET #import xml.etree.ElementTree as ET try: import lxml.etree as ET except Exception as err: self.em.rclog("Can't import lxml etree: %s" % err) self.extrameta = None self.pdfinfo = None return self.re_xmlpacket = re.compile(br'<\?xpacket[ ]+begin.*\?>' + br'(.*)' + br'<\?xpacket[ ]+end', flags = re.DOTALL) global EMF EMF = None if self.extrametafix: try: import importlib.util spec = importlib.util.spec_from_file_location( 'pdfextrametafix', self.extrametafix) EMF = importlib.util.module_from_spec(spec) spec.loader.exec_module(EMF) except Exception as err: self.em.rclog("Import extrametafix failed: %s" % err) EMF = None pass # Extract all attachments if any into temporary directory def extractAttach(self): if self.attextractdone: return True self.attextractdone = True global tmpdir if not tmpdir or not self.pdftk: # no big deal return True try: tmpdir.vacuumdir() # Note: the java version of pdftk sometimes/often fails # here with writing to stdout: # Error occurred during initialization of VM # Could not allocate metaspace: 1073741824 bytes # Maybe insufficient resources when started from Python ? # In any case, the important thing is to discard the # output, until we fix the error or preferably find a way # to do it with poppler... subprocess.check_call( [self.pdftk, self.filename, "unpack_files", "output", tmpdir.getpath()], stdout=sys.stderr) self.attachlist = sorted(os.listdir(tmpdir.getpath())) return True except Exception as e: self.em.rclog("extractAttach: failed: %s" % e) # Return true anyway, pdf attachments are no big deal return True # Extract PDF custom properties into a dict. Only works with newer pdfinfo versions def _customfields(self): if not self.pdfinfo or self.pdfinfoversion < 211000: return {} try: fields = {} output = subprocess.check_output([self.pdfinfo, "-custom", self.filename], stderr=subprocess.STDOUT) output = output.decode("utf-8") for line in output.split("\n"): colon = line.find(":") if colon > 0: fld = line[:colon].strip() value = line[colon+1:].strip() if fld not in ("Author", "CreationDate", "Creator", "ModDate", "Producer", "Subject", "Title"): fields[fld] = value return fields except: return {} # pdftotext (used to?) badly escape text inside the header # fields. We do it here. This is not an html parser, and depends a # lot on the actual format output by pdftotext. # We also determine if the doc has actual content, for triggering OCR def _fixhtml(self, input): #print input inheader = False inbody = False didcs = False output = [] isempty = True fields = {} if self.pdfinfoversion > 211000: fields = self._customfields() for line in input.split(b'\n'): if re.search(b'', line): inheader = False if re.search(b'
', line): inbody = False if inheader: if not didcs: output.append(b'\n') for fld,val in fields.items(): output.append(self._metatag(fld, val)) didcs = True if self.needescape: m = re.search(b'''(.*)(.*)(<\/title>.*)''', line) if not m: m = re.search(b'''(.*content=")(.*)(".*/>.*)''', line) if m: line = m.group(1) + rclexecm.htmlescape(m.group(2)) + \ m.group(3) # Recoll treats "Subject" as a "title" element # (based on emails). The PDF "Subject" metadata # field is more like an HTML "description" line = re.sub(b'name="Subject"', b'name="Description"', line, 1) elif inbody: s = line[0:1] if s != b"\x0c" and s != b"<": isempty = False # We used to remove end-of-line hyphenation (and join # lines), but but it's not clear that we should do # this as pdftotext without the -layout option does it ? line = rclexecm.htmlescape(line) if re.search(b'<head>', line): inheader = True if re.search(b'<pre>', line): inbody = True output.append(line) return b'\n'.join(output), isempty def _metatag(self, nm, val): return b"<meta name=\"" + rclexecm.makebytes(nm) + b"\" content=\"" + \ rclexecm.htmlescape(rclexecm.makebytes(val)) + b"\">" # metaheaders is a list of (nm, value) pairs def _injectmeta(self, html, metaheaders): if self.re_head is None: self.re_head = re.compile(br'<head>', re.IGNORECASE) metatxt = b'' for nm, val in metaheaders: metatxt += self._metatag(nm, val) + b'\n' if not metatxt: return html res = self.re_head.sub(b'<head>\n' + metatxt, html) #self.em.rclog("Substituted html: [%s]"%res) if res: return res else: return html def _xmltreetext(self, elt): '''Extract all text content from subtree''' text = '' for e in elt.iter(): if e.text: text += e.text + " " return text.strip() # or: return reduce((lambda t,p : t+p+' '), # [e.text for e in elt.iter() if e.text]).strip() def _setextrameta(self, html): if not self.pdfinfo: return html emf = EMF.MetaFixer() if EMF else None # Execute pdfinfo and extract the XML packet all = subprocess.check_output([self.pdfinfo, "-meta", self.filename]) res = self.re_xmlpacket.search(all) xml = res.group(1) if res else '' #self.em.rclog(f"extrameta: XML: [{xml}]") if not xml: return html metaheaders = [] # The namespace thing is a drag. Can't do it from the top. See # the stackoverflow ref above. Maybe we'd be better off just # walking the full tree and building the namespaces dict. root = ET.fromstring(xml) #self.em.rclog(f"root nsmap: {root.nsmap}") namespaces = {'rdf' : "http://www.w3.org/1999/02/22-rdf-syntax-ns#"} RDFROOTTAG = "{" + namespaces["rdf"] + "}RDF" if root.tag == RDFROOTTAG: rdf = root else: rdf = root.find("rdf:RDF", namespaces) if rdf is None: #self.em.rclog(f"_setextrameta: rdf is none") return html #self.em.rclog("rdf nsmap: %s"% rdf.nsmap) rdfdesclist = rdf.findall("rdf:Description", rdf.nsmap) for metanm,rclnm in self.extrameta: #self.em.rclog(f"_setextrameta: metanm {metanm} rclnm {rclnm}") for rdfdesc in rdfdesclist: try: elts = rdfdesc.findall(metanm, rdfdesc.nsmap) except: # We get an exception when this rdf:Description does not # define the required namespace. continue if elts: for elt in elts: text = None try: # First try to get text from a custom element handler text = emf.metafixelt(metanm, elt) except: pass if text is None: # still nothing here, read the element text text = self._xmltreetext(elt) try: # try to run metafix text = emf.metafix(metanm, text) except: pass if text: # Can't use setfield as it only works for # text/plain output at the moment. #self.em.rclog("Appending: (%s,%s)"%(rclnm,text)) metaheaders.append((rclnm, text)) else: # Some docs define the values as attributes. don't # know if this is valid but anyway... try: prefix,nm = metanm.split(":") fullnm = "{%s}%s" % (rdfdesc.nsmap[prefix], nm) except: fullnm = metanm text = rdfdesc.get(fullnm) if text: try: # try to run metafix text = emf.metafix(metanm, text) except: pass metaheaders.append((rclnm, text)) if metaheaders: #self.em.rclog(f"extrameta: {metaheaders}") if emf: try: emf.wrapup(metaheaders) except: pass return self._injectmeta(html, metaheaders) else: return html def maybemaketmpdir(self): global tmpdir if tmpdir: if not tmpdir.vacuumdir(): self.em.rclog("openfile: vacuumdir %s failed" % tmpdir.getpath()) return False else: tmpdir = rclexecm.SafeTmpDir("rclpdf", self.em) #self.em.rclog("Using temporary directory %s" % tmpdir.getpath()) if self.pdftk and re.match("/snap/", self.pdftk): # We know this is Unix (Ubuntu actually). Check that tmpdir # belongs to the user as snap commands can't use /tmp to share # files. Don't generate an error as this only affects # attachment extraction ok = False if "TMPDIR" in os.environ: st = os.stat(os.environ["TMPDIR"]) if st.st_uid == os.getuid(): ok = True if not ok: self.em.rclog("pdftk is a snap command and needs TMPDIR to be owned by you") def _process_annotations(self, html): doc = Poppler.Document.new_from_file( 'file://%s' % urllib.request.pathname2url(os.path.abspath(self.filename)), None) n_pages = doc.get_n_pages() all_annots = 0 # output format f = 'P.: {0}, {1:10}, {2:10}: {3}' # Array of annotations indexed by page number. The page number # here is the physical one (evince -i), not a page label (evince # -p). This may be different for some documents. abypage = {} for i in range(n_pages): page = doc.get_page(i) pnum = i+1 annot_mappings = page.get_annot_mapping () num_annots = len(annot_mappings) for annot_mapping in annot_mappings: atype = annot_mapping.annot.get_annot_type().value_name if atype != 'POPPLER_ANNOT_LINK': # Catch because we sometimes get None values try: atext = f.format( pnum, annot_mapping.annot.get_modified(), annot_mapping.annot.get_annot_type().value_nick, annot_mapping.annot.get_contents()) + "\n" if pnum in abypage: abypage[pnum] += atext else: abypage[pnum] = atext except: pass #self.em.rclog("Annotations: %s" % abypage) pagevec = html.split(b"\f") html = b"" annotsfield = b"" pagenum = 1 for page in pagevec: html += page if pagenum in abypage: html += abypage[pagenum].encode('utf-8') annotsfield += abypage[pagenum].encode('utf-8') + b" - " html += b"\f" pagenum += 1 if annotsfield: self.em.setfield("pdfannot", annotsfield) return html def _process_outline(self): import xml.sax data = subprocess.check_output([self.pdftohtml, "-i", "-s", "-enc", "UTF-8", "-xml", "-stdout", self.filename]) class OutlineHandler(xml.sax.ContentHandler): def __init__(self): self.outlinelevel = 0 self.page = 0 self.alltext = "" self.initem = False def startElement(self, name, attrs): if name == "outline": self.outlinelevel += 1 elif name == "item" and self.outlinelevel: self.initem = True if "page" in attrs: self.alltext += f"[P. {attrs['page']}] " def endElement(self, name): if name == "item" and self.outlinelevel: self.initem = False self.alltext += "\n" elif name == "outline": self.outlinelevel -= 1 def characters(self, content): if self.initem: self.alltext += content handler = OutlineHandler() xml.sax.parseString(data, handler) return handler.alltext def _selfdoc(self): '''Extract the text from the pdf doc (as opposed to attachment)''' self.em.setmimetype('text/html') if self.attextractdone and len(self.attachlist) == 0: eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof html = subprocess.check_output([self.pdftotext, "-htmlmeta", "-enc", "UTF-8", "-eol", "unix", "-q", self.filename, "-"]) html, isempty = self._fixhtml(html) #self.em.rclog("ISEMPTY: %d : data: \n%s" % (isempty, html)) if isempty: self.config.setKeyDir(os.path.dirname(self.filename)) s = self.config.getConfParam("pdfocr") if rclexecm.configparamtrue(s): try: cmd = [sys.executable, os.path.join(_execdir, "rclocr.py"), self.filename] global ocrproc ocrproc = subprocess.Popen(cmd, stdout=subprocess.PIPE) data, stderr = ocrproc.communicate() ocrproc = None html = _htmlprefix + rclexecm.htmlescape(data) + _htmlsuffix except Exception as e: self.em.rclog(f"{cmd} failed: {e}") pass if self.extrameta: try: html = self._setextrameta(html) except Exception as err: self.em.rclog(f"Metadata extraction failed: {err} {traceback.format_exc()}") if self.dooutline: try: outlinetext = self._process_outline() html = self._injectmeta(html, [("description" , outlinetext),]) except Exception as err: self.em.rclog(f"Outline extraction failed: {err} {traceback.format_exc()}") if havepopplerglib: try: html = self._process_annotations(html) except Exception as err: self.em.rclog(f"Annotation extraction failed: {err} {traceback.format_exc()}") return (True, html, "", eof) def extractone(self, ipath): #self.em.rclog("extractone: [%s]" % ipath) if not self.attextractdone: if not self.extractAttach(): return (False, "", "", rclexecm.RclExecM.eofnow) if type(ipath) != type(""): ipath = ipath.decode('utf-8') path = os.path.join(tmpdir.getpath(), ipath) if os.path.isfile(path): f = open(path, "rb") docdata = f.read(); f.close() if self.currentindex == len(self.attachlist) - 1: eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, docdata, ipath, eof) ###### File type handler api, used by rclexecm ----------> def openfile(self, params): if not self.pdftotext: print("RECFILTERROR HELPERNOTFOUND pdftotext") sys.exit(1); self.filename = rclexecm.subprocfile(params["filename"]) #self.em.rclog("openfile: [%s]" % self.filename) self.currentindex = -1 self.attextractdone = False if self.pdftk: preview = os.environ.get("RECOLL_FILTER_FORPREVIEW", "no") if preview != "yes": # When indexing, extract attachments at once. This # will be needed anyway and it allows generating an # eofnext error instead of waiting for actual eof, # which avoids a bug in recollindex up to 1.20 self.extractAttach() else: self.attextractdone = True return True def getipath(self, params): ipath = params["ipath"] ok, data, ipath, eof = self.extractone(ipath) return (ok, data, ipath, eof) def getnext(self, params): # self.em.rclog("getnext: current %d" % self.currentindex) if self.currentindex == -1: self.currentindex = 0 return self._selfdoc() else: self.em.setmimetype('') if not self.attextractdone: if not self.extractAttach(): return (False, "", "", rclexecm.RclExecM.eofnow) if self.currentindex >= len(self.attachlist): return (False, "", "", rclexecm.RclExecM.eofnow) try: ok, data, ipath, eof = \ self.extractone(self.attachlist[self.currentindex]) self.currentindex += 1 #self.em.rclog("getnext: returning ok for [%s]" % ipath) return (ok, data, ipath, eof) except Exception as ex: self.em.rclog("getnext: extractone failed for index %d: %s" % (self.currentindex, ex)) return (False, "", "", rclexecm.RclExecM.eofnow) # Main program: create protocol handler and extractor and run them _execdir = os.path.dirname(sys.argv[0]) proto = rclexecm.RclExecM() extract = PDFExtractor(proto) rclexecm.main(proto, extract) ���������������������������������������������������������recoll-1.36.1/filters/rclxls.py���������������������������������������������������������������������0000755�0001750�0001750�00000005052�14410615043�013337� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python3 # Extractor for Excel files. import rclexecm import rclexec1 import xlsxmltocsv import re import sys import os import xml.sax class XLSProcessData: def __init__(self, em, ishtml = False): self.em = em self.out = [] self.gotdata = 0 self.xmldata = [] self.ishtml = ishtml def takeLine(self, line): if not line: return if self.ishtml: self.out.append(line) return if not self.gotdata: self.out.append(b'''<html><head>''' + \ b'''<meta http-equiv="Content-Type" ''' + \ b'''content="text/html;charset=UTF-8">''' + \ b'''</head><body><pre>''') self.gotdata = True self.xmldata.append(line) def wrapData(self): if not self.gotdata: raise Exception("xls-dump returned no data") return b'' if self.ishtml: return b'\n'.join(self.out) handler = xlsxmltocsv.XlsXmlHandler() xml.sax.parseString(b'\n'.join(self.xmldata), handler) self.out.append(rclexecm.htmlescape(b'\n'.join(handler.output))) return b'\n'.join(self.out) + b'</pre></body></html>' class XLSFilter: def __init__(self, em): self.em = em self.ntry = 0 def reset(self): self.ntry = 0 pass def getCmd(self, fn): if self.ntry: return ([], None) self.ntry = 1 # Some HTML files masquerade as XLS try: data = open(fn, 'rb').read(512) if data.find(b'html') != -1 or data.find(b'HTML') != -1: return ("cat", XLSProcessData(self.em, True)) except Exception as err: self.em.rclog("Error reading %s:%s" % (fn, str(err))) pass cmd = rclexecm.which("xls-dump.py") if cmd: # xls-dump.py often exits 1 with valid data. Ignore exit value # We later treat an empty output as an error return ([sys.executable, cmd, "--dump-mode=canonical-xml", \ "--utf-8", "--catch"], XLSProcessData(self.em), rclexec1.Executor.opt_ignxval) else: return ([], None) if __name__ == '__main__': if not rclexecm.which("xls-dump.py"): print("RECFILTERROR HELPERNOTFOUND ppt-dump.py") sys.exit(1) proto = rclexecm.RclExecM() filter = XLSFilter(proto) extract = rclexec1.Executor(proto, filter) rclexecm.main(proto, extract) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/filters/rclxslt.py��������������������������������������������������������������������0000644�0001750�0001750�00000002646�14410615043�013526� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2014-2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ###################################### # Common code for the remaining Python xslt-based filters (most xslt work is # now done in the c++ mh_xslt module, the ones remaining don't fit with its # model). import sys try: from lxml import etree except: print("RECFILTERROR HELPERNOTFOUND python3:lxml") sys.exit(1); def _apply_sheet_doc(sheet, doc): styledoc = etree.fromstring(sheet) transform = etree.XSLT(styledoc) return bytes(transform(doc)) def apply_sheet_data(sheet, data): doc = etree.fromstring(data) return _apply_sheet_doc(sheet, doc) def apply_sheet_file(sheet, fn): doc = etree.parse(fn) return _apply_sheet_doc(sheet, doc) ������������������������������������������������������������������������������������������recoll-1.36.1/filters/rclorgmode.py�����������������������������������������������������������������0000755�0001750�0001750�00000007157�14410615043�014175� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python3 # Copyright (C) 2020-2022 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. '''Read an org-mode file, optionally break it into subdocs" along level 1 headings''' import sys import re import rclexecm import rclconfig import conftree class OrgModeExtractor: def __init__(self, em): self.file = "" self.em = em self.selftext = "" self.docs = [] config = rclconfig.RclConfig() self.createsubdocs = conftree.valToBool(config.getConfParam("orgmodesubdocs")) def extractone(self, index): if index >= len(self.docs): return(False, "", "", True) docdata = self.docs[index] #self.em.rclog(docdata) iseof = rclexecm.RclExecM.noteof if self.currentindex >= len(self.docs) -1: iseof = rclexecm.RclExecM.eofnext self.em.setmimetype("text/x-orgmode-sub") try: self.em.setfield("title", docdata.splitlines()[0]) except: pass return (True, docdata, str(index), iseof) ###### File type handler api, used by rclexecm ----------> def openfile(self, params): self.file = params["filename"] try: data = open(self.file, "rb").read() except Exception as e: self.em.rclog("Openfile: open: %s" % str(e)) return False self.currentindex = -1 if not self.createsubdocs: self.selftext = data return True res = rb'''^\* ''' self.docs = re.compile(res, flags=re.MULTILINE).split(data) # Note that there can be text before the first heading. This goes into the self doc, # because it's not a proper entry. self.selftext = self.docs[0] self.docs = self.docs[1:] #self.em.rclog("openfile: Entry count: %d" % len(self.docs)) return True def getipath(self, params): try: if params["ipath"] == b'': index = 0 else: index = int(params["ipath"]) except: return (False, "", "", True) return self.extractone(index) def getnext(self, params): if not self.createsubdocs: return (True, self.selftext, "", rclexecm.RclExecM.eofnext) if self.currentindex == -1: # Return "self" doc self.currentindex = 0 self.em.setmimetype(b'text/plain') if len(self.docs) == 0: eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, self.selftext, "", eof) if self.currentindex >= len(self.docs): self.em.rclog("getnext: EOF hit") return (False, "", "", rclexecm.RclExecM.eofnow) else: ret= self.extractone(self.currentindex) self.currentindex += 1 return ret proto = rclexecm.RclExecM() extract = OrgModeExtractor(proto) rclexecm.main(proto, extract) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/filters/rcltex������������������������������������������������������������������������0000755�0001750�0001750�00000006225�14426500174�012712� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # @(#$Id: rcltex,v 1.2 2007-11-09 15:56:14 dockes Exp $ (C) 2004 J.F.Dockes #================================================================ # Translate TeX files for recoll. Uses either untex or detex to translate to html #================================================================ # set variables LANG=C ; export LANG LC_ALL=C ; export LC_ALL progname="rcltex" filetype=TeX #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE # Try to detect encoding encoding=`egrep '\usepackage\[.*\]{inputenc}' "$infile" | \ sed -e 's/.*\[//' -e 's/\].*//'` if test "X$encoding" = Xutf8x ; then encoding=utf8 fi # Found no usepackage directive to specify encoding. Have the system have a guess at it: if test "X$encoding" = X ; then encoding=$(file -i "$infile" | awk -Fcharset= '{print $2}') if test "X$encoding" = "Xus-ascii" -o "X$encoding" = X; then # 'file' only reads the beginning of the text, which could still have some 8bit # chars later-on. utf-8 is self-identifying, so do a full test for it. This is # expensive, but important, and a common case these days. Finally, default to # extended latin1 if iconv -s -f utf-8 -t utf-8 < "$infile" > /dev/null; then encoding=utf8 else encoding=windows-1252 fi fi fi if iscmd detex ; then checkcmds iconv CMD="detex -n -e ''" else checkcmds untex iconv CMD="untex -giso -a" fi # output the result cat <<EOF <html><head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> </head><body><pre> EOF $CMD "$infile" | \ iconv -c -f "$encoding" -t utf-8 | \ sed -e 's/</</g' -e 's/&/&/g' echo '</pre></body></html>' exit 0 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/filters/rclaptosidman�����������������������������������������������������������������0000755�0001750�0001750�00000004663�14410615043�014250� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # @(#$Id: rclaptosidman,v 1.1 2010-12-11 12:40:05 dockes Exp $ (C) 2004 J.F.Dockes # Parts taken from Estraier: #================================================================ # Estraier: a personal full-text search system # Copyright (C) 2003-2004 Mikio Hirabayashi #================================================================ #================================================================ # rclaptosidman # Strip the menu part from aptosid manual pages to improve search precision #================================================================ # set variables LANG=C ; export LANG LC_ALL=C ; export LC_ALL progname="rclaptosidman" filetype="aptosid manual htm" #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE checkcmds sed # Delete everything from <div id="menu"> to <div id="main-page"> # This prints an additional blank line at top which does not matter sed -n -e '1,/<div id="menu">/{x;p' -e '}' \ -e '/<div id="main-page">/,$p' < "$infile" # exit normally exit 0 �����������������������������������������������������������������������������recoll-1.36.1/filters/rcljoplin.py������������������������������������������������������������������0000755�0001750�0001750�00000021165�14507454402�014036� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python3 """This sample uses the Recoll Python API to index the notes table from a Joplin database""" # This is an example of an indexer for which the embedded documents are standalone ones, not # subdocuments of a main file. This is possible because the notes have update timestamps so that we # can decide individually if they need a reindex. See rclmbox.py for an example with subdocs. # # Configuration files: Note that the mimeconf and mimeview additions, and the backends file need to # exist for any index which would add the Joplin one as an external index. # # rcljoplin.py -c will take care to update the chosen configuration for you. Here follows a # description anyway: # # mimeconf: this tells recoll that the data from this indexer, to which we give a specific MIME # type, should be turned into text by a null operation. We could also choose to format the notes in # HTML in this script, in which case, change text/plain to text/html. # # [index] # application/x-joplin-note = internal text/plain # # mimeview: this tells recoll to open notes by using the URL (which was created by this indexer as a # browser-appropriate link to a joplin note). Can't use the default entry because it badly encodes # the URL (%U) for some reason which I can't remember. # # xallexcepts+ = application/x-joplin-note # [view] # application/x-joplin-note = xdg-open %u # # backends: this is the link between recoll and this script. Fix the path of course, or copy the # script somewhere in the PATH or the Recoll filters directory and use just the script name. Recoll # will add parameters to the base commands. # # [JOPLIN] # fetch = /path/to/rcljoplin.py fetch # makesig = /path/to/rcljoplin.py makesig # index = /path/to/rcljoplin.py index import sys import os import sqlite3 from getopt import getopt import signal from recoll import recoll from recoll import rclconfig from recoll import conftree def msg(s): print(f"{s}", file=sys.stderr) # Signal termination handling: try to cleanly close the index rcldb = None def handler(signum, frame): logmsg(f"Got signal") if rcldb: rcldb.close() logmsg(f"Exiting") sys.exit(1) signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGTERM, handler) class joplin_indexer: def __init__(self, rclconfdir, sqfile): self.rclconfdir = rclconfdir self.sqconn = sqlite3.connect(sqfile) def _sig(self, note): """Create update verification value for note: updated_time looks ok""" return str(note["updated_time"]) def sigfromid(self, id): #msg(f"sigfromid: {id}") c = self.sqconn.cursor() stmt = "SELECT updated_time FROM notes WHERE id = ?" c.execute(stmt, (id,)) r = c.fetchone() if r: return self._sig({"updated_time":r[0]}) return "" def _udi(self, note): """Create unique document identifier for message. This should be shorter than 150 bytes, which we optimistically don't check here, as we just use the note id (which we also assume to be globally unique)""" return note["id"] # Walk the table, check if index is up to date for each note, update the index if not def index(self): global rcldb rcldb = recoll.connect(confdir=self.rclconfdir, writable=1) # Important: this marks all non-joplin documents as present. Else our call to purge() would # delete them. This can't happen in the recollindex process (because we're using our own db # instance). rcldb.preparePurge("JOPLIN") cols = ["id", "title", "body", "updated_time", "author"] c = self.sqconn.cursor() stmt = "SELECT " + ",".join(cols) + " FROM notes" c.execute(stmt) for r in c: note = dict((nm,val) for nm, val in zip(cols, r)) if not rcldb.needUpdate(self._udi(note), self._sig(note)): #msg(f"Index is up to date for {note['id']}") continue #msg(f"Indexing {str(note)[0:200]}") self._index_note(rcldb, note) rcldb.purge() rcldb.close() # Index one note record def _index_note(self, rcldb, note): doc = recoll.Doc() # Misc standard recoll fields # it appears that the joplin updated_time is in mS, we want secs doc.mtime = str(note["updated_time"])[0:-3] doc.title = note["title"] doc.author = note["author"] # Main document text and MIME type doc.text = note["body"] doc.dbytes = str(len(doc.text.encode('UTF-8'))) doc.mimetype = "application/x-joplin-note" # Store data for later "up to date" checks doc.sig = self._sig(note) # The rclbes field is the link between the index data and this # script when used at query time doc.rclbes = "JOPLIN" # These get stored inside the index, returned at query # time, and used for opening the note (as set in mimeview) doc.url = f"joplin://x-callback-url/openNote?id={note['id']}" # The udi is the unique document identifier, later used if we # want to e.g. delete the document index data (and other ops). udi = self._udi(note) rcldb.addOrUpdate(udi, doc) def getdata(self, sqfile, id): """Implements the 'fetch' data access interface (called at query time from the command line).""" c = self.sqconn.cursor() stmt = "SELECT body FROM notes WHERE id = ?" c.execute(stmt, (id,)) r = c.fetchone() if r: return r[0] return "" # Update the current recoll configuration with the joplin bits. def update_config(confdir): scriptpath = os.path.realpath(__file__) # mimeconf conf = conftree.ConfSimple(os.path.join(confdir, "mimeconf"), readonly=False) conf.set("application/x-joplin-note", "internal text/plain", "index") # mimeview conf = conftree.ConfSimple(os.path.join(confdir, "mimeview"), readonly=False) xalle = conf.get("xallexcepts+") if not xalle: xalle = "application/x-joplin-note" elif xalle.find("application/x-joplin-note") == -1: xalle += " application/x-joplin-note" conf.set("xallexcepts+", xalle) conf.set("application/x-joplin-note", "xdg-open %u", "view") # backends conf = conftree.ConfSimple(os.path.join(confdir, "backends"), readonly=False) conf.set("fetch", scriptpath + " fetch", "JOPLIN") conf.set("makesig", scriptpath + " makesig", "JOPLIN") conf.set("index", scriptpath + " index", "JOPLIN") ######## # Main program. This is called from cron or the command line for indexing, or called from the recoll # main code with a specific command line for retrieving data, checking up-to-date-ness (for # previewing mostly), or updating the index. # We hard-code the path to the joplin db for simplicity. It could also be set in the recoll # configuration file and retrieved with something like the following, # # from recoll import rclconfig # config = rclconfig.RclConfig(argcnf=rclconfdir) # joplindb = config.getConfParam("joplindbpath") # Or any other approach... joplindb = os.path.expanduser("~/.config/joplin-desktop/database.sqlite") usage_string="""Usage: rcljoplin.py config Update the recoll configuration to include the Joplin parameters. rcljoplin.py index Index the joplin notes table (the path to the sqlite db is hard-coded inside the script) rcljoplin.py <fetch|makesig> <udi> <url> <ipath> fetch subdoc data or make signature (query time). ipath must be set but is ignored """ # Note: the -c option is only for command line tests, the recoll process always sets the config in # RECOLL_CONFDIR def usage(f=sys.stderr): print(f"{usage_string}", file=f) sys.exit(1) confdir = None try: options, args = getopt(sys.argv[1:], "hc:", ["help", "config="]) except Exception as err: print(err, file=sys.stderr) usage() for o,a in options: if o in ("-h", "--help"): usage(sys.stdout) elif o in ("-c", "--config"): confdir = a if len(args) == 0: usage() cmd = args[0] rclconfig = rclconfig.RclConfig(argcnf=confdir) confdir = rclconfig.getConfDir() if cmd == "index": indexer = joplin_indexer(confdir, joplindb) indexer.index() sys.exit(0) elif cmd == "config": update_config(confdir) sys.exit(0) # cmd [fetch|makesig] udi url ipath. # We ignore url and ipath if len(args) != 4: usage() udi = sys.argv[2] fetcher = joplin_indexer(confdir, joplindb) if cmd == 'fetch': print(f"{fetcher.getdata(joplindb, udi)}", end="") elif cmd == 'makesig': print(f"{fetcher.sigfromid(udi)}", end="") else: usage() sys.exit(0) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������recoll-1.36.1/filters/openxml-xls-body.xsl����������������������������������������������������������0000644�0001750�0001750�00000000674�14410615043�015431� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <xsl:output omit-xml-declaration="yes"/> <xsl:template match="/"> <div> <xsl:apply-templates/> </div> </xsl:template> <xsl:template match="x:t"> <p> <xsl:value-of select="."/> </p> </xsl:template> </xsl:stylesheet> ��������������������������������������������������������������������recoll-1.36.1/filters/rclkar.py���������������������������������������������������������������������0000755�0001750�0001750�00000023656�14410615043�013320� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python3 # Read a .kar midi karaoke file and translate to recoll indexable format import rclexecm import sys import os.path import string import re import codecs from rclbasehandler import RclBaseHandler try: import rcllatinclass except: pass try: import rclmidi as midi except: print("RECFILTERROR HELPERNOTFOUND python3:midi") sys.exit(1); try: import chardet has_chardet = True except: has_chardet = False # Prototype for the html document we're returning htmltemplate = ''' <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>%s %s ''' nlbytes = b'\n' bsbytes = b'\\' nullchar = 0 class KarTextExtractor(RclBaseHandler): # Afaik, the only charset encodings with null bytes are variations on # utf-16 and utf-32 and iso relatives. A hopefully comprehensive # list follows, compiled from iconv and python values. This is used for # stripping garbage from some files. acceptnullencodings = \ set(('csucs4', 'csunicode', 'csunicode11', 'iso-10646-ucs-2', 'iso-10646-ucs-4', 'u16', 'u32', 'ucs-2', 'ucs-2-internal', 'ucs-2-swapped', 'ucs-2be', 'ucs-2le', 'ucs-4', 'ucs-4-internal', 'ucs-4-swapped', 'ucs-4be', 'ucs-4le', 'unicode-1-1', 'unicodebig', 'unicodelittle', 'utf-16', 'utf-16be', 'utf-16le', 'utf-32', 'utf-32be', 'utf-32le', 'utf16', 'utf32', 'utf_16', 'utf_16_be', 'utf_16_le', 'utf_32', 'utf_32_be', 'utf_32_le')) def __init__(self, em): super(KarTextExtractor, self).__init__(em) self.encoding = "" self.defaultencoding = "" self.hadnulls = False self.classifier = None # Compute the fallback encoding to use if we can't determine # one when processing the file. Based on the nls environment try: self.defaultencoding = sys.getfilesystemencoding() except: pass if self.defaultencoding is None: self.defaultencoding = sys.getdefaultencoding() if not self.defaultencoding or \ self.defaultencoding.lower().find('ascii') != -1: self.defaultencoding = 'cp1252' try: codecs.lookup(self.defaultencoding) except: self.defaultencoding = 'cp1252' def nulltrunc(self, data): '''Truncate data after 1st null byte. For messages with garbage after a null byte. Must not be done for utf-16/32 of course''' if not data: return data try: firstnull = data.index(nullchar) self.hadnulls = True data = data[0 : firstnull] except: pass return data def reencode(self, data): '''Decode from whatever encoding we think this file is using and reencode as UTF-8''' # self.em.rclog("Reencoding from [%s] to UTF-8" % self.encoding) if data: try: data = data.decode(self.encoding, 'ignore') except Exception as err: self.em.rclog("Decode failed: " + str(err)) return "" try: data = data.encode('utf-8') except Exception as err: self.em.rclog("Encode failed: " + str(err)) return "" data = rclexecm.htmlescape(data).decode('utf-8').replace('\n', '
\n') return data def encodingfromfilename(self, fn): '''Compute encoding from file name: some karaoke files have the encoding as part of the file name as 'some title (encoding).xxx'. This is not an established convention though, just one our users could use if there is trouble with guessing encodings''' rexp = b'''\(([^\)]+)\)\.[a-zA-Z]+$''' m = re.search(rexp, fn) if m: return m.group(1) else: return "" def chardet_detect(self, text): encodconf = chardet.detect(text) encoding = encodconf['encoding'] confidence = encodconf['confidence'] #self.em.rclog("Chardet returns %s %.2f" % (encoding,confidence)) # chardet is awfully bad at detecting 8bit european # encodings/languages and will mostly return iso-8859-2 for # everything, which is a bad default (iso-8859-1/cp1252 being # much more common). We use our own ad-hoc stopwords based # module to try and improve if encoding.lower() == 'iso-8859-2': if self.classifier is None: try: import __main__ dir = os.path.dirname(__main__.__file__) langszip = os.path.join(dir, 'rcllatinstops.zip') f = open(langszip) f.close() classifier = rcllatinclass.European8859TextClassifier(langszip) except: self.em.rclog("Can't build euroclassifier (missing stopwords zip?") return (encoding, confidence) try: lang,code,count = classifier.classify(text) #self.em.rclog("euclass lang/code/matchcount: %s %s %d" % \ # (lang, code, count)) if count > 0: confidence = 1.0 encoding = code except Exception as err: self.em.rclog("stopwords-based classifier failed: %s" % err) return (encoding, confidence) return (encoding, confidence) def html_text(self, filename): # Character encoding from file name ? self.encoding = self.encodingfromfilename(filename) if self.encoding: try: codecs.lookup(self.encoding) except: self.encoding = "" # Read in and midi-decode the file stream = midi.read_midifile(filename) title = None author = None language = None lyrics = b"" lyricsN = b"" self.hadnulls = False for event in stream.iterevents(): edata = b"" if isinstance(event, midi.TextMetaEvent): if not event.data: continue elif event.data[0] == b'/'[0] or event.data[0] == bsbytes[0]: edata = nlbytes + event.data[1:] elif event.data[0] == b'['[0] or event.data[0] == b']'[0]: edata = event.data[1:] elif event.data[0] == b'@'[0]: if len(event.data) == 1: continue else: if event.data[1] == b'I'[0]: edata = event.data[2:] + nlbytes elif event.data[1] == b'L'[0]: language = self.nulltrunc(event.data[2:]) languageN = event.data[2:] elif event.data[1] == b'T'[0]: if title is None: title = self.nulltrunc(event.data[2:]) titleN = event.data[2:] elif author is None: author = self.nulltrunc(event.data[2:]) authorN = event.data[2:] else: edata = event.data elif isinstance(event, midi.LryricsEvent) or \ isinstance(event, midi.TrackNameEvent): space = b"" if isinstance(event, midi.TrackNameEvent): nl = nlbytes if not event.data: continue elif event.data[0] == b'/'[0] or event.data[0] == bsbytes[0]: edata = nlbytes + event.data[1:] + nl else: edata = event.data + nl lyrics += self.nulltrunc(edata) lyricsN += edata # Try to guess the encoding. First do it with the data # possibly containing nulls. If we get one of the accepted # nullbyte encodings, go with this, else repeat with the # de-nulled data # self.em.rclog("Lyrics length %d" % len(lyrics)) if self.encoding == "" and has_chardet: if self.hadnulls: (encoding, confidence) = self.chardet_detect(lyricsN) # self.em.rclog("With nulls: chardet: enc [%s], conf %.2f" % \ # (encoding, confidence)) if confidence > 0.6 and \ encoding.lower() in self.acceptnullencodings: self.encoding = encoding lyrics = lyricsN title = titleN author = authorN if self.encoding == "": (encoding, confidence) = self.chardet_detect(lyrics) #self.em.rclog("No nulls: chardet: enc [%s], conf %.2f" % \ # (encoding, confidence)) if confidence > 0.6: self.encoding = encoding if self.encoding == "": self.em.rclog("Encoding not guessed, defaulting to [%s]" % \ (self.defaultencoding,)) self.encoding = self.defaultencoding if title is None: title = "" if author is None: author = "" if language is None: language = "" title = self.reencode(title) author = self.reencode(author) lyrics = self.reencode(lyrics) language = self.reencode(language) return htmltemplate % (title, author, language, lyrics) proto = rclexecm.RclExecM() extract = KarTextExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rcluncomp0000755000175000017500000000221514410615043013401 00000000000000#!/bin/sh # @(#$Id: rcluncomp,v 1.3 2007-10-27 08:40:25 dockes Exp $ (C) 2004 J.F.Dockes # Uncompress file using any program like gunzip/bunzip2. We jump through # hoops to let the decompressor remove the compression suffix in the file # name. The input directory must be empty on entry. if test $# != 3 -o ! -f "$2" -o ! -d "$3" ; then echo "Usage: rcluncomp " exit 1 fi uncomp=$1 infile=$2 outdir=$3 sinfile=`basename "$infile"` #echo "rcluncomp: $sinfile" 1>&2 case "$sinfile" in # File with a suffix: try to decompress in file mode so that the # decompressor can transform the file name if it knows how. Try # filter mode if this fails *.*) cp "$infile" "$outdir/$sinfile" || exit 1 # try uncompress if $uncomp "$outdir/$sinfile" ; then uncompressed=`echo $outdir/*` else rm -f "$outdir/sinfile" $uncomp < "$infile" > "$outdir/$sinfile" || exit 1 uncompressed="$outdir/$sinfile" fi ;; # Suffixless file names: use filter mode *) $uncomp < "$infile" > "$outdir/$sinfile" || exit 1 uncompressed="$outdir/$sinfile" ;; esac cat <&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE checkcmds ps2pdf pdftotext ps2pdf "$infile" - | pdftotext -htmlmeta -enc UTF-8 -eol unix -q - - | \ egrep -v '^.*$' recoll-1.36.1/filters/opendoc-body.xsl0000644000175000017500000000124314463114077014574 00000000000000


recoll-1.36.1/filters/rclbasehandler.py0000644000175000017500000000554414426501047015011 00000000000000#!/usr/bin/env python3 # Copyright (C) 2016 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Base for extractor classes. With some common generic implementations # for the boilerplate functions. import os import sys import rclexecm class RclBaseHandler(object): '''Base Object for simple extractors. This implements the boilerplate code for simple extractors for file types with a single document. The derived class would typically need only to implement the html_text method to return the document text in HTML format by default, but any output MIME value is also possible by setting self.outputmimetype (do *not* call self.em.setmimetype(), the value would be clobbered. ''' def __init__(self, em): self.em = em def extractone(self, params): #self.em.rclog("extractone fn %s mt %s" % (params["filename"], \ # params["mimetype"])) if not "filename" in params: self.em.rclog("extractone: no file name") return (False, "", "", rclexecm.RclExecM.eofnow) fn = params["filename"] if "mimetype" in params: self.inputmimetype = params["mimetype"] else: self.inputmimetype = None self.outputmimetype = 'text/html' try: # Note: "html_text" can change self.outputmimetype and # output text/plain html = self.html_text(fn) except Exception as err: #import traceback #traceback.print_exc() self.em.rclog("%s : %s" % (fn, err)) return (False, "", "", rclexecm.RclExecM.eofnow) self.em.setmimetype(self.outputmimetype) return (True, html, "", rclexecm.RclExecM.eofnext) ###### File type handler api, used by rclexecm ----------> def openfile(self, params): self.currentindex = 0 return True def getipath(self, params): return self.extractone(params) def getnext(self, params): if self.currentindex >= 1: return (False, "", "", rclexecm.RclExecM.eofnow) else: ret= self.extractone(params) self.currentindex += 1 return ret recoll-1.36.1/filters/rclrtf.py0000755000175000017500000000322014410615043013317 00000000000000#!/usr/bin/env python3 import rclexecm import rclexec1 import re import sys import os # Processing the output from unrtf class RTFProcessData: def __init__(self, em): self.em = em self.out = [] self.gothead = 0 self.patendhead = re.compile(b'''''') self.patcharset = re.compile(b'''^') self.out.append(line) self.gothead = 1 elif not self.patcharset.search(line): self.out.append(line) else: self.out.append(line) def wrapData(self): return b'\n'.join(self.out) class RTFFilter: def __init__(self, em): self.em = em self.ntry = 0 def reset(self): self.ntry = 0 def getCmd(self, fn): if self.ntry: return ([], None) self.ntry = 1 cmd = rclexecm.which("unrtf") if cmd: return ([cmd, "--nopict", "--html"], RTFProcessData(self.em)) else: return ([], None) if __name__ == '__main__': if not rclexecm.which("unrtf"): print("RECFILTERROR HELPERNOTFOUND unrtf") sys.exit(1) proto = rclexecm.RclExecM() filter = RTFFilter(proto) extract = rclexec1.Executor(proto, filter) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclics.py0000755000175000017500000001271714444307651013327 00000000000000#!/usr/bin/env python3 # Read an ICS file, break it into "documents" which are events, todos, # or journal entries, and interface with recoll execm # # For historical reasons, this can use either the icalendar or the # vobject Python modules, or an internal splitter. The default is now # to use the internal splitter, the other modules are more trouble # than they're worth (to us and until we will want to get into date # computations etc.) import rclexecm import sys # Decide how we'll process the file. modules = ("internal", "icalendar", "vobject") usemodule = "internal" forcevobject = 0 if usemodule != "internal": try: if forcevobject: raise Exception from icalendar import Calendar, Event usemodule = "icalendar" except: try: import vobject usemodule = "vobject" except: print("RECFILTERROR HELPERNOTFOUND python3:icalendar") print("RECFILTERROR HELPERNOTFOUND python3:vobject") sys.exit(1); class IcalExtractor: def __init__(self, em): self.file = "" self.contents = [] self.em = em def extractone(self, index): if index >= len(self.contents): return(False, "", "", True) docdata = self.contents[index] #self.em.rclog(docdata) iseof = rclexecm.RclExecM.noteof if self.currentindex >= len(self.contents) -1: iseof = rclexecm.RclExecM.eofnext self.em.setmimetype("text/plain") return (True, docdata, str(index), iseof) ###### File type handler api, used by rclexecm ----------> def openfile(self, params): self.file = params["filename"] try: calstr = open(self.file, "rb") except Exception as e: self.em.rclog("Openfile: open: %s" % str(e)) return False self.currentindex = -1 if usemodule == "internal": self.contents = ICalSimpleSplitter().splitcalendar(calstr) elif usemodule == "icalendar": try: cal = Calendar.from_string(calstr.read()) except Exception as e: self.em.rclog("Openfile: read or parse error: %s" % str(e)) return False self.contents = cal.walk() self.contents = [item.as_string() for item in self.contents if (item.name == "VEVENT" or item.name == "VTODO" or item.name == "VJOURNAL")] else: try: cal = vobject.readOne(calstr) except Exception as e: self.em.rclog("Openfile: can't parse object: %s" % str(e)) return False for lstnm in ("vevent_list", "vtodo_list", "vjournal_list"): lst = getattr(cal, lstnm, []) for ev in lst: self.contents.append(ev.serialize()) #self.em.rclog("openfile: Entry count: %d"%(len(self.contents))) return True def getipath(self, params): try: if params["ipath"] == b'': index = 0 else: index = int(params["ipath"]) except: return (False, "", "", True) return self.extractone(index) def getnext(self, params): if self.currentindex == -1: # Return "self" doc self.currentindex = 0 self.em.setmimetype(b'text/plain') if len(self.contents) == 0: eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, "", "", eof) if self.currentindex >= len(self.contents): self.em.rclog("getnext: EOF hit") return (False, "", "", rclexecm.RclExecM.eofnow) else: ret= self.extractone(self.currentindex) self.currentindex += 1 return ret # Trivial splitter: cut objects on BEGIN/END (only for 'interesting' objects) # ignore all other syntax class ICalSimpleSplitter: # Note that if an 'interesting' element is nested inside another one, # it will not be extracted (stay as text in external event). This is # not an issue and I don't think it can happen with the current list interesting = (b'VTODO', b'VEVENT', b'VJOURNAL') def splitcalendar(self, fin): curblkname = b'' curblk = b'' lo = [] for line in fin: line = line.rstrip() if line == b'': continue if curblkname: curblk = curblk + line + b'\n' l = line.split(b':') if len(l) < 2: continue # If not currently inside a block and we see an # 'interesting' BEGIN, start block if curblkname == b'' and l[0].upper() == b'BEGIN': name = l[1].upper() if name in ICalSimpleSplitter.interesting: curblkname = name curblk = curblk + line + b'\n' # If currently accumulating block lines, check for end if curblkname and l[0].upper() == b'END' and \ l[1].upper() == curblkname: lo.append(curblk) curblkname = b'' curblk = b'' if curblk: lo.append(curblk) curblkname = b'' curblk = b'' return lo proto = rclexecm.RclExecM() extract = IcalExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/xls-dump.py0000755000175000017500000002127414463114123013606 00000000000000#!/usr/bin/env python3 # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # from builtins import range import sys, os.path, optparse sys.path.insert(0, os.path.join(os.path.dirname(__file__), "msodump.zip")) import traceback from msodumper import ole, xlsstream, globals, node, xlsmodel, olestream from msodumper import xlsparser, msocrypto from msodumper.globals import error def equalsName (name, array): if len(name) != len(array): return False for i in range(0, len(name)): if globals.indexbytes(name, i) != array[i]: return False return True def isOleStream (dirname): """Determine whether or not a stream is an OLE stream. According to the spec, an OLE stream is always named '\1Ole'.""" name = [0x01, 0x4F, 0x6C, 0x65] # 0x01, 'Ole' return equalsName(dirname, name) def isCompObjStream (dirname): name = [0x01, 0x43, 0x6F, 0x6D, 0x70, 0x4F, 0x62, 0x6A] # 0x01, 'CompObj' return equalsName(dirname, name) class XLDumper(object): def __init__ (self, filepath, params): self.filepath = filepath self.params = params self.strm = None self.strmData = None def __printDirHeader (self, direntry, byteLen): dirname = direntry.Name dirname = globals.encodeName(dirname) globals.outputln("") globals.outputln("="*globals.OutputWidth) if direntry.isStorage(): globals.outputln("%s (storage)"%dirname) else: globals.outputln("%s (stream, size: %d bytes)"%(dirname, byteLen)) globals.outputln("-"*globals.OutputWidth) def __parseFile (self): file = open(self.filepath, 'rb') self.strmData = xlsstream.StreamData() self.strm = xlsstream.XLStream(file.read(), self.params, self.strmData) file.close() def dumpXML (self): self.__parseFile() dirs = self.strm.getDirectoryEntries() docroot = node.Root() root = docroot.appendElement('xls-dump') for d in dirs: if d.Name != b"Workbook": # for now, we only dump the Workbook directory stream. continue dirstrm = self.strm.getDirectoryStream(d) data = self.__readSubStreamXML(dirstrm) self.__dumpDataAsXML(data, root) node.prettyPrint(globals.utfwriter(), docroot, utf8 = self.params.utf8) def dumpCanonicalXML (self): self.__parseFile() docroot = node.Root() root = docroot.appendElement('xls-dump') dirEntries = self.strm.getDirectoryEntries() for entry in dirEntries: dirname = entry.Name if dirname != b"Workbook": # for now, we only dump the Workbook directory stream. continue dirstrm = self.strm.getDirectoryStream(entry) wbmodel = self.__buildWorkbookModel(dirstrm) wbmodel.encrypted = self.strmData.encrypted root.appendChild(wbmodel.createDOM()) node.prettyPrint(globals.utfwriter(), docroot, utf8 = self.params.utf8) def dump (self): self.__parseFile() self.strm.printStreamInfo() self.strm.printHeader() self.strm.printMSAT() self.strm.printSAT() self.strm.printSSAT() self.strm.printDirectory() dirEntries = self.strm.getDirectoryEntries() for entry in dirEntries: dirname = entry.Name if len(dirname) == 0: continue dirstrm = self.strm.getDirectoryStream(entry) self.__printDirHeader(entry, len(dirstrm.bytes)) if entry.isStorage(): continue elif dirname == b"Workbook": success = True while success: success = self.__readSubStream(dirstrm) elif dirname == b"Revision Log": dirstrm.type = xlsstream.DirType.RevisionLog self.__readSubStream(dirstrm) elif dirname == b"EncryptionInfo": globals.dumpBytes(dirstrm.bytes, 512) globals.outputln("-"*globals.OutputWidth) info = msocrypto.EncryptionInfo(dirstrm.bytes) info.read() info.output() elif self.strmData.isPivotCacheStream(dirname): dirstrm.type = xlsstream.DirType.PivotTableCache self.__readSubStream(dirstrm) elif isOleStream(dirname): self.__readOleStream(dirstrm) elif isCompObjStream(dirname): self.__readCompObjStream(dirstrm) else: globals.dumpBytes(dirstrm.bytes, 512) def __readSubStream (self, strm): try: # read bytes from BOF to EOF. header = 0x0000 while header != 0x000A: header = strm.readRecord() return True except xlsstream.EndOfStream: return False def __readOleStream (self, dirstrm): strm = olestream.OLEStream(dirstrm.bytes) strm.read() def __readCompObjStream (self, dirstrm): try: strm = olestream.CompObjStream(dirstrm.bytes) strm.read() except olestream.CompObjStreamError: globals.error("failed to parse CompObj stream.\n") def __dumpDataAsXML(self, data, root): if isinstance(data, tuple): newRoot = root.appendElement(data[0]) if isinstance(data[1], dict): # attrs for key,val in data[1].iteritems(): newRoot.setAttr(key, val) if len(data) > 2: # data has a list of children self.__dumpDataAsXML(data[2], newRoot) else: self.__dumpDataAsXML(data[1], newRoot) elif isinstance(data, list): for x in data: self.__dumpDataAsXML(x, root) else: pass # we're skipping all unknown elems def __readSubStreamXML (self, strm): handlers = [] try: while True: handler = strm.getNextRecordHandler() handlers.append(handler) except xlsstream.EndOfStream: pass parser = xlsparser.XlsParser(handlers) return parser.dumpData() def __buildWorkbookModel (self, strm): model = xlsmodel.Workbook() try: while True: strm.fillModel(model) except xlsstream.EndOfStream: pass return model def main (): parser = optparse.OptionParser() parser.add_option("-d", "--debug", action="store_true", dest="debug", default=False, help="Turn on debug mode") parser.add_option("--show-sector-chain", action="store_true", dest="show_sector_chain", default=False, help="Show sector chain information at the start of the output.") parser.add_option("--show-stream-pos", action="store_true", dest="show_stream_pos", default=False, help="Show the position of each record relative to the stream.") parser.add_option("--dump-mode", dest="dump_mode", default="flat", metavar="MODE", help="Specify the dump mode. Possible values are: 'flat', 'xml', or 'canonical-xml'. The default value is 'flat'.") parser.add_option("--catch", action="store_true", dest="catch_exceptions", default=False, help="Catch exceptions and try to continue.") parser.add_option("--utf-8", action="store_true", dest="utf8", default=False, help="Output strings as UTF-8.") options, args = parser.parse_args() params = globals.params params.debug = options.debug params.showSectorChain = options.show_sector_chain params.showStreamPos = options.show_stream_pos params.catchExceptions = options.catch_exceptions params.utf8 = options.utf8 if len(args) < 1: globals.error("takes at least one argument\n") parser.print_help() sys.exit(1) dumper = XLDumper(args[0], params) if options.dump_mode == 'flat': dumper.dump() elif options.dump_mode == 'xml': dumper.dumpXML() elif options.dump_mode == 'canonical-xml' or options.dump_mode == 'cxml': try: dumper.dumpCanonicalXML() except Exception as err: if globals.params.catchExceptions: traceback.print_exc() else: raise globals.error("Dump failed") sys.exit(1) else: error("unknown dump mode: '%s'\n"%options.dump_mode) parser.print_help() sys.exit(1) if __name__ == '__main__': main() # vim:set filetype=python shiftwidth=4 softtabstop=4 expandtab: recoll-1.36.1/filters/okular-note.xsl0000755000175000017500000000150714410615043014447 00000000000000 Okular notes about: <xsl:value-of select="/documentInfo/@url" />

recoll-1.36.1/filters/rclkwd0000755000175000017500000001264214444307651012704 00000000000000#!/bin/sh # @(#$Id: rclkwd,v 1.2 2008-10-08 08:27:34 dockes Exp $ (C) 2004 J.F.Dockes # Parts taken from Estraier: #================================================================ # Estraier: a personal full-text search system # Copyright (C) 2003-2004 Mikio Hirabayashi #================================================================ #================================================================ # Extract text from a kword file #================================================================ # set variables LANG=C ; export LANG LC_ALL=C ; export LC_ALL progname="rclkwd" filetype=kword #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE checkcmds unzip gunzip tar xsltproc # We need a temporary directory if test z"$RECOLL_TMPDIR" != z; then ttdir=$RECOLL_TMPDIR elif test z"$TMPDIR" != z ; then ttdir=$TMPDIR else ttdir=/tmp fi tmpdir=$ttdir/rclkwd_tmp$$ mkdir $tmpdir || exit 1 mkdir $tmpdir/rclkwdtmp || exit 1 cleanup() { # Note that we're using a constant part (rclkwdtmp), that hopefully # guarantees that we can't do big mistakes here. rm -rf $tmpdir/rclkwdtmp rmdir $tmpdir } trap cleanup EXIT HUP QUIT INT TERM # Old kwd files are gzip/tar archibes. Newer ones are zip archives. if file "$infile" | grep -qi gzip ; then # Unzip the input file and change to the unzipped directory gunzip < "$infile" | (cd $tmpdir/rclkwdtmp;tar xf -) else # Unzip the input file and change to the unzipped directory unzip -q -d $tmpdir/rclkwdtmp "$infile" fi cd $tmpdir/rclkwdtmp metafile=documentinfo.xml contentfile=maindoc.xml echo ' ' if test -f $metafile ; then xsltproc --nonet --novalid - $metafile < author abstract keywords <xsl:value-of select="."/> EOF fi echo '' xsltproc --nonet --novalid - $contentfile <

EOF echo '' cd / # exit normally exit 0 recoll-1.36.1/filters/rclexecm.py0000644000175000017500000003372414410615043013636 00000000000000################################# # Copyright (C) 2014-2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################################## ## Recoll multifilter communication module and utilities # # All data is binary. This is important for Python3 # All parameter names are converted to and processed as str/unicode import sys import os import tempfile import shutil import getopt import rclconfig import cmdtalk _g_mswindows = (sys.platform == "win32") _g_execdir = os.path.dirname(sys.argv[0]) _g_config = rclconfig.RclConfig() _g_debugfile = _g_config.getConfParam("filterdebuglog") _g_errfout = None def logmsg(msg): global _g_debugfile, _g_errfout if _g_debugfile and not _g_errfout: try: _g_errfout = open(_g_debugfile, "a") except: pass if _g_errfout: print("%s" % msg, file=_g_errfout) elif not _g_mswindows: print("%s" % msg, file=sys.stderr) # Convert to bytes if not already such. def makebytes(data): if type(data) == type(u''): return data.encode("UTF-8") return data # Possibly decode binary file name for use as subprocess argument, # depending on platform. def subprocfile(fn): # On Windows Python 3 the list2cmdline() method in subprocess assumes that all args are str, and # we receive file names as UTF-8. So we need to convert. # On Unix all list elements get converted to bytes in the C _posixsubprocess module, nothing to # do. if _g_mswindows and type(fn) != type(''): return fn.decode('UTF-8') else: return fn # Check for truthness of rclconfig value. def configparamtrue(value): if not value: return False try: ivalue = int(value) return True if ivalue else False except: return True if value[0] in 'tT' else False # Escape special characters in plain text for inclusion in HTML doc. # Note: tried replacing this with a multiple replacer according to # http://stackoverflow.com/a/15221068, which was **10 times** slower def htmlescape(txt): # & must stay first (it somehow had managed to skip # after the next replace, with rather interesting results) try: txt = txt.replace(b'&', b'&').replace(b'<', b'<').\ replace(b'>', b'>').replace(b'"', b'"') except: txt = txt.replace("&", "&").replace("<", "<").\ replace(">", ">").replace("\"", """) return txt ############################################ # RclExecM implements the communication protocol with the recollindex # process. It calls the object specific of the document type to # actually get the data. class RclExecM(cmdtalk.CmdTalk): noteof = 0 eofnext = 1 eofnow = 2 noerror = 0 subdocerror = 1 fileerror = 2 def __init__(self): self.mimetype = b"" self.fields = {} try: self.maxmembersize = int(os.environ["RECOLL_FILTER_MAXMEMBERKB"]) except: self.maxmembersize = 50 * 1024 self.maxmembersize = self.maxmembersize * 1024 # Tell cmdtalk where to log self.debugfile = _g_config.getConfParam("filterdebuglog") # Some of our params are binary, cmdtalk should not decode them self.nodecodeinput = True super().__init__() def rclog(self, s, doexit = 0, exitvalue = 1): # On windows, and I think that it changed quite recently (Qt # change?), we get stdout as stderr?? So don't write at all if # output not a file until this mystery is solved if self.debugfile or sys.platform != "win32": super().log(s, doexit, exitvalue) # Our worker sometimes knows the mime types of the data it sends def setmimetype(self, mt): self.mimetype = makebytes(mt) def setfield(self, nm, value): self.fields[nm] = value # Send answer: document, ipath, possible eof. def answer(self, docdata, ipath, iseof = noteof, iserror = noerror): if iserror != RclExecM.fileerror and iseof != RclExecM.eofnow: self.fields["Document"] = docdata if len(ipath): self.fields["Ipath"] = ipath if len(self.mimetype): self.fields["Mimetype"] = self.mimetype # If we're at the end of the contents, say so if iseof == RclExecM.eofnow: self.fields["Eofnow"] = b'' elif iseof == RclExecM.eofnext: self.fields["Eofnext"] = b'' if iserror == RclExecM.subdocerror: self.fields["Subdocerror"] = b'' elif iserror == RclExecM.fileerror: self.fields["Fileerror"] = b'' super().answer(self.fields) self.fields = {} def processmessage(self, processor, params): # We must have a filename entry (even empty). Else exit if "filename" not in params: print("%s" % params, file=sys.stderr) self.rclog("no filename ??", 1, 1) if len(params["filename"]) != 0: try: if not processor.openfile(params): self.answer("", "", iserror = RclExecM.fileerror) return except Exception as err: self.rclog("processmessage: openfile raised: [%s]" % err) self.answer("", "", iserror = RclExecM.fileerror) return # If we have an ipath, that's what we look for, else ask for next entry ipath = "" eof = True self.mimetype = "" try: if "ipath" in params and len(params["ipath"]): ok, data, ipath, eof = processor.getipath(params) else: ok, data, ipath, eof = processor.getnext(params) except Exception as err: self.rclog("getipath/next: exception: %s" %err) self.answer("", "", eof, RclExecM.fileerror) return #self.rclog("processmessage: ok %s eof %s ipath %s"%(ok, eof, ipath)) if ok: self.answer(data, ipath, eof) else: self.answer("", "", eof, RclExecM.subdocerror) # Main routine: loop on messages from our master def mainloop(self, processor): while 1: params = dict() # Read at most 10 parameters (normally 1 or 2), stop at empty line # End of message is signalled by empty paramname for i in range(10): paramname, paramdata = self.readparam() if paramname == "": break params[paramname] = paramdata # Got message, act on it self.processmessage(processor, params) # Helper routine to test for program accessibility # Note that this works a bit differently from Linux 'which', which # won't search the PATH if there is a path part in the program name, # even if not absolute (e.g. will just try subdir/cmd in current # dir). We will find such a command if it exists in a matching subpath # of any PATH element. # This is very useful esp. on Windows so that we can have several bin # filter directories under filters (to avoid dll clashes). The # corresponding c++ routine in recoll execcmd works the same. def which(program): def is_exe(fpath): return os.path.exists(fpath) and os.access(fpath, os.X_OK) def ext_candidates(fpath): yield fpath for ext in os.environ.get("PATHEXT", "").split(os.pathsep): yield fpath + ext def path_candidates(): yield os.path.dirname(sys.argv[0]) rclpath = _g_config.getConfParam("recollhelperpath") if rclpath: for path in rclpath.split(os.pathsep): yield path for path in os.environ["PATH"].split(os.pathsep): yield path if os.path.isabs(program): if is_exe(program): return program else: for path in path_candidates(): exe_file = os.path.join(path, program) for candidate in ext_candidates(exe_file): if is_exe(candidate): return candidate return None # Execute Python script. cmd is a list with the script name as first elt. def execPythonScript(icmd): import subprocess cmd = list(icmd) if _g_mswindows: if not os.path.isabs(cmd[0]): cmd[0] = os.path.join(_g_execdir, cmd[0]) cmd = [sys.executable] + cmd return subprocess.check_output(cmd) # Temp dir helper class SafeTmpDir: def __init__(self, tag, em=None): self.tag = tag self.em = em self.toptmp = None self.tmpdir = None def __del__(self): if self.toptmp: try: if self.tmpdir: shutil.rmtree(self.tmpdir, True) os.rmdir(self.toptmp) except Exception as err: if self.em: self.em.rclog("delete dir failed for " + self.toptmp) def vacuumdir(self): if self.tmpdir: for fn in os.listdir(self.tmpdir): path = os.path.join(self.tmpdir, fn) if os.path.isfile(path): os.unlink(path) return True def getpath(self): if not self.tmpdir: envrcltmp = os.getenv('RECOLL_TMPDIR') if envrcltmp: self.toptmp = tempfile.mkdtemp(prefix='rcltmp', dir=envrcltmp) else: self.toptmp = tempfile.mkdtemp(prefix='rcltmp') self.tmpdir = os.path.join(self.toptmp, self.tag) os.makedirs(self.tmpdir) return self.tmpdir # Common main routine for all python execm filters: either run the # normal protocol engine or a local loop to test without recollindex def main(proto, extract): if len(sys.argv) == 1: proto.mainloop(extract) # mainloop does not return. Just in case sys.exit(1) # Not running the main loop: either acting as single filter (when called # from other filter for example), or debugging def usage(): print("Usage: rclexecm.py [-d] [-f] [-h] [-i ipath] [-s] ", file=sys.stderr) print(" rclexecm.py -w ", file=sys.stderr) sys.exit(1) actAsSingle = False debugDumpData = False debugDumpFields = False ipath = b"" args = sys.argv[1:] opts, args = getopt.getopt(args, "dfhi:sw:") for opt, arg in opts: if opt in ['-d']: debugDumpData = True elif opt in ['-f']: debugDumpFields = True elif opt in ['-h']: usage() elif opt in ['-i']: ipath = makebytes(arg) elif opt in ['-w']: ret = which(arg) if ret: print("%s" % ret) sys.exit(0) else: sys.exit(1) elif opt in ['-s']: actAsSingle = True else: print("unknown option %s\n"%opt, file=sys.stderr) usage() if len(args) != 1: usage() path = args[0] def mimetype_with_file(f): cmd = 'file -i "' + f + '"' fileout = os.popen(cmd).read() lst = fileout.split(':') mimetype = lst[len(lst)-1].strip() lst = mimetype.split(';') return makebytes(lst[0].strip()) def mimetype_with_xdg(f): cmd = 'xdg-mime query filetype "' + f + '"' return makebytes(os.popen(cmd).read().strip()) def debprint(out, s): if not actAsSingle: proto.breakwrite(out, makebytes(s+'\n')) params = {'filename' : makebytes(path)} # Some filters (e.g. rclaudio.py) need/get a MIME type from the indexer. # We make a half-assed attempt to emulate: mimetype = _g_config.mimeType(path) if not mimetype and not _g_mswindows: mimetype = mimetype_with_file(path) if mimetype: params['mimetype'] = mimetype if not extract.openfile(params): print("Open error", file=sys.stderr) sys.exit(1) ioout = sys.stdout.buffer if ipath != b"" or actAsSingle: params['ipath'] = ipath ok, data, ipath, eof = extract.getipath(params) if ok: debprint(ioout, "== Found entry for ipath %s (mimetype [%s]):" % \ (ipath, proto.mimetype.decode('cp1252'))) bdata = makebytes(data) if debugDumpData or actAsSingle: proto.breakwrite(ioout, bdata) ioout.write(b'\n') sys.exit(0) else: print("Got error, eof %d"%eof, file=sys.stderr) sys.exit(1) ecnt = 0 while 1: ok, data, ipath, eof = extract.getnext(params) if ok: ecnt = ecnt + 1 bdata = makebytes(data) debprint(ioout, "== Entry %d dlen %d ipath %s (mimetype [%s]):" % \ (ecnt, len(data), ipath, proto.mimetype.decode('cp1252'))) if debugDumpFields: for k,v in proto.fields.items(): debprint(ioout, " %s -> %s" % (k,v)) proto.fields = {} if debugDumpData: proto.breakwrite(ioout, bdata) ioout.write(b'\n') if eof != RclExecM.noteof: sys.exit(0) else: print("Not ok, eof %d" % eof, file=sys.stderr) sys.exit(1) # Not sure this makes sense, but going on looping certainly does not if actAsSingle: sys.exit(0) recoll-1.36.1/filters/rclgenxslt.py0000755000175000017500000000254514410615043014221 00000000000000#!/usr/bin/env python3 # Copyright (C) 2018 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ###################################### # Base class for simple (one stylesheet) xslt-based handlers import sys import rclxslt import gzip from rclbasehandler import RclBaseHandler class XSLTExtractor(RclBaseHandler): def __init__(self, em, stylesheet, gzip=False): super(XSLTExtractor, self).__init__(em) self.stylesheet = stylesheet self.dogz = gzip def html_text(self, fn): if self.dogz: data = gzip.open(fn, 'rb').read() else: data = open(fn, 'rb').read() return rclxslt.apply_sheet_data(self.stylesheet, data) recoll-1.36.1/filters/rcllatinclass.py0000755000175000017500000001063714410615043014673 00000000000000#!/usr/bin/env python3 """Try to guess a text's language and character set by checking how it matches lists of common words. This is not a primary method of detection because it's slow and unreliable, but it may be a help in discrimating, for example, before european languages using relatively close variations of iso-8859. This is used in association with a zip file containing a number of stopwords list: rcllatinstops.zip As a note, I am looking for a good iso-8859-7 stop words list for greek, the only ones I found were utf-8 and there are errors when transcoding to iso-8859-7. I guess that there is something about Greek accents that I don't know and would enable fixing this (some kind of simplification allowing transliteration from utf-8 to iso-8859-7). An example of difficulty is the small letter epsilon with dasia (in unicode but not iso). Can this be replaced by either epsilon or epsilon with acute accent ? """ import sys import glob import os import os.path from zipfile import ZipFile class European8859TextClassifier: def __init__(self, langzip=""): """langzip contains text files. Each text file is named like lang_code.txt (ie: french_cp1252.txt) and contains an encoded stop word list for the language""" if langzip == "": langzip = os.path.join(os.path.dirname(__file__), 'rcllatinstops.zip') self.readlanguages(langzip) # Table to translate from punctuation to spaces self.punct = b'''0123456789<>/*?[].@+-,#_$%&={};.,:!"''' + b"'\n\r" spaces = len(self.punct) * b' ' self.spacetable = bytes.maketrans(self.punct, spaces) def readlanguages(self, langzip): """Extract the stop words lists from the zip file. We build a merge dictionary from the lists. The keys are the words from all the files. The values are a list of the (lang,code) origin(s) for the each word. """ zip = ZipFile(langzip) langfiles = zip.namelist() self.allwords = {} for fn in langfiles: langcode = os.path.basename(fn) langcode = os.path.splitext(langcode)[0] (lang,code) = langcode.split('_') text = zip.read(fn) words = text.split() for word in words: if word in self.allwords: self.allwords[word].append((lang, code)) else: self.allwords[word] = [(lang, code)] def classify(self, rawtext): # Note: we can't use an re-based method to split the data because it # should be considered binary, not text. # Limit to reasonable size. if len(rawtext) > 10000: i = rawtext.find(b' ', 9000) if i == -1: i = 9000 rawtext = rawtext[0:i] # Remove punctuation rawtext = rawtext.translate(self.spacetable) # Make of list of all text words, order it by frequency, we only # use the ntest most frequent words. ntest = 20 words = rawtext.split() dict = {} for w in words: dict[w] = dict.get(w, 0) + 1 lfreq = [a[0] for a in sorted(dict.items(), \ key=lambda entry: entry[1], reverse=True)[0:ntest]] #print(lfreq) # Build a dict (lang,code)->matchcount langstats = {} for w in lfreq: lcl = self.allwords.get(w, []) for lc in lcl: langstats[lc] = langstats.get(lc, 0) + 1 # Get a list of (lang,code) sorted by match count lcfreq = sorted(langstats.items(), \ key=lambda entry: entry[1], reverse=True) #print(lcfreq[0:3]) if len(lcfreq) != 0: lc,maxcount = lcfreq[0] maxlang = lc[0] maxcode = lc[1] else: maxcount = 0 # If the match is too bad, default to most common. Maybe we should # generate an error instead, but the caller can look at the count # anyway. if maxcount == 0: maxlang,maxcode = ('english', 'cp1252') return (maxlang, maxcode, maxcount) if __name__ == "__main__": f = open(sys.argv[1], "rb") rawtext = f.read() f.close() classifier = European8859TextClassifier() lang,code,count = classifier.classify(rawtext) if count > 0: print("%s %s %d" % (code, lang, count)) else: print("UNKNOWN UNKNOWN 0") recoll-1.36.1/filters/msodump.zip0000644000175000017500000035734114410615043013676 00000000000000PKzKO~u1{Imsodumper/globals.pyUT ]]ux  m]'I1h;- Kҳ@$$k20a\;Fe:Dosh: T-y$P30C2>@=J&Ad^<ԼH$C6$sQBZP*M0YE&S*W"ȶրהIԷM C+AmW O{7 ULrz5O"{Dk.璗0_U*HH:mDR!?(J" N%*܆iiykOq{A9L*V]< xU`U; /LvsON N]ɾ\ Kla≵yi YM6pʌ8SUvWwrz Em9.1`آRuJtdAҍÂoI$x@y@DF!L&&L#b5 G^v6OBuRF}&L'#'i%QN@ ^̢`ۆDA5@J(76KbA|lr+坚.н`ew]5_&wuoTqб_ӻ[CfV” fII4 d.8Oga</~ |WvLn,)ɀOF'R \p\Cܥ'=xVh[-mpӮC%N`0W(?2t`| 19,b`ԣoUZa8@h\vG!&2_ARCb|jYOYc $B=M!u^ElYSچ=nCXP +0ϰ^ iU|Զ.#,iZI!, XU ?$GyjլI1_ 4\@W96 O`j)P]!/քT,/ɰKK8`fD.XkUF7@NHׁ8)Wͱo-;yl|@I&J!'[`up'uaVK@w7w-F-A6&%q&L6gaqOg?\腰,5 FW 9Z$]`iiU`996iPOa43\MXgWDwߜ% PxiTyk!1. ,j4f 0A45SYKE .`F"<}$C47TLe3le\NF /ULÖcq{[wnWHV@bR&I-(8Y_уTX܏UmNA^EB- 0E9@PaJ"\9,N-M m!2Ĵ6(ONfZ%ͼҸ\lH0rM:zQ vd3pqp"rej_>2=w~l, j ]&~)~k)uI1ܧOoa| b_$/nLy񡘠qҧp|R sѰɟb?c&%3/e+ʣZ%RԊmO|G|e|a[|xO2P6{\ 녤;HS. 1)')\zD I*NtPuqܜo&:̤iбMS3 }N9S= 2K˅VrlX:-Ӆ wNwPJv: :NP0Е؄'`&4@1<]} fd6@y"+fKT| yHsH;9S)唸g4DewPX I((N`ީ2A:aV$ Q& QU,n;[Sra7 ݞ.QX>*@CdeN(r ̴fil~@x8X]BRqEoWTWX̓px+b+ >4iމ>{֔H?d>ھ>Y[S1\+9 w}kfw3';qUi;PX̶ +=wg }o~fIIz05qkj)9j=#p [_L8dGDЋ ֆ}d* l&c}*byǹ.zR%ͪmv[Kr g59`^wF@r&+Hˌg@DoMFG)N, W.16_a`*pVHLW+RDH# Ǒ.1ytl1]NucP^ -1"1 Z$\ۃ2 tԽ*x t!~UQ$&iSq~~کz$%x-X{q?HqբRj&cr\؊ڍ}Qna]h[>En`[ư#oLNWKa=hvmK?1<#<1We\|3!?M$urBbФPy¿vqdŇ;)!eDs_L՝ c-wԫllAI ?EBL9k&߇PZPi1'^܃HE; =Rg&22ꘛ$TEzb801\i1;LbY9^p c*O})͘RuuSECx]O%a̤RW7]l~Wɣg S->LWV8J1̉@.H#.dvP$l :kÿہhzF,_ %:B $(Z85#l7J 7ŵ}5ߥ Q6}+JIg5d=}}SB#R`݃NHJ n r~i}[WM&X1uOtHm]jC!v[_N,?8LaslhZt6r] lu'?ci'1n3f:+N@\mU#3vZB3P|Ox*Alaz6sO+\1 K/ O/%ެlYئ[kkQ)z~7w[1jmUlgh,΢"[l>Q"b{@q jQ:N A'Ǧ-hN7T·ycUHN+~U+=9 w~ZN՘ #sșVT/#+<*%@{/{//rr 5/"+O(ւ͏[o杠Lyv tg%=ncz 2*B)q9y$[υQ51ɧ_ ]Z[GuŽBO {ǃnfTU,%Z-#idӪ' ij$Jwr ߰M_ۖYC%<42 /tqy9D#0DxW@o c\ݡ}#Th R+ WqqG0o?}-x:5u[Ȧ 4kZD٣ؙ#ae)3E&,v Ŗ]c#f'|9 lDrd |7PKkuzN,U|msodumper/ole.pyUT y,\]ux =s۶Sx,Ų"ˮqĹfn3{wo<J,6GRqN.')Iۻטb,*ߥI5,U_ Xnk{k]Ғ]b+(eob['rV8x(Y>t>ٻxNy:YӀ{;e19KKҲ* K%Ps |&qqOY\YU-OA^>OhȴlJU,],bE-Rޗjo8.ezmVY X{ϧ ;)yO+ `c%u>Y-`={g) )'|ɳ|:N$`N yL_![[y\}WU^ķ{y—Ug-XAE‹hsV՜eI ~ʆez+s/,OqT瓏WK.^\p`CMҀїiPQ /I42!Q#aݜN/"++,x5) ֝ l<R(8?#B)`^jR)TtNoX W"ӣ30sJIVs>YZ}%Oԓe\ t@Е]t!'P1[v ̘*JV )-AFjɨK>"\4\ tfB/8Q˳Od{˶2Yd}PoҢ$Q3`q&dlڷ` );>rPŰCC pDd)g'OZlt4&$N޾V-"N.'UHX3%8X| ܘ$k-])4nx'޺ͮR?oYY>{I Kg"$_` F:;@uzFOOf,/f/a;POwwTW J+<3h*z]DLX6f#]77%WV%Œ׌ȧT P_+o52bnst(h_!dr!rj]jLN҃ar֫UҪh\66CU^svF'J%I4-_+t`nf8H)`-MjxU ", + ).]-L@QV/^n=> ЪYv: oh2u&붹vڑM5i A^p d9[jVR ܕi©3Z# Aia1i>68m?~G/,z,N=~F 侉`NUiYlkφ= :VQϐٸ0>HR88x,CI?GBhƏp!~,g|S:|~q&b1e܄BI4:dt!f$4GGaPKю:k GBHDÛG\Nat8ଧ`'S7矗T_l6kb2EG7ǷZT{mtw^Pg#kiDߎ F=.r ׄ<\|+Tوy|$z5 6a>Eώm=Ar 50eV͝4`RA 2jJɓf 쮸(bt[-ќA3L vOf] v(sV3  hK;#a'B1ɑm$- x @<kDA\!­4&dv1HMAMjImth^sv]Jv ֿ("K5byx\Z$Ł*| l]+npSR|EsOtܠ xh(CPvš«CWZv{R.5 s6 WJ KwI5$U8;P6:Bm+ն~?[ՠ4yhdʽV@u{j,Jpwk1M g$ȝVo_B iK=ڨ]_U>*|Jq ivZB >*dϬyyjO }{wThPj V 90DkKͰHdX@/=,:lt:B1tP ! 2_䤥@EX*[v,*5 ISi-"2%ē }ZH9bUT$_ ,-Zǭ|'rX'UAA-z쇲6dBh@U!#ډ Q|ИHѸ9okzP>hO p 6mۢ8 Bm'NqP {T12۸72s]ƻ_)H>&oGlR@Yis?pGv-?#-?/UlAji[p:YW\v]{yfw\fn^:jDC=lD=\dւ F4FCl֖6$_eUi~q=;ڌ'`| Bډ>ϧD.F=m / m[Oش0zqΩ*m鷋v*dL*GTrp0 ʑzףp{ˡ0JR:Q׊Cg&xAOoc-$w5"f6z~ڶķВaCҵ@ձ-PF%d`w+KBy "ma<$FN!7fTO@bq#~ *wwjv{YnA"u)wYim :D0#wiܡ K0xU`$&,Dl]|dtQX /}ܒ #TaEXI" էz|GdVzf&.FΠHݮX"& TaL(cmMm7ncgYUqc[Q(FOY.0tW&bT![जQM2l-_#3GT9'_JQ+| =d%&S? Bs'_/͑ 2))ꉙN#tn┄>7 L5x=jă .(Wk,5Ls4 zD76&81K ?PGHr?XۗFELr[|uo>,=5207R% 1.[rcA 8\wa=j֢J#\21'b1]qWž6Z%&ƓY4c~jFgG@YD%̊eZ`N0NK^'Íaoֽ`'h([*k羕g 1!ZV+yyO*5 ;j7UB青SOإD:y[j+Bano~owxJ\][~+ڔ-{u|u-0q◇&|]6&A2ُ3{Z,Q.dCzuK^B)ZK&g\y\}g' f.gh'yby3Rp evmHVjϫbM7H.qN7g;%>STA+ =V\Xy1q`qjTO6spahG*|972zt ߬k{C+:dC *R=aonc6AxZ^Pn: >iD á`c{f-q )Y tn܏SVGF>*`6${V`pT B*:ت_9j0zNydgSZH o֎U)h֘FXv-.YEAm,w0leى>gt0*#Գ#AJWN[w.Xe_pi{}ζC=O* ɴ{a_qs܃2~@} AT̪P~}M"bxlVkt $AyL3GPK _O5&Mh#֢jΓBY ݂cߠxeQ$0]\T&sA%tS%~tF[,lzvfy֐2lbJdZeZ\{ݟBaY#Z b Z qܠrP;W%]zO R"-OQ+z.E]䩌Su:iS pP4e ~-tX$)qt@H} Q)El 6lrk:h3s(5揙uUpacK:W оrhsդ *+f(@VJdmGϕL&U7TXKY`Ƣ=t77hI'![K lh[R<OL\0թ‚eI|0 /?Ed$# ur 0LWƃ$J1D[n 'fj&|[]YX^^'$jh[#LTmCM͋))3:Ė@,%!ث׈@Gp36 Z6ZBU#r~KapԹDVbIOش}!N` {Hg1Tpoō-{wCgPꞲgrQ{Ka3k͒ƙ8fGx]V)b-n]RR8ƞ£8S(o)87@ -p%>Oa31ty_@tZ/1i*_= PKːeLjnmsodumper/olestream.pyUT  yZ]ux YOF=k"4dcT(hH0PӦB}Nw Wvcf ޝ;{iO{ԕ1\d_^]2F}E:p5&<G8 '< ߉x8$9' Im>u: w}}830.BFtK=s$Q<#<fҦRphlhHX|jr|[1/&2"vpE<JYw !Ҥ[Rkst-%PF XLd^=m 4 !o-kr:0>ځ r[Od7nw Q)nJa! vGg8}D{+޾75\E1zgIwкkK,tOM4=8ԣNwܮq~E ޓC\a^}:W5tƈ{ʤJ \,^c5zWD,>!RDY>|Y:( aQ<<囼J& E:(FEF'n3]҃\o0@DjXaoة65 EpIЎ_7$Z}En'ω<#23Q"n"`G$!xJ.EH̗D "WHS"!%<>Py}#7{dG-%r䖦$< g,x$.2ӈEPIVRG/^u{"Y/{БE"dP(%|DFK{Pi@s* E%i2W4e  %O_y寿q…g(~.J%|Now~'Y 2at}%8a Sn}kvMvfȱfm?7/UjrVٱfiQ5w("dm #lf.=1FL5d,(bP `|m2˹N05h5(\\C_TC7<=[ǖ ,AN_P@x~rR^$g0-gUΒrl )ms@CEoiZsМ*yLk~N1RBf`_hXslw,}Ză0e`xZQʗTGn԰k?416pU!}r|/[>&!U>!~.%.ۘ5rrHY6RtgV4]Pnb7@7=\̮|KNKm'LuBX6ƻNM3)&.jZMa #nᶇ9Yd5IX0^6fo] ǀ0p vG\dx[s Lmn`˳U#|9hX$O 3*1GPkU\ޫ ?Oӧ=?~7wDOG%RUKZn;V:@HA HgƖhk=O!E6LVRi!_*(q&˵y l֖WUGذHr[ ELeb}^)˗3\B͐7Cq)B j%DsN ]spwQ?Rc}+PF$nU"0IcIgzV|*fҘXyvN b.*-֩ƹDFDN_qѹC BTX…dC"fh0ah;ԙaZ&fRY$/@pLRy1q}>g8N- 48)H&sR5i|m Kajhࠖ}"E5OksEEM5RGź0G\:*#\"> m_s ~om ;vm疺c[`kK&[r{[vF΀زmg|ގu{-tMߓ{)?/մ$T ȎQc[ZXbN*:}@jsrR ڦ*9 4#C@:-,9L)%I3 1%[YNzC(Hj<޴[iD*kBa} tNKwд$!G^ZiL[ }F\`6=%GA# 7DGfWoۋ?lѥO9>KVS9 VUW n"XB}oػh!BT?ED]њIqdDeBf8XMB}wjwySA\d{g)t=<`ouBީPՃM?~aHܰdY 6ptE-Rщ5<_tL^; }k4Pup+ֽ˜ۼGd䚝<Eʤ]֍[m]4B  T[.ʰTKsr24`xh hí9j?jh+B_S/cZtm}cf^EJ_'@be(Ws%V" I[Rz.uowpj{!" xSFWӨEu߇57e~~.W4FRSU?/opgƉO6ϺP\-D[Va-:U|o\@nKFWڊ:dc= Hw,[qqEKQbƞzKBT{%bQ/oڴU61c]ۚRqȺ{&͚ 01Y fyjA ܵ$d~βع}i7?"i] .ۈ4q7U&B'THEԋmI+;m%\J*æ wXrw ?i^Z2WJ)Y/V9BD1OhXB5GwqXktW :Ag6P] 3_ x=jgixѯ5$)ómjFbq>d*5aBOBj"Z΍HAfHU[8ݻ.?kiŭYmKZ箴r0*ɡ-WYewyPLϖm?=2m*e_RY^%Ι*x}v0lP=gxa1FTh=7͏AUBX)K),bF8zPd;Ξ-ҡC{ڛ oM`1r 6PHg5{7k$B/{ڵ'UNH3C;$A+YUU*8֥J)Nb@Jqa{ŀbyHet9#A÷Վ(Q++z+_'/+aB!.qg@֭xs!lP7 axn1.TAў#yϑ0+N{2[ߐWʕ4dLfdR'YL]4|ϯH+~sDZ=3?Tʻ5P O,L$I;{0EQĀRjKomnW_qqeK9'g/qb_˙Lf65EIcԒٺxhDgVm$6Fht7 P*-VJ ;o|/(U-}@VyKݼ3-{zF1 -۪*qGikƀy 2؁ߴ4'CY\BƧR NT2.yMgxE1m^E~wR.rp3(N|rUcbJUs+28lqT]̼3V֮?{0fs>j]v6?YUwרy dOx55_fS6ZP؞G`yuag,Z$ cJV_G[Mpzސ4QkB[m& i!N9I3iN6 Jxg%=6#btUfzj[6 dX JJ\5Ppc Hμ\Gԉ<cF9&c;>- ޼gɮq x];,|k2y0ZYF&ζɐczˤ2Vi Kw VNtr u߅ K,d u 6|7NY?-(άN$(nvKW=$VǷv&MZbӥofQ{Y};/S rC%/>C;i|JҾ@2oZ~(k$.~]}j_f4RU;kvYS׻(ܬ?4a;GoU AP#B'e_@ woJL8?!q(Ⲱ_`Aۿ3[+euU Na:>G_6$m;ܹ<+ߤ+WW;7q㯨LN*乣,I+yxhx+Oxv-_tX߄6V9[q)>%(%*!x)ϛQV 跐0p5e4ApƏ2eȽZvߌՃ<{^g]m̘mƋ'yS;~^iWu߶^c|dN֪iMF?}̅w2{MRNFAu l{ױH6<ʕ?T$΄|#>q1'~"/Q 3s%Cy3Cz_lV1r'@G¼9_ vv0*EY:%y?wOg36׷ -A8 ܩt9L*M$Ml& v$/^Hq–!gdӀo&莇a~Y b^cP$,-V[he :pG탰# T^(;:Jhrsc g zbj+NCPݶME_s$iOp'0xVCoPXg'5`ac>1x~md?YB\i jM'w7P+Y 5Ǧ8pG{rZaZ@1 2 QЁ1t-5͡(.6G@L'PLf誨FH,h*8bfلH$"|drvLG?zp'j]ϸw wl'ӸP@W2z&:|c߅"Xkٳ\H|YxB";{coĶA JB|_?&BO-!L;1l'ۅTqȜ5>p"D͜jr7?`'Y: \!dZHm^8>xώA%*³d2q .*8w>Fj(#%W1*ΐ0F&uW8soya!"26ȡtNՎhɒډsxFWNg@GObBj@԰%';3j ^bXl)!pшI*Ӏ,(&ԊG5 ΄Jte@YQ(,(>J5c@dz Z-6<| c߮Czhҵ`Kމ*GrS!K.=&ɂVxfA`in 44g1`,\' D} ȁ$ \rQ e^q)Ӧ y>ln\TIz@Dv}QsX : E.ۆ,<(MKuȶ?E}y oM4'!|AtEN v׶#^&#@lz8enϐǐ:T3ȺjUZŭdÏi`2ԔCJo#vj`Cu! U@bYrl=8LvC!|2I hufmU8|JG bJL}[Ea #pi8|P|\E]T2F ~}*a(P="7%laukR $5ċ& @. !QЩQ+(p8 v fk)XС&x Sg NO/ޮ/\gan)SXB)({=+CMX}J0Y1pwpVgg//^1 }le $9xVL?!wLXJx~}H LÕ `Ey6@oՂaeՂטp^T|tzpyryp}4UU~oʾ[I[t~>X%q(74, vb"G?`$q,q^Ō˔{+\\. r6r.z|!A&.5;rA.(gsag g[sC'xXK1}r̸'8Ȯu]H<u $}f67$Z҉~ Fsݽ rf22C-8;!W~Yhb᳇|:ß~TڨѺf:<;?ryC6v 5Sqd HX}j! ڌuqæ"hqxR:*+8jƀہ44W"6?q , Z±K OE%o@-{:*+И$9:4 0|gq|lp%Exa(*{DFNNHx x8+xO9X IÔ)Yx/M}4ĕl(luV|.媣kdSpvl[;+B}P r&)H6< Q8լ2zɂ[av`=yp XlES^ [ "c2kAYD¥Ԃly<s$2G,x\e-QEqpHe_dMV:X7ek@,{VjAY6n%eؕ[BܔR*ld쯐رF 7K6+p2Ao:J C%q3.5>0>Hi4 aH ۲ z's!v&^!G$qAFu4x[*(Y=0obXJ.s] ?a y"8بG\JrM1۩ vjkBFmh 'cHjlFCᙀ04JsSoui& |3A rh& ݶ3.ެؒ .ެؒۑ])18l?ĎHMIÖ;iȥPfbSøGK\"-g F-h- Ȟj cY1#<^Xk&pl6v ,8 0a5s^H.4ڎn1LiGYO@f18<'vpXeQXDQ"pYƨLnyip'~Ϥb'Ԏc3,Q"N+\XMGb %s̈YAl$!eٸMGb 1#FwdOoP" `¥?"6 a%bTF=^-6'I1 QRfUhz=Ip=@eBFc~ $i o|Q a)84*GH"0m| / N40XPtZQv*ڕ=B˱8u=)2knS =,iG:u4.PrN^A> g3-@Vc{4'lF`(2uZHM\4jYqA/ש1G5_vZ9P. Ժ%MZw% o[|X4xNNGX _ݺc?nQ=cuX=gZ(d|a?Z'HT*3}>$^XrgCrIyF@}lOn(B` y,fVAg;|_||/dXR9O?=?Vq-}# sv y>0 G:SP?DE8oIngbDHBksQT 9y*E3W4dާqU V/>= 1o_'*.S (TW>Lqe 8U:R@7zTzEx |yA>uPnwnu2lqԋ(ɉK~3/X}ˑ{>~Cbs2]zl0S0f?3l]+X/Kׂ97ev?2Shג)9e5l(&:$/0N;u RZOWjAQ;u)"qkb' aшߚaf޾:˃;)5P@mVơ,@Mt/?8oWi1wmKDF< `.EǑˊJ },18f#u]+m\D MƩ|P}=bw/ f >=fl,~COCb&T>=fPAE`aoO+xROE8GEtP6^ qQ #f~'ox!!w&0ƕ9r6*90@l>DoOgGrQvYoTbC% |>T<ݝnogowg+1@(]0OY*\>fڋ9Q< y)V '*s+wCb !~d tZ42BsyW$F(7WF`VaHqtS˝]LRDEγqZdDH ~ԭFƃ*(ET[E )9po{⊰l#A&<`;wo-.y?M Rj{r&L& .ҖxC<*HGPwoi/CC8]YGI1R|ֽ ej ܁T(iL TLO7ibθ5f*eMT}ɬǐP x@@ >03bCfHV){9 |x67vZQ ޚ;~珟*;j\ >oxgo7I+ ƶ a\ЋDlu'Z F} c D]ܩd -Cifєux)\K׭R"F(}~/R"XE< P,V'0!Ȭk[ 0UkN0wEӳGEjɒzM}F7LB t4oR^=\+Va4%j. p tcɜ)-.w;V]kwmAa7jPp#sQ_%NP7=JlAu7F"3 xo*@ƾ}k?~"hy';K~D| O +悄A;5O2>W1%\;ƒ# 09 b1?O'a$[Yϓrǀzu,s3oؚ!~v$Pt(2,Gf7Y.P!=\Q:r!G$s>.Ċb "}%I_9Իc;bIVNHt>,9l| r<jŻ$pp5w.EA1۟3a6`(AQo4yv zq%鏳 L)2xݸ0AbK&d+J?=3lXS-gcBE\]9h4A)fMiD[uR1;ݢ4v[5{ :/9dѼZ0gh/V?ϥr4fNxC$It@KN1in/,||˝rg7"`amLjA !+tns0N(:ġJN.*7'w"36B*r*7dVtLL1G 1)'}:E\[7O(.nKn`qIOŊ1-y.A}8U}D٪Vy~a;;JU} ~dMⶾֽA'JCnaXY42QIYjCcUQŞЈ Nl# G#" RD.[0e"_FӋgs)aBaǼŋŋ\Fx?_i &~ dG"UmWPDc[k ڣSq2g䲩[m:E*4AthY\2.eJBtW;\$aPm<C9o%.؀+m.\6h\Ac&2-֬q4]B͛Ɵ#5h|9Βz%#ȆND.2$k |c4Ny[cq ٢3m=ڬI{ާGLmv~m5J;-9Y1YȽ#"(%il;dUui.]gsv.{.=e__tlS~M'EϠ[Im /%mK$۱4JOyL svQ(]"}UO]D5itH{5jWx6gk6)nN%^qO?EzWn!e@[CzvҦ$sOgxǤf'KKƵX6?%2YhO8gΞƸ!Z5}Zjm j]s ~_YE7:Avi;X/6\2o_ci$S{"0Y{wN[yO~__IjK:WS=-g1;[҉HPX(=Y8ԗG:34XxnNg<)JU{ݥ S']?Q 0혽xkvNv{f*Z͖T]Bk'_kV}4Fl2y ʼn̂Fɮq4bΒ媸X˒zݚx)%「!squzwJֳvݳ"|c]#Pʭ}9LOy,z n>YGk gU; Ȟ=e h`l7F}.J3tERmfsն  'e/njDYxlm!vSqڧCxDLVܱχzw}BTlP]ⱄ^)9} e0t1lfƎj5lfiu Y8(H-ޮaE~kѢM;}הOև ?`eոou tBfyw2z'霪t6d`$ګgi\$i} #:@b(' 1 VQK5fuNj6 Rka$P\Z8UW4W+kv;Vu:4+' c)ӓ㴼\OmOkIRZZza]1pNn]|Hcu۪iz{,c*W+f"ErRq¿@8yE$y'myy #Dm|,O>/U{}E?x,֩u1M.y vQUk@> ZՖÒ7lI+|o.deoN$oxH8`P\-IN[[WG_lWJ cnO7❼lٓ-~W1r$0-֓dgim*ڧ]HWZ?~5r(m`T%a_k>.U(J#3ֆ0LӢlkj|h%ծk%9q%٤uܱ%87Hvh+F_-V d2ےՋ_q@7DEd% 9ՠG4C!P$/Z>^yG-[g>x tV8g/-z\ɔPKmdLԅ@msodumper/pptstream.pyUT =0Z]ux \mS#9_sG/11Ꮍ0`34{ ʶlk(W9T`bTR#f瓩|SfJ~GOx8as:#',ӘÇ:3Hjjd22NtF%P0I>t O_QiH&gQQ> |b3gEۤ>l ,ټ,O%Hp2gO4tN^_e e"INfaL)Sr|svp.Vn'Dbm0d&k2YY$)'i/iM}fɉXm%0Zd)%\|ZmYFn1plgtY73M`P#~l|{Ii!ɤc^V$/4$, d,Nr[Jy|d1㏏h"U 34\gj5%mI$&e(ici ił@*R+D@Rg H6)s&+|/> yؒ7%-Ԋ8H3~'CDx)b V_J,lʵZpNS@p?o Lӑ6p!D2hjF6)zL7eZ1Lm0tlyōD>ٟ_Lمd7")P);uOz+!HeY`/^0BN۰]C&mrAW)'qœ4%@LjLD6"m#V-)b@`WGlv}4W`Њ-il5·43)aC˵RձR5SpMU*w*W 37h}bӐzTyʡI >$Lf&%p⥧K0h=QB6[}$#Jf$fR5(fmxj?mׇâ+5x3șT3Ðߌ / B0R2gR?rieԜO<%|p.T6KeLL Ů 5UJCVOEI2y݈ouN3|7?Ҵ]VoXTᾘW;$1.F6t Lڍ52e%ΈH3}zE [ $ͫܡr^6S3Yټ!xQU>gT^M)a'Jk:wLٙf"O4?Ͼ+C[˻ ׾)1*~6Gni7A[B?R^(fܿ` ~%2ơb6F-f\|δKEE,Έw"I )؍CgC)C$IpBI5.%G2wڝ]-VEJ";e|+{r:2!דez̀b4}sj&bZ=4?sK L=eº"uƏ6ԇExv5$D&98. bs!uQ\p4me-OwZNC1ranPά7at}.٬VU"p3F[8B?!pD)4H *=2H(t Y3tEF3BWht$ͭ%k葚hGE<ՙhNSa!w`x1v`"u-l樂~QO6յ}:] h{&yɨ{h&M1($JP\%kP<ǫEנnkrO2m]ʖ0OEVkEg)=V'Lcӈ*!LQCPY;* Q*tvpuH'O0TBuA8eM@4%IKa4ۛGVkoq?MWM=R H آPnbuu]iO3 =@fu]O"tO;@(+FTNx&vuG]p/&1yyX8ۉP :nH:t@,k03wZ(fubb~^{ME9"HbWc}y%E. ?x5َo}qqa,]Ϯ"#ʡ(3hZge1m*55ULϠIo^%NG4" zB3` şXlnX`K5qi4HӟY%O&q)0j" &bH2_i8:-P56^#xLHDɗFH5 `wKh9: 8N,B'MB hUdaR1!c8 &V_?>ӐqD+g [G&ϬD(}[bOL 5LؘmCPqHHHOqV+ޡtb4͞-bߢ*~th'^-xqvd4̙[= A2^1MESvvl"4 tBwNҟ*.!_Wn5gf}pwEuꦟϙ^_n/,2VWIKzj\U}%z̗wpq9&>+'^ΫГZxz)45pl 85 0ڞ<%vkI%Iwe.n}G<Qt%' ijT| /nT{#cJ{T1PL:L`< :,&CiA_gbʯD$hRH7W%߅MMl<HRMo"oiK`VذƃҨ"El3diQХ&‚^`0(t).Fo8]k#ΡWի ?@'O9l%9De rʱ&&}uz-i-WB~yVIXh ;pt䪿x(')RtudOl 8wmƾ7V4虺9tpJƱ%QJʦ~Ė@Og% eEpt p *0-K=xV|Nd<-4uFTs].tI+C<~PI˛Pb,9Pn}tЄzachGSY~qzS oe>i]؂2s6d:h ГXړΟJ u˃SQPb=yGfUq{%ؓa?W9S'nL®'rOjMrJ'ڀyʖ)ut'Z>jzf볌(~|WIL[pow rd?PK\fL(pцGmsodumper/xmlpp.pyUT ӞZ]ux XmoXί8C57·*$"6IkpӨ,bcT y}νv6YXU=99:K?ޅG?9_$gMa*ViGA2[/E5t~>:9 9}>c&9Nou&zΕAlTU9]٣%~ZgNn#g[0fyNgܳF48vzsew۰dVoFg6Nuֳ!uFvh6O$LrvXэ L 1I]ʺ@h+AI:}.5w|z7l 2Ϯ=frI+5vm< 32m<]s9}C[(5-ver}*40(@ޤKp_8.2bzo_ fQ0ʵ-qYQf-sȲDJ=֔$眬7VJ:E`w.t.WI jhr WRit6hip,?{S.l~+1$JM{%4 ʎ }_GuHYg CfEfA %ngB 0$VA^W+Sha@-قf~lmMydwDam2 jJH:THX%`HTIXN6_OQ{?== ƘSZJ*,} H]90ÜX/VSkŃL%A8txh);ʠ&K6ϊQ,'}t1ZZ;Ng # .L*/5:@:-S%M`D)ijn߇槿&Zt~"@+hmNn'''eoQ c j;xW EU6IaoY~Uqmbȭ6I>"-:VFnkWv#hռ8Qݾ~#:hmBp/Nog`mUj|Q&Mxʌ^]l5O3γ4IΏDž֫+nܱx(Teɭ1 rNo'˗i/4OC:,CbXLr1swD4"2ThGma$N!۩XXAjJ["]o̠@0:1q(N% Q m>(ZSiPC)*$h|!ޒE&'1mIe5M|3⮥,PWۓRAw dS>[`6ozrG"N)ۄVFdH%Ϣ;uUs&@5鉶&`ck.b}la=}9,N}Ж㬼e2Yu٦nqX]$tUURQwT/ZQf7y)(mΪ;b-U6J9n$.8!Uc7pd dk.?E٢.~5 y y@ޭǹ? tMun_PP})A cNNj顷Cm]Yᯝٷx,td}:]a<A?X|LnBDF-Ҵ dfj5̄\|773YAT|~Nv,MEu uPK{hL9-msodumper/node.pyUT HZ]ux YoFοb åKCAHSMpcE.=UҶZ}\r@ewGz6{9DBpQ2P肣_˦9]xS9l6UqP%~Cdf1/$_]g:S`վez LBQ*HT4'p/ D)Poe1+(&O))UFsTSyN#tyB"`^#Q_+IfԄ$5Ȧ|U]2x A(4QzJ@nES&Ign(*D^AZ9D`WYa 8cR;( AF7> X[BB3)&ǨB]"+Gl;ĸ, D8.\+j8MDYF/$?6(Hc`6l0R?lo%!(l$RLrP ). Ԇy@o~Ϥ0)܈BKK- F.vd\.mTڈ[=)kS8p)`dVUH^kzV4DC%2 | S)qp]߂5x|{,(}΋ŖhwjT\ ܥTR"u%[rK(Op21S~|GE<iY #a}Dj{+8x,)EK꙯I>ӕO97b<*.fQ+v8zN;QXlZI‰w5Y KדJT_^Z!ϱʪXez )k?{AR/C0ҕJvؤ9bZ8)ts4wx&'XwBRgx]:f< ?'|>*9/فACY嚤P3,=fxG#ƻmd|"&D&"SloU 酬[J/jWfz>*eE:^w )͵hFZk"XKtګ]Y B(0tfX%M^EAb<TRK漅G4ia1nth޿Y Niȏ3L^YXЈc5j%'_LLnkV n9^IO|/?|onM4G8#5n/[3DqګKDW9x1j{3.I Q̋&H"vmM/#U'}8+ۡvq0&: 83(Ky#5bd,Ԭغ$ZϢٱVN^tM=s=e߾fS|k]~CuG!: ayYYl5LxݡHjfYSl'5 O"Y>h6SH9k7Z:,h1?-q2;#p&^y^hb`rh+@hoZ( IBsu2>U=:k̉u؉GwƗb>e.a$s-< RZ|OY֊'=҈6"wQSk$42d*>(~@Ű0`Sω‹YwPa YܷXWW/jA.lG{o cy9;1iz*}$®bkDӎc ;z[;Ql#Oc9;Ѱ.>3ScYШ>n 4v^8^Dc$;*@REQeOPKCzNMˠJmsodumper/xlsmodel.pyUT Hՙ\]ux ks~ŅEL,ɵg;c)I;' 50(ww{b w{{ݽO$6K9{W)/ 4֓U9+}VrS-9;^V |ٰd=g V5+o?Y'"ϖ"[cY|w,arC~`ۤfEٰ4*[lm@V,,7l\4I Β7zJK@&{#UbM7YQl.UIqEWrIa[v'H X/6+^ئc?.j !GҤI&$s9(LGa ZɊNOOx=.r=F CQD񷤤E,ۆsqV Kxc6խɎg]!Ј:z DsiAˋ6V@H5u-. ]„`$M#8ӍggLnp_Ɵ )5~-zA9p#d+ \FH>da$rS4v9MeT3e3֊"zZ\jҋ Ez,﹭`)lp@W⌫rpQӫC粬:|8ynk|nvBu fS%$ŝlnu,sU,E^{z$>vɞHCːpfXSFЊ05=`$s6\!!AG.,MUnScx|ÕI {(-.Eg@fF{ S"Hd@eהwEg#'I|TSBIhKY: `|6G"!8Q* pRQ0ixBrX5 4 mu ipiH櫓6`lvh csWpO/}Bx\Bij:]Mu)3.yWkңC*`e !ߜ\#1Q|3\8b sAj!jz݄3Í(X#tWC8˄L/s* O߄`C6A;2|j ]/*7 t "2lz:q0ٛQ,ˢɊ :3jx-3hmiGz787;y} lݠ(;`F:x'[5wNc{Fj"JuSm}b)W$SAaXt4Y鈂ՆE5g^xAi#oTec"Y sJku!63ўk/^rq#*iHqGmA*&74![.u w>\Y"v hu5!5v^WJѓhl d=5'%skb& 7Vj<T)D%ư(C,V-!z)83pRʭ@w #Ҿ5u{@d{*&7faFa Cy`cIpf CT钺72i4b!d8†;T6J:|gn[@E!7 11qx޿cItNdUˤY{!%3GW3msdkN8{*)Q lo m`EH[{ɛ@Vt#How͞YZ%[}vnC1 .%MHl?pԕ/cYq(W܋8F+|!lںm9h=YyB7g]`ɪC0v׽Qk)* s7g ſۆfv *nu kSˮJe&emfZ? &躬<;`CdJVrm?m^Bѓ׿;) *p<>Lc]@*7B@#[#e0t ?!D >!l[x?M>Θff,d5v><1J{~=9 > 9a>Dɋz*ÎӔl?ONkKOZvh'^ħMY=j>cdsfoop2+;2JDr)v:d =n=ާ<9̌9ŌN= f:Zda<'8רatH7ab4+h]Uy$l9ňc 4# DO'$G&*;$FIӏ͗ Y4v4ۄENHrRТ W< >dz8O{W,p4Z <3(W4FO[l B!gі '&{@ z$s\2l%(n[7(knH,2!+#P7ÿs{}f}JC7*(hto' 36V&ډKE܄rgf`\2mC?dX xqpwo* 0u7r:O]4?wu`CrnEUtElwX}UFvj!q6^M'U>zCvH*]h <<&+jɝfŴXe?/PKbL mKZcmsodumper/xlsparser.pyUT Z]ux =[sLq-}_68m\@oQTJ@c볐Id9}oR!>>dm[0^ (kôDA%H0^'3J-%gz'e$IBIFeJq#CmO'{0g0BjJ/K=OApJ94ɹ@c8٘!`La0}R9= 5uvzdץ2)'ĒQ ި :yz%+~`(Ze$ymٌ.ܜ\e0n6|UT#.#&0ɼO\e֠W{C|j/'s ӪD#-EMp옍%)E(H?@] ,0<ͩ&Vcx,|8QdHmB:onI9ޢ0§Q?*&?r$[E}CUکo:ߠvu}_ YQG~0޿@b){%='z%e" 8?}R)kLǿ{ [x#ߊ"SLllw,S[ cm^`Q gg Pq",FKri0mwl>j[f'ӗxI~dR/Wm2=~Z:P= j,< zS EIdM I϶'`)t? v= ji=_Iv Qjtfɵ!2lK@赴c=ή^$ Ei4&[= Al9wJzO߀=Mu i Tb4zЃ"G=v4qNw0O98p2|ZQCWm ~  LudyRQ^Ѷunzݻeέ &qE1&`:xy?qXw]|/jHU;͒,>R:qe_ j{[ZщZNU&P2^P牢/n8h)4:਎#T>h푔 icP:MHTU  !*]# ob+lj&H}vD;jٶ~ubHc{īTFf&ݖ(;:(2HjXR cf5H=7;#bzi7d$Bw2267f$79nF (xj^,1 q4ZќqA9m 2 y|(V6Qa#keDekզa8W"]Q7OdH&SYR~G֛G5BIf&2ڗ89*m+m!{ f"GHzthW5]'(Q. Y;0[LW`0Qɴ .MTa;~Ni"0P#s܁DXh.񰟁]s9Ke c`{s X{~ ~ޔϛQP bkBB *"34dQi![׉;I1khs-YCA-h32=):ajH6qvJ2/)Yk*c;Mb{9OE3?N3/p\t*tC{<=uclNp#.%i, NSes"d6nYgܸcYflY!PP=EjIv I:bdbsim~C)}ץ(;O:]u* W8`ZV|ef%[E5JENs:^A=w*SE7>f~ZH|1Lk3!EQbyQ?G[Bg`P)1X)I%%ğ,%[:<yhYg_ jp;NYˁI+a,@z1)>5v}9+N+SǤkl໺]+ZqZ?Em2 svMW7Dl vu8Am[5phܚYR25v?Zy8X[șEYp.<+"˕.ᵦWj2N)&=|AM=kDM;1(1uRٺJ^k]RYIzW-+Y-.Ar5p T8O64ӄIS` hxUAI.\+dr_9 EVGg7fCrlXHc cšNV-)[ye p*PAZ2BDK[DuL,`3-)ub V/jZ)[$X,L'0_6d7PWz(G=2")Aj:ceBTAkP,65+#//%nsYXX0 F| ڕV'܍[)]*mZ۸- Z.ZCf4Aށ跙U\,KWJW<+nJ.g4 p&])fUVŠݴDgRcI߶#-Yh*ĪCHG]+bnij+lrxEZIJ(ڢUKmE)]IgJMi+ AYS[P}a7j[1 qZ:.㧑ru.,[ 8qR->$<:e^g'q-ByL2]r#3p)%lsCC4HS NjVL9%;-Mˮm&t'tXg)w\HihsP, puEy!:~-"y8ȍDjubM7o mp!Ƽlq|G!Qj}tpVi9@K+WqꤪckL&͟Y'n}فWSO@[Q"xwqoq0EB/#1Wh<ϖJYR;<~CI B+BVyi2xm-D[w:M|s MbυuŲx xs||svJ q:Oaݷ#vVS`zQU;H?6(CkܤF]f809`?+Ŵ1|UۅzwNvռvi/H\4>˔'g_lmZ<1*,j\L-Q$B};}TV`ƙ,kEB~֪/fą~uj`jjEq U'u_HZ;GsxN<ϡ]kNop 0o4WV ]qS}O:^%86l &^LL]{`|1׸/74lfz^]xݲ^j1ڸC0>,S1wX^bkqqFx+E7+?PKgL5]4pmsodumper/xlsrecord.pyUT Z]ux =kw۶+PyY'uZo'vɡHHbMZ $"d{wW"  f0@݇gC "Fl eC!mmomE8K`8 3/^=M_7%Q̈, >ŷP#ΈF$27cý<b{,d! U0ќn@2XO(yO0t= >y ST'4aK6O|k 4%ԋN$CLEȽf*8g#g8Շo:} ]|Zx|ܬ8~{Q>ߚc^wl`緷W?~>;=?E|:#?Q>&NJYMa),LQL V4GԿ33.zh,L> 4LW',K"qw^gGZ`NWڻCthw~N* TX!hSblbe4 ߻IJgՔNFo'm9;0+d'@LtMiXV#tt lR|9BlqxM Ⰵ(rYpBvl8J6$Cm6%d2;|=e=Ζ@+s|-tCg5 .KsQʕ6:;~gQz96e*zU7PZko EbȎv> _=KΎ!{Z ފqаS>zCP8Xz½ DGX@,4 A p8vclgʡϩ6w6c`$)>Ku pjLm!c[s6/*8{yTK6"I \QRKH9o-dnnm[[x bU 3-TR ajL/<:53??'"qپF_@7Çr\]ϧN2WDY8n>n`&NbƆx6 YY⇨vDc)l~Xd?4I}LbEŷ|.Cd @3׸=9D4IEMNuFO`ڎl0W# oI\-6NIc[,p׊i@3:$4=#/wB@1Ke5BV @S} GGޯp303Id .O@~bh!>xPG AKedOhXGv 뛯&nǵa 3 k/!sZ@RzkXgޡשR dC|2b׳d$K_yJѨ/AbOJ\i vr XU}8XB[ c+|i";2F#g, "'I|l +xh7+w- <`tWIo J <_EiġvmJPi .o:DYny}4-rios5 mfww&vRk2lYV09hb24MvµBvqީڔ 饰\$V.őap30yG钮f)N+خ:7YntZ.F`d=K/QD9]G+L3GU%IОH4hCuy$x! [~sCpTrzMHv,}iaQ5&ϧo!_ 0G Dm%1"( >cК)ٴ%:_"g,&h;PZV)ߓZk*SBe|dw}rΊto B&|fA)3<#7t\R_'F 2W1b# xq &䭯?uq=ԮX-.>ѼM\liwsGH ~E25g[,yY 5FN&^镢B#m8P"Tĩgi1Wt E(- 54SϦfzjKxSrH0*A0)lUOmق:.+͖5e破N_`./tݰv CĠyֵXcV=[3v{e_#.?ٔϘF FW ygGwܩ%&3%" bi\e&Td~B,wv(VJK-*")Kjbr%R/5[{.ŤO:bhWeʥuly Z8sAUqZ0E`T3~IWןiE_8W\%˽]k(9rze@17qR4P^e ;S<@ɾ8sv(wv9t7&gZ۲kٍPLq0`OxW%F뮊|l#?@cx/aY+cYjJds.Tܑe (XexM#o$;:~DXHmx'|bo8oX;*jrd+:v(V7Jg ^LULWjW+*! Zr4ܟ9zl8w^=m5qtľ<%4sx<;j4yN|<KIa.|Wl=R1}y.yҐwem HS*|ڰ,!0 S K+5fj߸_I UʍrkI7'e&5KOu;h8cO>OsJpc'ǔ%`pW4#>9AdJ"Jˁ:<7$ۯ52ӫ_K>(1L< #/P4L T1ţhDHUTPA=h'E;Rtmw}(WQ-#㣤zZJU~IˁZQ)|̮MfiIjS_fix &>7_*bޔ= OH'-UG]0\PgQ2\<LjWrٴԍ3oUhOǕ8 ǸSN*Ck)`!$7 zI^|^^xJ/鄬6ٳ1%я>),m5tr{R{6t5v敧t~!S1M0m}5Q }pGP<@t0H ՟Mj4oANZ Pټ9!4o /1ox O{""Tb E`[f*ی8I!Lݠ!(i?y@P"  r*?K5}z5M֙\ƿaoW#^IW ڊpkTQ~\!YvݬDՍZ|^YAtˀ{e>gKӈN~jqDv27h+g뀷N޲߇s̀КG>4( (]ρv M6Cvܦo–IEVEǢ\U4btjo>|?,Z(4_Fz,:s4[D8i <*FM =ذ=->ۗ];od!0o + JnHs~;?L"i*^J]1Uo qIJKЍU<݂nu:ú׌NZwon_0p |ڂ[#҆47/lz- A/= wo{oXoS#Z`؛J'n0!r5; ev<{q۱?=Ulpb6b12!74(fBWmᝎ5ǜH\M @eIqڭʐnͯ:QOY{&KTM+=uE4r;X!lD[ED6!ZѺ_hD4Q|1=}R˛eZf)"/@}/ѲCr iIkqX5tFL@F&i(0 Of9 !BJG,gVPK"sPfB-joJYm]N- KPdZ#Hgaҹ_ڮ;t]eV5cme| [݇ZY]>!0|{P"gǦ x']N9"n:O`,+LYZǕfkR܎_#2 `$y..uok lc}>z9UH!0Vk%JtQb鶲k Di)t؎b$!H[Ag=׌W=t}CK( LF#N `qO#^<zZxt~J j+dYv${G3AȢ+hΎ%YJ⏒}=s=(UtݬxjA83@jr1}㒨k ,%a e*TЊ D9\dp)lfNGu4I-nN4 eT]Ƿ"1cKeMq}![*t>/,7l־$xl9 'y:bZgAC(4Tj&ϱxߠNvuH&+yK^:;j6,`AP9\@ʱ4afB2fr8"” hPIi8kDcB6VWZSh@`~ JrS"jg4'31.WLE(lꊇ4Bȃ)%h+둢Բ +՟~:3yXۮQ+hf]]r  51-PX;Fx:khIi)_scQC0qە%ʭhzcqo}cLZ3<Շhݧ?+/g м([ծkS>R\M50fR< OBL>]2H%%J4|4) į;`o]B)ǰ>.R̀fwĕYB vaT{;"D}<<ᏲpUiX)=CqRόe)=1؆ʌ E˱mx[*H5*F,v_ŃGj1oq!R1PyP0F~T lYJ=.# U:YfEGj݆ UD]g?N)V_;yb]d0@E@#H>~uc|1Β+9 7xn $_E'&kcF\ xm(P)|%QEĢ2@w<_Y`c`8yVoʑ1T0Fv=@MՏ[76֝Ye!km\\AjlCBhJd'cVjbBR K&DYiu4lH+? HVsԝ(!]:N Pch`ӆLȞ.F,kG(W3S) 7,`FhSFGkpk”)#Hx,' Rw`!CP9icU`]vdl nrlO`q1[? l_4dgWU#w+ox4M2W]B'Zn,]],Q:IGZYYB;-e%Y@ap}'_蠔3?7a&ֻoJ:GC VA'֍V}u8ىg癪 Y#>i̐ܪJ7J  Fixv? 06H FyV`,:v{ rԸHpw1-eCP5C8efp ~bTy|'oFXJ6#*: aA[@ԍy;?ۨ9MMA.ˏTƍAIaioYF=pEv &/}x,$5 6_[t900}%+lIZj@@ 5N j1EѲF[2Ogry?>[L&4>)5Pè>fsL4_-V2nQQ5Myd$෉OXI"~1_Y ?_ͮiɷ.ōs?jQ"(vOP&"޸_Dlɫ8Ć?E'b$Cq q9 I7l;ta;VC"vVsAYzi{&o$6Jg[S>jHɖKf &'`y,j~k $]&~ܫy pS~wCF(HYn(#:}3؄M "|q,)JlUfuCVŬ':O?"$Bz55]Hރ-ab“+/fIID Ł)a%DGfFR B8c:bx泸34W1kxD] YWOJYL菤ix& Dh<*aOJ{JY!qØ&/0{f)󇞔CuJGb4=Jc(>A:"RlY_j"+p΂6ifva0}Au d=\4Vu)2jkacYm D<^IˮFDٰP$ hv\I:9G,vH{)GlYzaMdՉ>ؚ(~<"]%j/և"Ä [{2b)]A*jTVPKw*kqvd$[bbm/AJ|;[QhGFl NK ;rʮ'S JAt;^/>_MH(dS0Uݣ4}Y ZM5I#MѢ!7w`ILzLoP Ӱ4*! /4 b2qH+2a7%y&iJ* T4Z-_ֽ/9V[|NF$\ 2b+ii$߅K4e/=M! <<_Bq 7\Fe2Vоlwmoɓ)_bF-X-tKFt|Ո5*/N*{;&(AY㙣c ,u#iӔ**ĞDQj,TU؄L,' Q}Yq^\r뉱Nv8oVN?-`X dS+4*f5)`HWs!u}RUv.@btU n!dOEomz.x@CP@!88…%j:x_NkuC28'=~l7zUQroq}qvNuY!M\}{X?J<8ym¶m#H 4F-^Z^It J)j6nF:ԢMtea(g~"JMnYl搨zl|4ހVhVL$!5~C&bw&솽6ރN@Hd=TBEZRuuTƮ1 j}dEB!M)fY`~Sޟ6لvg5 ztʩ @6lڧfP[MZQDﱴ5WHIC6[oKCIe`luRk=9MhǾcљʊQoXFm[qm.Е㨅 Z1fxۓwώ-$[1x.:par}0Nnuf AU]Soed@vNVï:Z"]-ҿv49izDNЊx]aS!ѦyP+B@"WxPpD@x|5Ţ%RQ:nsp6TVG_b=FЄ5.FQ|7f)Y!-H4c/BYFq?U L/+D A)jFPπ <>LcQ5k`0|!^ ɤHsj)$_ *~8+={ǽ3mxy1lOrp+(+&rB5Q< :Z!48W}/L.Hv2բx$xl应I_u5M L5s7=q4wvB re%ZޞZIZ4/G@YD.bm`)埂蘜V$܈ْƕZaV\"?Qs NgեdCtZz9G7vY:~I, r4kAi:~i0/uMT-ү#dSm\mS:e!eS!3HjaMJQWEfyYҾCՏl%?H A {GZ*>nKR\d-Ng{Jz)]ER^uG~@ZmSCtccm1S·dki1`< {qi6զL@OZ8̖Ӭ(OҼ|]מT 5_sV1;0V'WA*OP3niau7|cW|]X}C9@%̰Wߎ_ŭ?ksA2 c<{lQ 5ʳc@ 2͓Y($a*  %{[1Ӫ:a"ϋ?d.#){>`+DQHP;ڎh>~gFe}iڷK#G_A8>" x) u0g$"K;rҁvF"(|r+Xȉ6xlSD[5q97dZΒӎF+,7d HE:x((QEeSƲ"V{ƒy1-B"J5oIDKDQ >e7:~jS{4 iW iי*̈́Qxa"G?ޥUReJsGw0TwM;י ;ܣ ˜bPЁl8#uƥrD/,8?:]|R1M\$wp$QВVgT E!pxRb(ğ 'kfM5=3X]Ç\ܘHCIxEDviG6Cԡ7dV Z`.6i !T>xƭSը&FZY?X-5wtѧ#pԀݨTBR[oyYfnUq+y)Pp'RL0xӻ8<3N+OrޖCNN~}캲XP9džEa|>pm2f{xBǷGb*jn-h֪zQ6nJd|nE#*wDHQr$Kd`,)MR]>jYw;ž99feT;Y5묪Ba*ӽJ\٠ctcf!]!PvnGnkƍ JyEd]GUI *"i͙1xutynW"Rp!.a؟:?kJi㟪dba]78қDѕ/.iybuDІA7mkxroZ׽>j`Oй {88)Z*hU\9aEaN5--DuuDZjge'kbd!]}CK{c~#dӥaVldCT Pꅹ>*FD @!Vl#F{rB8nt{UHaRIg3Mgj6|ixϟ5 b5Ul*[UɅt<365Q1gʅ zP(5)kHga 2B"_*uwg!\CRQnsuQl4h j2ZlGo5SWH&~ p`9 Cz )5T=v>aP<OO#lU'|T2!O{}E ¹'(A Πx ѱ^Dً)!Jb5"b-8]D_7zQʠ8-8e΃BZټ̋F6Ԑķ ָUVs(1_E>W[ͳ^oԬJʊ+?9a ')\4=4o^SH8J5K^|k|JC]{"9&6W=,yu5 i8q/ADg!)T+TT6O')ySrw|mc1<4h͉>'烪5VDmt⹼hyk7A9 _J;M0ڸm,b`[!* \fnnsoy4`ᜤgpE>䃥,7H8[@&dL͙q\yq4bGG\`V?G:,鈙[n^VGjni#5ݎm.?\+*WwkUL|h6 )&tXjx)K>LS Nnoi2m;̦!sw%<)H[ CPOSE1L~L 5N'%V%A\=rvOKl44AYorҵՂ?r rF;D;f 2vu|Ft̬;5d9y-NmN}( w y# q"H }DM'6WՔt\dX4 ԦF/bVFMgyh]Ƿ#c-fxD1%(2\&iITtG`v@3lK13xN$,L b645NA; lG.AV5q-qQEl.Qh_%5Oi@0/%TyuEz-kh~⭪PN-:p|&%l:SLSTK:*?Vz} V٣qm.dtC><^)mWܧEa5P6ܸJF(Hk6P2BA;.ju/fFv 6ե#wD|f\Wsy4R.&(GrJk^d#e xU&!YRբKZn]G[@/޻Ua||wx#u'ߏߝ^ӷa o{ݟa7>s7xI|8݀ls]^I}^Gg7ߏᅳ ,ͻC7􈺽;__^)' 5QXLuH=Oʭ{6Z4fRFlś|&3-z6H hpT4 8UaV3FF h̘# թQV1_`VTVREzcPOj v =1-IXa[rO}Vg!"fv9PGk1OPU-X>LTT[k1qNvW_8>Jz|x{K3y S0`ޑZo.ӓ< 7q:M ,AqEp4\`8)=B(*^x(1ysmD۴L;,G0f[ze < 6_Tir}Xrf ,A:#ZIx|7~|b۔T>J`#G<L5壥x[#jc}Hzd@!7("e~5O\f빙NB )6Y-Cf~P 5] kGӷn(bmʠOX-=G/+smțZ<(*::ˋ$xk<''w%(x8=cUveS=psyerjV@h{\zmhtycIJ::cTơ=SpKHƈ(LjBu'_a}{o;]f BVС;|&?-ÆJ>O͋5/X+ͻ f 8?'HΓqעa( Aqc@/U77EQ8c% +mz8 GŻk]! QVD˸ ž"lIc"a(~r"RPڣ CqT5 @t| űbpvxq#10Fq[PNHnpK~?@Ul^4i6 W6Y8.gV()E1$3;@;~ E/݆w+Jnhi-aieBG<^8N+FƷÞ SʹΩ bCݔB7(EF9q1%&:BQAKRs8973No W.vALS%@lWcM$t׮amT[YqHGm_Q&g(Ikzk*6Fmge6 ߑ㼈/*s];o\@@9\g-5$(*$Š9rka-!1w%طsMei5zHgY@&}B(Z_*,O#:ۊv'V ,ژSuzJkqju$7:~IPQ(HYp;r:/ɯ!pd#56]mi~n)O9VA*GөF'I?~rVl6OD$OK†DzRg~L`Zڤj(&vXV)qC1l:j؀Cx{F1y"P9-K痷w ڟ-XDmtLf[K4.*DWmQw[#/+p Ժ)e+L~a\}_NOF7(BdObȱNڊ@M"zOqa}Kr3:)?ٲˌfJaC~s;ڰ }fd11iԩ (QE@t[4ꃒFV1sTmV5.Y J'nJq _*ܭ9$/? 1uFox> Y8T2ޥWa 39VS`oscnh óÛC%iN4j}4J[;vf+ZE")YI@GHV| @R ڦTK6WyHdemo8(RrHG=5YV8J(5*(*d6oOa'R z*jFKt%GXU,Ukp#` 2\O'.)pUfZZ @qdMQژ*űb0ž@qbǘkʍ?F3Pej6?$% ]?=⭲k;cI2JJD0J EWAgDSPlrr9{tb_AqfgDSɎiB!_ M&DǹF EWEq`(&F)-Bj.X3k(Szg9YQf rc7 e](f1ϞXes7WP{cx%k 1#Ks(yy;|S^ 8;?۷Cf+-?U$3wwuK:#YFd3a[ŋS5]\Fф ܜfRz6 v-ƶrUQ{tL>NODJjT5@ɉMDD3rBl(h:=&#S Y" R/4aMKEL?Qdhq>Gm- M_V0/ÒjHZ)ְ ]Zdu2MOEs>#i:AC lX?4⦄OYV"3]t뜷P+9"Z&K)VqUUZ,nn؃&bl) 5 (yclWS1H} #ce(WKSb&Vb*}gX0WOy y'[)F\ҌK_DHV|z ͈%^/1>R3]s[9At%0JѥW%:Cc@)V`nj/Zsk?.6SK7تwdz5j}0%:7xIr7D~Tի?JnՈxD&rbGT݇kjƓ{攜=<5ѶbLZ2*/i súxJW@&2@h,F2 ZNhݫ+R6MʼUd쨖Q@k8/- hߵАF^ z`H}sX]Ӝ:;wr/\iY<j\oٺt(gU 9M6gSG?_D%Gz;i:0;7Y*$dqZ^ղcږ r0Jqf>ȮI i6g'^$ܯ ήI>U}UAB'/mHz-ܐu: ||Um=dw} u$W:Y 7dI~1SfjAYn᏾>hEnf|ߖܭ[alw4o^+h`'m  & n<6} xh"(S*XJN(K4}(;"옚>,k<¦¸;T`5vX7J~urAi}Ѝ*݃f0&ha@yJZ Z3ۤu:Yb{*@0 ӚӊKl칝##}TaIm4`"J]'t+\[]f9f6+WaXڻlu[wFv%UNCuXWMٸ_yykx.orgg'Uχܘk!+o5lz4T/U4hpr C#6h898̝_r*0ْr*, I])S܆)g5רzq Թ |S^ؗY#ΒRiQoY%` {qU&}U-$d 0?,Vk֒*rRΑ!1C8qgOFɲt%ʞ$GVNvkOIhcZFDe:T ݀9q<9<Կ$`GJD 0[${7RXg *cK bA[EuUPeV4';Zn<٥<ˌ[3ϙpHBCzֻ|tе}^G,Q\KeIfzRnbf9v s\LA;D&j>Hnix pޥ}zo_zD]=oy\ѪF09&*XO⹺lcNCxjJ<]!Ǣ|4uP07PuXy߳Q1ʖ 7Rp.F1(XΆk1F ?VT~IT@Mb`&pf(&[u'J>fMnO0pRϒG H#Քu5hA:Z:OrD|mc29ͻ6Ikd!ےlh̃oSN0v(+0x8?wӱaˆKZXN&붭 WG x>q cq% 2#E_Qޢ9,rQ>^g#aH4Ը(k=YbuBNԓédEƴ1iu[e`#l2DbAj1F@֒y' }@OO. Y=^ [ u˃'ܟҳ'ܦQ+Ǐvvɗ  `Ĩe0vQOB2/\N9L#EB;hZ ‚[ 6gk\FИ%x [#`GE~o$_ޥI%=+/EGwHMyM͗J?f'ee2׿ӯ|53&p?c?F,p'K5-/{ᵲ,KYzN. |Ư,,TYyx] 6@.K\L_;.&dzy!.C3-$w-{uxg dʊluf} |ܟd `B˴ՠVF~ zCt>;Ѭ, WI' [ՃtџQ%ar@wUQfsЃ<ܫ.''y tY^xKðtS튰EqIU{>Qx}6gEk4O| YsT*;-U.CP5PdžqEGj]b8'PPBT2c*j3AM-LR- Z}ztA%D9xQ "KΨ9r%/՜˲]. P!=@WL[)4TVu ҹpPj>-_"kIV8(o`Jr`.Ւtlzcx|]1l]aC_HYUkO"y_DP}Mpos B`|D<A`?!`i8͂HX[ȓh#REFR1Y@-TG Bv;_ףKc١&gq-)7>~O21+gu"m ndL  _ŷ!gM6 l?/"DH9F|%(l ο hOH?ܕXGiމhs a<YQuj~`1d#2>KF*ec>@GKZVikU9)vC*̈OziKVEA05< 5H-betPe׿Bq UwP~4 T2b2Q0? Y2_z`XM٪-u.씚fPhȶۅX0pn)T5˕€Rpݣ'^Tr,&1 4B ērgKJU@cXzRrUz|sɊz Øb$qqUu:%#^  YӭnjFҝ552=OBIl08'6O+  @eH#C!>?^\|LGҹ9*ܧv33m|e$ΣVOg )Շ+ٙ٢v)/l0䊫&S-r4[}):VQm-{ԩo4^F*B /pDm"0t܍?~5%*#]2aNr.כ_z^?;tXw淗IcO/nzo{+y@@ϠGvyh}|>^~.̯G|Ƨ5pxmM\1~bNoO=zkiw.i, a&rcS*q|X=zyrtݰG'#,[Y,GF(>4^ux8l|>>dO8:2#΅P`A's(tf*kB !ѾTᛨ(j y~1~R5j,Xf~d^V@R' w1lABJS5BUzʦ^s_gTZv1tXP*̉ÃlБxJ.Vtq?|AF\re⨌hقvI%kB.Dpo\XAz NjmRR7-`O!Hk5~1g)@0`{9a{ϋyT<;| e]l[SRڀ6}c//ESL󢚐O=@1 }IpF)hSˠZq-w1w1Y6ͦǨ]<Ӗ|4iqԣ$4Qa]D6> f*}l̴ّ#ʔ.~Rlm eQJ?J†J|pa<HlFqjqbA &@gD{l9"TĮْS'K}\["J n1vq(mJ LdFw .*F-P9iFn‡UOJ>)6Br>aUP'gWx3V7AsFC_'# чDr`棸V/pBef.[0CZy 6"a,e%?_93^Lzׇo{vÏv ;c7$6ܜ>W!'ćk;@(I1a8_"|Lt@"Lz2 ` E>O|L?|d?!l~մxK3 Qrs Sf8%4+U o*=8ÇTw[ mQ[-CkbثpZAZi[d>~uL)?i9Ж A(/i*RRfUsnlյf%1̷qJ2^,\ Ag;$VxK6H!lK%gRj0FMSA`w<@.n2 \5A;ڂ gXbS0 E9-֓Ff(&T:W2֎P&{+=ߦGDŽpKODbps dGmq"$J@BQ:l k[F.AGSmNZB]Oi!ӓ'Ǽ{+!<+@\ tQ"^HE3Pm1:9"gJQnyy!!A/7@6YR akLxD/Y.3&̩UJɹY4t&5|U2묚> t!{:B?ё}ɠB$2lF:ZhbJS#~29!1\˳M3\_>/vXV֯]DmWRgsԛp̋hus\ȸ~;G{oz:ɚVE MðXPw*?_ȧ>'يȩ!dհ@"JU[5R3h e>;h5d!_fEZA>T#MNp rw+ϊƅ : ͇Uc2֫PP*VOW# ,WaL־; ̗9Wf am )X#B59$݀D58 qF6̓4+az׍kb ؉@bŸUG1l2) ǢV@dZ@188JOO{U5YvUJ|y@*-㨍A-6Gfә<5ڒo`{[ϛCD`*M[Ӂ*hH,2\o *E1 ]]G4zkS:eȊUVM%_pHDca|G0I9]t_L*[WPIV˚0Hk{͏mClԠ=[ nM&9\D]WAiDH.X>uFQ]ru$V*@8 aմ$;%Y2bٰ2wG_U=ՐYCH|e*ʼn aƊQhb@h7o:*WU;[: 3cDK9%|XJcߛӕ~|)1M RKP4ݔ!KHҶjNo( zQ& їY]i#&9DW3pVKiz/7Ye3?'[e2_fyM wԔ'kMQ|Ne7JJҪF*H`u9! E~阾B9ib\M nN\`ҝfe}mS^F0`ⱌpqʤwG+ -D4@x·A#.< ⌯d&<ea;zQqrgxc'diB&?/pp%uDJre ITs:4Bb7>/~NulSzRg˟&bDt@7cF-OGZXXA_DϹHYV&Xɨ,"ˆ L)TB{<~CH*,+SѦ߃$ o`K. 2\5^Z}?ǟDP1#>ƛF<+s#jf4 BՉ17uvUPc邬T.(U/\:>njUc4g p|N;DOQX>=jHjV@z.c6T)> &e<ԁ.vꆑA2Hܐ.)pV_ OhaJ"eUI|Xy_% +ЕlGBFAl` 5#Vc{mI fay-SpN).}-3x;, eNt7#yCu'a.O8Q.;i ߫!Ӈ/|CV*iiiwU.6{F78Va=528h cTt uv ǡ* AÑOQ(x~a4u.5lMͷj(Fu‹3*dVCUQ` n5JZX=.`h3O$8V pcf(3*x~4Zg^7XpA;CR;Ɂ8{]%sM4-t /20]Rxk qϙ#ZI}LR8#NwzxƷyr 9k&&ch]XDy=6 -2 U^X9de(Vs;%dc'v6ysI0cKe"KR)%r _P6Bќt>򗰔\R Ί)'5y^ji< tv`Q|v񃞺A:Pm(-J;[@O~F4\_z-p~ '+ydًpC%SǗ(\3ү`4:n)!R@RP/}36T]Pʆ l'AcP6jZ9#w}5f Nw-6K0I[LU#hː,џNOTHq-Xt0sZ-dn)rxT^qՉ2>+ѵLмQ_H7[7HE"ڤwR3.IZa͓tBzq:[pf;<- V|X7FΖ3͠0ㆸԶ<%!; òՂ4هd+M| 0gYT 㙚əqf=1%YWd7Nvq;$M^Fɵy{oC諂KD fAzcc/Ǜ÷, bP&De =<ه$og)FɤH="_?^#-_:׎+CGz*|V/87,ɦ<+q,6>ٖ=\SwUTtU,Ye1ml7[@ԮG/cQdT }XDU,/t;كb-X^Y[gJY#UиG0݈` oQMAEmY biS'kNZ֢ރ^*jrЏ6KcЌH;bD M(}أz%"EbS[%).T8@U<@Q!<;Reb,I1$!ħ2Xp+%smuM_ɵzA+ ?jk.[_ds޹񞨩k otnOR?xwgSZ\z)BIFLɒ'MV.mMV AYޯ|_亀-Ee`F<"J{d-A: ΐi=$ⱎ\~,zGt; LVC_F0-Թ3VVx n# AX!ދ 7JTݫk$X$2ues0S3ч 1'IBuWb2KGVvz (4x$ɇӞ%0*6p+ab3yBH<ې7ׇwhج Y<(N2q4<']Q>;8|k\VV=ɶxr8%R+Ymo]쐖uBڶYי>mr9R NS+; rCz+YqV ș@&axޕ(tXE=VPPQAQy5Br@G_0A;aSqI21c:j\t7@~I|.ٶ"4k {$&DTQ &:@dqK6nBS $†Lqk9۴ԑb`G'\wMׁG վu~>9iNW ~~~n5LFzwp~pe7:bX~Qw/Chp/DT'-4}ZNRe)Q8oX$'EN\62V5׉.0mJZApZ*QL㵉y)z \s&˫\f\:Zycx\r5PcZaDFGh۵ V=W[v|kH'$-!^2_H&WS5>Ɨv˳|_ebve#@ Itÿ 3Jʇ7w5^ |CLACt}*dZ(^|hj;\A ̌(KfAhnJ "@g> *v5ԓ"[6T |Fc!ZaRND8@Ca^zp<$|25g`[=[-=D?ibIrV huUPi wk]]eaQpGH#U÷J0hNT{vHk5OIo~x1Y8~~ޚ>QM!_SmHmN² a>6P& B1O5K`W= TaX@Bσ n! p-bZZ֨QҏbCH٨>YRa?OtG19Y!YB}QcgY9MP٘p/O Z8P#ۚ~k흚9XKgCf]PƇ@jqp+dS擇I!Ux͵R4b5qަG$ O0\8m_6hжYb^!l*ޗgL4L.]ව|h -?,cwh=Ybds&wls'Ȣ3kg~jqDr(󖲚;90IV,`JF)R;zN%!H*'Y(b!Ϫ ;%鈱J .*dh y#x0a gszOB E7=T*RM[eW:'(ckQ7xטAA5Ob&SHcj .ڣX0<"0Mn^L#:MZx ,e\Nî[YYk Yrr8iGyJHb "H_fu,GqU EIxcE z>f+(j,!l3s^<Yy&v>*'n08|}^U7fouat/ISVl R_4Z+Af;B<_v“Z$e Dp~GnwŅ~?7QU.`K*{||-ςQV^7Ԟ?/Dqu -3ؚWo%+ q~ܩ7EG_q~fe-ʏ߀㫯Єt~ۻ -+օxOXys>ti89` =s⇀"a7e5hӒft5cS. 2*Z8 f{Z`ز@Acr]1hkAԀG\ݺM9,@Dtݘ6c?z8n ْFKtY`@-#!C%0P(ͱs&5ЦF&O%rn=~} r&P8Q}# O ?i4swAϙ \==Y/"쭋43Ȫβ:-E"Ka:IiQ$kB$Ͳb}(U|eq?Fd;0M}r7Jwq*MrHGAjb O9 rUb77te ^1 Wn^U-P\e7|=d:gSۓ#mtMs\7^`lO+7mz(p@A#ôWa]_ʱ%׎0(eĖBTB6XYٞQ]CpE4dC|GKKM }_Ygv/FG_\Sh1jNї(-FG0um[{>#V?E]=Z]OXҝ)u&3B͞Կl]j=HX|#ys}{=WrfҮP{\ SZ*1!e5f;?k֙ 4+%oKa e'쨐`K{)Vk 7jXJWL~;Ї%x5+o  `ԃYd 5W*eSn}2M|?TEqoovS]^C!R1]ۮVwJHk9K&K) $+mUpU& ]b- BKv %'K!}N-(^d+SRxӡ*aE44i2p18ݸߓutId,*SSJnPmMI9Նq2KJuMv5ck@׻JVؒa_gP R; 6}vU&[7j*QC8.7.Ԝ&q<[l7Ym{ᇨ:KW^DԾ4#tk`iY]s[7>1) ߶<`8•m}k36d;K617MUF 9^lcn&3Ƌ69IkAgpҌt2,ӳ@'Fm9/H5}S[XI3:*N 80ֶШ64U_Noz\pw}z EM]񆁼Hߥ Y$޲q߰.J6Oi& Ck3j5=csE弖4͒EQFf')&׭Q\)"AY5'wHz.0m}n Bw6KEpo0PF[7<%tT _: 7 &nUgimUDV?mǘPY! VFYÃJ@z <Ú6Luh@^En :TH3vc쌆m|Dp?Fդ3V+}Xie !.4I~{(.8w㭍\x>i}o枭G(@yу=nGt ߍ#[ѧEͲ-l3[:ˌAIԽƩ.N"JdOa}|gK'Je%eo1x +W:iqf.gEvDL= ,+]ջv̔_>S -.-R8G@=>d(8]6>LlKBJ5R6ȋxMG].mNeXK30!oI=W׋+7e6PDUbJ s%F6jZDPAqbu*pnm4QLbwkhS(3V s=ьԡᴡǁt'Vr~BIy$cy ${@P%nbGu}Jŭr 恥Ȁ`xTlްXU'Pgee@Hˈ,d:_9J_TQ͘Ƭ %=I/eH2Mn f+Ƶ;eEIW-4,| Jc?vU pw 99pV%ɐDGdR6G@s<+c J6&|FfۆCX8?[ߎ2 äHn 6E7[nq@'V;DzdKjOmsYf|]_ѧAsTďwwJ8mb(+AYm5अWYсybqwʘO,j3s3i`6 ze)|ޜ1"D"!-Qx w^" ;#eV"kZ*KW#G;eP~L @p^у)G/_s@k .H9g=pW2 ..oz(f ن!Ze ECsgc]|,|j@Lhv'IuXpL,k}Wʻ>6Gf˽JؼE {4~! jPBP/h\WR2OÊ$d#A+WgjCMi΍/j}j7WJ[Yh Ŧy?}S4_>7*O[̡zJJWA {Py](jaKW^v.3*|3tpZN- -*##* 8.SD ( eJ {1+ArVb*4y~6i%¢)`TEj3Y֢.DIDJkǤ;ͲX̛,]?]&yobxqu!Ųؽw$s_ƕȋZҪ.NV570!:̀E#q.:Ny"ZlzW(ʗK`y ]^AeMi^t+Zq|+uKV4yS% YI!>mS9K^ꯏnIO8u8yL*KJsA엿x%q`ObP9lKZxK̑s?_%t q ;զh$yѼlPO$Af E/iX5\=PT {lَ_wvuuu 2΂7>VeMTnB~lOk 3sn `X%2|˚-z$0&?vdf;8[&h.TyX^ͦ8}H+sq^- Y'e/+j;~d[r6ѷ;l/r#SngcEע_s,~ф(Tk"7q=9X+i}BcNӼ$^/7@lnX_` ÆU6vpv0me-7'|Sc!C]i8$&rM.+d`>v$رL%hpw+fpҿF8OW8|6#cp_LMP>yu&XGU8CS\=#ވeQql2{RsEAGi0)c5$6CXC?pޫ|'łhzMFOF0lG:$*mꎔ8,mTM$ ya^GD-y,#`|t[ P|MpwSD$- J$8#{-|+ErBpxA_4b02]`cWq9 ^E!N3[069e@ 6f_g1@iRve&z9ܑhƛj=]sxK*K!hbM;9;GgIn&3g01X-_H]L.31:$3LfꎍB. P' i\3M#7B|SvJNmaO7jTS鈌*)ԃ4!3V pז'4=K|/B "V7%/ 7߃'-%꽷 f[ ЊT>Kse*҆Hk:]tFZ0 Aεsb$f5;Ha,b8tB(\cxk" w5򕻭Ut{racOt9*5w.DOTRLnSp׃G9jFm1Vfi_|U%l=mF0]x'N!TVEm[w4HՀ1ñ˲-LudS+vÀ'Ф}r[x"moLQ.4:@WLhѮִ/1iҐt11fEp>/EDͧ.(T ѩh<=^|E_^"}jůAA񪒙0 qMiط )f{o}Rm#ޥ؎laҵ-Y((A^H'exM3}?7Cg})ɱx%ڪdպ uw ?xa=4'4pXh-9G>tjkj(~Sb&XD?"y4Mg+١:My4PRNlvp klDkGۯ7ؘ cV▵ke3w3a}x=2X7=IjX̊Wuk^ 0hetKuHZKb:7cfC6euy_"s1wd0H*y݀\9rPWv0$?y#FX`3|?JBN X%TK<*k\CFȕ:um 8{_*B(ğe9pP4VÖ"p:bNX1a[1;r9įͳI;/ {+WPpQ)V ~^O{.ŁrWGCfn =\ΆO]}mUEvdr"jd\ 0Xz\-_h^eoPVau;%ԴQnda)؁+opET>_•1-#UC5;v.^j?ܒFd2ZB,dmŏ V`)`+AD Qm &Y~m_GMQ%Gg҃Цb>bv*JidM#)5wE?~/Z|"4R=r|m?YZC,i0R9&0L.ھ+2 |^Ech&Q:Q/ zSb5.؈{ _?ɟOϛf+ ùg[=AtIWrFƵ.|3 8D1aSԣpR+]|Nk~+4Fr3v7 pȉBڵÞ%~V2˯IO^퐽GFU&2C#`3ibV:,9L`hݟcp,΍qXjB+E$˺4gYɉ*7[ :DyOiYEd=yt'09Rq=.9=U<}ĺb HJ2<wuHrίxeaƋ%$Nw XXpՏ}$:wwUuuu逢QM_HSH-۔ i"x QK^+7&#p ău/y)CV Y?>%q9Kzfv3YYS|,6dw`eɎKRt^G,Q0W)=|Hh >"qc&#ƄDaN9pJn6$#2w#Â5XL9Ԝ8;W>(X41C%̾͜e @(;er4#|^-¢V,g>XK.̨b%^s:.-ۊ]w/n bw ';.*խ)dQ wmڢ;YG>фǧTFqDžl(1&LcD(ĬP@ȇ'0 \q[zfW<(:> Db4 WN]tH!NJ;Sf@7::LOA 2z v܎`oJwZ((G$*Qbg6h7f[vW272JCmH)6+toJ⤼-Ƒ?qek}ұIyIYDL|d#vّB?Ù@qAcPw.}Y{pؒK [-IF455G/$q+p {On2Niڣ}8Fcϋ* "qp5)JW ?30V~= {V t@IVM[oR(Oq1#cϙR,%^aL>ix\\Nj+?gBvOY-맸~/kSr>ZxnMF{Swٹ4|3 ǔ_XMrWδR%X}NG0dq: ݤ\PZ#n.1Lj0.پ;tu^Y R%/~In(ODq9(Dlj\:]xᕜϔ K^$D:(bwi-Ku-D-~fJtzJı]q7n,RWLΑՖ-Bt2 MBOe4ZϏfϔtxx&)&j\%l/)udt,Eܪ#-hq iPF!v*I!/EBp -t;҃*oh{l&aɱO}nn9 }!%0}g.@̾i& `TnKy;f qN~G˵ Y 1~$8o'Z3pF64*RFAP9gIGFA4g׼1,sȶ1 =b|E%#\kE3xpck! HnHY !4f9Ogw!2tM46sMYH3'4 ޙt%'р}Hm j֒xWw3u11끰ITrEP`!eh)ESX\y2Ib< דl˂x%) ;G`hOlLYí k"|VMMjC{hr/Zj&3M * %WAD0[puB|9;(Unug}g'ks^/~֪-h?Yk 0,@(S~/ nn1h˹ܲ$sy#sQY[Ӂt> 9ǐE@R\vMcffw㙟c&ؖvUj( ,f 't7+3ˡZl,^,7F(,kBɚ\3`J_> `LdEӺT DHj rԜ(~ʈn=W~~;G+ g#ءi~I\b kJۖ7ՠ $WX_QA,Z3~߼:L⏤, @9ن_2u dYkڴ 6US-5^ly]57jI4Ùq8W~R%MS xJzC^Yͷ37=(g][Xj1R` Noɫ%kp:xթ o?T}|2X,߮7Ji*'E*RY_"&:iqtDK4xT`?$k)W(4"-X 32p/{,z9 R/*DODw`aᢟXR_!:Q0"sp?^WW6f6" z"jI~E2_WPKJeL׉w0Wmsodumper/formula.pyUT Z]ux T`!f x7FlFŐzY2\ri- ApOvFF8`eyb#B?ZbRy&ա+ ec] VTJȇAjTS_tk6wu,9P0ߘfPJf]Gmت ֕{A=\!([fꤩe4: <%aLet4|@ad4Q̣O~R+V =4m#}? bRb)z]LY*kVhqxTL CXRK2jV68U!݄(HCqbp5]0<ҨzW5$ NTDӒelo'S./@ijVRxF)e"C5f0Qs֒뗟b mtՄ`tθ@bOq2XIƳQm(G9€,7`4 ¸Yo9Lfl3"H[@iԧT۵b0x_hZk7MeDR2݉N& ZM pKhC%)3aEz^'“+'SxӔ|OB_+tGl[<>RL-s4T!wh뫪@A(Lf4ɒGSG>{u* y[a} ~mNIҼ~u sO{븖^*J-s$=eF}Qœ1~1ô$cwd[;urbdEPrjDx*3''5EF+o`U[mBgkÜB6U}Me"!P&b^dm~mKűyF.I"}61[]&4 "BҨ[):U+@zK{K0~qhw0 Zsr('OQREK8mFU [[$d/*eIYOЬMl9sTx;Gw/2p@" C)T#-9Ke)ʙVB3?\T_m2r.*W_#?Z<ԂeYv?^}vֻz24D1rA^t_Q`JC v%jvuv:f'?eMlP ǾAaa;bo/)z\\ ěbY3&"xDIh 7[Q;ꗸPyxU׾YSF33Ⱥ;,y/g܎Zr2:' U(l:y Bk,^';#xWM[:wUT P@#Oשg 07] d~ԌˉSN,J`>_7IE 4 P]U OΑfTD2ohֈIY'{Te &>WuM q Ր'Dפ24)F[3&*W~.&jwuFjƐ("˞t/݊z*s|KjPHzԱD'SS EmA?O{jFHF-[j.ʶUK9kp8ļ1w)1f:e3 Sysu/B$XW*gU,Þe8&vƓc: .>c h?RM+yI"( :*墚 )2 ų{ܖNP*޸7@x $(A1jԝT 2pb 3GFb-e3$GR}bq 6;OkFƧuqq;)k҈7jdP(!q*NO$ TŝJ<':E9sk!k@邋&\ ¿RWKGRp'P8Xu-pfJҴP}f" y=,lePȲHw*]Ydy,P~W@@uUe,sH!~򪥃Y <tݍ@"[Xl?CSNK*WαΔq<#}D%y $ĕ?8(I X;qS2S>CaP:^v 7rH姍"rwA9>M$5![0.KbsVb} ›6:nَ)biP>RqNU&c a&11  LX4heDbzV=C{g弋b!&⩍¹{քӳƓw'~Z^u  h+`pfMjaaX<6IiO$tm%AE(O؊ oMzԦlChتIxL&d_ϴ@L`k8M u|C*`ڑL:il普 Zői  %j QQ*|CǤSk8@թjIg1"TZpϤ^h(v2UBE7I*DٲI.ۺ6N,&c&ޜ[iI8 _t~_3$].Pi?w$t Jvl;Iwjlit4RCX"3)/4FCWI=$tc] <y*cd#JSA29HH*T'O*?t4QbEs.%ÆS511DiѽR(iBGJP&)lȤ_Pl:˓xlc֤%1͕ΑeHA6 /`6>(7Liؽ'LK-fV jFu{*tִo·L:uYXAUU7̦86 TQ-dL:z: JAm'Pk+D01dk$"IP 0 Qc*$Jl.҉Wn$)2U,% F ;zDEM^wҵUi; Jh}bp*˟!CQ-g8M0'p:Xd9BE{x6% ]ֻ.t{ݤ+l1tu#zo4xLL\[*&o bҤc.\>*&WݰT;e/8%W?k)bHs/t?A~^Vko?)O$>[/V'V=bxF-,FЫat)uUUr͞my8[׻pW"ēj >7KcC|YB&Wq$DʯΟ I/6}/ [ uO%Gr^+JOjz ♁=+xq :[.om_ήC@Eaա\J*cK鮉d(ZNg,gB1}MU-:7]N ^Bmy8vn=&Fߺ@1}pm =n;)-o-/-&-U$zBm)DЊ AB a|eoPN/0-} N:1vI i>0 qMNqs0t4- ͌U|.|m=XŻr:Y;8^D-A[ylHO?CE$y 9G "-Du@Qw1\BgvExaT!Ozu;lFo $؜GI#*с8ĥ́sCpCVg:|!yMoHp%z2? OIg>g.} 3w?&sP幭/(IʼUad8cTfEFlw!42SFjߌ9Yʯ,zm`O}2gEjS1q:@A+zxoWh{xtD@irudA⅑F\B+&X-!ڌfߐg1|g[{UNyAK Y}; bz8<ӀfwML5@gob|r PKgLx,msodumper/msodraw.pyUT  "Z]ux ]{s8?'RF%Zqc;aDtDN><4~ʡ{W_h|IkvΨʅTOA[oe>zs膭ӝ'% įZ?6^L+rz6:_P!^>mS8 c T_Ob^ '$ CP&յÐNѳd#.:&'-VLvjg',:ԍ^;Nw;^u}aDX ;A"ƞ9WLF~0='CLǁ1D#"G&z]\܅RƈS:PxZ|dيl+qgEK揩J{vvkc-p0S/?+24\1qR9Iʀ0 E3##81In -yVn7B`egwUص^~l8 Z;vd;%unS4ޯwь)접y_6'!evT$Jh+ V EMMr鳓2= :;xT9WP!myar؈lvnO{ꔓGFu26خU=tN Fs% Y%v4COI7lE<;]3 `Va4a34/Bvz?hH-w߼8`C?U98}/Mo߾ \$x9o]]LKZV2u4^4t}J Í~T#V NGP'Oi:NuYs~[21)DUJچ=fChIpP0\|qХ@/['G"4E.3SɥdSLu#6v~*`œ.j,e.7m:PLJ(I56,˭WS^olT!XdD} 5C# ,l<(HO 5%ńV!e8]Cya NRN`V8RYadf~nؿE\ ! RP!)WJ;d| "4h 굷U*C! A Q^l=\~*cN[AYy5-&QG'֞[45Ag槌̺ܤIS6.ek5Mlfil4['|"Vڛ%_~ w)g#nc9\0tA۪ \N;MhG1=¥:u27 ywRΈ8rD#GB~/#YJIg9aO, 奖=®Var0^-?ZKfAf۲QEX߯V5,좩,έiz!.No'gxRdwvBZ~: V|VQ I=Nmea%`L4҇1Zne"U|]Isr HѢg@e͓0A!'#yYp@q!YƖ/.dS>V|Gjn"5rvFtkk+7iq;H OL n&@>if\Ǚ4ewX !m._0]Щy4j [Ҳ!)͗V%nId3Y|?t{+)jהruFD`_ vOvO,] Oքó9n ?{OOw~WLNƄb,[F5geRFRJB> ƗR6!gSJF;sSr$9 ~b38c2.gi}y8=q9c=i)Oc\93ycU\yU?}Zq9D)\dD5 rB`Úܹ>vs>Ѽl,5%lፖ=QںG8dem鳾r_-Zn'b7^:?{fMrat؛~Fn?qZ n ;n,gFy'ل\%Fũf\dC O OϤPhj.LO?śDt"Ik&%s&m7WHa}J"coľ쀥d9}0Ie0(‡˱hK*(fisLBQf|Јhch}>1/qZ+t`yS[. {J[^rwǧo.oON[8ۅD)*5]-SIJ(,X(B7&2Oh<߷%1яvRFq$ɇ2a RbE KrŁ"H KX0c4AG(w *2a?P]V5M2R`<R/B:{7qeHi)&2 !KPٞ}]_S}r{wd_@B)E[ ~sb#*I? 2eq'H[^rӔl[l#_מOpUޔ~yJMrQ e|H$'̓w/7ANS&9( w"#Gn3q|#CU~kChz)/4{j{sQWMrQz$ȯ4TMF =_WS(oFy`k};vMrc&l $,Kz:܍>$e4h(M>$CGY&yo4hn4ha4h|0P|If4hMi)M>$#GdlG)MB f ^G}.@j)M_7XX#KY^_t<*|*x6[;#y@V..{-GSYneamokOTyc\`Z]f:ʺ rwZV}W[d3@A1$;A'b5ɾ"Q:ǷȈObB57zLfȃ-M \%fa`Edzg@daT SVjl YaY/YS2n Faz"-/3͋[66n!Y.rvLq^؀Cξ:~|,92%S7Ƿ9\.r@LC,4)[f2sp/Yn(smvR%8{|?9 8&N(>oޓqqkz+`\ i(׆;/M /;8VY1K~%y'd--RCHVI1 mFEh<5p_㛈UgHU5 N18d]Qt텾rȳWŵ6+H^\``Gjb ?|!~etR+/M%\}}fu䁇{W $~pu}P &J1f60501뱳}Y&aq<?\"T׆.&P9f-tQ:7 uňbP˳TStרagɉ*qs7],i[Q~L`t5<ߨZ70N6J+?9whm/ȀYA]`bx||7ˊ'2g`ܕwx-4w10X3 YgU:Ms2&zEj^! kGPɈ}{fLCPfzvG/͊ 9Zy3k}7 ej ykYkvaS|`O2|M >_7| 7N~ZE;޵F o{]9= \X<[a|uP"NTCОtbh),-MZ\ﻹ|̈́P@O LX՗ c#Giu$ږ }qz,"d-FJ8˵6a M (yڈ\cV)Z=Z~vPpY5?T^Ɋb><;880=*-U ez6W=9Av7l"yoŻxO> _0㥿/ޢPO/^ .Er~rmmS׼3Pצ LVқ/?/b͈"|[-8Ljő`k9۱vA\q:$qgFyv2&op :jFt\8'~v'Xg0lX:hǑЖ{Xp/zX[\n|D,5nQRh@|\Lt:R4gԌeЈEMm^:Q#Jӈ_a:[{//crS+%%DF2}WQI.i#!ζ$VI c8 aפ \(uhe #S%#[ 7hJn|s5FfU Ha.d߄9`^AmP۬?y)l|̬?Kw{ZAo0me\&`JP&G$ 4k1n[n(8xݕxE2΂G ] y1.7w .|3bɟc$_Xe0=q}nMa #U(NL'' Eϴy~uWS -VtÒpn}؝Լ;-״-)-\܅k"CoRտ@:zQJך m/ Z7, b( ( wnotQLT)a@M>36P̈́ڋ{$T$)t2X hD({F7ϊje YzQ!;xH-a 赶2J|ptWrj-> n^;hTnrSɇףIۺ;2GęI2{@@x}M!{C 8O@0$$w(fH;ʷ;=X  cH_EMh<' Z>FƓ}]"k1۸꽓kz|Y_< #Ul쪁R_ X(q-FWf= 20 $ʖhN e]!jGVA/(s@i9ò ~Πh#ap;fQL3rmO)xa^J FD(Wsz7jQYMh1ӳ[?oq|l@5hۍ"ľQ0X9VGƁ?a96e#oNn TٌG&= 4הK:0C/eM%$`&F˹aԖV~T =:ZSmP2#pqU-֏S5)Wz'Jw3n6+{D{7)׆e4B}܉luC´bT~gזFc7p#" 4 +Yi)m;$CZWfa]#1|]- P`5ӎ bf`M@&~%"266q/2"EAd|,k:r5"nн^`1^ ~d /*8Z v?W !LRg@y\" g!t>VFԕ; dTu aZE-NW[9T%*F o*3nC yݶUu "56u]Ƿo?Y=>·tIL';MAMg;e;b-}m9`>ɴ{C776{aw[?ܰF9F 6^տ_:ٶ漬s oD$ڭ^}\[w-6k&FqW5UoI 4Xxdh.4dGie9*,H*W+t] Q |x>W D#R8k|i=ᬨa^`ɫn7_n~9 :?/4!+o7DGk 2r#u`F߼^fHI걜{DcvRdWe9eb+߻?PKbbLm.Pc msodumper/msocrypto.pyUT 3Z]ux VKo8WB^IJ݅9 h`PfW"rJ1e=4"q8̓83 eB,T4c:(9d4;iN&F]M,ec@;B[i,p[F壌QY<.Eĺ{ۏ(,(MHKFF%av -2cu P#_ A숊|מCmsyȉRsA6LG"Q kab/HjQtt5?}j/?\ޑP0 K^~"\FѽJrQH0{$C`1K!iIC.LfX gA߰GIQ7\|Ն}\n&EE'\-L[~-Z&#.o{g s8Ŗ!f=C7UG5K3=Yepo7lTiQ?)m 阽х!{EwF˜gl$Z0 %!'olJPZLuϷ>.OtK=4I Sڴ)wU xI?̕j ͜%%:C#L|x4=8J9oc2%RZU'5jL̮̎ςod7NTp-cj=d keywords author keywords description <xsl:value-of select="."/>

recoll-1.36.1/filters/abiword.xsl0000644000175000017500000000443414410615043013635 00000000000000

author keywords keywords abstract <xsl:value-of select="."/>
recoll-1.36.1/filters/openxml-meta.xsl0000644000175000017500000000554514410615043014620 00000000000000 title keywords author author keywords abstract date recoll-1.36.1/filters/rcllatinstops.zip0000644000175000017500000003062114410615043015100 00000000000000PKҝ#@% 6bulgarian_iso-8859-5.txtUT  MO MOux 5[ E2b2N1laUҕB*F,#[UnZ7JA+rӮhhie}-: 0_*Avm2:G(gƌ-A,R;jIY3as"l!w!}@b&~jYdVEjn80h%v ݚ:FG\ tbwoݣ"&HE׊:lWoI*$5_)!B7 3#؝Vm$ߎx5S$bMMR!SlJM7ZÑx19>]7i Yة-țy^ߒ%CLB_np{+9I.u5# šю\ pCZ(R?Raqqrl(]Jď_ؒ,bId[FX 4*"כ Q6[$^gI -{}9=)O@v`n29Xtf*|G"} =-A?U%>tP"Eņ ])`@81@!_A]؞YUpRA͛MƅÄ^r1K[>)%U=cӪD?5˿1enPKٝ#@MVM6bulgarian_windows-1251.txtUT MOMOux 5 s1{CmDM>03 "|a2*U]~$(Ա-w1zVעÐ$h,csrF k"5`m1c_>7|._W{rBw $f跨&ܑuIf5\]dQxYbpЭcttM-zf=-b;j(1T$qq~e䌬BO\B*t03riզKB툧Q>ՁI*Ԕ!5:5~Ȧt3j8y'ص}3ۀeڂW-Y>`;$ǿRi*ZW=`_u}>"])6)džҥDKJL-r+dIe 9MJ8>-2)r)!n#[{E:|]?QY!;bzYw[ݓ \]`W 6&#.5?ͿOh6wI *ҷs#^<_ysH u/^Tl5 &jpԋ# ؅홟U '$8ڼd\8L%+퓪r*YR3v8JTSˍSPK>>- czech_iso-8859-2.txtUT dEMIOux 5RK0 -Ě AnƆ[N;?Ue $x(.6'tog{c),ry5Ӝ.3M$Xj2Ѳ<x3k;-kNV>xeg1KāI}A6L`UƳI1(K[OǔA7~(Sd@!z@eJFv3_*Z'H7K7cpsٵqR1# ^p%ZOJ4"6gVpubyXqq(Om,<{ykMy}%f]:nb:\Ң($lB8 rmB!&K ~4y@"Eh+B q\Tx#DְOVĿh|`>"ҔPK>>-Idanish_cp1252.txtUT EMIOux -PA!>qrsdutch_cp1252.txtUT 1EMIOux Q 0D;,4l AQ 7%Wy8IHEaEը@;"CQX:E剦d@n Fc2w*y1RRvdPKd>>english_cp1252.txtUT [EMIOux ER[!^Ns.gG1~7@H#=u'Fh.Ճ79+ŵ@\vqCrӆ O ̺cٕ(O]6ĒB: y@ނ+u#R&=4Mr:n@bwxG[ItC?DtcG1~6at\~AnM.l6@fЂٓ #7N)uʬ3:R#pY ^6YCl,3b3wńс1Z?ߊc?]#p뤢"ڡAyfrPKY>>!Ybfinnish_cp1252.txtUT EMIOux UX[(޺F2KEb{+"H zis}-,'7!\UȃX ?Z"Ǿ'3F*oݯe[[EVIBȱȯT#=*Ӝ"syt:VX)v0y[ޚRZe)iF/wAvˋXr RXgOw˓zv6~҆yApC2Ȇ&[cZ⍕y+m0@8I\qdk᧲=&07*D @R+SG*aM1ڌB '1J[ƀ}37A\pHM?Z[-n0Lcȯ9b,1ba[b CktwT#2 cr(12 #YF1hvVRݤޱL˲`7A8Jp* E g Lʧ\,K ʥyL.K]xdz,If#B'Ik USiG4M&jx_PrUL$_FHvĜKU'׻ 9N)9/&} _ KjP/QVr|9V…Dcӈ6-}D#b];JΙSΰڲ5rRҮFDZ{. I vPT7;M-Bk9xۍlw }4oD֏׎&A'avfwic̈Qa Ѿ;Yqg scg((OSG ť6]NCq5B|:w5e+weǭn?tE0F&e?U*d#E]Z ي,jNWRDϾ<~B޿!XcTx(l8Tߍ* Q2*fnH6Bl<UROZA)6f~WE25p20Gփփ×VjbVUo61(yT7hFׁ P JR`{feWˮ]C?~[s0tC<}gpk}ڿkRpSf#)p+dbvT;i5@$;)۱05Ӗe``S9HCrV"-g56ܖx+dEW e|)(7͠_mI`~+p-q!"rt3>`։@ej4ӭ_uD MBY[~I K7ں`D t#ٶ_fq P[`Z|B[Kud܈?}  _\ 8|IăGy*MQJ-1KY8=eyH:Qs!} &lr°t<ȗv64'#&̙.û8r'U[2~.0+fpz5 cWJ#Jā!K1^), z2OE雑 Cߚ4M'0e7U**"6iwgCpL}qUo}Ra<& ROTXjSq7T)u!}v<>>&l0S;ĝ6޸z#0fAslH12m2=oӵAi s6[g2jt.^\"N9]K13;H KCs_[ A#,%qPQ@v-9.Ȝ ?'5lUT° ΌkX݊$L<[Ɓ߹:B;F$=y PؑlC@Zc`rc(ȩ[+y2.>A2 *:N$W2w Ãڼs pwv10RFPKL>>T:8german_cp1252.txtUT EMIOux %An E)z5R<`$ 4[tE7R1cH:tH:]RdO0C`NTX t؇ GA(9+S1CrUQ}ܒ][ƎD u =9uӶPHUMdTTeTjc$OhA ?S^,ן)- hH#q;FĐ`icᔈ/#LX@لW:~G¿ aa2}0[ f!/5bB9uӶL~l B{k!7PKܛ#@]hzgreek_iso-8859-7.txtUT _IOIOux  C#h+T8DRg[F8NRwǍ`~3"Iz6N*?.~U\ yr9p[%vHDN-XRV[GqЇͯnC1.OܚMO+OrWpg꾍DSd#0PKŬ>>Ռdhungarian_iso-8859-2.txtUT 2EMJOux  0 k^YD ֥p6_N/,T壕5^֓>dCiitalian_cp1252.txtUT FEMIOux MRK0[фߢ+8 iXQBnhG8^FeL/[HyXc&"<^.{"̻6m ×q'1]N N 28me;'[<=6` .\>0R/qDzвJ4+sA`Vf\)meFߘ;Eb89A$&j\چMLXDTӽaw`Nٰc_j}'6`~4ttPDYY8ܕ4}(!2ޤ=fCdʠ}Hi3!1:π)<t%PK{>>=e9Bnorwegian_cp1252.txtUT EMIOux Qn D;Ո` %0W^+yƉ H -xlD-圴1bj4X0#~+Qm(5,9l'H7G#g{SEף{8l*O7rʏE{h J> U<LJ{Ri\P8ZIg>2opolish_iso-8859-2.txtUT  EMIOux %A DS4u:Dn},f;eFr=ܲ=! 'o?„76ePiV/{H~6cm=S]q`\,n|0=i]NFZhkmwtˁ 6nN}PK˪>>yWQportuguese_cp1252.txtUT ~EMIOux =RA0!drussian_cp1251.txtUT NMIOux -뮣0 ?`{ !rV~iWƗحZW2˓RУì m62%`ŮҬ^KSܳL+͵_A~k@KEmKee 4w tDC&ޔx˷d鉭s¢H7?5gmIU G9Eb5dR\_:mA@j [ÀϠ!ݵ\v}`LRZjl'VVt&Wy12DŽȯ F#ehӉC5HPäYmpQl.ӹy^: >jgs9^0QɿEL |?ovGbQc(] YQHSBLBT2*XPK>>!ʤtspanish_cp1252.txtUT  EMWKOux USQ {53lE͈jWx_ڪMFQ5@.-I[y"VEǿBe?.$P}Kj!J4S]'_;Zd"6GmH_vȌ 61 ֆlXCødč"ð[-!pl!gTu2oucX >au*>|t[&Kz*Bd eˮWdCgƫTabW` +l {j.Ȇ`mw\4$(߯uc}Ps k.*gjn귤ZjN K6z+nX0)G*A:5C+Z,9\ 8.@BcO0 'cc6Bh<8Q0i89V /˄4 T.w)MoKT8DtO6tWQPK>>γ)C swedish_cp1252.txtUT pEMIOux M[r D]&9-|e!@H<|m!MK(ioP zn9}6l쮶\Qou'Jq}\Tm hD1벿ɿ4 IPV:0*?H|阽Y$|D"b%*h 5,Oұܸ dԘgf2lE% DT1dtx]EU*JAhy B I~D`/E2Y8m{E ׽LޚHbr5b^ůU.Vw4pԮ2a ZՈZ@8ݰ7 ܙ1FGѿ%(nc/ s=H|4ݎP'(`/.wjLZgKVH/P5X&ŕ4cMMopө77sts__JNzC!ba3ڮ$A YrR܇Wk[57:XMq` :k̒fsmlɨm$+Ȇͭq1 b"V*w qPiF1moGܾ?gBȗJghefVl|DdCڛH9No9]mDT3Spۖe&ۋxķY۸oSyuq-*r3`q'1tl`XVFݟ#.S?D95KCgVU&MJPT'BvuvK?& Y2:N/Mu$Ar;X ո i%\C-+"b2ATe{l>l 3QӮW+%%ft8kJ%aB*xX s\Ŧ}9ԓ(@TW.;Ы⥩앐҄3 >- czech_iso-8859-2.txtUTdEMux PK>>-Idanish_cp1252.txtUTEMux PK+>>qrsdutch_cp1252.txtUT1EMux PKd>> english_cp1252.txtUT[EMux PKY>>!Yb finnish_cp1252.txtUTEMux PK.;%@ gaP french_cp1252.txtUTWBOux PKL>>T:8german_cp1252.txtUTEMux PKܛ#@]hzOgreek_iso-8859-7.txtUT_IOux PKŬ>>ՌdFhungarian_iso-8859-2.txtUT2EMux PK%>>dCiitalian_cp1252.txtUTFEMux PK{>>=e9Bnorwegian_cp1252.txtUTEMux PK>>2o8polish_iso-8859-2.txtUT EMux PK˪>>yWQportuguese_cp1252.txtUT~EMux PKF>!d russian_cp1251.txtUTNMux PK>>!ʤt"spanish_cp1252.txtUT EMux PK>>γ)C %swedish_cp1252.txtUTpEMux PK2#@e;i#La)turkish_iso-8859-9.txtUT?Oux PK*recoll-1.36.1/filters/rclimg0000755000175000017500000001345714444307651012700 00000000000000#!/usr/bin/perl # @(#$Id: rclimg,v 1.5 2008-10-09 06:41:21 dockes Exp $ (C) 2007 Cedric Scott ####################################################### # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ###################################################### # # Extract image tags with exiftool and convert the data to html for # recoll indexing. # # # maps image file tags to xapian tags # $tagMap = { 'subject' => 'subject', 'comment' => 'description', 'title' => 'title', 'headline' => 'title', 'caption' => 'caption', 'caption-abstract' => 'caption', 'author' => 'author', 'creator' => 'creator', 'from' => 'from', 'keywords' => 'keywords', 'keyword' => 'keyword', 'tag' => 'tag', }; %options = ('CharsetIPTC' => 'UTF8'); # set to non-zero if tags which map to xapian tags are to output # in the body as well as the header # $headAndBody = 1; # xapianTag # returns a xapian tag to be used for this tag # sub xapianTag { my $imgtag = shift; while ( ( $tagre, $xapiantag) = each %{$tagMap} ) { return $xapiantag if $imgtag =~ /^$tagre$/i; } return undef; } sub imgTagsToHtml { my $imageFile = shift; my $output = ""; $imageFile = '-' if $imageFile eq ''; unless ( open(IMGF, $imageFile) ) { print STDERR "$0: can't open file $imageFile\n"; return $output; # file doesn't exist or can't be read } $info = ImageInfo(\*IMGF, \%options); return $output unless $info; close(\*IMGF); $fields = []; $other = []; $titleHtmlTag = ""; foreach $tagname ( sort keys %{$info} ) { $xapiantag = xapianTag($tagname); if (defined $xapiantag ) { if ($xapiantag eq 'title') { if ($titleHtmlTag) { $titleHtmlTag = $titleHtmlTag . " - $info->{$tagname}"; } else { $titleHtmlTag = $info->{$tagname}; } } else { push @{$fields}, [ $xapiantag, $info->{$tagname} ]; } push @{$other}, [ $tagname, $info->{$tagname} ] if $headAndBody; } else { push @{$other}, [ $tagname, $info->{$tagname} ]; } } $output = "\n\n"; if ($titleHtmlTag) { $output = $output . "" . $titleHtmlTag . "\n"; } $output = $output . "\n"; foreach $tagpair ( @{$fields} ) { ($tagname, $value) = @{$tagpair}; $output = $output . "\n"; } $output = $output . "\n"; foreach $tagpair (@{$other} ) { ($tagname, $value) = @{$tagpair}; $output = $output . sprintf("%30s : %s
\n", $tagname, $value); } $output = $output . "\n\n"; return $output; } #################################################################### # Code for the rclexecm filter<->indexer protocol from here # Get one line from stdin (from recollindex), exit on eof sub readlineorexit { my $s = ; unless ($s) { # print STDERR "RCLIMG: EOF\n"; exit 0; } return $s } # Read one named parameter sub readparam { my $s = readlineorexit(); if ($s eq "\n") { return ("",""); } my @l = split(' ', $s); if (scalar(@l) != 2) { print STDERR "RCLIMG: bad line:", $s; exit 1; } my $paramname = lc $l[0]; my $paramsize = $l[1]; if ($paramsize > 0) { my $n = read STDIN, $paramdata, $paramsize; if ($n != $paramsize) { print STDERR "RCLIMG: [$paramname] expected $paramsize, got $n\n"; exit 1; } } # print STDERR "RCLIMG: [$paramname] $paramsize bytes: [$paramdata]\n"; return ($paramname, $paramdata); } # # Main program starts here. Talks the rclexecm protocol # # JFD: replaced the "use" call with a runtime load with error checking, # for compat with the missing filter detection code. # use Image::ExifTool qw(:Public); eval {require Image::ExifTool; Image::ExifTool->import(qw(:Public));}; if ($@) { print "RECFILTERROR HELPERNOTFOUND Perl::Image::ExifTool\n"; exit(1); } binmode(STDIN) || die "cannot binmode STDIN"; binmode(STDOUT) || die "cannot binmode STDOUT"; #print STDERR "RCLIMG: Starting\n"; $| = 1; while (1) { # print STDERR "RCLIMG: waiting for command\n"; my %params = (); # Read at most 10 parameters (we only actually use one), stop at empty line for($i = 1; $i < 10; $i++) { my ($name, $value) = readparam; if ($name eq "") { last; } $params{$name} = $value; } unless (defined $params{"filename:"}) { print STDERR "RCLIMG: no filename ??\n"; # Recoll is requesting next subdocument (it shouldn't cause we # returned eofnext last time), but we have none, just say so: print "Eofnow:0\nDocument: 0\n\n"; next; } print "Mimetype: 9\ntext/html"; my $data = imgTagsToHtml($params{"filename:"}); my $l = length($data); print "Document: $l\n"; # print STDERR "RCLIMG: writing $l bytes of data\n"; print $data; # Say we have no further documents for this file print "Eofnext: 0\n"; # End of output parameters: print empty line print "\n"; # print STDERR "RCLIMG: done writing data\n"; } #print STDERR "RCLIMG: Exiting\n"; recoll-1.36.1/filters/rclhwp.py0000755000175000017500000000611014410615043013323 00000000000000#!/usr/bin/env python3 # Copyright (C) 2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################### # Recoll Hanword .hwp handler # # The real work is done by pyhwp: # https://github.com/mete0r/pyhwp # https://pypi.org/project/pyhwp/ # pip3 install pyhwp # import sys import os from io import BytesIO import subprocess import rclexecm from rclbasehandler import RclBaseHandler from hwp5.filestructure import Hwp5File as fs_Hwp5File from hwp5.transforms import BaseTransform from hwp5.xmlmodel import Hwp5File as xml_Hwp5File from hwp5.utils import cached_property # Associate HTML meta names and hwp summaryinfo values def metafields(summaryinfo): yield(('Description', summaryinfo.subject + " " + summaryinfo.comments)) yield(('Author', summaryinfo.author)) yield(('Keywords', summaryinfo.keywords)) yield(('Date', summaryinfo.lastSavedTime)) # Extractor class. We use hwp summaryinfo to extract metadata and code # extracted from hwp.hwp5txt.py to extract the text. class HWP5Dump(RclBaseHandler): def __init__(self, em): super(HWP5Dump, self).__init__(em) def html_text(self, fn): # hwp wants str filenames. This is unfortunate fn = fn.decode('utf-8') try: hwpfile = fs_Hwp5File(fn) except Exception as ex: self.em.rclog("hwpfile open failed: %s" % ex) raise ex try: tt = hwpfile.summaryinfo.title.strip() if tt: tt = rclexecm.htmlescape(tt.encode('utf-8')) self.em.setfield('caption', tt) for k,v in metafields(hwpfile.summaryinfo): v = "{0}".format(v) v = v.strip() if v: v = rclexecm.htmlescape(v.encode('utf-8')) k = k.encode('utf-8') self.em.setfield(k, v) except Exception as e: self.em.rclog("Exception: %s" % e) finally: hwpfile.close() # The first version of this file used conversion to text using # the hwp5 module (no subproc). But this apparently mishandled # tables. Switched to executing hwp5html instead. See 1st git # version for the old approach. return rclexecm.execPythonScript(["hwp5html", "--html", fn]) if __name__ == '__main__': proto = rclexecm.RclExecM() extract = HWP5Dump(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclzip.py0000755000175000017500000002042014507456214013341 00000000000000#!/usr/bin/env python3 # Copyright (C) 2014 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Zip file extractor for Recoll import os import posixpath import fnmatch import datetime from zipfile import ZipFile import rclexecm import rclconfig import conftree # Note about file names (python 2.6. 2.7, don't know about 3.) # # There is a bit in zip entries to indicate if the filename is encoded # as utf-8 or not. If the bit is set, zipfile decodes the file name # and stores it in the catalog as an unicode object. Else it uses the # binary string, which it decodes as CP437 (zip standard). # # When reading the file, the input file name is used by rclzip.py # directly as an index into the catalog. # # When we send the file name data to the indexer, we have to serialize # it as byte string, we can't pass unicode objects to and from. This # means that we have to test if the name is unicode. If it is, we send # the string encoded as utf-8. When reading, if the input is utf-8, we # turn it to unicode and use this to access the zip member, else we # use the binary string. # # In the case where an archive member name is a valid non-ascii utf-8 # string, but the flag is not set (which could probably happen if the # archiver did not try to detect utf-8 file names), this will fail, # because we'll convert back the utf-8 string to unicode and pass this # to zipfile, but an utf-8 string, not a unicode object is actually in # the catalog in this case, so the access will fail (will be seen at # preview or open time). This does not affect ascii file names because # the representation is the same anyway. # # To avoid this problem, we'd need to pass a separate bit of # information indicating that encoding was performed, not just rely on # the utf-8 validity test (ie have a 1st char switch), but this would be # incompatible with existing indexes. Instead we try both ways... # # Also, some zip files contain file names which are not encoded as # CP437 (Ex: EUC-KR which was the test case). Python produces garbage # paths in this case (this does not affect the ipath validity, just # the display), which is expected, but unzip succeeds in guessing the # correct encoding, I have no idea how, but apparently the magic # occurs in process.c:GetUnicodeData(), which succeeds in finding an # utf-8 string which zipfile does not see (to be checked: was a quick look). # Anyway: this is a python zipfile issue. class ZipExtractor: def __init__(self, em): self.filename = None self.f = None self.zip = None self.currentindex = 0 self.em = em def closefile(self): #self.em.rclog("Closing %s" % self.filename) if self.zip: self.zip.close() if self.f: self.f.close() self.f = None self.zip = None def extractone(self, ipath): #self.em.rclog("extractone: [%s]" % ipath) docdata = "" try: info = self.zip.getinfo(ipath) # There could be a 4GB Iso in the zip. We have to set a limit if info.file_size > self.em.maxmembersize: self.em.rclog("extractone: entry %s size %d too big" % (ipath, info.file_size)) docdata = "" #raise BadZipfile() else: docdata = self.zip.read(ipath) try: # We are assuming here that the zip uses forward slash # separators, which is not necessarily the case. At # worse, we'll get a wrong or no file name, which is # no big deal (the ipath is the important data # element). filename = posixpath.basename(ipath) self.em.setfield("filename", filename) dt = datetime.datetime(*info.date_time) self.em.setfield("modificationdate", str(int(dt.timestamp()))) except: pass ok = True except Exception as err: ok = False iseof = rclexecm.RclExecM.noteof if self.currentindex >= len(self.zip.namelist()) -1: self.closefile() iseof = rclexecm.RclExecM.eofnext return (ok, docdata, rclexecm.makebytes(ipath), iseof) ###### File type handler api, used by rclexecm ----------> def openfile(self, params): self.closefile() filename = params["filename"] self.filename = filename self.currentindex = -1 self.skiplist = [] config = rclconfig.RclConfig() config.setKeyDir(os.path.dirname(filename)) usebaseskipped = config.getConfParam("zipUseSkippedNames") if usebaseskipped: skipped = config.getConfParam("skippedNames") self.em.rclog("skippedNames: %s"%self.skiplist) self.skiplist += conftree.stringToStrings(skipped) skipped = config.getConfParam("zipSkippedNames") if skipped is not None: self.skiplist += conftree.stringToStrings(skipped) try: # Note: py3 ZipFile wants an str file name, which # is wrong: file names are binary. But it accepts an # open file, and open() has no such restriction self.f = open(filename, 'rb') self.zip = ZipFile(self.f) return True except Exception as err: self.em.rclog("openfile: failed: [%s]" % err) return False def getipath(self, params): ipath = params["ipath"] ok, data, ipath, eof = self.extractone(ipath) if ok: return (ok, data, ipath, eof) # Not found. Maybe we need to decode the path? try: ipath = ipath.decode("utf-8") return self.extractone(ipath) except Exception as err: self.em.rclog("extractone: failed: [%s]" % err) return (ok, data, ipath, eof) def getnext(self, params): if self.currentindex == -1: # Return "self" doc self.currentindex = 0 self.em.setmimetype('text/plain') if len(self.zip.namelist()) == 0: self.closefile() eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, "", "", eof) if self.currentindex >= len(self.zip.namelist()): #self.em.rclog("getnext: EOF hit") self.closefile() return (False, "", "", rclexecm.RclExecM.eofnow) else: entryname = self.zip.namelist()[self.currentindex] # This is how we'd fix a badly decoded entry, but then # this can't be used as ipath any more #fixedname = entryname.encode('cp437').decode('euc-kr') #self.em.rclog("REENCODED: %s"%fixedname) if len(self.skiplist) != 0: while self.currentindex < len(self.zip.namelist()): entryname = self.zip.namelist()[self.currentindex] for pat in self.skiplist: if fnmatch.fnmatch(entryname, pat): entryname = None break if entryname is not None: break self.currentindex += 1 if entryname is None: self.closefile() return (False, "", "", rclexecm.RclExecM.eofnow) ret= self.extractone(entryname) self.currentindex += 1 return ret # Main program: create protocol handler and extractor and run them proto = rclexecm.RclExecM() extract = ZipExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rcltxtlines.py0000755000175000017500000001032314410615043014400 00000000000000#!/usr/bin/env python3 """Index text lines as document (execm handler sample). This exists to demonstrate the execm interface and is not meant to be useful or efficient""" import sys import os import rclexecm # Here try to import your document module if you need one. There is # not much risk of 'sys' missing, but this shows what you should do if # something is not there: the data will go to the 'missing' file, which # can be displayed by the GUI as a list of MIME type and missing # helpers. try: import sys except: print("RECFILTERROR HELPERNOTFOUND python3:sys") sys.exit(1); # Our class. class rclTXTLINES: def __init__(self, em): # Store a ref to our execm object so that we can use its services. self.em = em # This is called once for every processed file during indexing, or # query preview. For multi-document files, it usually creates some # kind of table of contents, and resets the current index in it, # because we don't know at this point if this is for indexing # (will walk all entries) or previewing (will request # one). Actually we could know from the environment but it's just # simpler this way in general. Note that there is no close call, # openfile() will just be called repeatedly during indexing, and # should clear any existing state def openfile(self, params): """Open the text file, create a contents array""" self.currentindex = -1 try: f = open(params["filename"].decode('UTF-8'), "r") except Exception as err: self.em.rclog("openfile: open failed: [%s]" % err) return False self.lines = f.readlines() return True # This is called during indexing to walk the contents. The first # time, we return a 'self' document, which may be empty (e.g. for # a tar file), or might contain data (e.g. for an email body, # further docs being the attachments), and may also be the only # document returned (for single document files). def getnext(self, params): # Self doc. Here empty. # # This could also be the only entry if this file type holds a # single document. We return eofnext in this case # # !Note that the self doc has an *empty* ipath if self.currentindex == -1: self.currentindex = 0 if len(self.lines) == 0: eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, "", "", eof) if self.currentindex >= len(self.lines): return (False, "", "", rclexecm.RclExecM.eofnow) else: ret= self.extractone(self.currentindex) self.currentindex += 1 return ret # This is called for query preview to request one specific (or the # only) entry. Here our internal paths are stringified line # numbers, but they could be tar archive paths or whatever we # returned during indexing. def getipath(self, params): return self.extractone(int(params["ipath"])) # Most handlers factorize common code from getipath() and # getnext() in an extractone() method, but this is not part of the # interface. def extractone(self, lno): """Extract one line from the text file""" # Need to specify the MIME type here. This would not be # necessary if the ipath was a file name with a usable # extension. self.em.setmimetype("text/plain") # Warning of upcoming eof saves one roundtrip iseof = rclexecm.RclExecM.noteof if lno == len(self.lines) - 1: iseof = rclexecm.RclExecM.eofnext try: # Return the doc data and internal path (here stringified # line number). If we're called from getipath(), the # returned ipath is not that useful of course. return (True, self.lines[lno], str(lno), iseof) except Exception as err: self.em.rclog("extractone: failed: [%s]" % err) return (False, "", lno, iseof) # Initialize: create our protocol handler, the filetype-specific # object, link them and run. proto = rclexecm.RclExecM() extract = rclTXTLINES(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rcldia.py0000755000175000017500000000524114410615043013266 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # dia (http://live.gnome.org/Dia) file filter for recoll # stefan.friedel@iwr.uni-heidelberg.de 2012 # # add the following to ~/.recoll/mimeconf into the [index] section: # application/x-dia-diagram = execm rcldia.py;mimetype=text/plain;charset=utf-8 # and into the [icons] section: # application/x-dia-diagram = drawing # and finally under [categories]: # other = ...\ # application/x-dia-diagram # # in ~/.recoll/mimemap: # .dia = application/x-dia-diagram # Small fixes from jfd: dia files are sometimes not compressed. import rclexecm from rclbasehandler import RclBaseHandler import re from gzip import GzipFile import xml.parsers.expat # some regexps to parse/format the xml data: delete #/spaces at the b/eol and # ignore empty lines rhs = re.compile(r'^#\s*(.*)') rhe = re.compile(r'(.*)\s*#$') rempty = re.compile(r'^#?\s*#?$') # xml parser for dia xml file class Parser: def __init__(self,rclem): self._parser = xml.parsers.expat.ParserCreate(encoding='UTF-8') self._parser.StartElementHandler = self.startelement self._parser.EndElementHandler = self.endelement self._parser.CharacterDataHandler = self.chardata self.string = [] self.handlethis = False self.rclem = rclem def startelement(self, name, attrs): if name == 'dia:string': self.handlethis = True else: self.handlethis = False def chardata(self,data): if self.handlethis: # check if line is not empty and replace hashes/spaces if not rempty.search(data): self.string.append(rhe.sub(r'\1',rhs.sub(r'\1',data))) def endelement(self,name): self.handlethis = False def feed(self, fh): self._parser.ParseFile(fh) del self._parser class DiaExtractor(RclBaseHandler): def __init__(self, em): super(DiaExtractor, self).__init__(em) def html_text(self, fn): try: dia = GzipFile(fn, 'rb') # Dia files are sometimes not compressed. Quite weirdly, # GzipFile does not complain until we try to read. data = dia.readline() dia.seek(0) except: # File not compressed ? dia = open(fn, 'rb') diap = Parser(self.em) diap.feed(dia) html = '
'
        html += rclexecm.htmlescape('\n'.join(diap.string))
        html += '
' return html # Main program: create protocol handler and extractor and run them proto = rclexecm.RclExecM() extract = DiaExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/recollepub.zip0000644000175000017500000002760714410615043014345 00000000000000PK =pMepub/UT ܇[܇[ux PKdWMWE epub/utils.pyUT ۱[屇[ux mSM0+FTEaaeJUZ0Fff}dž/͛PZf#Ig;(K9,AuuQR+B'EQ}%~"Z2j< BC`%Ng5;jnY.>ItP[xFЊ?xaB5MEpSfĮwq[ٍ<{3,uPJנL?L p 8:E,zU2k7|=' oс`Iw gF\ kZݳMp (aJ*@@m=8 ZN/S+K~/`m{D>W:>zP?qC~ "'Q|Anlׄ)Cǹ4}8U'۶g0.!,{ 1|1\s{ _X޼q?|~%xk2.z՝ms¶a߲KXkE5\Wt~)U;bh|ymIʕ5%+"bd  d\0/+[xć_ #yV٪ yy$,*֭3  ހ,Vs=̎;Nd8e{XR A*)@\4 Y*RpuX6LM 7gVʹ?ϳd H2H&fĭ ].:U+ZJuM9vpMVڠӹ2W/r 8]N+z-|4 6&HOEQN2hMA種.i%k9>CM*4w RP?`s6d*u*kuGDL0tBW S7؂Q㳓bnH\_4f՘MA)E`)!җw걵{ 7¯І&E s3E=D"l/+^dǫ)6L(Do&ƾɖ G˚8<>Ȝ,$7Y=CvN=qS.}D#.P c~xP1 NcV´W7*fszŽpUa!QuWژs7kLnc{Pv hiRlC6,B6-r<1)?&?:hNRku0b:Y:F; ÷1N#3G?O ̡IhJ 6[VSHC;!KaK>`"턮,eNA3g3f0Ϟ+{yl^՘]*#],e1[Lu+MԚ3gzg-c` gN%p?svDG@ _lPY rC8?#mmFuwH +P o ѠI2<䵣~C#MVn~󐮳6pg|GJDw-- QQn)lE: Sܨ^ "-t%uX# 9 ftJżbVm=Tb M ˕4'rz*z Nwa,6T; C\:~jI[+Jݗ˦X@uMÇr|]<|\3̳ #[) qgtܰ;ct.5H fq3txkۜW6cd+^ӝU؁?x pGKf{z.JПwFup:<$8Bag`%`eq@iҩsBw!>  ЊQ{10 QA,bc;\]^?Q^1[-s28D5r$ĽóaP"r9v|ؤgUȞY iG%@;x 4p@{ 99֩ڐ#Ǣkl;Uv; O܍V~[Sw؈z<Nbҟm0H<=Hv$]Yۦ8EOȚhYx͐QgӋ{cQIA?na}ME}2a>*ͨwh9Wh;3"8>gڇ=|>؇yEJ ܊+q>.cocy:%yhmؑf]fHm<+K|CUk38'vQ2}I RG[>VRBW5 p3vͩeI9yt$svVXw؇NnPKQl)}d.&n:?WoǕk2O"Ht3bLz" >wnWt.#!޾sd526$kt! zܮuV)*JHHR*!Q%չ;DT'u~O: # J-VXz毂AIijR:OGg k{r }e/`P=ES~4|5K_PUDo+/VUmbݩ)$njQ^A>#)4_* &13kT*E1` pq ""[.D+䒡)R7 "h]c>m>vU@ZxeHY+"LUE?x"EO?$It;#A`8ކ/\ÿĿ|g ɓk b}ΒqJiO1ֲFi0s9'>_2`%L#ZkSgCsQ.!5/./ s $iE!a #^6vdF&gx6wceo(Cffv&۩ĬG +%;[31$z^lҟge}f/l4Zň) t(tJcCIGok0viUcV6hU+botc u v/D~ U5EV_l[,JB? ADC K$ 1qz9]3=DM{wsk2 Ħ[FUMbLf:-l@O湪e3/7Fu^iDO}'Z9@K/AZӒ :'=idPKdWM,2epub/__init__.pyUT ۱[屇[ux ks; =.$f8SɑMmIc㴎:G8@뻻(CA"q{{=`=`Q'݌U;|2E-+soDxB-THd\ &6Ղ=B&yƾo,5 eUVC7yQ*K`I) h*Wy@s69K".D*^gqIDo7)=<7ɔM UeZ3e<+1JqJbHM߿% Ti1׋$K^ c`k ]Nq|?z o6iy `Wgm\,6`ã{~'F?r(g '<=>YH-`e(.÷7p*Ye"g_ 9+UG`Lpd"Xy(VD5"τ?1]*TP)Xkۅdsd|ֈS8Fx5" ?#gq7.oF)&|a;wRIE*K\'U֛rB /xRZ hERl(97"˃śd(DYwA,y*vv|[ @0prB'/儽Z'?z.oA( Q,gN8 +7)!`ɳiQ@&bC6#Og/P,Qxթ>upPQsdH^B*3aAx2O GB๪hOaOSU؏Ǽ+yoN{xzyRweɷ\]?,{XEutm @IY`Ÿ^7X j Aѓ}"ݹЃ /&N=M.:P16F'~wk`vEx 8:.EUѭE 'F݆_KzJ~/(a.cY=wݳm&sc)M(~ֲPWa2[B5My5ߍ|'R6AFx"/(q|q쩼?݌6T.ո`E|Дgyn!g[ Lɶ$5B!TB{2 # a/ G(vD1=c(-( %!?"(< ߮ܒƛA QKюZnPf]y b1UrTjߚvU3[u |{sA(=.GWAiTO)4aLB=N2!s! @of I:!}IEip ~c=4U*|bz>ǪS880(`Ԑ5$f`ox{R,@Q@gLǚ QZ6Z*,P](0 0ƻڦ>0 y Q_$2ubzt]6©Y]V A!r}k`Nkgr m%OPc JAMI1\{T(gZW (8S! X)@6Pꉰ].PbfeȞyY1zknbL-`U19V8wC '?P8$ORzmjh1=_8p: LJ[S;.&d6핦qèSv?6)%j&H/A0z|/nyNKJM o䈰QӃOv Ԣ/dI$3n4.\lWY,HఞPa'em"ZPokTQMn-ˋ5ф!`Ŗҝy郧|+ܓ쐮_h7,[܉'Tؒʌ6=B5o޺F ~ ʼn]O_#9`9a4x ԩOx]Җŏ3ITw+(zv[t7^aL98g .G]["$:DZcA }(SET0ॉ\uGϠnq%rRw%pڄ7H4 qYzl q@;=h7ȼ*}DȦ<^:CU:' i?Ġrܭ1"PȘm<]:DLt/Pj[M#ndF/9K3#Ҹf$ڝϘf*ntBzӤjw=Ԑ Ɂp/idyv(,TJ0d ߍ2 7 a8iS}kPJcks7BܠO $f[Sm\I_PK =pMAepub/UT܇[ux PKdWMWE ?epub/utils.pyUT۱[ux PKdWMwf~ Z xepub/ncx.pyUT۱[ux PKdWM9EyE mepub/opf.pyUT۱[ux PKdWM,2epub/__init__.pyUT۱[ux PK-recoll-1.36.1/filters/thunderbird-open-message.sh0000755000175000017500000000106114410615043016701 00000000000000#!/bin/sh # # Create a symbolic link with a .eml extension to convince thunderbird # that it should open the target file as a message. fatal() { echo $* exit 1 } usage() { fatal Usage: `basename $0` '' } tempdir=/tmp/recoll-nonexistent cleanup() { rm -rf -- "$tempdir" } test $# -eq 1 || usage message="$1" tempdir="`mktemp -d`" test -d "$tempdir" || exit 1 trap cleanup EXIT lnk="$tempdir/message.eml" ln -s "$message" "$lnk" # set -x thunderbird --profile "$tempdir" --no-remote --new-instance -file "$lnk" recoll-1.36.1/filters/xml.xsl0000755000175000017500000000133414410615043013005 00000000000000 <xsl:value-of select="//*[local-name() = 'title'][1]"/>

recoll-1.36.1/filters/rclman0000755000175000017500000000672514410615043012665 00000000000000#!/bin/sh # @(#$Id: rclman,v 1.2 2007-06-08 13:51:09 dockes Exp $ (C) 2004 J.F.Dockes # Parts taken from Estraier: #================================================================ # Estraier: a personal full-text search system # Copyright (C) 2003-2004 Mikio Hirabayashi #================================================================ #================================================================ # Process a manual page with groff and output html # We'd like to use -Thtml, but this doesn't seem to be always available. # So we use -Tutf8 and postprocess this to remove the ^H overstriking # #================================================================ # set variables LANG=C ; export LANG LC_ALL=C ; export LC_ALL progname="rclman" filetype=man #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE checkcmds groff # We need a temporary directory if test z"$RECOLL_TMPDIR" != z; then ttdir=$RECOLL_TMPDIR elif test z"$TMPDIR" != z ; then ttdir=$TMPDIR else ttdir=/tmp fi tmpdir=$ttdir/rclman_tmp$$ mkdir $tmpdir || exit 1 mkdir $tmpdir/rclmantmp || exit 1 cleanup() { # Note that we're using a constant part (rclmantmp), that hopefully # guarantees that we can't do big mistakes here. rm -rf $tmpdir/rclmantmp rmdir $tmpdir } trap cleanup EXIT HUP QUIT INT TERM # Note: the qt preview really likes to find an tag at the top echo '' # Filter out some unwanted data when indexing if test X$RECOLL_FILTER_FORPREVIEW = Xyes ; then (cd $tmpdir/rclmantmp; groff -man -Thtml) < "$infile" else # When indexing we try to avoid adding terms for the section headers, which # are unfortunately somewhat ill defined. We eliminate rather loosely # lines containing likely section header words preceded by a tag. (cd $tmpdir/rclmantmp; groff -man -Thtml) < "$infile" | \ egrep -v \ '>NAME|>SYNOPSIS|>DESCRIPTION|>EXIT|>OPTIONS|>DIAGNOSTICS|>SUPPORTED HARDWARE|>CONFIGURATION|>AUTHOR|>BUGS|>REPORTING BUGS|>KNOWN BUGS|>COPYRIGHT|>SEE ALSO|>HISTORY|>ENVIRONMENT|>FILES' fi exit 0 recoll-1.36.1/filters/rclxmp.py0000755000175000017500000000346314410615043013341 00000000000000#!/usr/bin/env python3 # Copyright (C) 2016 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Code to extract XMP tags using libexempi and python-xmp can_xmp = True try: import libxmp.utils except: can_xmp = False import re import sys xmp_index_re = re.compile('''\[[0-9+]\]$''') #xmp_index_re = re.compile('''3''') def rclxmp_enabled(): return can_xmp def rclxmp(filename): if not can_xmp: return None, "python-xmp not accessible" errstr = "" try: xmp = libxmp.utils.file_to_dict(filename) except Exception as e: errstr = str(e) if errstr: return None, errstr out = {} for ns in xmp.keys(): for entry in xmp[ns]: if entry[1]: k = xmp_index_re.sub('', entry[0]) if k.find("/") != -1: continue if k in out: out[k] += " " + entry[1] else: out[k] = entry[1] return out, "" if __name__ == "__main__": d, err = rclxmp(sys.argv[1]) if d: print("Data: %s" % d) else: print("Error: %s" % err) recoll-1.36.1/filters/rclfb2.py0000755000175000017500000000477414410615043013214 00000000000000#!/usr/bin/env python3 # Copyright (C) 2014 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ###################################### import sys import rclexecm import rclxslt import rclgenxslt stylesheet_all = ''' <xsl:value-of select="."/> author

''' if __name__ == '__main__': proto = rclexecm.RclExecM() extract = rclgenxslt.XSLTExtractor(proto, stylesheet_all) rclexecm.main(proto, extract) recoll-1.36.1/filters/cmdtalk.py0000644000175000017500000001721514410615043013450 00000000000000################################# # Copyright (C) 2016 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################################## # Command communication module and utilities. See commands in cmdtalk.h # # All data is binary. This is important for Python3 # All parameter names are converted to and processed as str/unicode import sys import os import tempfile import shutil import getopt import traceback def makebytes(data): if data is None: return b"" if isinstance(data, bytes): return data else: return data.encode("UTF-8") ############################################ # CmdTalk implements the communication protocol with the master # process. It calls an external method to use the args and produce # return data. class CmdTalk(object): def __init__(self, outfile=sys.stdout, infile=sys.stdin, exitfunc=None): try: self.myname = os.path.basename(sys.argv[0]) except: self.myname = "???" self.outfile = outfile self.infile = infile self.exitfunc = exitfunc self.fields = {} if sys.platform == "win32": import msvcrt msvcrt.setmode(self.outfile.fileno(), os.O_BINARY) msvcrt.setmode(self.infile.fileno(), os.O_BINARY) try: self.debugfile except: self.debugfile = None try: self.nodecodeinput except: self.nodecodeinput = False if self.debugfile: self.errfout = open(self.debugfile, "a") else: self.errfout = sys.stderr def log(self, s, doexit = 0, exitvalue = 1): print("CMDTALK: %s: %s" % (self.myname, s), file=self.errfout) if doexit: if self.exitfunc: self.exitfunc(exitvalue) sys.exit(exitvalue) def breakwrite(self, outfile, data): if sys.platform != "win32": outfile.write(data) else: # On windows, writing big chunks can fail with a "not enough space" # error. Seems a combined windows/python bug, depending on versions. # See https://bugs.python.org/issue11395 # In any case, just break it up total = len(data) bs = 4*1024 offset = 0 while total > 0: if total < bs: tow = total else: tow = bs #self.log("Total %d Writing %d to stdout: %s" % (total,tow,data[offset:offset+tow])) outfile.write(data[offset:offset+tow]) offset += tow total -= tow # Read single parameter from process input: line with param name and size # followed by data. The param name is returned as str/unicode, the data # as bytes def readparam(self): inf = self.infile.buffer s = inf.readline() if s == b'': if self.exitfunc: self.exitfunc(0) sys.exit(0) s = s.rstrip(b'\n') if s == b'': return ('', b'') l = s.split() if len(l) != 2: self.log(b'bad line: [' + s + b']', 1, 1) paramname = l[0].decode('ASCII').rstrip(':') paramsize = int(l[1]) if paramsize > 0: paramdata = inf.read(paramsize) if len(paramdata) != paramsize: self.log("Bad read: wanted %d, got %d" % (paramsize, len(paramdata)), 1, 1) else: paramdata = b'' if not self.nodecodeinput: try: paramdata = paramdata.decode('utf-8') except Exception as ex: self.log("Exception decoding param: %s" % ex) paramdata = b'' #self.log("paramname [%s] paramsize %d value [%s]" % # (paramname, paramsize, paramdata)) return (paramname, paramdata) def senditem(self, nm, data): data = makebytes(data) l = len(data) self.outfile.buffer.write(makebytes("%s: %d\n" % (nm, l))) self.breakwrite(self.outfile.buffer, data) # Send answer: document, ipath, possible eof. def answer(self, outfields): for nm,value in outfields.items(): #self.log("Senditem: [%s] -> [%s]" % (nm, value)) self.senditem(nm, value) # End of message print(file=self.outfile) self.outfile.flush() #self.log("done writing data") # Call processor with input params, send result def processmessage(self, processor, params): # In normal usage we try to recover from processor errors, but # we sometimes want to see the real stack trace when testing safeexec = True if safeexec: try: outfields = processor.process(params) except Exception as err: self.log("processmessage: processor raised: [%s]" % err) traceback.print_exc() outfields = {} outfields["cmdtalkstatus"] = "1" outfields["cmdtalkerrstr"] = str(err) else: outfields = processor.process(params) self.answer(outfields) # Loop on messages from our master def mainloop(self, processor): while 1: #self.log("waiting for command") params = dict() # Read at most 10 parameters (normally 1 or 2), stop at empty line # End of message is signalled by empty paramname for i in range(10): paramname, paramdata = self.readparam() if paramname == "": break params[paramname] = paramdata # Got message, act on it self.processmessage(processor, params) # Common main routine for testing: either run the normal protocol # engine or a local loop. This means that you can call # cmdtalk.main(proto,processor) instead of proto.mainloop(processor) # from your module, and get the benefits of command line testing def main(proto, processor): if len(sys.argv) == 1: proto.mainloop(processor) # mainloop does not return. Just in case sys.exit(1) # Not running the main loop: run one processor call for debugging def usage(): print("Usage: cmdtalk.py pname pvalue [pname pvalue...]", file=sys.stderr) sys.exit(1) def debprint(out, s): proto.breakwrite(out, makebytes(s+'\n')) args = sys.argv[1:] if len(args) == 0 or len(args) % 2 != 0: usage() params = dict() for i in range(int(len(args)/2)): params[args[2*i]] = args[2*i+1] res = processor.process(params) ioout = sys.stdout.buffer for nm,value in res.items(): #self.log("Senditem: [%s] -> [%s]" % (nm, value)) bdata = makebytes(value) debprint(ioout, "%s->" % nm) proto.breakwrite(ioout, bdata) ioout.write(b'\n') recoll-1.36.1/filters/fb2.xsl0000755000175000017500000000272414410615043012662 00000000000000 <xsl:value-of select="."/> author

recoll-1.36.1/filters/rclcheckneedretry.sh0000755000175000017500000000266114410615043015515 00000000000000#!/bin/sh # This script is called by recollindex to determine if it would be # worth retrying files which previously failed to index. # # This is the default implementation, it is pointed to by the # 'checkneedretryindexscript' variable in the default recoll.conf # # The script exits with 0 if retrying should be performed (something # changed), 1 else. # # We check /usr/bin and /usr/local/bin modification date against the # previous value recorded inside ~/.config/Recoll.org/needidxretrydate # # If any argument is given, we record the new state instead of # generating it (this should be used at the end of an indexing pass # with retry set). # # If $HOME does not exist, there is nothing we can do (happens, for example when run as upmpdcli) if test ! -d "$HOME" ; then exit 0 fi # Bin dirs to be tested: bindirs="/usr/bin /usr/local/bin $HOME/bin /opt/*/bin" rfiledir=$HOME/.config/Recoll.org rfile=$rfiledir/needidxretrydate nrfile=$rfiledir/tneedidxretrydate test -d $rfiledir || mkdir -p $rfiledir # If any argument is given, we are called just to record the new # state. We do not recompute it as it may have changed during # indexing, but just move the state in place if test $# != 0 ; then mv -f $nrfile $rfile exit 0 fi # Compute state of bin dirs and see if anything changed: > $nrfile for dir in $bindirs; do ls -ld $dir >> $nrfile 2> /dev/null done if cmp -s $rfile $nrfile ; then exit 1 else exit 0 fi recoll-1.36.1/filters/rclgaim0000755000175000017500000000710614410615043013021 00000000000000#!/bin/sh # @(#$Id: rclgaim,v 1.4 2007-06-08 13:51:08 dockes Exp $ (C) 2004 J.F.Dockes # Parts taken from Estraier: #================================================================ # Estraier: a personal full-text search system # Copyright (C) 2003-2004 Mikio Hirabayashi #================================================================ #================================================================ # Extract text and other information from gaim logs # #================================================================ # set variables LANG=C ; export LANG LC_ALL=C ; export LC_ALL progname="rclgaim" filetype="gaim log" #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE checkcmds awk iconv awk ' # First line: parse from, to , output html header NR == 1 { if (NF != 9) { printf("Bad format: (NF %d) %s\n", NF, $0) exit 1 } to = $3 from = $8 proto = $9 date = $5 " " $6 #printf("from [%s] to [%s] proto [%s] date [%s]\n", from, to, proto, date) print "" print " " $0 "" print "" # Yes there is no such thing as a "date" meta tag. This probably should # be http-equiv=last-modified or such printf("\n", date) print "" # Remember who the main persons are. authors[from] = "yes" authors[to] = "yes" next } # Message first line. We strip from/to and time when indexing /^\([0-2][0-9]:[0-5][0-9]:[0-5][0-9]\)/ { if (ENVIRON["RECOLL_FILTER_FORPREVIEW"] == "yes") { # Preview: output everything print $0 " " "
" } else { # Index: output only text, except each new author once #printf("INDEX: NF %d [%s] [%s] [%s] ", NF, $1, $2, $3); from = $2 sub(":$", "", from); if (authors[from] == "") { authors[from] = "yes" printf("%s : ", from); } for (idx = 3; idx <= NR; idx++) { printf("%s ", $idx) } printf("
\n") } next } # Continuation line: print it { printf("%s
\n", $0) } END { printf("\n") } ' < "$infile" # exit normally exit 0 recoll-1.36.1/filters/recoll-we-move-files.py0000755000175000017500000001266314444307651016005 00000000000000#!/usr/bin/python3 # Copyright (C) 2017-2022 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., #51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ Move pages created by the recoll-we extension from the download directory to the recoll web queue. The files (content file and metadata side file) are then renamed and moved to the Recoll web queue directory. We recognize recoll files by their pattern: recoll-we-[cm]-.rclwe, and the fact that both a d and an m file must exist. While not absolutely foolproof, this should be quite robust. The script is normally executed by recollindex at appropriate times, but it can also be run by hand. """ import sys import os import re import getopt try: from hashlib import md5 as md5 except: import md5 import shutil try: from recoll import rclconfig except: import rclconfig _mswindows = (sys.platform == "win32") verbosity = 0 def logdeb(s): if verbosity >= 4: print("%s"%s, file=sys.stderr) # # wnloaded instances of the same page are suffixed with (nn) by the # browser. We are passed a list of (hash, instancenum, filename) # triplets, sort it, and keep only the latest file. def delete_previous_instances(l, downloadsdir): l.sort(key = lambda e: "%s-%05d"%(e[0], e[1]), reverse=True) ret = {} i = 0 while i < len(l): hash,num,fn = l[i] logdeb("Found %s"%fn) ret[hash] = fn j = 1 while i + j < len(l): if l[i+j][0] == hash: ofn = l[i+j][2] logdeb("Deleting %s"%ofn) os.unlink(os.path.join(downloadsdir, ofn)) j += 1 else: break i += j return ret fn_re = re.compile('''recoll-we-([mc])-([0-9a-f]+)(\([0-9]+\))?\.rclwe''') def list_all_files(dir): files=os.listdir(dir) mfiles = [] cfiles = [] for fn in files: mo = fn_re.match(fn) if mo: mc = mo.group(1) hash = mo.group(2) num = mo.group(3) if not num: num = "(0)" num = int(num.strip("()")) if mc == 'm': mfiles.append([hash, num, fn]) else: cfiles.append([hash, num, fn]) return mfiles,cfiles ####################### def msg(s): print(f"{s}", file=sys.stderr) def usage(): msg("Usage: recoll-we-move-files.py [-c ]") msg(" The script needs the recoll configuration directory. This can be set either through") msg(" the RECOLL_CONFDIR environment variable or the '-c' command line option (which takes") msg(" precedence). If none is set, the default configuration directory will be used.") sys.exit(1) opts, args = getopt.getopt(sys.argv[1:], "c:") if not len(args) == 0: usage() configdir = None for opt,val in opts: #logdeb(f"opt {opt} val {val}") if opt == "-c": configdir = val if not os.path.isdir(val): msg(f"{val} is not a directory") usage() else: usage() config = rclconfig.RclConfig(argcnf=configdir) # Get the directory where the browser extension creates the page files. Our user can set it as a # subdirectory of the default Downloads directory, for tidiness downloadsdir = config.getConfParam("webdownloadsdir") if not downloadsdir: downloadsdir = "~/Downloads" downloadsdir = os.path.expanduser(downloadsdir) if not os.path.isdir(downloadsdir): msg(f"Downloads directory {downloadsdir} does not exist") sys.exit(1) # Get the target recoll webqueue directory, into which we are going to move the downloaded files. webqueuedir = config.getConfParam("webqueuedir") if not webqueuedir: if _mswindows: webqueuedir = "~/AppData/Local/RecollWebQueue" else: webqueuedir = "~/.recollweb/ToIndex" webqueuedir = os.path.expanduser(webqueuedir) os.makedirs(webqueuedir, exist_ok = True) #logdeb(f"recoll confdir [{configdir}] downloadsdir [{downloadsdir}] webqueuedir [{webqueuedir}]") # Get the lists of all files created by the browser addon mfiles, cfiles = list_all_files(downloadsdir) # Only keep the last version mfiles = delete_previous_instances(mfiles, downloadsdir) cfiles = delete_previous_instances(cfiles, downloadsdir) #logdeb("Mfiles: %s"% mfiles) #logdeb("Cfiles: %s"% cfiles) # Move files to webqueuedir target directory # The webextensions plugin creates the metadata files first. So it may # happen that a data file is missing, keep them for next pass. # The old plugin created the data first, so we move data then meta for hash in cfiles.keys(): if hash in mfiles.keys(): newname = "firefox-recoll-web-" + hash shutil.move(os.path.join(downloadsdir, cfiles[hash]), os.path.join(webqueuedir, newname)) shutil.move(os.path.join(downloadsdir, mfiles[hash]), os.path.join(webqueuedir, "_" + newname)) recoll-1.36.1/filters/rclrar.py0000755000175000017500000001503314410615043013315 00000000000000#!/usr/bin/env python3 # Rar file filter for Recoll # Adapted from the Zip archive filter by mroark. # Copyright (C) 2004 J.F.Dockes + mroark for rar bits # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import sys import rclexecm import os # We can use two different unrar python modules. Either python3-rarfile # which is a wrapper over the the unrar command line, or python3-unrar # which is a ctypes wrapper of the unrar lib. # # Python3-rarfile is the one commonly packaged on linux. # # Python3-unrar needs the libunrar library, built from unrar source # code (https://www.rarlab.com/rar_add.htm), which is not packaged on # Debian or Fedora, probably because of licensing issues. It is # packaged as libunrar5 on Ubuntu, and there are Fedora packages on # RPM Fusion, and it's probably easy to build the Ubuntu package on # Debian. Python-unrar works much better and is the right choice if # the licensing is not an issue. # # The interfaces is similar, but python-unrar uses forward slashes # in internal file paths while python-rarfile uses backslashes (ipaths # are opaque anyway). using_unrar = False try: from unrar import rarfile using_unrar = True except Exception as ex: try: from rarfile import RarFile except: print("RECFILTERROR HELPERNOTFOUND python3:rarfile/python3:unrar") sys.exit(1); # Requires RarFile python module. Try "sudo pip install rarfile" or # install it with the system package manager # # Also for many files, you will need the non-free version of unrar # (https://www.rarlab.com/rar_add.htm). The unrar-free version fails # with the message "Failed the read enough data" # # This is identical to rclzip.py except I did a search/replace from zip # to rar, and changed this comment. class RarExtractor: def __init__(self, em): self.currentindex = 0 self.em = em def extractone(self, ipath): #self.em.rclog("extractone: [%s]" % ipath) docdata = "" isdir = False try: if using_unrar: if type(ipath) == type(b''): ipath = ipath.decode('UTF-8') rarinfo = self.rar.getinfo(ipath) # dll.hpp RHDF_DIRECTORY: 0x20 isdir = ((rarinfo.flag_bits & 0x20) != 0) else: rarinfo = self.rar.getinfo(ipath) isdir = rarinfo.isdir() except Exception as err: self.em.rclog("extractone: using_unrar %d rar.getinfo failed: [%s]" % (using_unrar,err)) return (True, docdata, ipath, False) if not isdir: try: if rarinfo.file_size > self.em.maxmembersize: self.em.rclog("extractone: entry %s size %d too big" % (ipath, rarinfo.file_size)) docdata = "" else: docdata = self.rar.read(ipath) ok = True except Exception as err: self.em.rclog("extractone: rar.read failed: [%s]" % err) ok = False else: docdata = "" ok = True self.em.setmimetype("application/x-fsdirectory") iseof = rclexecm.RclExecM.noteof if self.currentindex >= len(self.rar.namelist()) -1: iseof = rclexecm.RclExecM.eofnext return (ok, docdata, rclexecm.makebytes(ipath), iseof) def closefile(self): self.rar = None ###### File type handler api, used by rclexecm ----------> def openfile(self, params): self.currentindex = -1 try: if using_unrar: # There might be a way to avoid the decoding which is # wrong on Unix, but I'd have to dig further in the # lib than I wish to. This is used on Windows anyway, # where all Recoll paths are utf-8 fn = params["filename"].decode("UTF-8") self.rar = rarfile.RarFile(fn, 'rb') else: # The previous versions passed the file name to # RarFile. But the py3 version of this wants an str as # input, which is wrong of course, as filenames are # binary. Circumvented by passing the open file f = open(params["filename"], 'rb') self.rar = RarFile(f) return True except Exception as err: self.em.rclog("RarFile: %s"%err) return False def getipath(self, params): ipath = params["ipath"] ok, data, ipath, eof = self.extractone(ipath) if ok: return (ok, data, ipath, eof) # Not found. Maybe we need to decode the path? try: ipath = ipath.decode("utf-8") return self.extractone(ipath) except Exception as err: return (ok, data, ipath, eof) def getnext(self, params): if self.currentindex == -1: # Return "self" doc self.currentindex = 0 self.em.setmimetype('text/plain') if len(self.rar.namelist()) == 0: self.closefile() eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, "", "", eof) if self.currentindex >= len(self.rar.namelist()): #self.em.rclog("getnext: EOF hit") self.closefile() return (False, "", "", rclexecm.RclExecM.eofnow) else: ret = self.extractone(self.rar.namelist()[self.currentindex]) self.currentindex += 1 if ret[3] == rclexecm.RclExecM.eofnext or \ ret[3] == rclexecm.RclExecM.eofnow: self.closefile() return ret # Main program: create protocol handler and extractor and run them proto = rclexecm.RclExecM() extract = RarExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclchm.py0000755000175000017500000003357714410615043013315 00000000000000#!/usr/bin/env python3 """Extract Html files from a Microsoft Compiled Html Help file (.chm)""" import sys import os import re import posixpath from urllib.parse import unquote as urllib_unquote from urllib.parse import urlparse as urlparse_urlparse from html.parser import HTMLParser import subprocess import rclconfig import rclexecm # pychm has no official port to Python3, hence no package in the # standard place. Linux Recoll bundles a python3 port which is identical # to pychm, but named recollchm to avoid conflicts because it is installed # as a normal python package (in /usr/lib/pythonxx/dist-packages, # not recoll/filters.). No such issues on Windows try: # First try the system (or recoll-local on Windows) version if any from chm import chm,chmlib except: try: from recollchm import chm,chmlib except: print("RECFILTERROR HELPERNOTFOUND python3:chm") sys.exit(1); def _deb(s): print("%s"%s, file=sys.stderr) # Small helper routines def getfile(chmfile, path): """Extract internal file text from chm object, given path""" if type(path) != type(b''): raise Exception("Chm:getfile: must be called with path as bytes") res, ui = chmfile.ResolveObject(path) if res != chmlib.CHM_RESOLVE_SUCCESS: #_deb("ResolveObject failed: %s" % path) return "" res, doc = chmfile.RetrieveObject(ui) if not res: _deb("RetrieveObject failed: %s" % path) return "" return doc def peekfile(chmfile, path, charset): """Check that path resolves in chm object""" if type(path) == type(u''): path = path.encode(charset) res, ui = chmfile.ResolveObject(path) if res != chmlib.CHM_RESOLVE_SUCCESS: return False return True # CHM Topics tree handler class ChmTopicsParser(HTMLParser): """Parse the chm's Topic file which is basically a listing of internal nodes (html files mostly). Build a list of all nodes (parent.contents), which will then be used to walk and index the chm. Most nodes in the Topic file look like the following:
  • Maybe we should filter out non "text/sitemap" Objects, and maybe there are things of interest whose name is not Local, but for now, we just take all values for parameters named "Local" (with some filtering/massaging), until proven wrong """ def __init__(self, rclchm): HTMLParser.__init__(self) self.em = rclchm.em self.rclchm = rclchm def handle_starttag(self, tag, attrs): #self.em.rclog("Beginning of a %s tag" % tag) # If this is a param tag with name Local, we're interested in # the value which lists a file ref. Discard those with # # in them (references inside files) # Sometimes it seems that refs are like Vendor:filename::path, # we only keep the path, and only if the file matches if tag != 'param': return name = '' value = '' for (nm,val) in attrs: if nm == 'name': name = val if nm == 'value': value = val #self.em.rclog("Name [%s] value [%s]" %(name, value)) if name != 'Local' or value == '': return # value may be url-encoded. Decode it. If there are no % in there, will # do nothing value = urllib_unquote(value) localpath = "" ll = value.split(":") if len(ll) == 1: localpath = value elif len(ll) == 4 and ll[-1] and ll[-3]: #self.em.rclog("File: [%s] sfn [%s]" % ((ll[-3]), self.rclchm.sfn)) # We used to test against the simple file name, but this does # not work if the file is renamed. Just check that the internal # path resolves. Old: if ll[-3] == self.rclchm.sfn: localpath = ll[-1] if not peekfile(self.rclchm.chm, localpath, self.rclchm.charset): #self.em.rclog("SKIPPING %s" % ll[-3]) localpath = "" if len(localpath) != 0 and localpath.find("#") == -1: if localpath[0] != '/': localpath = "/" + localpath self.rclchm.contents.append(localpath.encode(self.rclchm.charset)) # Used when there is no Topics node. Walk the links tree class ChmWalker(HTMLParser): """Links tree walker. This recursively follows all internal links found in the tree from the top node given as input, and augments the contents list.""" def __init__(self, rclchm, path, contents): HTMLParser.__init__(self) self.rclchm = rclchm self.chm = rclchm.chm self.contents = contents if type(path) == type(u''): path = path.encode(self.rclchm.charset) self.path = posixpath.normpath(path) self.dir = posixpath.dirname(self.path) contents.append(self.path) def handle_starttag(self, tag, attrs): if tag != 'a': return href = '' for (nm,val) in attrs: if nm == 'href': href = val path = "" res = urlparse_urlparse(href) if (not res.scheme or res.scheme.lower == "ms-its"): path = res.path lpath = path.split(':') if len(lpath) == 3: # MS-ITS::somefile.chm:/some/path/file.htm ? As far as I # know this never happens because there was a runtime error # in this path path = lpath[2] if not peekfile(self.chm, path, self.rclchm.charset): path = "" elif len(lpath) == 1: path = lpath[0] else: path = "" if path: bpath = path.encode(self.rclchm.charset) if path[0] == "/"[0]: npath = posixpath.normpath(bpath) else: npath = posixpath.normpath(posixpath.join(self.dir, bpath)) if not npath in self.contents: #_deb("Going into [%s] paths [%s]\n" % (npath,str(self.contents))) text = getfile(self.chm, npath) if text: try: newwalker = ChmWalker(self.rclchm, npath, self.contents) t,c = self.rclchm.fixencoding(text) newwalker.feed(t) except: pass class rclCHM: """RclExecM slave worker for extracting all files from an Msoft chm file. We first extract the list of internal nodes, and them return them one by one. The ipath is the node path""" def __init__(self, em): self.contents = [] self.chm = chm.CHMFile() self.em = em cf = rclconfig.RclConfig() self.catenate = cf.getConfParam("chmcatenate") self.catenate = int(self.catenate) if self.catenate else False self.em.setmimetype("text/html") expr = b'''()''' self.asciito1252re = re.compile(expr, re.IGNORECASE) expr = b'''''' self.findcharsetre = re.compile(expr, re.IGNORECASE) self._headtagre = re.compile(b'', re.IGNORECASE) self._headerre = re.compile(b'()', re.IGNORECASE|re.DOTALL) self._bodyre = re.compile(b']*>(.*)', re.IGNORECASE|re.DOTALL) def extractone(self, path, norclaptag=False): """Extract one path-named internal file from the chm file""" #self.em.rclog("extractone: [%s]" % (path,)) if type(path) == type(u''): path = path.encode(self.charset) iseof = rclexecm.RclExecM.noteof if self.currentindex >= len(self.contents) -1: iseof = rclexecm.RclExecM.eofnext res, ui = self.chm.ResolveObject(path) #self.em.rclog("extract: ResolveO: %d [%s]" % (res, ui)) if res != chmlib.CHM_RESOLVE_SUCCESS: return (False, "", path, iseof) # RetrieveObject() returns len,value res, doc = self.chm.RetrieveObject(ui) #self.em.rclog("extract: RetrieveObject: %d [%s]" % (res, doc)) if res > 0: if not norclaptag: doc = self._headtagre.sub(b'''''', doc) return (True, doc, path, iseof) return (False, "", path, iseof) def dumpall(self): alltxt=b"" first = True for pth in self.contents: ret,doc,path,iseof = self.extractone(pth, norclaptag=True) if not ret: continue if first: # Save a header headmatch = self._headerre.search(doc) if headmatch: header = headmatch[1] #_deb("HEADER [%s]" % header) #_deb("type(self.chm.title) %s" % type(self.chm.title)) if type(self.chm.title) == type(""): title = self.chm.title.encode(self.charset) else: title = self.chm.title header = re.sub(b"", b"" + title + b"", doc, re.IGNORECASE|re.DOTALL) first = False alltxt += header + b"" body = self._bodyre.search(doc) if body: body = body[1] #_deb("BODY [%s]" % body[0:200]) alltxt += body alltxt += b"" return alltxt def fixencoding(self, text): """Fix encoding for supposedly html document. We do 2 things here: - Change any 'ASCII' charset decl to windows-1252 - Decode the string if it's originally bytes because that's what Python HTMLParser actually expects even if it does not really say so. See http://bugs.python.org/issue3932. """ # Memo. Charset decl example. Maybe we should also process the # HTML5 charset tag ? # if type(text) == type(b''): # Fix an ascii charset decl to windows-1252 text = self.asciito1252re.sub(b'''\1windows-1252\4''', text, 1) # Convert to unicode according to charset decl m = self.findcharsetre.search(text) if m: charset = m.group(1).decode('cp1252') else: charset = 'cp1252' text = text.decode(charset, errors='replace') return text, charset def closefile(self): self.chm.CloseCHM() def openfile(self, params): """Open the chm file and build the contents list by extracting and parsing the Topics object""" self.currentindex = -1 self.contents = [] filename = params["filename"] if not self.chm.LoadCHM(filename): self.em.rclog("LoadCHM failed") return False #self.em.rclog("home [%s] topics [%s] title [%s]" % # (self.chm.home, self.chm.topics, self.chm.title)) self.topics = self.chm.GetTopicsTree() self.charset = 'cp1252' if self.topics: # Parse Topics file and extract list of internal nodes #self.em.rclog("Got topics"); tp = ChmTopicsParser(self) text,self.charset = self.fixencoding(self.topics) tp.feed(text) tp.close() else: # No topics. If there is a home, let's try to walk the tree #self.em.rclog("GetTopicsTree failed") if not self.chm.home: self.em.rclog("No topics and no home") return False home = self.chm.home if home[0] != b'/'[0]: home = b"/" + home text = getfile(self.chm, home) if not text: self.em.rclog("No topics and no home content") return False walker = ChmWalker(self, self.chm.home, self.contents) text,self.charset = self.fixencoding(text) walker.feed(text) walker.close() # Eliminate duplicates but keep order (can't directly use set) u = set() ct = [] for t in self.contents: if t not in u: ct.append(t) u.add(t) self.contents = ct #self.em.rclog("Contents size %d contents %s" % (len(self.contents), self.contents)) return True def getipath(self, params): return self.extractone(params["ipath"]) def getnext(self, params): if self.catenate: alltxt = self.dumpall() self.closefile() if alltxt: return (True, alltxt, "", rclexecm.RclExecM.eofnext) else: return (False, "", "", rclexecm.RclExecM.eofnow) if self.currentindex == -1: # Return "self" doc self.currentindex = 0 self.em.setmimetype('text/plain') if len(self.contents) == 0: self.closefile() eof = rclexecm.RclExecM.eofnext else: eof = rclexecm.RclExecM.noteof return (True, "", "", eof) if self.currentindex >= len(self.contents): self.closefile() return (False, "", "", rclexecm.RclExecM.eofnow) else: ret = self.extractone(self.contents[self.currentindex]) if ret[3] == rclexecm.RclExecM.eofnext or \ ret[3] == rclexecm.RclExecM.eofnow: self.closefile() self.currentindex += 1 return ret proto = rclexecm.RclExecM() extract = rclCHM(proto) rclexecm.main(proto, extract) recoll-1.36.1/filters/rclpurple0000755000175000017500000001115214410615043013407 00000000000000#!/bin/sh # @(#$Id: rclpurple,v 1.1 2008-09-15 08:00:17 dockes Exp $ (C) 2004 J.F.Dockes # Parts taken from Estraier: #================================================================ # Estraier: a personal full-text search system # Copyright (C) 2003-2004 Mikio Hirabayashi #================================================================ #================================================================ # Extract text and other information from gaim logs # #================================================================ # set variables LANG=C ; export LANG LC_ALL=C ; export LC_ALL progname="rclpurple" filetype="purple/pidgin log" #RECFILTCOMMONCODE ############################################################################## # !! Leave the previous line unmodified!! Code imported from the # recfiltcommon file # Utility code common to all shell filters. This could be sourced at run # time, but it's slightly more efficient to include the code in the # filters at build time (with a sed script). # Describe error in a way that can be interpreted by our caller senderror() { echo RECFILTERROR $* # Also alert on stderr just in case echo ":2:$progname::: $*" 1>&2 exit 1 } iscmd() { cmd=$1 case $cmd in */*) if test -x $cmd -a ! -d $cmd ; then return 0; else return 1; fi ;; *) oldifs=$IFS; IFS=":"; set -- $PATH; IFS=$oldifs for d in $*;do test -x $d/$cmd -a ! -d $d/$cmd && return 0;done return 1 ;; esac } checkcmds() { for cmd in $*;do if iscmd $cmd then a=1 else senderror HELPERNOTFOUND $cmd fi done } # show help message if test $# -ne 1 -o "$1" = "--help" then echo "Convert a $filetype file to HTML text for Recoll indexing." echo "Usage: $progname [infile]" exit 1 fi infile="$1" # check the input file existence (may be '-' for stdin) if test "X$infile" != X- -a ! -f "$infile" then senderror INPUTNOSUCHFILE "$infile" fi # protect access to our temp files and directories umask 77 ############################################################################## # !! Leave the following line unmodified ! #ENDRECFILTCOMMONCODE checkcmds awk awk ' # First line: parse from, to , output html header NR == 1 { if (NF != 14 && NF != 13 && NF != 9) { printf("Bad format: (NF %d) %s\n", NF, $0) exit 1 } to = $3 if (NF == 14 || NF == 13) { mon_i["Jan"] = "01" mon_i["Feb"] = "02" mon_i["Mar"] = "03" mon_i["Apr"] = "04" mon_i["May"] = "05" mon_i["Jun"] = "06" mon_i["Jul"] = "07" mon_i["Aug"] = "08" mon_i["Sep"] = "09" mon_i["Oct"] = "10" mon_i["Nov"] = "11" mon_i["Dec"] = "12" date = $8 "-" mon_i[$7] "-" $6 "T" $9 if (NF == 14) { from = $13 } if (NF == 13) { from = $12 } } if (NF == 9) { from = $8 date = $5 } #printf("from [%s] to [%s] date [%s]\n", from, to, date) print "" print " " $0 "" # Yes there is no such thing as a "date" meta tag. This probably should # be http-equiv=last-modified or such, but recollindex understands "date" printf("\n", date) printf("\n", from) printf("\n", to) print "" print "
    "
    
        if (ENVIRON["RECOLL_FILTER_FORPREVIEW"] == "yes") {
            printf("%s\n", $0)
        }
    
        # Remember who the main persons are. This is so that we output
        # them once while indexing the conversation body, to avoid giving
        # excessive weight by repeated indexing to the term.
        authors[from] = "yes"
        authors[to] = "yes"
        next
    }
    
    /^\([0-2][0-9]:[0-5][0-9]:[0-5][0-9]\)/ {
        # Conversation element 1st line. We strip from/to (except 1st
        # occurrence) and time when indexing.  Time is not interesting and
        # repeated from/to indexing would give excessive weight
        if (ENVIRON["RECOLL_FILTER_FORPREVIEW"] == "yes") {
            # Preview: output everything
            print $0
        } else {
    	# Index: output only text, except each new author once. Unfortunately,
            # it is too late to add them to the "author" field.
    	from = $2
    	sub(":$", "", from);
    	if (authors[from] == "") {
    	   authors[from] = "yes"
    	   printf("%s : ", from);
    	}
            for (idx = 3; idx <= NR; idx++) {
                printf("%s ", $idx)
    	}
    	printf("\n")
        }
        next
    }
    # Conversation element continuation line: print it
    {
        printf("%s\n", $0) 
    }
    END {
        printf("
    \n") } ' < "$infile" # exit normally exit 0 recoll-1.36.1/filters/rclocrtesseract.py0000755000175000017500000002004614410615043015232 00000000000000#!/usr/bin/env python3 ################################# # Copyright (C) 2020 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ######################################################## # Running tesseract for Recoll OCR (see rclocr.py) import os import sys import tempfile import subprocess import glob import rclexecm _mswindows = (sys.platform == "win32") if _mswindows: ocrlangfile = "rclocrlang.txt" else: ocrlangfile = ".rclocrlang" _okexts = ('.tif', '.tiff', '.jpg', '.png', '.jpeg') tesseractcmd = None pdftoppmcmd = None pdftocairocmd = None def _deb(s): rclexecm.logmsg("rclocrtesseract: %s" % s) tmpdir = None def _maybemaketmpdir(): global tmpdir if tmpdir: if not tmpdir.vacuumdir(): _deb("openfile: vacuumdir %s failed" % tmpdir.getpath()) return False else: tmpdir = rclexecm.SafeTmpDir("rclocrtesseract") def cleanocr(): global tmpdir if tmpdir: del tmpdir tmpdir = None # Return true if tesseract and the appropriate conversion program for # the file type (e.g. pdftoppt for pdf) appear to be available def ocrpossible(config, path): # Check for tesseract global tesseractcmd if not tesseractcmd: config.setKeyDir(os.path.dirname(path)) tesseractcmd = config.getConfParam("tesseractcmd") if tesseractcmd: # It is very tempting to quote this value, esp. on Windows where it # will contain whitespace. There is no chance that an actual # command line would have quotes, so unquote it. tesseractcmd = tesseractcmd.strip('"') else: tesseractcmd = rclexecm.which("tesseract") if not tesseractcmd: _deb("tesseractcmd not found") return False if not os.path.isfile(tesseractcmd): _deb("tesseractcmd parameter [%s] is not a file" % tesseractcmd) return False # Check input format base,ext = os.path.splitext(path) ext = ext.lower() if ext in _okexts: return True if ext == '.pdf': # Check for pdftoppm. We could use pdftocairo, which can # produce a multi-page pdf and make the rest simpler, but the # legacy code used pdftoppm for some reason, and it appears # that the newest builds from conda-forge do not include # pdftocairo. So stay with pdftoppm. global pdftoppmcmd, pdftocairocmd if not pdftoppmcmd and not pdftocairocmd: pdftocairocmd = rclexecm.which("pdftocairo") if not pdftocairocmd: pdftocairocmd = rclexecm.which("poppler/pdftocairo") if not pdftocairocmd: pdftoppmcmd = rclexecm.which("pdftoppm") if not pdftoppmcmd: pdftoppmcmd = rclexecm.which("poppler/pdftoppm") if pdftoppmcmd or pdftocairocmd: return True return False # Try to guess tesseract language. This should depend on the input # file, but we have no general way to determine it. So use the # environment and hope for the best. def _guesstesseractlang(config, path): tesseractlang = "" dirname = os.path.dirname(path) # First look for a language def file in the file's directory pdflangfile = os.path.join(dirname, ocrlangfile) if os.path.isfile(pdflangfile): tesseractlang = open(pdflangfile, "r").read().strip() if tesseractlang: _deb("Tesseract lang from file: %s" % tesseractlang) return tesseractlang # Then look for a config file option. config.setKeyDir(dirname) tesseractlang = config.getConfParam("tesseractlang") if tesseractlang: _deb("Tesseract lang from config: %s" % tesseractlang) return tesseractlang # Half-assed trial to guess from LANG then default to english try: localelang = os.environ.get("LANG", "").split("_")[0] if localelang == "en": tesseractlang = "eng" elif localelang == "de": tesseractlang = "deu" elif localelang == "fr": tesseractlang = "fra" except: pass if not tesseractlang: tesseractlang = "eng" _deb("Tesseract lang (guessed): %s" % tesseractlang) return tesseractlang # Process pdf file: use pdftoppm to split it into ppm pages, then run # tesseract on each and concatenate the result. It would probably be # possible instead to use pdftocairo to produce a tiff, buf pdftocairo # is sometimes not available (windows). def _pdftesseract(config, path): if not tmpdir: return b"" tesseractlang = _guesstesseractlang(config, path) #tesserrorfile = os.path.join(tmpdir.getpath(), "tesserrorfile") tmpfile = os.path.join(tmpdir.getpath(), "ocrXXXXXX") # Split pdf pages try: tmpdir.vacuumdir() if pdftocairocmd: cmd = [pdftocairocmd, "-tiff", "-tiffcompression", "lzw", "-r", "300", path, tmpfile] else: cmd = [pdftoppmcmd, "-r", "300", path, tmpfile] #_deb("Executing %s" % cmd) subprocess.check_call(cmd) except Exception as e: _deb("%s failed: %s" % (pdftoppmcmd,e)) return b"" # Note: unfortunately, pdftoppm silently fails if the temp file # system is full. There is no really good way to check for # this. We consider any empty file to signal an error pages = glob.glob(tmpfile + "*") for f in pages: size = os.path.getsize(f) if os.path.getsize(f) == 0: _deb("pdftoppm created empty files. " "Suspecting full file system, failing") return False, "" nenv = os.environ.copy() cnthreads = config.getConfParam("tesseractnthreads") if cnthreads: try: nthreads = int(cnthreads) nenv['OMP_THREAD_LIMIT'] = cnthreads except: pass for f in sorted(pages): out = b'' try: out = subprocess.check_output( [tesseractcmd, f, f, "-l", tesseractlang], stderr=subprocess.STDOUT, env=nenv) except Exception as e: _deb("%s failed: %s" % (tesseractcmd,e)) errlines = out.split(b'\n') if len(errlines) > 5: _deb("Tesseract error output: %d %s" % (len(errlines),out)) # Concatenate the result files txtfiles = glob.glob(tmpfile + "*" + ".txt") data = b"" for f in sorted(txtfiles): data += open(f, "rb").read() return True,data def _simpletesseract(config, path): tesseractlang = _guesstesseractlang(config, path) try: out = subprocess.check_output( [tesseractcmd, path, 'stdout', '-l', tesseractlang], stderr=subprocess.DEVNULL) except Exception as e: _deb("%s failed: %s" % (tesseractcmd,e)) return False, "" return True, out # run ocr on the input path and output the result data. def runocr(config, path): _maybemaketmpdir() base,ext = os.path.splitext(path) ext = ext.lower() if ext in _okexts: return _simpletesseract(config, path) else: return _pdftesseract(config, path) if __name__ == '__main__': import rclconfig config = rclconfig.RclConfig() path = sys.argv[1] if ocrpossible(config, path): ok, data = runocr(config, sys.argv[1]) else: _deb("ocrpossible returned false") sys.exit(1) if ok: sys.stdout.buffer.write(data) else: _deb("OCR program failed") recoll-1.36.1/filters/rclppt.py0000755000175000017500000000306614410615043013337 00000000000000#!/usr/bin/env python3 # Recoll PPT text extractor import rclexecm import rclexec1 import re import sys import os class PPTProcessData: def __init__(self, em): self.em = em self.out = [] self.gotdata = 0 def takeLine(self, line): if not self.gotdata: self.out.append(b'' + \ b'' + \ b'
    ')
                self.gotdata = True
            self.out.append(rclexecm.htmlescape(line))
    
        def wrapData(self):
            return b'\n'.join(self.out) + b'''
    ''' class PPTFilter: def __init__(self, em): self.em = em self.ntry = 0 def reset(self): self.ntry = 0 pass def getCmd(self, fn): if self.ntry: return ([], None) self.ntry = 1 cmd = rclexecm.which("ppt-dump.py") if cmd: # ppt-dump.py often exits 1 with valid data. Ignore exit value return ([sys.executable, cmd, "--no-struct-output", "--dump-text"], PPTProcessData(self.em), rclexec1.Executor.opt_ignxval) else: return ([], None) if __name__ == '__main__': if not rclexecm.which("ppt-dump.py"): print("RECFILTERROR HELPERNOTFOUND ppt-dump.py") sys.exit(1) proto = rclexecm.RclExecM() filter = PPTFilter(proto) extract = rclexec1.Executor(proto, filter) rclexecm.main(proto, extract) recoll-1.36.1/filters/rcldjvu.py0000755000175000017500000000543314410615043013504 00000000000000#!/usr/bin/env python3 # Copyright (C) 2016 J.F.Dockes # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Recoll DJVU extractor import os import sys import re import rclexecm import subprocess from rclbasehandler import RclBaseHandler class DJVUExtractor(RclBaseHandler): def __init__(self, em): super(DJVUExtractor, self).__init__(em) self.djvutxt = rclexecm.which("djvutxt") if not self.djvutxt: print("RECFILTERROR HELPERNOTFOUND djvutxt") sys.exit(1); self.djvused = rclexecm.which("djvused") def html_text(self, fn): self.em.setmimetype('text/html') fn = rclexecm.subprocfile(fn) # Extract metadata metadata = b"" if self.djvused: try: metadata = subprocess.check_output( [self.djvused, fn, "-e", "select 1;print-meta"]) except Exception as e: self.em.rclog("djvused failed: %s" % e) author = "" title = "" metadata = metadata.decode('UTF-8', 'replace') for line in metadata.split('\n'): line = line.split('"') if len(line) >= 2: nm = line[0].strip() if nm == "author": author = ' '.join(line[1:]) elif nm == "title": title = ' '.join(line[1:]) # Main text txtdata = subprocess.check_output([self.djvutxt, fn]) txtdata = txtdata.decode('UTF-8', 'replace') data = '''''' data += '''''' + rclexecm.htmlescape(title) + '''''' data += '''''' if author: data += '''''' data += '''
    '''
    
            data += rclexecm.htmlescape(txtdata)
            data += '''
    ''' return data # Main program: create protocol handler and extractor and run them proto = rclexecm.RclExecM() extract = DJVUExtractor(proto) rclexecm.main(proto, extract) recoll-1.36.1/xaposix/0000755000175000017500000000000014521161751011561 500000000000000recoll-1.36.1/xaposix/safesysstat.h0000644000175000017500000000567514410615043014233 00000000000000/* safesysstat.h: #include , but enabling large file support. * * Copyright (C) 2007,2012 Olly Betts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #ifndef XAPIAN_INCLUDED_SAFESYSSTAT_H #define XAPIAN_INCLUDED_SAFESYSSTAT_H #include // For most platforms, AC_SYS_LARGEFILE enables support for large files at // configure time, but MSVC doesn't use configure so we have to put the // magic somewhere else - i.e. here! #ifdef _MSC_VER // MSVC needs to call _stati64() instead of stat() and the struct which holds // the information is "struct _stati64" instead of "struct stat" so we just // use #define to replace both in one go. We also want to use _fstati64() // instead of fstat() but in this case we can use a function-like macro. // // This hack is a problem is we ever want a method called "stat", or one called // fstat which takes 2 parameters, but we can probably live with these // limitations. #ifdef stat # undef stat #endif #ifdef fstat # undef fstat #endif // NB: _stati64 not _stat64 (the latter just returns a 64 bit timestamp). #define stat _stati64 #define fstat(FD, BUF) _fstati64(FD,BUF) #endif #ifdef __WIN32__ // MSVC lacks these POSIX macros and other compilers may too: #ifndef S_ISDIR # define S_ISDIR(ST_MODE) (((ST_MODE) & _S_IFMT) == _S_IFDIR) #endif #ifndef S_ISREG # define S_ISREG(ST_MODE) (((ST_MODE) & _S_IFMT) == _S_IFREG) #endif // On UNIX, mkdir() is prototyped in but on Windows it's in // , so just include that from here to avoid build failures on // MSVC just because of some new use of mkdir(). This also reduces the // number of conditionalised #include statements we need in the sources. #include // Add overloaded version of mkdir which takes an (ignored) mode argument // to allow source code to just specify a mode argument unconditionally. // // The () around mkdir are in case it's defined as a macro. inline int (mkdir)(const char *pathname, mode_t /*mode*/) { return _mkdir(pathname); } #else // These were specified by POSIX.1-1996, so most platforms should have // these by now: #ifndef S_ISDIR # define S_ISDIR(ST_MODE) (((ST_MODE) & S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG # define S_ISREG(ST_MODE) (((ST_MODE) & S_IFMT) == S_IFREG) #endif #endif #endif /* XAPIAN_INCLUDED_SAFESYSSTAT_H */ recoll-1.36.1/xaposix/safefcntl.h0000644000175000017500000000427314410615043013620 00000000000000/* safefcntl.h: #include , but working around broken platforms. * * Copyright (C) 2006,2007 Olly Betts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #ifndef XAPIAN_INCLUDED_SAFEFCNTL_H #define XAPIAN_INCLUDED_SAFEFCNTL_H #include #if defined __cplusplus && defined open // On some versions of Solaris, fcntl.h pollutes the namespace by #define-ing // "open" to "open64" when largefile support is enabled. This causes problems // if you have a method called "open" (other symbols are also #define-d // e.g. "creat" to "creat64", but only "open" is a problem for Xapian so // that's the only one we currently fix). #ifdef _MSC_VER // MSVC #define-s open but also defines a function called open, so just undef // the macro. # undef open #else inline int fcntl_open_(const char *filename, int flags, mode_t mode) { return open(filename, flags, mode); } inline int fcntl_open_(const char *filename, int flags) { return open(filename, flags); } #undef open inline int open(const char *filename, int flags, mode_t mode) { return fcntl_open_(filename, flags, mode); } inline int open(const char *filename, int flags) { return fcntl_open_(filename, flags); } #endif #endif // O_BINARY is only useful for platforms like Windows which distinguish between // text and binary files, but it's cleaner to define it to 0 here for other // platforms so we can avoid #ifdef where we need to use it in the code. #ifndef __WIN32__ # ifndef O_BINARY # define O_BINARY 0 # endif #endif #endif /* XAPIAN_INCLUDED_SAFEFCNTL_H */ recoll-1.36.1/xaposix/safeunistd.h0000644000175000017500000000466314410615043014023 00000000000000/* safeunistd.h: , but with compat. and large file support for MSVC. * * Copyright (C) 2007 Olly Betts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #ifndef XAPIAN_INCLUDED_SAFEUNISTD_H #define XAPIAN_INCLUDED_SAFEUNISTD_H #ifndef _MSC_VER # include #else // sys/types.h has a typedef for off_t so make sure we've seen that before // we hide it behind a #define. # include // MSVC doesn't even HAVE unistd.h - io.h seems the nearest equivalent. // We also need to do some renaming of functions to get versions which // work on large files. # include # ifdef lseek # undef lseek # endif # ifdef off_t # undef off_t # endif # define lseek(FD, OFF, WHENCE) _lseeki64(FD, OFF, WHENCE) # define off_t __int64 // process.h is needed for getpid(). # include #endif #ifdef __WIN32__ #ifdef _MSC_VER /* Recent MinGW versions define this */ inline unsigned int sleep(unsigned int seconds) { // Use our own little helper function to avoid pulling in . extern void xapian_sleep_milliseconds(unsigned int millisecs); // Sleep takes a time interval in milliseconds, whereas POSIX sleep takes // a time interval in seconds, so we need to multiply 'seconds' by 1000. // // But make sure the multiplication won't overflow! 4294967 seconds is // nearly 50 days, so just sleep for that long and return the number of // seconds left to sleep for. The common case of sleep(CONSTANT) should // optimise to just xapian_sleep_milliseconds(CONSTANT). if (seconds > 4294967u) { xapian_sleep_milliseconds(4294967000u); return seconds - 4294967u; } xapian_sleep_milliseconds(seconds * 1000u); return 0; } #endif /* _MSC_VER*/ #endif /* __WIN32__ */ #endif /* XAPIAN_INCLUDED_SAFEUNISTD_H */ recoll-1.36.1/xaposix/safesyswait.h0000644000175000017500000000244514410615043014214 00000000000000/** @file safesyswait.h * @brief #include , with portability stuff. */ /* Copyright (C) 2010 Olly Betts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #ifndef XAPIAN_INCLUDED_SAFESYSWAIT_H #define XAPIAN_INCLUDED_SAFESYSWAIT_H #ifndef __WIN32__ # include #else // We don't try to replace waitpid(), etc - they're only useful for us when // we can fork(). But it's handy to be able to use WIFEXITED() and // WEXITSTATUS(). # ifndef WIFEXITED # define WIFEXITED(STATUS) (STATUS != -1) # endif # ifndef WEXITSTATUS # define WEXITSTATUS(STATUS) (STATUS) # endif #endif #endif /* XAPIAN_INCLUDED_SAFESYSWAIT_H */